Skip to content

Commit 3c0638c

Browse files
committed
✨ Add response_handlers kwarg to Net::IMAP.new
This ensures every server response is handled, including the greeting.
1 parent b84be38 commit 3c0638c

File tree

2 files changed

+53
-3
lines changed

2 files changed

+53
-3
lines changed

lib/net/imap.rb

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -235,8 +235,9 @@ module Net
235235
# Use paginated or limited versions of commands whenever possible.
236236
#
237237
# Use #add_response_handler to handle responses after each one is received.
238-
# Use #extract_responses, #clear_responses, or #responses (with a block) to
239-
# prune responses.
238+
# The +response_handlers+ argument to ::new assigns response handlers before
239+
# the receiver thread is started. Use #extract_responses, #clear_responses,
240+
# or #responses (with a block) to prune responses.
240241
#
241242
# == Errors
242243
#
@@ -961,6 +962,12 @@ def idle_response_timeout; config.idle_response_timeout end
961962
#
962963
# See DeprecatedClientOptions.new for deprecated SSL arguments.
963964
#
965+
# [response_handlers]
966+
# A list of response handlers to be added before the receiver thread is
967+
# started. This ensures every server response is handled, including the
968+
# #greeting. Note that the greeting is handled in the current thread, but
969+
# all other responses are handled in the receiver thread.
970+
#
964971
# [config]
965972
# A Net::IMAP::Config object to use as the basis for #config. By default,
966973
# the global Net::IMAP.config is used.
@@ -1032,7 +1039,7 @@ def idle_response_timeout; config.idle_response_timeout end
10321039
# [Net::IMAP::ByeResponseError]
10331040
# Connected to the host successfully, but it immediately said goodbye.
10341041
#
1035-
def initialize(host, port: nil, ssl: nil,
1042+
def initialize(host, port: nil, ssl: nil, response_handlers: nil,
10361043
config: Config.global, **config_options)
10371044
super()
10381045
# Config options
@@ -1057,6 +1064,7 @@ def initialize(host, port: nil, ssl: nil,
10571064
@receiver_thread = nil
10581065
@receiver_thread_exception = nil
10591066
@receiver_thread_terminating = false
1067+
response_handlers&.each do add_response_handler(_1) end
10601068

10611069
# Client Protocol Sender (including state for currently running commands)
10621070
@tag_prefix = "RUBY"
@@ -3255,6 +3263,10 @@ def response_handlers
32553263
# end
32563264
# }
32573265
#
3266+
# Response handlers can also be added when the client is created before the
3267+
# receiver thread is started, by the +response_handlers+ argument to ::new.
3268+
# This ensures every server response is handled, including the #greeting.
3269+
#
32583270
# Related: #remove_response_handler, #response_handlers
32593271
def add_response_handler(handler = nil, &block)
32603272
raise ArgumentError, "two Procs are passed" if handler && block
@@ -3281,6 +3293,7 @@ def remove_response_handler(handler)
32813293
def start_imap_connection
32823294
@greeting = get_server_greeting
32833295
@capabilities = capabilities_from_resp_code @greeting
3296+
@response_handlers.each do |handler| handler.call(@greeting) end
32843297
@receiver_thread = start_receiver_thread
32853298
rescue Exception
32863299
state_logout!

test/net/imap/test_imap_response_handlers.rb

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,41 @@ def teardown
5252
end
5353
end
5454

55+
test "::new with response_handlers kwarg" do
56+
greeting = nil
57+
expunges = []
58+
alerts = []
59+
untagged = 0
60+
handler0 = ->{ greeting ||= _1 }
61+
handler1 = ->{ alerts << _1.data.text if _1 in {data: {code: {name: "ALERT"}}} }
62+
handler2 = ->{ expunges << _1.data if _1 in {name: "EXPUNGE"} }
63+
handler3 = ->{ untagged += 1 if _1.is_a?(Net::IMAP::UntaggedResponse) }
64+
response_handlers = [handler0, handler1, handler2, handler3]
65+
66+
run_fake_server_in_thread do |server|
67+
port = server.port
68+
imap = Net::IMAP.new("localhost", port:, response_handlers:)
69+
assert_equal response_handlers, imap.response_handlers
70+
refute_same response_handlers, imap.response_handlers
71+
72+
# handler0 recieved the greeting and handler3 counted it
73+
assert_equal imap.greeting, greeting
74+
assert_equal 1, untagged
75+
76+
server.on("NOOP") do |resp|
77+
resp.untagged "1 EXPUNGE"
78+
resp.untagged "1 EXPUNGE"
79+
resp.untagged "OK [ALERT] The first alert."
80+
resp.done_ok "[ALERT] Did you see the alert?"
81+
end
82+
83+
imap.noop
84+
assert_equal 4, untagged
85+
assert_equal [1, 1], expunges # from handler2
86+
assert_equal ["The first alert.", "Did you see the alert?"], alerts
87+
ensure
88+
imap&.logout! unless imap&.disconnected?
89+
end
90+
end
91+
5592
end

0 commit comments

Comments
 (0)