Skip to content

Commit 3a1af34

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

File tree

2 files changed

+53
-2
lines changed

2 files changed

+53
-2
lines changed

lib/net/imap.rb

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,9 @@ module Net
160160
# Use paginated or limited versions of commands whenever possible.
161161
#
162162
# Use #add_response_handler to handle responses after each one is received.
163-
# Use #extract_responses, #clear_responses, or #responses (with a block) to
164-
# prune responses.
163+
# Use the +response_handlers+ argument to ::new to assign response handlers
164+
# before the receiver thread is started. Use #extract_responses,
165+
# #clear_responses, or #responses (with a block) to prune responses.
165166
#
166167
# == Errors
167168
#
@@ -1996,6 +1997,11 @@ def idle_done
19961997
# end
19971998
# }
19981999
#
2000+
# Response handlers can also be added when the client is created before the
2001+
# receiver thread is started, by the +response_handlers+ argument to ::new.
2002+
# This ensures every server response is handled, including the #greeting.
2003+
#
2004+
# Related: #remove_response_handler, #response_handlers
19992005
def add_response_handler(handler = nil, &block)
20002006
raise ArgumentError, "two Procs are passed" if handler && block
20012007
@response_handlers.push(block || handler)
@@ -2031,6 +2037,12 @@ def remove_response_handler(handler)
20312037
# OpenSSL::SSL::SSLContext#set_params as parameters.
20322038
# open_timeout:: Seconds to wait until a connection is opened
20332039
# idle_response_timeout:: Seconds to wait until an IDLE response is received
2040+
# response_handlers:: A list of response handlers to be added before the
2041+
# receiver thread is started. This ensures every server
2042+
# response is handled, including the #greeting. Note
2043+
# that the greeting is handled in the current thread,
2044+
# but all other responses are handled in the receiver
2045+
# thread.
20342046
#
20352047
# The most common errors are:
20362048
#
@@ -2073,6 +2085,7 @@ def initialize(host, port_or_options = {},
20732085
@responses = Hash.new([].freeze)
20742086
@tagged_responses = {}
20752087
@response_handlers = []
2088+
options[:response_handlers]&.each do |h| add_response_handler(h) end
20762089
@tagged_response_arrival = new_cond
20772090
@continued_command_tag = nil
20782091
@continuation_request_arrival = new_cond
@@ -2089,6 +2102,7 @@ def initialize(host, port_or_options = {},
20892102
if @greeting.name == "BYE"
20902103
raise ByeResponseError, @greeting
20912104
end
2105+
@response_handlers.each do |handler| handler.call(@greeting) end
20922106

20932107
@client_thread = Thread.current
20942108
@receiver_thread = Thread.start {

test/net/imap/test_imap_response_handlers.rb

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,41 @@ def teardown
5050
end
5151
end
5252

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

0 commit comments

Comments
 (0)