From 6411ac1218e4fc3feb643e745c32dfb522dae272 Mon Sep 17 00:00:00 2001 From: nick evans Date: Mon, 7 Oct 2024 18:23:46 -0400 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Responses=20with=20no=20args=20can?= =?UTF-8?q?=20return=20frozen=20dup=20hash?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds a new `:frozen_dup` option to `config.responses_without_block` which allows it to return a frozen copy of the responses hash, with each response type array also being a frozen copy. This seems like a reasonable API so, now that we have a config option to control it, it will tentatively become the new default in v0.6. --- lib/net/imap.rb | 15 ++++++++++++--- lib/net/imap/config.rb | 20 +++++++++++++++++--- test/net/imap/test_imap_responses.rb | 9 +++++++++ 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/lib/net/imap.rb b/lib/net/imap.rb index 5d6120da..58637998 100644 --- a/lib/net/imap.rb +++ b/lib/net/imap.rb @@ -2497,6 +2497,7 @@ def idle_done private_constant :RESPONSES_DEPRECATION_MSG # :call-seq: + # responses -> hash of {String => Array} (see deprecation note) # responses(type) -> frozen array # responses {|hash| ...} -> block result # responses(type) {|array| ...} -> block result @@ -2533,9 +2534,10 @@ def idle_done # frozen, and it is unsafe to modify elements in the array from multiple # threads. # - # Calling without +type+ or a block is unsafe and deprecated. Future - # releases may raise ArgumentError unless a block is given. See - # Config#responses_without_block. + # With the default v0.5 config, calling #responses without any arguments + # is unsafe and prints a warning. In v0.6, it will return a frozen hash + # with frozen array values. Set Config#responses_without_block to silence + # the warning or opt-in to the frozen dup behavior. # # Previously unhandled responses are automatically cleared before entering a # mailbox with #select or #examine. Long-lived connections can receive many @@ -2565,6 +2567,13 @@ def responses(type = nil) raise ArgumentError, RESPONSES_DEPRECATION_MSG when :warn warn(RESPONSES_DEPRECATION_MSG, uplevel: 1, category: :deprecated) + when :frozen_dup + synchronize { + responses = @responses.transform_values(&:freeze) + responses.default_proc = nil + responses.default = [].freeze + return responses.freeze + } end @responses end diff --git a/lib/net/imap/config.rb b/lib/net/imap/config.rb index e3a897ed..086d2b81 100644 --- a/lib/net/imap/config.rb +++ b/lib/net/imap/config.rb @@ -242,16 +242,30 @@ def self.[](config) # :markup: markdown # # Controls the behavior of Net::IMAP#responses when called without any - # arguments (a type or block). Valid options are `:warn`, + # arguments (`type` or block). Valid options are `:frozen_dup`, `:warn`, # `:silence_deprecation_warning`, or `:raise`. # + # [+:silence_deprecation_warning+] + # Return the responses hash without any deprecation warnings. + # + # [+:warn+] + # Return the responses hash, after printing a deprecation warning. + # + # [+:frozen_dup+] + # Return a frozen copy of the unhandled responses. This is always the + # behavior when calling IMAP#responses with a `type` argument but + # without a block. + # + # [+:raise+] + # Raise an ArgumentError with the deprecation warning. + # # | Starting with version | The default value is | # |-------------------------|--------------------------------| # | v0.4.13 | +:silence_deprecation_warning+ | # | v0.5 | +:warn+ | - # | _eventually_ | +:raise+ | + # | v0.6 _(planned)_ | +:frozen_dup+ | 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 alias responses_without_args= responses_without_block= diff --git a/test/net/imap/test_imap_responses.rb b/test/net/imap/test_imap_responses.rb index 233235bf..9dcbeb52 100644 --- a/test/net/imap/test_imap_responses.rb +++ b/test/net/imap/test_imap_responses.rb @@ -166,6 +166,15 @@ def assert_responses_warn assert_equal [], imap.responses["FAKE"] end assert_empty stderr + # opt-in to future behavior + imap.config.responses_without_block = :frozen_dup + stderr = EnvUtil.verbose_warning do + assert imap.responses.frozen? + assert imap.responses["CAPABILITY"].frozen? + assert_equal(%w[IMAP4REV1 NAMESPACE MOVE IDLE UTF8=ACCEPT], + imap.responses["CAPABILITY"].last) + end + assert_empty stderr end end