mail_room 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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