From 3c8389a5453c056a68e0a955ec437b0f72e0edbc Mon Sep 17 00:00:00 2001 From: nick evans Date: Thu, 6 Nov 2025 09:34:15 -0500 Subject: [PATCH 1/7] =?UTF-8?q?=F0=9F=9A=9A=20Extract=20Config::AttrVersio?= =?UTF-8?q?nDefaults=20module?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will be used to simplify the code in lib/net/imap/config.rb. In this first step, only a couple pieces are extracted: * `AttrVersionDefaults` stores the `Config.version_defaults` hash. The hash's `#default_proc` is defined there, too. * `AttrVersionDefaults.compile` only freezes that hash. --- lib/net/imap/config.rb | 14 +++----- lib/net/imap/config/attr_version_defaults.rb | 36 ++++++++++++++++++++ 2 files changed, 40 insertions(+), 10 deletions(-) create mode 100644 lib/net/imap/config/attr_version_defaults.rb diff --git a/lib/net/imap/config.rb b/lib/net/imap/config.rb index 370ff719..2fab8894 100644 --- a/lib/net/imap/config.rb +++ b/lib/net/imap/config.rb @@ -3,6 +3,7 @@ require_relative "config/attr_accessors" require_relative "config/attr_inheritance" require_relative "config/attr_type_coercion" +require_relative "config/attr_version_defaults" module Net class IMAP @@ -141,15 +142,7 @@ def self.global; @global if defined?(@global) end # Net::IMAP::Config[0.5] == Net::IMAP::Config[0.5r] # => true # Net::IMAP::Config["current"] == Net::IMAP::Config[:current] # => true # Net::IMAP::Config["0.5.6"] == Net::IMAP::Config[0.5r] # => true - def self.version_defaults; @version_defaults end - @version_defaults = Hash.new {|h, k| - # NOTE: String responds to both so the order is significant. - # And ignore non-numeric conversion to zero, because: "wat!?".to_r == 0 - (h.fetch(k.to_r, nil) || h.fetch(k.to_f, nil) if k.is_a?(Numeric)) || - (h.fetch(k.to_sym, nil) if k.respond_to?(:to_sym)) || - (h.fetch(k.to_r, nil) if k.respond_to?(:to_r) && k.to_r != 0r) || - (h.fetch(k.to_f, nil) if k.respond_to?(:to_f) && k.to_f != 0.0) - } + def self.version_defaults; AttrVersionDefaults.version_defaults end # :call-seq: # Net::IMAP::Config[number] -> versioned config @@ -189,6 +182,7 @@ def self.[](config) include AttrAccessors include AttrInheritance include AttrTypeCoercion + extend AttrVersionDefaults # The debug mode (boolean). The default value is +false+. # @@ -542,7 +536,7 @@ def defaults_hash version_defaults[:next] = Config[current + 0.1r] version_defaults[:future] = Config[current + 0.2r] - version_defaults.freeze + AttrVersionDefaults.compile_version_defaults! if ($VERBOSE || $DEBUG) && self[:current].to_h != self[:default].to_h warn "Misconfigured Net::IMAP::Config[:current] => %p,\n" \ diff --git a/lib/net/imap/config/attr_version_defaults.rb b/lib/net/imap/config/attr_version_defaults.rb new file mode 100644 index 00000000..df93cd9f --- /dev/null +++ b/lib/net/imap/config/attr_version_defaults.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require "forwardable" + +module Net + class IMAP + class Config + # >>> + # *NOTE:* This module is an internal implementation detail, with no + # guarantee of backward compatibility. + # + # Adds a +defaults+ parameter to +attr_accessor+, which is used to compile + # Config.version_defaults. + module AttrVersionDefaults + # See Config.version_defaults. + singleton_class.attr_accessor :version_defaults + + @version_defaults = Hash.new {|h, k| + # NOTE: String responds to both so the order is significant. + # And ignore non-numeric conversion to zero, because: "wat!?".to_r == 0 + (h.fetch(k.to_r, nil) || h.fetch(k.to_f, nil) if k.is_a?(Numeric)) || + (h.fetch(k.to_sym, nil) if k.respond_to?(:to_sym)) || + (h.fetch(k.to_r, nil) if k.respond_to?(:to_r) && k.to_r != 0r) || + (h.fetch(k.to_f, nil) if k.respond_to?(:to_f) && k.to_f != 0.0) + } + + # :stopdoc: internal APIs only + + def self.compile_version_defaults! + version_defaults.freeze + end + + end + end + end +end From 81e8fe52fbdf3c186579d6387c8ecb8ed5f1bf55 Mon Sep 17 00:00:00 2001 From: nick evans Date: Thu, 6 Nov 2025 09:45:21 -0500 Subject: [PATCH 2/7] =?UTF-8?q?=F0=9F=9A=9A=20Assign=20config=20aliases=20?= =?UTF-8?q?in=20AttrVersionDefaults?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/net/imap/config.rb | 14 ------------- lib/net/imap/config/attr_version_defaults.rb | 22 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/lib/net/imap/config.rb b/lib/net/imap/config.rb index 2fab8894..acba33d2 100644 --- a/lib/net/imap/config.rb +++ b/lib/net/imap/config.rb @@ -522,20 +522,6 @@ def defaults_hash version_defaults[0.8r] = Config[0.7r].dup.update( ).freeze - # Safe conversions one way only: - # 0.6r.to_f == 0.6 # => true - # 0.6 .to_r == 0.6r # => false - version_defaults.to_a.each do |k, v| - next unless k in Rational - version_defaults[k.to_f] = v - end - - current = VERSION.to_r - version_defaults[:original] = Config[0] - version_defaults[:current] = Config[current] - version_defaults[:next] = Config[current + 0.1r] - version_defaults[:future] = Config[current + 0.2r] - AttrVersionDefaults.compile_version_defaults! if ($VERBOSE || $DEBUG) && self[:current].to_h != self[:default].to_h diff --git a/lib/net/imap/config/attr_version_defaults.rb b/lib/net/imap/config/attr_version_defaults.rb index df93cd9f..0e18b1c4 100644 --- a/lib/net/imap/config/attr_version_defaults.rb +++ b/lib/net/imap/config/attr_version_defaults.rb @@ -12,6 +12,15 @@ class Config # Adds a +defaults+ parameter to +attr_accessor+, which is used to compile # Config.version_defaults. module AttrVersionDefaults + # The x.y part of Net::IMAP::VERSION, as a Rational number. + CURRENT_VERSION = VERSION.to_r + + # The config version used for Config[:next]. + NEXT_VERSION = CURRENT_VERSION + 0.1r + + # The config version used for Config[:future]. + FUTURE_VERSION = CURRENT_VERSION + 0.2r + # See Config.version_defaults. singleton_class.attr_accessor :version_defaults @@ -27,6 +36,19 @@ module AttrVersionDefaults # :stopdoc: internal APIs only def self.compile_version_defaults! + # Safe conversions one way only: + # 0.6r.to_f == 0.6 # => true + # 0.6 .to_r == 0.6r # => false + version_defaults.to_a.each do |k, v| + next unless k in Rational + version_defaults[k.to_f] = v + end + + version_defaults[:original] = Config[0.0r] + version_defaults[:current] = Config[CURRENT_VERSION] + version_defaults[:next] = Config[NEXT_VERSION] + version_defaults[:future] = Config[FUTURE_VERSION] + version_defaults.freeze end From 19c093d97e103e8987d6fba0ac03ab9bc50ad905 Mon Sep 17 00:00:00 2001 From: nick evans Date: Thu, 6 Nov 2025 09:52:12 -0500 Subject: [PATCH 3/7] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Compile=20version=5Fde?= =?UTF-8?q?faults=20hashes=20into=20configs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This refactoring decouples the assignment of versioned default values and the creation of `version_defaults` config objects. At first, `version_defaults` stores hashes for each version's updated defaults. At the end, the hashes are transformed into config objects by `Config::AttrVersionDefaults.compile!`. --- lib/net/imap/config.rb | 30 +++++++++----------- lib/net/imap/config/attr_version_defaults.rb | 12 ++++++++ 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/lib/net/imap/config.rb b/lib/net/imap/config.rb index acba33d2..4726b15c 100644 --- a/lib/net/imap/config.rb +++ b/lib/net/imap/config.rb @@ -482,45 +482,41 @@ def defaults_hash @global = default.new - version_defaults[:default] = Config[default.send(:defaults_hash)] + version_defaults[:default] = default.send(:defaults_hash) - version_defaults[0r] = Config[:default].dup.update( + version_defaults[0r] = { sasl_ir: false, responses_without_block: :silence_deprecation_warning, enforce_logindisabled: false, max_response_size: nil, parser_use_deprecated_uidplus_data: true, parser_max_deprecated_uidplus_data_size: 10_000, - ).freeze - version_defaults[0.0r] = Config[0r] - version_defaults[0.1r] = Config[0r] - version_defaults[0.2r] = Config[0r] - version_defaults[0.3r] = Config[0r] + } - version_defaults[0.4r] = Config[0.3r].dup.update( + version_defaults[0.4r] = { sasl_ir: true, parser_max_deprecated_uidplus_data_size: 1000, - ).freeze + } - version_defaults[0.5r] = Config[0.4r].dup.update( + version_defaults[0.5r] = { enforce_logindisabled: true, max_response_size: 512 << 20, # 512 MiB responses_without_block: :warn, parser_use_deprecated_uidplus_data: :up_to_max_size, parser_max_deprecated_uidplus_data_size: 100, - ).freeze + } - version_defaults[0.6r] = Config[0.5r].dup.update( + version_defaults[0.6r] = { responses_without_block: :frozen_dup, parser_use_deprecated_uidplus_data: false, parser_max_deprecated_uidplus_data_size: 0, - ).freeze + } - version_defaults[0.7r] = Config[0.6r].dup.update( - ).freeze + version_defaults[0.7r] = { + } - version_defaults[0.8r] = Config[0.7r].dup.update( - ).freeze + version_defaults[0.8r] = { + } AttrVersionDefaults.compile_version_defaults! diff --git a/lib/net/imap/config/attr_version_defaults.rb b/lib/net/imap/config/attr_version_defaults.rb index 0e18b1c4..733ca72a 100644 --- a/lib/net/imap/config/attr_version_defaults.rb +++ b/lib/net/imap/config/attr_version_defaults.rb @@ -21,6 +21,8 @@ module AttrVersionDefaults # The config version used for Config[:future]. FUTURE_VERSION = CURRENT_VERSION + 0.2r + VERSIONS = ((0.0r..0.8r) % 0.1r).to_a.freeze + # See Config.version_defaults. singleton_class.attr_accessor :version_defaults @@ -36,6 +38,16 @@ module AttrVersionDefaults # :stopdoc: internal APIs only def self.compile_version_defaults! + version_defaults[:default] = Config[version_defaults[:default]] + version_defaults[0.0r] = Config[:default].dup + .update(**version_defaults[0.0r]).freeze + + VERSIONS.each_cons(2) do |prior, version| + updates = version_defaults[version] + version_defaults[version] = version_defaults[prior] + .then { updates ? _1.dup.update(**updates).freeze : _1 } + end + # Safe conversions one way only: # 0.6r.to_f == 0.6 # => true # 0.6 .to_r == 0.6r # => false From abede07b726ca63e81628691655a413ceba65d0b Mon Sep 17 00:00:00 2001 From: nick evans Date: Thu, 6 Nov 2025 10:18:10 -0500 Subject: [PATCH 4/7] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Allow=20Config[version?= =?UTF-8?q?]=20to=20work=20up=20to=20v1.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This also causes Config[0.7r] through Config[1.0r] to be aliases (rather than clones) of each other. --- lib/net/imap/config.rb | 6 ------ lib/net/imap/config/attr_version_defaults.rb | 4 ++-- test/net/imap/test_config.rb | 5 ++--- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/lib/net/imap/config.rb b/lib/net/imap/config.rb index 4726b15c..3e605f69 100644 --- a/lib/net/imap/config.rb +++ b/lib/net/imap/config.rb @@ -512,12 +512,6 @@ def defaults_hash parser_max_deprecated_uidplus_data_size: 0, } - version_defaults[0.7r] = { - } - - version_defaults[0.8r] = { - } - AttrVersionDefaults.compile_version_defaults! if ($VERBOSE || $DEBUG) && self[:current].to_h != self[:default].to_h diff --git a/lib/net/imap/config/attr_version_defaults.rb b/lib/net/imap/config/attr_version_defaults.rb index 733ca72a..d1c81da0 100644 --- a/lib/net/imap/config/attr_version_defaults.rb +++ b/lib/net/imap/config/attr_version_defaults.rb @@ -19,9 +19,9 @@ module AttrVersionDefaults NEXT_VERSION = CURRENT_VERSION + 0.1r # The config version used for Config[:future]. - FUTURE_VERSION = CURRENT_VERSION + 0.2r + FUTURE_VERSION = 1.0r - VERSIONS = ((0.0r..0.8r) % 0.1r).to_a.freeze + VERSIONS = ((0.0r..FUTURE_VERSION) % 0.1r).to_a.freeze # See Config.version_defaults. singleton_class.attr_accessor :version_defaults diff --git a/test/net/imap/test_config.rb b/test/net/imap/test_config.rb index 05618c40..ecded27b 100644 --- a/test/net/imap/test_config.rb +++ b/test/net/imap/test_config.rb @@ -7,7 +7,7 @@ class ConfigTest < Net::IMAP::TestCase Config = Net::IMAP::Config THIS_VERSION = Net::IMAP::VERSION.to_f NEXT_VERSION = THIS_VERSION + 0.1 - FUTURE_VERSION = THIS_VERSION + 0.2 + FUTURE_VERSION = 1.0 setup do Config.global.reset @@ -184,8 +184,7 @@ class ConfigTest < Net::IMAP::TestCase assert_raise(RangeError) do Config[0.01] end assert_raise(RangeError) do Config[0.11] end assert_raise(RangeError) do Config[0.111] end - assert_raise(RangeError) do Config[0.9] end - assert_raise(RangeError) do Config[1] end + assert_raise(RangeError) do Config[1.1] end end test ".[] key errors" do From 6b063121de9a5acdbf49d5eed0046b091e6ed189 Mon Sep 17 00:00:00 2001 From: nick evans Date: Thu, 6 Nov 2025 09:57:02 -0500 Subject: [PATCH 5/7] =?UTF-8?q?=F0=9F=9A=9A=20Compile=20Config[:defaults]?= =?UTF-8?q?=20in=20AttrVersionDefaults?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This avoids usage of the private `Config#defaults_hash` method, relying instead on the public `#load_defaults` API. --- lib/net/imap/config.rb | 2 -- lib/net/imap/config/attr_version_defaults.rb | 5 ++++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/net/imap/config.rb b/lib/net/imap/config.rb index 3e605f69..02c4aeb7 100644 --- a/lib/net/imap/config.rb +++ b/lib/net/imap/config.rb @@ -482,8 +482,6 @@ def defaults_hash @global = default.new - version_defaults[:default] = default.send(:defaults_hash) - version_defaults[0r] = { sasl_ir: false, responses_without_block: :silence_deprecation_warning, diff --git a/lib/net/imap/config/attr_version_defaults.rb b/lib/net/imap/config/attr_version_defaults.rb index d1c81da0..9f1f8ef9 100644 --- a/lib/net/imap/config/attr_version_defaults.rb +++ b/lib/net/imap/config/attr_version_defaults.rb @@ -38,7 +38,10 @@ module AttrVersionDefaults # :stopdoc: internal APIs only def self.compile_version_defaults! - version_defaults[:default] = Config[version_defaults[:default]] + # Temporarily assign Config.default, enabling #load_defaults(:default) + version_defaults[:default] = Config.default + # Use #load_defaults so some attributes are inherited from global. + version_defaults[:default] = Config.new.load_defaults(:default).freeze version_defaults[0.0r] = Config[:default].dup .update(**version_defaults[0.0r]).freeze From 7fd5b56b016eaa373162ec0143a23fa2bb3ce814 Mon Sep 17 00:00:00 2001 From: nick evans Date: Thu, 6 Nov 2025 10:08:36 -0500 Subject: [PATCH 6/7] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Define=20config=20defa?= =?UTF-8?q?ults=20with=20the=20attr=20definitions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit At the cost of one big conflict now, this should significantly reduce future merge conflicts in the config file. It's also easier to match documentation with default values when they're right next to each other. This also assigns all of the original (`Config[0]`) defaults explicitly, and not only where they differ from Config.default. --- lib/net/imap/config.rb | 67 +++++++++----------- lib/net/imap/config/attr_version_defaults.rb | 14 +++- 2 files changed, 43 insertions(+), 38 deletions(-) diff --git a/lib/net/imap/config.rb b/lib/net/imap/config.rb index 02c4aeb7..02fd1f8b 100644 --- a/lib/net/imap/config.rb +++ b/lib/net/imap/config.rb @@ -212,7 +212,7 @@ def self.[](config) # See Net::IMAP.new and Net::IMAP#starttls. # # The default value is +30+ seconds. - attr_accessor :open_timeout, type: Integer + attr_accessor :open_timeout, type: Integer, default: 30 # Seconds to wait until an IDLE response is received, after # the client asks to leave the IDLE state. @@ -220,7 +220,7 @@ def self.[](config) # See Net::IMAP#idle and Net::IMAP#idle_done. # # The default value is +5+ seconds. - attr_accessor :idle_response_timeout, type: Integer + attr_accessor :idle_response_timeout, type: Integer, default: 5 # Whether to use the +SASL-IR+ extension when the server and \SASL # mechanism both support it. Can be overridden by the +sasl_ir+ keyword @@ -236,7 +236,10 @@ def self.[](config) # # [+true+ (default since +v0.4+)] # Use +SASL-IR+ when it is supported by the server and the mechanism. - attr_accessor :sasl_ir, type: :boolean + attr_accessor :sasl_ir, type: :boolean, defaults: { + 0.0r => false, + 0.4r => true, + } # Controls the behavior of Net::IMAP#login when the +LOGINDISABLED+ # capability is present. When enforced, Net::IMAP will raise a @@ -260,7 +263,10 @@ def self.[](config) # attr_accessor :enforce_logindisabled, type: Enum[ false, :when_capabilities_cached, true - ] + ], defaults: { + 0.0r => false, + 0.5r => true, + } # The maximum allowed server response size. When +nil+, there is no limit # on response size. @@ -294,7 +300,10 @@ def self.[](config) # # * original: +nil+ (no limit) # * +0.5+: 512 MiB - attr_accessor :max_response_size, type: Integer? + attr_accessor :max_response_size, type: Integer?, defaults: { + 0.0r => nil, + 0.5r => 512 << 20, # 512 MiB + } # Controls the behavior of Net::IMAP#responses when called without any # arguments (+type+ or +block+). @@ -324,7 +333,11 @@ def self.[](config) # Note: #responses_without_args is an alias for #responses_without_block. attr_accessor :responses_without_block, type: Enum[ :silence_deprecation_warning, :warn, :frozen_dup, :raise, - ] + ], defaults: { + 0.0r => :silence_deprecation_warning, + 0.5r => :warn, + 0.6r => :frozen_dup, + } alias responses_without_args responses_without_block # :nodoc: alias responses_without_args= responses_without_block= # :nodoc: @@ -369,7 +382,11 @@ def self.[](config) # ResponseParser _only_ uses AppendUIDData and CopyUIDData. attr_accessor :parser_use_deprecated_uidplus_data, type: Enum[ true, :up_to_max_size, false - ] + ], defaults: { + 0.0r => true, + 0.5r => :up_to_max_size, + 0.6r => false, + } # The maximum +uid-set+ size that ResponseParser will parse into # deprecated UIDPlusData. This limit only applies when @@ -393,7 +410,13 @@ def self.[](config) # * +0.5+: 100 # * +0.6+: 0 # - attr_accessor :parser_max_deprecated_uidplus_data_size, type: Integer + attr_accessor :parser_max_deprecated_uidplus_data_size, type: Integer, + defaults: { + 0.0r => 10_000, + 0.4r => 1_000, + 0.5r => 100, + 0.6r => 0, + } # Creates a new config object and initialize its attribute with +attrs+. # @@ -482,34 +505,6 @@ def defaults_hash @global = default.new - version_defaults[0r] = { - sasl_ir: false, - responses_without_block: :silence_deprecation_warning, - enforce_logindisabled: false, - max_response_size: nil, - parser_use_deprecated_uidplus_data: true, - parser_max_deprecated_uidplus_data_size: 10_000, - } - - version_defaults[0.4r] = { - sasl_ir: true, - parser_max_deprecated_uidplus_data_size: 1000, - } - - version_defaults[0.5r] = { - enforce_logindisabled: true, - max_response_size: 512 << 20, # 512 MiB - responses_without_block: :warn, - parser_use_deprecated_uidplus_data: :up_to_max_size, - parser_max_deprecated_uidplus_data_size: 100, - } - - version_defaults[0.6r] = { - responses_without_block: :frozen_dup, - parser_use_deprecated_uidplus_data: false, - parser_max_deprecated_uidplus_data_size: 0, - } - AttrVersionDefaults.compile_version_defaults! if ($VERBOSE || $DEBUG) && self[:current].to_h != self[:default].to_h diff --git a/lib/net/imap/config/attr_version_defaults.rb b/lib/net/imap/config/attr_version_defaults.rb index 9f1f8ef9..207d8949 100644 --- a/lib/net/imap/config/attr_version_defaults.rb +++ b/lib/net/imap/config/attr_version_defaults.rb @@ -37,13 +37,23 @@ module AttrVersionDefaults # :stopdoc: internal APIs only + def attr_accessor(name, defaults: nil, default: (unset = true), **kw) + unless unset + defaults ||= { 0.0r => default } + end + defaults&.each_pair do |version, default| + AttrVersionDefaults.version_defaults[version] ||= {} + AttrVersionDefaults.version_defaults[version][name] = default + end + super(name, **kw) + end + def self.compile_version_defaults! # Temporarily assign Config.default, enabling #load_defaults(:default) version_defaults[:default] = Config.default # Use #load_defaults so some attributes are inherited from global. version_defaults[:default] = Config.new.load_defaults(:default).freeze - version_defaults[0.0r] = Config[:default].dup - .update(**version_defaults[0.0r]).freeze + version_defaults[0.0r] = Config[version_defaults.fetch(0.0r)] VERSIONS.each_cons(2) do |prior, version| updates = version_defaults[version] From 3cbf35a741e85de363c8026009ae9ac73c8e4cbb Mon Sep 17 00:00:00 2001 From: nick evans Date: Thu, 6 Nov 2025 12:06:26 -0500 Subject: [PATCH 7/7] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Compile=20Config.defau?= =?UTF-8?q?lt=20using=20AttrVersionDefaults?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similar to the versioned defaults, this could be a source of conflicts when merging, rebasing, or cherry-picking feature and backport branches. Now that all of the attributes specify their defaults, the current version's default config can be compiled from that. The sanity-check at the end of `config.rb` has been removed. It was there to protect against bad merges. Now that `Config.default` is also compiled from the version defaults added to each attr_accessor, the tests in `test/net/imap/test_config.rb` are good enough. --- lib/net/imap/config.rb | 24 +++----------------- lib/net/imap/config/attr_version_defaults.rb | 12 +++++++++- test/net/imap/test_config.rb | 1 + 3 files changed, 15 insertions(+), 22 deletions(-) diff --git a/lib/net/imap/config.rb b/lib/net/imap/config.rb index 02fd1f8b..ea7602ee 100644 --- a/lib/net/imap/config.rb +++ b/lib/net/imap/config.rb @@ -194,7 +194,7 @@ def self.[](config) # # *NOTE:* Versioned default configs inherit #debug from Config.global, and # #load_defaults will not override #debug. - attr_accessor :debug, type: :boolean + attr_accessor :debug, type: :boolean, default: false # method: debug? # :call-seq: debug? -> boolean @@ -491,28 +491,10 @@ def defaults_hash to_h.reject {|k,v| DEFAULT_TO_INHERIT.include?(k) } end - @default = new( - debug: false, - open_timeout: 30, - idle_response_timeout: 5, - sasl_ir: true, - enforce_logindisabled: true, - max_response_size: 512 << 20, # 512 MiB - responses_without_block: :frozen_dup, - parser_use_deprecated_uidplus_data: false, - parser_max_deprecated_uidplus_data_size: 0, - ).freeze - - @global = default.new - + @default = AttrVersionDefaults.compile_default! + @global = default.new AttrVersionDefaults.compile_version_defaults! - if ($VERBOSE || $DEBUG) && self[:current].to_h != self[:default].to_h - warn "Misconfigured Net::IMAP::Config[:current] => %p,\n" \ - " not equal to Net::IMAP::Config[:default] => %p" % [ - self[:current].to_h, self[:default].to_h - ] - end end end end diff --git a/lib/net/imap/config/attr_version_defaults.rb b/lib/net/imap/config/attr_version_defaults.rb index 207d8949..6457bddb 100644 --- a/lib/net/imap/config/attr_version_defaults.rb +++ b/lib/net/imap/config/attr_version_defaults.rb @@ -39,7 +39,8 @@ module AttrVersionDefaults def attr_accessor(name, defaults: nil, default: (unset = true), **kw) unless unset - defaults ||= { 0.0r => default } + version = DEFAULT_TO_INHERIT.include?(name) ? nil : 0.0r + defaults = { version => default } end defaults&.each_pair do |version, default| AttrVersionDefaults.version_defaults[version] ||= {} @@ -48,6 +49,15 @@ def attr_accessor(name, defaults: nil, default: (unset = true), **kw) super(name, **kw) end + def self.compile_default! + raise "Config.default already compiled" if Config.default + default = VERSIONS.select { _1 <= CURRENT_VERSION } + .filter_map { version_defaults[_1] } + .prepend(version_defaults.delete(nil)) + .inject(&:merge) + Config.new(**default).freeze + end + def self.compile_version_defaults! # Temporarily assign Config.default, enabling #load_defaults(:default) version_defaults[:default] = Config.default diff --git a/test/net/imap/test_config.rb b/test/net/imap/test_config.rb index ecded27b..9290cd28 100644 --- a/test/net/imap/test_config.rb +++ b/test/net/imap/test_config.rb @@ -72,6 +72,7 @@ class ConfigTest < Net::IMAP::TestCase test ".default" do default = Config.default assert default.equal?(Config.default) + assert_nil default.parent assert default.is_a?(Config) assert default.frozen? refute default.debug?