Skip to content

Commit

Permalink
✨ Responses with type can return frozen dup array
Browse files Browse the repository at this point in the history
Because responses(type) is relatively new and has always raised an
exception, we can update it to return a frozen dup array without
breaking backward compatibility.

Additionally, `config.responses_without_args` was added as an alias for
`config.responses_without_block`.  The original name is a little
misleading now, but it's kept for backward compatibility.
  • Loading branch information
nevans committed Oct 13, 2024
1 parent 572f764 commit ad951c9
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 12 deletions.
20 changes: 12 additions & 8 deletions lib/net/imap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2503,17 +2503,21 @@ def idle_done
private_constant :RESPONSES_DEPRECATION_MSG

# :call-seq:
# responses(type) -> frozen array
# responses {|hash| ...} -> block result
# responses(type) {|array| ...} -> block result
#
# Yields unhandled server responses and returns the result of the block.
# Unhandled responses are stored in a hash, with arrays of
# <em>non-+nil+</em> UntaggedResponse#data keyed by UntaggedResponse#name
# and ResponseCode#data keyed by ResponseCode#name.
# Yields or returns unhandled server responses. Unhandled responses are
# stored in a hash, with arrays of UntaggedResponse#data keyed by
# UntaggedResponse#name and <em>non-+nil+</em> untagged ResponseCode#data
# keyed by ResponseCode#name.
#
# When a block is given, yields unhandled responses and returns the block's
# result. Without a block, returns the unhandled responses.
#
# [With +type+]
# Yield only the array of responses for that +type+.
# When no block is given, an +ArgumentError+ is raised.
# Yield or return only the array of responses for that +type+.
# When no block is given, the returned array is a frozen copy.
# [Without +type+]
# Yield or return the entire responses hash.
#
Expand All @@ -2534,7 +2538,7 @@ def idle_done
# For example:
#
# imap.select("inbox")
# p imap.responses("EXISTS", &:last)
# p imap.responses("EXISTS").last
# #=> 2
# p imap.responses("UIDNEXT", &:last)
# #=> 123456
Expand Down Expand Up @@ -2592,7 +2596,7 @@ def responses(type = nil)
if block_given?
synchronize { yield(type ? @responses[type.to_s.upcase] : @responses) }
elsif type
raise ArgumentError, "Pass a block or use #clear_responses"
synchronize { @responses[type.to_s.upcase].dup.freeze }
else
case config.responses_without_block
when :raise
Expand Down
11 changes: 10 additions & 1 deletion lib/net/imap/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -240,10 +240,19 @@ def self.[](config)
# [+:raise+ <em>(planned future default)</em>]
# Raise an ArgumentError with the deprecation warning.
#
# Note: #responses_without_args is an alias for #responses_without_block.
attr_accessor :responses_without_block, type: [
:silence_deprecation_warning, :warn, :raise,
:silence_deprecation_warning, :warn, :frozen_dup, :raise,
]

alias responses_without_args responses_without_block # :nodoc:
alias responses_without_args= responses_without_block= # :nodoc:

##
# :attr_accessor: responses_without_args
#
# Alias for responses_without_block

# Creates a new config object and initialize its attribute with +attrs+.
#
# If +parent+ is not given, the global config is used by default.
Expand Down
29 changes: 26 additions & 3 deletions test/net/imap/test_imap_responses.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,35 @@ def for_each_config_option(imap)
end
end

# with with a type and no block: always raise an exception
# with with a type and no block: always returns a frozen duplicate
test "#responses(type, &nil)" do
with_fake_server do |server, imap|
for_each_config_option(imap) do
assert_raise(ArgumentError) do imap.responses("CAPABILITY") end
stderr = EnvUtil.verbose_warning do
# Config options make no difference to responses(type)
for_each_config_option(imap) do
# responses available before SELECT/EXAMINE
assert imap.responses("CAPABILITY").frozen?
assert_equal(%w[IMAP4REV1 NAMESPACE MOVE IDLE UTF8=ACCEPT],
imap.responses("CAPABILITY").last)
end
# responses are cleared after SELECT/EXAMINE
imap.select "INBOX"
for_each_config_option(imap) do
assert imap.responses("CAPABILITY").frozen?
assert imap.responses("EXISTS").frozen?
assert imap.responses("UIDVALIDITIY").frozen?
assert_equal [], imap.responses("CAPABILITY")
assert_equal [172], imap.responses("EXISTS")
assert_equal [3857529045], imap.responses("UIDVALIDITY")
assert_equal 1, imap.responses("RECENT").last
assert imap.responses("UIDNEXT").frozen?
assert_equal [4392], imap.responses("UIDNEXT")
assert imap.responses("FLAGS").frozen?
assert_equal(%i[Answered Flagged Deleted Seen Draft],
imap.responses("FLAGS").last)
end
end
assert_empty stderr # never warn when type is given
end
end

Expand Down

0 comments on commit ad951c9

Please # to comment.