mail_room 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9feac02c627c54d87da87bc73e1aec3587f43ff3
4
- data.tar.gz: 55c2cd20c9e7b7036094c4d46c2efc93567f3e77
3
+ metadata.gz: 86c8cfc70ff25a195a9e59c4bc175eb68b107874
4
+ data.tar.gz: 3bc4c8778f7ff9e14233986a922033243482902e
5
5
  SHA512:
6
- metadata.gz: 29476b7c9e48f064087b59cc869ad6fab364044ca5a8fbf0f8fdb2dadb6a0222cdb07c473f4d82f0b75ccf895c063b56599bb1fd1acfa906309844f85bca3fb8
7
- data.tar.gz: 030b4c91c26c545d2103d2d6f7147606bfafa7998c3b31251356b8da4241614260210863c9ef8ce04423ec91f95a381cf23fcdc648fc397368d2d33e5e6039f1
6
+ metadata.gz: 1f84614429adeb0e94e6b22bc053861ad10f939e551d370cbfa8e091306737628616a999ce1c42c7a879d4b67f8bea3d58e6620657e0d01676e1c8fe673d9c5b
7
+ data.tar.gz: ce3e8a096c013bda86d20e86f31fae1753fb2fff7da7fb74b5db72e59bb76325086bce87505ceeb7b7927b9f49efdae28c88c0d8af9732a3f12f90eab328d73a
@@ -1,3 +1,9 @@
1
+ ## mail_room 0.8.0 ##
2
+
3
+ * Rework the mailbox watcher and handler into a new Connection class to abstract away IMAP handling details
4
+
5
+ *Tony Pitale <@tpitale>*
6
+
1
7
  ## mail_room 0.7.0 ##
2
8
 
3
9
  * Backports idle timeout from ruby 2.3.0
@@ -10,6 +10,6 @@ require "mail_room/backports/imap"
10
10
  require "mail_room/configuration"
11
11
  require "mail_room/mailbox"
12
12
  require "mail_room/mailbox_watcher"
13
- require "mail_room/mailbox_handler"
13
+ require "mail_room/connection"
14
14
  require "mail_room/coordinator"
15
15
  require "mail_room/cli"
@@ -4,7 +4,7 @@ module MailRoom
4
4
  # Wraps configuration for a set of individual mailboxes with global config
5
5
  # @author Tony Pitale
6
6
  class Configuration
7
- attr_accessor :mailboxes, :daemonize, :log_path, :pid_path, :quiet
7
+ attr_accessor :mailboxes, :log_path, :quiet
8
8
 
9
9
  # Initialize a new configuration of mailboxes
10
10
  def initialize(options={})
@@ -0,0 +1,175 @@
1
+ module MailRoom
2
+ class Connection
3
+ def initialize(mailbox)
4
+ @mailbox = mailbox
5
+
6
+ # log in and set the mailbox
7
+ reset
8
+ setup
9
+ end
10
+
11
+ def on_new_message(&block)
12
+ @new_message_handler = block
13
+ end
14
+
15
+ # is the connection logged in?
16
+ # @return [Boolean]
17
+ def logged_in?
18
+ @logged_in
19
+ end
20
+
21
+ # is the connection blocked idling?
22
+ # @return [Boolean]
23
+ def idling?
24
+ @idling
25
+ end
26
+
27
+ # is the imap connection closed?
28
+ # @return [Boolean]
29
+ def disconnected?
30
+ @imap.disconnected?
31
+ end
32
+
33
+ # is the connection ready to idle?
34
+ # @return [Boolean]
35
+ def ready_to_idle?
36
+ logged_in? && !idling?
37
+ end
38
+
39
+ def quit
40
+ stop_idling
41
+ reset
42
+ end
43
+
44
+ def wait
45
+ begin
46
+ # in case we missed any between idles
47
+ process_mailbox
48
+
49
+ idle
50
+
51
+ process_mailbox
52
+ rescue Net::IMAP::Error, IOError
53
+ reset
54
+ setup
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ def reset
61
+ @imap = nil
62
+ @logged_in = false
63
+ @idling = false
64
+ end
65
+
66
+ def setup
67
+ start_tls
68
+ log_in
69
+ set_mailbox
70
+ end
71
+
72
+ # build a net/imap connection to google imap
73
+ def imap
74
+ @imap ||= MailRoom::IMAP.new(@mailbox.host, :port => @mailbox.port, :ssl => @mailbox.ssl_options)
75
+ end
76
+
77
+ # start a TLS session
78
+ def start_tls
79
+ imap.starttls if @mailbox.start_tls
80
+ end
81
+
82
+ # send the imap login command to google
83
+ def log_in
84
+ imap.login(@mailbox.email, @mailbox.password)
85
+ @logged_in = true
86
+ end
87
+
88
+ # select the mailbox name we want to use
89
+ def set_mailbox
90
+ imap.select(@mailbox.name) if logged_in?
91
+ end
92
+
93
+ # is the response for a new message?
94
+ # @param response [Net::IMAP::TaggedResponse] the imap response from idle
95
+ # @return [Boolean]
96
+ def message_exists?(response)
97
+ response.respond_to?(:name) && response.name == 'EXISTS'
98
+ end
99
+
100
+ # @private
101
+ def idle_handler
102
+ lambda {|response| imap.idle_done if message_exists?(response)}
103
+ end
104
+
105
+ # maintain an imap idle connection
106
+ def idle
107
+ return unless ready_to_idle?
108
+
109
+ @idling = true
110
+
111
+ imap.idle(@mailbox.idle_timeout, &idle_handler)
112
+ ensure
113
+ @idling = false
114
+ end
115
+
116
+ # trigger the idle to finish and wait for the thread to finish
117
+ def stop_idling
118
+ return unless idling?
119
+
120
+ imap.idle_done
121
+
122
+ # idling_thread.join
123
+ # self.idling_thread = nil
124
+ end
125
+
126
+ def process_mailbox
127
+ return unless @new_message_handler
128
+
129
+ msgs = new_messages
130
+
131
+ msgs.
132
+ map(&@new_message_handler). # deliver each new message, collect success
133
+ zip(msgs). # include messages with success
134
+ select(&:first).map(&:last). # filter failed deliveries, collect message
135
+ each {|message| scrub(message)} # scrub delivered messages
136
+ end
137
+
138
+ def scrub(message)
139
+ if @mailbox.delete_after_delivery
140
+ imap.store(message.seqno, "+FLAGS", [Net::IMAP::DELETED])
141
+ end
142
+ end
143
+
144
+ # @private
145
+ # fetch all messages for the new message ids
146
+ def new_messages
147
+ # Both of these calls may results in
148
+ # imap raising an EOFError, we handle
149
+ # this exception in the watcher
150
+ messages_for_ids(new_message_ids)
151
+ end
152
+
153
+ # TODO: label messages?
154
+ # @imap.store(id, "+X-GM-LABELS", [label])
155
+
156
+ # @private
157
+ # search for all new (unseen) message ids
158
+ # @return [Array<Integer>] message ids
159
+ def new_message_ids
160
+ # uid_search still leaves messages UNSEEN
161
+ imap.uid_search(@mailbox.search_command).select { |uid| @mailbox.deliver?(uid) }
162
+ end
163
+
164
+ # @private
165
+ # fetch the email for all given ids in RFC822 format
166
+ # @param ids [Array<Integer>] list of message ids
167
+ # @return [Array<Net::IMAP::FetchData>] the net/imap messages for the given ids
168
+ def messages_for_ids(uids)
169
+ return [] if uids.empty?
170
+
171
+ # uid_fetch marks as SEEN, will not be re-fetched for UNSEEN
172
+ imap.uid_fetch(uids, "RFC822")
173
+ end
174
+ end
175
+ end
@@ -33,6 +33,8 @@ module MailRoom
33
33
  private
34
34
  # @private
35
35
  def sleep_while_running
36
+ # do we need to sweep for dead watchers?
37
+ # or do we let the mailbox rebuild connections
36
38
  while(running?) do; sleep 1; end
37
39
  end
38
40
  end
@@ -4,25 +4,15 @@ module MailRoom
4
4
  # Watch a Mailbox
5
5
  # @author Tony Pitale
6
6
  class MailboxWatcher
7
- attr_accessor :idling_thread
7
+ attr_accessor :watching_thread
8
8
 
9
9
  # Watch a new mailbox
10
10
  # @param mailbox [MailRoom::Mailbox] the mailbox to watch
11
11
  def initialize(mailbox)
12
12
  @mailbox = mailbox
13
13
 
14
- reset
15
14
  @running = false
16
- end
17
-
18
- # build a net/imap connection to google imap
19
- def imap
20
- @imap ||= MailRoom::IMAP.new(@mailbox.host, :port => @mailbox.port, :ssl => @mailbox.ssl_options)
21
- end
22
-
23
- # build a handler to process mailbox messages
24
- def handler
25
- @handler ||= MailboxHandler.new(@mailbox, imap)
15
+ @connection = nil
26
16
  end
27
17
 
28
18
  # are we running?
@@ -31,133 +21,38 @@ module MailRoom
31
21
  @running
32
22
  end
33
23
 
34
- # is the connection logged in?
35
- # @return [Boolean]
36
- def logged_in?
37
- @logged_in
38
- end
39
-
40
- # is the connection blocked idling?
41
- # @return [Boolean]
42
- def idling?
43
- @idling
44
- end
45
-
46
- # is the imap connection closed?
47
- # @return [Boolean]
48
- def disconnected?
49
- @imap.disconnected?
50
- end
51
-
52
- # is the connection ready to idle?
53
- # @return [Boolean]
54
- def ready_to_idle?
55
- logged_in? && !idling?
56
- end
57
-
58
- # is the response for a new message?
59
- # @param response [Net::IMAP::TaggedResponse] the imap response from idle
60
- # @return [Boolean]
61
- def message_exists?(response)
62
- response.respond_to?(:name) && response.name == 'EXISTS'
63
- end
64
-
65
- # log in and set the mailbox
66
- def setup
67
- reset
68
- start_tls
69
- log_in
70
- set_mailbox
71
- end
72
-
73
- # clear disconnected imap
74
- # reset imap state
75
- def reset
76
- @imap = nil
77
- @logged_in = false
78
- @idling = false
79
- end
80
-
81
- # start a TLS session
82
- def start_tls
83
- imap.starttls if @mailbox.start_tls
84
- end
85
-
86
- # send the imap login command to google
87
- def log_in
88
- imap.login(@mailbox.email, @mailbox.password)
89
- @logged_in = true
90
- end
91
-
92
- # select the mailbox name we want to use
93
- def set_mailbox
94
- imap.select(@mailbox.name) if logged_in?
95
- end
96
-
97
- # maintain an imap idle connection
98
- def idle
99
- return unless ready_to_idle?
100
-
101
- @idling = true
102
-
103
- imap.idle(@mailbox.idle_timeout, &idle_handler)
104
- ensure
105
- @idling = false
106
- end
107
-
108
- # trigger the idle to finish and wait for the thread to finish
109
- def stop_idling
110
- return unless idling?
111
-
112
- imap.idle_done
113
-
114
- idling_thread.join
115
- self.idling_thread = nil
116
- end
117
-
118
24
  # run the mailbox watcher
119
25
  def run
120
- setup
121
-
122
26
  @running = true
123
27
 
124
- # prefetch messages before first idle
125
- process_mailbox
28
+ connection.on_new_message do |message|
29
+ @mailbox.deliver(message)
30
+ end
126
31
 
127
- self.idling_thread = Thread.start do
32
+ self.watching_thread = Thread.start do
128
33
  while(running?) do
129
- begin
130
- # block for idle_timeout until we stop idling
131
- idle
132
-
133
- # when new messages are ready
134
- process_mailbox
135
- rescue Net::IMAP::Error, IOError => e
136
- # we've been disconnected, so re-setup
137
- setup
138
- end
34
+ connection.wait
139
35
  end
140
36
  end
141
37
 
142
- idling_thread.abort_on_exception = true
38
+ watching_thread.abort_on_exception = true
143
39
  end
144
40
 
145
- # stop running
41
+ # stop running, cleanup connection
146
42
  def quit
147
43
  @running = false
148
- stop_idling
149
- # disconnect
150
- end
151
44
 
152
- # trigger the handler to process this mailbox for new messages
153
- def process_mailbox
154
- handler.process
45
+ if @connection
46
+ @connection.quit
47
+ @connection = nil
48
+ end
49
+
50
+ watching_thread.join
155
51
  end
156
52
 
157
53
  private
158
- # @private
159
- def idle_handler
160
- lambda {|response| imap.idle_done if message_exists?(response)}
54
+ def connection
55
+ @connection ||= Connection.new(@mailbox)
161
56
  end
162
57
  end
163
58
  end
@@ -1,4 +1,4 @@
1
1
  module MailRoom
2
2
  # Current version of MailRoom gem
3
- VERSION = "0.7.0"
3
+ VERSION = "0.8.0"
4
4
  end
@@ -14,13 +14,13 @@ describe MailRoom::CLI do
14
14
  end
15
15
 
16
16
  it 'parses arguments into configuration' do
17
- MailRoom::CLI.new(args).configuration.should eq(configuration)
18
- MailRoom::Configuration.should have_received(:new).with({:config_path => 'a path'})
17
+ expect(MailRoom::CLI.new(args).configuration).to eq(configuration)
18
+ expect(MailRoom::Configuration).to have_received(:new).with({:config_path => 'a path'})
19
19
  end
20
20
 
21
21
  it 'creates a new coordinator with configuration' do
22
- MailRoom::CLI.new(args).coordinator.should eq(coordinator)
23
- MailRoom::Coordinator.should have_received(:new).with(configuration.mailboxes)
22
+ expect(MailRoom::CLI.new(args).coordinator).to eq(coordinator)
23
+ expect(MailRoom::Coordinator).to have_received(:new).with(configuration.mailboxes)
24
24
  end
25
25
  end
26
26
 
@@ -35,7 +35,7 @@ describe MailRoom::CLI do
35
35
  it 'starts running the coordinator' do
36
36
  cli.start
37
37
 
38
- coordinator.should have_received(:run)
38
+ expect(coordinator).to have_received(:run)
39
39
  end
40
40
  end
41
41
  end
@@ -9,7 +9,7 @@ describe MailRoom::Configuration do
9
9
 
10
10
  configuration = MailRoom::Configuration.new(:config_path => config_path)
11
11
 
12
- configuration.mailboxes.should eq(['mailbox1', 'mailbox2'])
12
+ expect(configuration.mailboxes).to eq(['mailbox1', 'mailbox2'])
13
13
  end
14
14
 
15
15
  it 'sets mailboxes to an empty set when config_path is missing' do
@@ -17,9 +17,9 @@ describe MailRoom::Configuration do
17
17
 
18
18
  configuration = MailRoom::Configuration.new
19
19
 
20
- configuration.mailboxes.should eq([])
20
+ expect(configuration.mailboxes).to eq([])
21
21
 
22
- MailRoom::Mailbox.should have_received(:new).never
22
+ expect(MailRoom::Mailbox).to have_received(:new).never
23
23
  end
24
24
  end
25
25
  end
@@ -0,0 +1,63 @@
1
+ require 'spec_helper'
2
+
3
+ describe MailRoom::Connection do
4
+ let(:imap) {stub}
5
+ let(:mailbox) {MailRoom::Mailbox.new(delete_after_delivery: true)}
6
+
7
+ before :each do
8
+ MailRoom::IMAP.stubs(:new).returns(imap)
9
+ end
10
+
11
+ context "with imap set up" do
12
+ let(:connection) {MailRoom::Connection.new(mailbox)}
13
+
14
+ before :each do
15
+ imap.stubs(:starttls)
16
+ imap.stubs(:login)
17
+ imap.stubs(:select)
18
+ end
19
+
20
+ it "is logged in" do
21
+ expect(connection.logged_in?).to eq(true)
22
+ end
23
+
24
+ it "is not idling" do
25
+ expect(connection.idling?).to eq(false)
26
+ end
27
+
28
+ it "is not disconnected" do
29
+ imap.stubs(:disconnected?).returns(false)
30
+
31
+ expect(connection.disconnected?).to eq(false)
32
+ end
33
+
34
+ it "is ready to idle" do
35
+ expect(connection.ready_to_idle?).to eq(true)
36
+ end
37
+
38
+ it "waits for a message to process" do
39
+ new_message = 'a message'
40
+ new_message.stubs(:seqno).returns(8)
41
+
42
+ connection.on_new_message do |message|
43
+ expect(message).to eq(new_message)
44
+ true
45
+ end
46
+
47
+ mailbox.stubs(:deliver?).returns(true)
48
+
49
+ imap.stubs(:idle)
50
+ imap.stubs(:uid_search).returns([]).then.returns([1])
51
+ imap.stubs(:uid_fetch).returns([new_message])
52
+ imap.stubs(:store)
53
+
54
+ connection.wait
55
+
56
+ expect(imap).to have_received(:idle)
57
+ expect(imap).to have_received(:uid_search).with(mailbox.search_command).twice
58
+ expect(imap).to have_received(:uid_fetch).with([1], "RFC822")
59
+ expect(mailbox).to have_received(:deliver?).with(1)
60
+ expect(imap).to have_received(:store).with(8, "+FLAGS", [Net::IMAP::DELETED])
61
+ end
62
+ end
63
+ end
@@ -7,15 +7,15 @@ describe MailRoom::Coordinator do
7
7
 
8
8
  coordinator = MailRoom::Coordinator.new(['mailbox1', 'mailbox2'])
9
9
 
10
- coordinator.watchers.should eq(['watcher1', 'watcher2'])
10
+ expect(coordinator.watchers).to eq(['watcher1', 'watcher2'])
11
11
 
12
- MailRoom::MailboxWatcher.should have_received(:new).with('mailbox1')
13
- MailRoom::MailboxWatcher.should have_received(:new).with('mailbox2')
12
+ expect(MailRoom::MailboxWatcher).to have_received(:new).with('mailbox1')
13
+ expect(MailRoom::MailboxWatcher).to have_received(:new).with('mailbox2')
14
14
  end
15
15
 
16
16
  it 'makes no watchers when mailboxes is empty' do
17
17
  coordinator = MailRoom::Coordinator.new([])
18
- coordinator.watchers.should eq([])
18
+ expect(coordinator.watchers).to eq([])
19
19
  end
20
20
  end
21
21
 
@@ -28,8 +28,8 @@ describe MailRoom::Coordinator do
28
28
  coordinator = MailRoom::Coordinator.new(['mailbox1'])
29
29
  coordinator.stubs(:sleep_while_running)
30
30
  coordinator.run
31
- watcher.should have_received(:run)
32
- watcher.should have_received(:quit)
31
+ expect(watcher).to have_received(:run)
32
+ expect(watcher).to have_received(:quit)
33
33
  end
34
34
 
35
35
  it 'should go to sleep after running watchers' do
@@ -37,15 +37,15 @@ describe MailRoom::Coordinator do
37
37
  coordinator.stubs(:running=)
38
38
  coordinator.stubs(:running?).returns(false)
39
39
  coordinator.run
40
- coordinator.should have_received(:running=).with(true)
41
- coordinator.should have_received(:running?)
40
+ expect(coordinator).to have_received(:running=).with(true)
41
+ expect(coordinator).to have_received(:running?)
42
42
  end
43
43
 
44
44
  it 'should set attribute running to true' do
45
45
  coordinator = MailRoom::Coordinator.new([])
46
46
  coordinator.stubs(:sleep_while_running)
47
47
  coordinator.run
48
- coordinator.running.should eq(true)
48
+ expect(coordinator.running).to eq(true)
49
49
  end
50
50
  end
51
51
 
@@ -55,7 +55,7 @@ describe MailRoom::Coordinator do
55
55
  MailRoom::MailboxWatcher.stubs(:new).returns(watcher)
56
56
  coordinator = MailRoom::Coordinator.new(['mailbox1'])
57
57
  coordinator.quit
58
- watcher.should have_received(:quit)
58
+ expect(watcher).to have_received(:quit)
59
59
  end
60
60
  end
61
61
  end
@@ -15,15 +15,15 @@ describe MailRoom::Delivery::LetterOpener do
15
15
  end
16
16
 
17
17
  it 'creates a new LetterOpener::DeliveryMethod' do
18
- ::LetterOpener::DeliveryMethod.should have_received(:new).with(:location => '/tmp/somewhere')
18
+ expect(::LetterOpener::DeliveryMethod).to have_received(:new).with(:location => '/tmp/somewhere')
19
19
  end
20
20
 
21
21
  it 'parses the message string with Mail' do
22
- ::Mail.should have_received(:read_from_string).with('a message')
22
+ expect(::Mail).to have_received(:read_from_string).with('a message')
23
23
  end
24
24
 
25
25
  it 'delivers the mail message' do
26
- delivery_method.should have_received(:deliver!).with(mail)
26
+ expect(delivery_method).to have_received(:deliver!).with(mail)
27
27
  end
28
28
  end
29
29
  end
@@ -11,7 +11,7 @@ describe MailRoom::Delivery::Logger do
11
11
 
12
12
  MailRoom::Delivery::Logger.new(mailbox)
13
13
 
14
- ::Logger.should have_received(:new).with(STDOUT)
14
+ expect(::Logger).to have_received(:new).with(STDOUT)
15
15
  end
16
16
  end
17
17
 
@@ -25,8 +25,8 @@ describe MailRoom::Delivery::Logger do
25
25
 
26
26
  MailRoom::Delivery::Logger.new(mailbox)
27
27
 
28
- File.should have_received(:open).with('/var/log/mail-room.log', 'a')
29
- ::Logger.should have_received(:new).with(file)
28
+ expect(File).to have_received(:open).with('/var/log/mail-room.log', 'a')
29
+ expect(::Logger).to have_received(:new).with(file)
30
30
  end
31
31
  end
32
32
  end
@@ -40,7 +40,7 @@ describe MailRoom::Delivery::Logger do
40
40
 
41
41
  MailRoom::Delivery::Logger.new(mailbox).deliver('a message')
42
42
 
43
- logger.should have_received(:info).with('a message')
43
+ expect(logger).to have_received(:info).with('a message')
44
44
  end
45
45
  end
46
46
  end
@@ -21,11 +21,11 @@ describe MailRoom::Delivery::Postback do
21
21
 
22
22
  MailRoom::Delivery::Postback.new(mailbox).deliver('a message')
23
23
 
24
- connection.should have_received(:token_auth).with('abcdefg')
25
- connection.should have_received(:post)
24
+ expect(connection).to have_received(:token_auth).with('abcdefg')
25
+ expect(connection).to have_received(:post)
26
26
 
27
- request.should have_received(:url).with('http://localhost/inbox')
28
- request.should have_received(:body=).with('a message')
27
+ expect(request).to have_received(:url).with('http://localhost/inbox')
28
+ expect(request).to have_received(:body=).with('a message')
29
29
  end
30
30
  end
31
31
  end
@@ -12,7 +12,7 @@ describe MailRoom::Mailbox do
12
12
 
13
13
  mailbox.deliver?(uid)
14
14
 
15
- noop.should have_received(:deliver?).with(uid)
15
+ expect(noop).to have_received(:deliver?).with(uid)
16
16
  end
17
17
  end
18
18
 
@@ -26,7 +26,7 @@ describe MailRoom::Mailbox do
26
26
 
27
27
  mailbox.deliver?(uid)
28
28
 
29
- redis.should have_received(:deliver?).with(uid)
29
+ expect(redis).to have_received(:deliver?).with(uid)
30
30
  end
31
31
  end
32
32
 
@@ -38,7 +38,7 @@ describe MailRoom::Mailbox do
38
38
 
39
39
  mailbox.deliver(stub(:attr => {'RFC822' => 'a message'}))
40
40
 
41
- noop.should have_received(:deliver).with('a message')
41
+ expect(noop).to have_received(:deliver).with('a message')
42
42
  end
43
43
  end
44
44
 
@@ -50,7 +50,7 @@ describe MailRoom::Mailbox do
50
50
 
51
51
  mailbox.deliver(stub(:attr => {'RFC822' => 'a message'}))
52
52
 
53
- logger.should have_received(:deliver).with('a message')
53
+ expect(logger).to have_received(:deliver).with('a message')
54
54
  end
55
55
  end
56
56
 
@@ -62,7 +62,7 @@ describe MailRoom::Mailbox do
62
62
 
63
63
  mailbox.deliver(stub(:attr => {'RFC822' => 'a message'}))
64
64
 
65
- postback.should have_received(:deliver).with('a message')
65
+ expect(postback).to have_received(:deliver).with('a message')
66
66
  end
67
67
  end
68
68
 
@@ -74,7 +74,7 @@ describe MailRoom::Mailbox do
74
74
 
75
75
  mailbox.deliver(stub(:attr => {'RFC822' => 'a message'}))
76
76
 
77
- letter_opener.should have_received(:deliver).with('a message')
77
+ expect(letter_opener).to have_received(:deliver).with('a message')
78
78
  end
79
79
  end
80
80
 
@@ -86,7 +86,7 @@ describe MailRoom::Mailbox do
86
86
 
87
87
  mailbox.deliver(stub(:attr => {'FLAGS' => [:Seen, :Recent]}))
88
88
 
89
- noop.should have_received(:deliver).never
89
+ expect(noop).to have_received(:deliver).never
90
90
  end
91
91
  end
92
92
 
@@ -6,245 +6,59 @@ describe MailRoom::MailboxWatcher do
6
6
  describe '#running?' do
7
7
  it 'is false by default' do
8
8
  watcher = MailRoom::MailboxWatcher.new(mailbox)
9
- watcher.running?.should eq(false)
9
+ expect(watcher.running?).to eq(false)
10
10
  end
11
11
  end
12
12
 
13
- describe '#logged_in?' do
14
- it 'is false by default' do
15
- watcher = MailRoom::MailboxWatcher.new(mailbox)
16
- watcher.logged_in?.should eq(false)
17
- end
18
- end
19
-
20
- describe '#idling?' do
21
- it 'is false by default' do
22
- watcher = MailRoom::MailboxWatcher.new(mailbox)
23
- watcher.idling?.should eq(false)
24
- end
25
- end
26
-
27
- describe '#imap' do
28
- it 'builds a new Net::IMAP object' do
29
- MailRoom::IMAP.stubs(:new).returns('imap')
30
-
31
- MailRoom::MailboxWatcher.new(mailbox).imap.should eq('imap')
32
-
33
- MailRoom::IMAP.should have_received(:new).with('imap.gmail.com', :port => 993, :ssl => true)
34
- end
35
- end
36
-
37
- describe '#setup' do
13
+ describe '#run' do
38
14
  let(:imap) {stub(:login => true, :select => true)}
39
-
40
- let(:mailbox) {
41
- MailRoom::Mailbox.new(:email => 'user1@gmail.com', :password => 'password', :name => 'inbox')
42
- }
43
-
44
- let(:watcher) {
45
- MailRoom::MailboxWatcher.new(mailbox)
46
- }
47
-
48
- it 'logs in and sets the mailbox to watch' do
49
- watcher.stubs(:imap).returns(imap)
50
-
51
- watcher.setup
52
-
53
- imap.should have_received(:login).with('user1@gmail.com', 'password')
54
- watcher.logged_in?.should eq(true)
55
- imap.should have_received(:select).with('inbox')
56
- end
57
-
58
- context 'with start_tls configured as true' do
59
- before(:each) do
60
- mailbox.start_tls = true
61
- imap.stubs(:starttls)
62
- watcher.stubs(:imap).returns(imap)
63
- end
64
-
65
- it 'sets up tls session on imap setup' do
66
- watcher.setup
67
-
68
- imap.should have_received(:starttls)
69
- end
70
- end
71
- end
72
-
73
- describe '#idle' do
74
- let(:imap) {stub}
75
15
  let(:watcher) {MailRoom::MailboxWatcher.new(mailbox)}
76
16
 
77
17
  before :each do
78
- watcher.stubs(:imap).returns(imap)
79
- end
80
-
81
- it 'returns if not logged in' do
82
- watcher.stubs(:logged_in?).returns(false)
83
-
84
- watcher.idle
85
-
86
- imap.should have_received(:idle).never
87
- end
88
-
89
- context "when logged in" do
90
- before :each do
91
- imap.stubs(:idle_done)
92
-
93
- watcher.stubs(:logged_in?).returns(true)
94
- end
95
-
96
- it 'handles any response with a name of EXISTS and stops idling' do
97
- response = stub(:name => 'EXISTS')
98
- imap.stubs(:idle).yields(response)
99
-
100
- watcher.idle
101
-
102
- imap.should have_received(:idle)
103
- imap.should have_received(:idle_done)
104
- end
105
-
106
- it 'does not finish idling when response is not EXISTS' do
107
- response = stub(:name => 'DESTROY')
108
- imap.stubs(:idle).yields(response)
109
-
110
- watcher.idle
111
-
112
- imap.should have_received(:idle)
113
- imap.should have_received(:idle_done).never
114
- end
18
+ Net::IMAP.stubs(:new).returns(imap) # prevent connection
115
19
  end
116
- end
117
20
 
118
- describe 'process_mailbox' do
119
- let(:imap) {stub}
120
- let(:mailbox) {MailRoom::Mailbox.new}
121
- let(:watcher) {MailRoom::MailboxWatcher.new(mailbox)}
21
+ it 'loops over wait while running' do
22
+ connection = MailRoom::Connection.new(mailbox)
23
+ connection.stubs(:on_new_message)
24
+ connection.stubs(:wait)
122
25
 
123
- it 'builds a new mailbox handler if none exists' do
124
- MailRoom::MailboxHandler.stubs(:new).returns(stub(:process))
125
- watcher.stubs(:imap).returns(imap)
26
+ MailRoom::Connection.stubs(:new).returns(connection)
126
27
 
127
- watcher.process_mailbox
128
-
129
- MailRoom::MailboxHandler.should have_received(:new).with(mailbox, imap)
130
- end
28
+ watcher.stubs(:running?).returns(true).then.returns(false)
131
29
 
132
- it 'processes with the handler' do
133
- handler = stub(:process)
134
- watcher.stubs(:handler).returns(handler)
135
-
136
- watcher.process_mailbox
137
-
138
- handler.should have_received(:process)
139
- end
140
- end
141
-
142
- describe '#stop_idling' do
143
- let(:imap) {stub}
144
- let(:idling_thread) {stub(:abort_on_exception=)}
145
- let(:watcher) {MailRoom::MailboxWatcher.new(nil)}
146
-
147
- before :each do
148
- watcher.stubs(:imap).returns(imap)
149
- watcher.stubs(:idling_thread).returns(idling_thread)
150
- end
151
-
152
- it "returns unless imap is idling" do
153
- imap.stubs(:idle_done)
154
- idling_thread.stubs(:join)
155
- watcher.stubs(:idling?).returns(false)
156
-
157
- watcher.stop_idling
158
-
159
- imap.should have_received(:idle_done).never
160
- idling_thread.should have_received(:join).never
161
- end
30
+ watcher.run
31
+ watcher.watching_thread.join # wait for finishing run
162
32
 
163
- context "when idling" do
164
- before :each do
165
- imap.stubs(:idle_done)
166
- idling_thread.stubs(:join)
167
- watcher.stubs(:idling?).returns(true)
168
- end
169
-
170
- it 'stops the idle' do
171
- watcher.stop_idling
172
- imap.should have_received(:idle_done)
173
- end
174
-
175
- it 'waits on the idling_thread to finish' do
176
- watcher.stop_idling
177
- idling_thread.should have_received(:join)
178
- end
33
+ expect(watcher).to have_received(:running?).times(2)
34
+ expect(connection).to have_received(:wait).once
35
+ expect(connection).to have_received(:on_new_message).once
179
36
  end
180
37
  end
181
38
 
182
- describe '#run' do
39
+ describe '#quit' do
40
+ let(:imap) {stub(:login => true, :select => true)}
183
41
  let(:watcher) {MailRoom::MailboxWatcher.new(mailbox)}
184
42
 
185
43
  before :each do
186
- Net::IMAP.stubs(:new).returns(stub)
187
- Thread.stubs(:start).yields.returns(stub(:abort_on_exception=))
188
- watcher.stubs(:setup)
189
- watcher.handler.stubs(:process)
190
- end
191
-
192
- it 'sets up' do
193
- watcher.stubs(:running?).returns(false)
194
-
195
- watcher.run
196
-
197
- watcher.should have_received(:setup)
198
- end
199
-
200
- it 'starts a thread for idling' do
201
- watcher.stubs(:running?).returns(false)
202
-
203
- watcher.run
204
-
205
- Thread.should have_received(:start)
206
- end
207
-
208
- it 'loops while running' do
209
- watcher.stubs(:running?).returns(true, false)
210
-
211
- watcher.stubs(:idle)
212
-
213
- watcher.run
214
-
215
- watcher.should have_received(:running?).times(2)
216
- end
217
-
218
- it 'idles' do
219
- watcher.stubs(:running?).returns(true, false)
220
-
221
- watcher.stubs(:idle)
222
-
223
- watcher.run
224
-
225
- watcher.should have_received(:idle).once
44
+ Net::IMAP.stubs(:new).returns(imap) # prevent connection
226
45
  end
227
46
 
228
- it 'processes messages' do
229
- watcher.stubs(:running?).returns(true, false)
47
+ it 'closes and waits for the connection' do
48
+ connection = MailRoom::Connection.new(mailbox)
49
+ connection.stubs(:wait)
50
+ connection.stubs(:quit)
230
51
 
231
- watcher.stubs(:idle)
52
+ MailRoom::Connection.stubs(:new).returns(connection)
232
53
 
233
54
  watcher.run
234
55
 
235
- watcher.handler.should have_received(:process).times(2)
236
- end
237
- end
238
-
239
- describe '#quit' do
240
- let(:watcher) {MailRoom::MailboxWatcher.new(mailbox)}
241
-
242
- it 'stops idling' do
243
- watcher.stubs(:stop_idling)
56
+ expect(watcher.running?).to eq(true)
244
57
 
245
58
  watcher.quit
246
59
 
247
- watcher.should have_received(:stop_idling)
60
+ expect(connection).to have_received(:quit)
61
+ expect(watcher.running?).to eq(false)
248
62
  end
249
63
  end
250
64
  end
@@ -12,7 +12,6 @@ require File.expand_path('../../lib/mail_room', __FILE__)
12
12
 
13
13
  RSpec.configure do |config|
14
14
  config.mock_with :mocha
15
- config.treat_symbols_as_metadata_keys_with_true_values = true
16
15
  config.run_all_when_everything_filtered = true
17
16
  config.filter_run :focus
18
17
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mail_room
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tony Pitale
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-04-30 00:00:00.000000000 Z
11
+ date: 2016-06-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -203,6 +203,7 @@ files:
203
203
  - lib/mail_room/backports/imap.rb
204
204
  - lib/mail_room/cli.rb
205
205
  - lib/mail_room/configuration.rb
206
+ - lib/mail_room/connection.rb
206
207
  - lib/mail_room/coordinator.rb
207
208
  - lib/mail_room/delivery.rb
208
209
  - lib/mail_room/delivery/letter_opener.rb
@@ -212,7 +213,6 @@ files:
212
213
  - lib/mail_room/delivery/que.rb
213
214
  - lib/mail_room/delivery/sidekiq.rb
214
215
  - lib/mail_room/mailbox.rb
215
- - lib/mail_room/mailbox_handler.rb
216
216
  - lib/mail_room/mailbox_watcher.rb
217
217
  - lib/mail_room/version.rb
218
218
  - mail_room.gemspec
@@ -220,12 +220,12 @@ files:
220
220
  - spec/lib/arbitration/redis_spec.rb
221
221
  - spec/lib/cli_spec.rb
222
222
  - spec/lib/configuration_spec.rb
223
+ - spec/lib/connection_spec.rb
223
224
  - spec/lib/coordinator_spec.rb
224
225
  - spec/lib/delivery/letter_opener_spec.rb
225
226
  - spec/lib/delivery/logger_spec.rb
226
227
  - spec/lib/delivery/postback_spec.rb
227
228
  - spec/lib/delivery/que_spec.rb
228
- - spec/lib/mailbox_handler_spec.rb
229
229
  - spec/lib/mailbox_spec.rb
230
230
  - spec/lib/mailbox_watcher_spec.rb
231
231
  - spec/spec_helper.rb
@@ -258,12 +258,12 @@ test_files:
258
258
  - spec/lib/arbitration/redis_spec.rb
259
259
  - spec/lib/cli_spec.rb
260
260
  - spec/lib/configuration_spec.rb
261
+ - spec/lib/connection_spec.rb
261
262
  - spec/lib/coordinator_spec.rb
262
263
  - spec/lib/delivery/letter_opener_spec.rb
263
264
  - spec/lib/delivery/logger_spec.rb
264
265
  - spec/lib/delivery/postback_spec.rb
265
266
  - spec/lib/delivery/que_spec.rb
266
- - spec/lib/mailbox_handler_spec.rb
267
267
  - spec/lib/mailbox_spec.rb
268
268
  - spec/lib/mailbox_watcher_spec.rb
269
269
  - spec/spec_helper.rb
@@ -1,59 +0,0 @@
1
- module MailRoom
2
- # Fetches new email messages for delivery
3
- # @author Tony Pitale
4
- class MailboxHandler
5
- # build a handler for this mailbox and our imap connection
6
- # @param mailbox [MailRoom::Mailbox] the mailbox configuration
7
- # @param imap [Net::IMAP::Connection] the open connection to gmail
8
- def initialize(mailbox, imap)
9
- @mailbox = mailbox
10
- @imap = imap
11
- end
12
-
13
- # deliver each of the new messages
14
- def process
15
- # return if idling? || !running?
16
-
17
- new_messages.each do |message|
18
- # loop over delivery methods and deliver each
19
- delivered = @mailbox.deliver(message)
20
-
21
- if delivered && @mailbox.delete_after_delivery
22
- @imap.store(message.seqno, "+FLAGS", [Net::IMAP::DELETED])
23
- end
24
- end
25
-
26
- @imap.expunge if @mailbox.delete_after_delivery
27
- end
28
-
29
- private
30
- # @private
31
- # fetch all messages for the new message ids
32
- def new_messages
33
- # Both of these calls may results in
34
- # imap raising an EOFError, we handle
35
- # this exception in the watcher
36
- messages_for_ids(new_message_ids)
37
- end
38
-
39
- # TODO: label messages?
40
- # @imap.store(id, "+X-GM-LABELS", [label])
41
-
42
- # @private
43
- # search for all new (unseen) message ids
44
- # @return [Array<Integer>] message ids
45
- def new_message_ids
46
- @imap.uid_search(@mailbox.search_command).select { |uid| @mailbox.deliver?(uid) }
47
- end
48
-
49
- # @private
50
- # fetch the email for all given ids in RFC822 format
51
- # @param ids [Array<Integer>] list of message ids
52
- # @return [Array<Net::IMAP::FetchData>] the net/imap messages for the given ids
53
- def messages_for_ids(uids)
54
- return [] if uids.empty?
55
-
56
- @imap.uid_fetch(uids, "RFC822")
57
- end
58
- end
59
- end
@@ -1,57 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe MailRoom::MailboxHandler do
4
- describe 'process mailbox' do
5
- let(:imap) {stub}
6
- let(:mailbox) {MailRoom::Mailbox.new}
7
-
8
- it 'fetches and delivers all new messages from ids' do
9
- imap.stubs(:uid_search).returns([1,2])
10
- message1 = stub(:attr => {'RFC822' => 'message1'})
11
- message2 = stub(:attr => {'RFC822' => 'message2'})
12
- imap.stubs(:uid_fetch).returns([message1, message2])
13
- mailbox.stubs(:deliver)
14
-
15
- handler = MailRoom::MailboxHandler.new(mailbox, imap)
16
- handler.process
17
-
18
- imap.should have_received(:uid_search).with('UNSEEN')
19
- imap.should have_received(:uid_fetch).with([1,2], 'RFC822')
20
- mailbox.should have_received(:deliver).with(message1)
21
- mailbox.should have_received(:deliver).with(message2)
22
- end
23
-
24
- it "fetches and delivers all new messages from ids that haven't been processed yet" do
25
- imap.stubs(:uid_search).returns([1,2])
26
- message1 = stub(:attr => {'RFC822' => 'message1'})
27
- message2 = stub(:attr => {'RFC822' => 'message2'})
28
- imap.stubs(:uid_fetch).returns([message2])
29
- mailbox.stubs(:deliver)
30
- mailbox.stubs(:deliver?).with(1).returns(false)
31
- mailbox.stubs(:deliver?).with(2).returns(true)
32
-
33
- handler = MailRoom::MailboxHandler.new(mailbox, imap)
34
- handler.process
35
-
36
- imap.should have_received(:uid_search).with('UNSEEN')
37
- imap.should have_received(:uid_fetch).with([2], 'RFC822')
38
-
39
- mailbox.should have_received(:deliver).with(message1).never
40
- mailbox.should have_received(:deliver).with(message2)
41
- end
42
-
43
- it 'returns no messages if there are no ids' do
44
- imap.stubs(:uid_search).returns([])
45
- imap.stubs(:uid_fetch)
46
- mailbox.search_command = 'NEW'
47
- mailbox.stubs(:deliver)
48
-
49
- handler = MailRoom::MailboxHandler.new(mailbox, imap)
50
- handler.process
51
-
52
- imap.should have_received(:uid_search).with('NEW')
53
- imap.should have_received(:uid_fetch).never
54
- mailbox.should have_received(:deliver).never
55
- end
56
- end
57
- end