main
1# frozen_string_literal: true
2
3module Minbox
4 class Inbox
5 include Enumerable
6
7 def self.instance(root_dir:)
8 @instances ||= {}
9 @instances[root_dir] ||= new(root_dir: root_dir)
10 end
11
12 def initialize(root_dir:)
13 @semaphore = Mutex.new
14 start_listening(root_dir)
15 empty!
16 end
17
18 def emails(count: 0)
19 wait_until { |x| x.count >= count } if count.positive?
20 with_lock(&:values)
21 end
22
23 def wait_until(seconds: 10, wait: 0.1)
24 iterations = (seconds / wait).to_i
25 iterations.times do
26 result = yield(self)
27 return result if result
28
29 sleep wait
30 end
31 nil
32 end
33
34 def wait_until!(seconds: 10, wait: 0.1, &block)
35 raise "timeout: expired" unless wait_until(seconds: seconds, wait: wait, &block)
36 end
37
38 def open(subject:)
39 wait_until do
40 emails.find do |email|
41 subject.is_a?(String) ? email.subject == subject : email.subject.match?(subject)
42 end
43 end
44 end
45
46 def empty!
47 with_lock do
48 @emails = {}
49 end
50 end
51
52 def each
53 @emails.each do |_id, email|
54 yield email
55 end
56 end
57
58 private
59
60 def changed(_modified, added, removed)
61 with_lock do |emails|
62 added.each do |file|
63 mail = Mail.read(file)
64 Minbox.logger.debug("Received: #{mail.subject}")
65 emails[File.basename(file)] = mail
66 end
67 removed.each do |file|
68 emails.delete(File.basename(file))
69 end
70 end
71 end
72
73 def listener_for(dir)
74 ::Listen.to(File.expand_path(dir), only: /\.eml$/, &method(:changed))
75 end
76
77 def start_listening(root_dir)
78 listener_for(root_dir).start
79 end
80
81 def with_lock
82 @semaphore.synchronize do
83 yield @emails if block_given?
84 end
85 end
86 end
87end