Skip to content

Commit 61b2e4d

Browse files
authored
🔀 Merge pull request #544 from ruby/config/defaults
♻️ Refactor Config.versioned_defaults to reduce merge conflcts
2 parents 079167e + 3cbf35a commit 61b2e4d

File tree

3 files changed

+134
-96
lines changed

3 files changed

+134
-96
lines changed

lib/net/imap/config.rb

Lines changed: 38 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
require_relative "config/attr_accessors"
44
require_relative "config/attr_inheritance"
55
require_relative "config/attr_type_coercion"
6+
require_relative "config/attr_version_defaults"
67

78
module Net
89
class IMAP
@@ -141,15 +142,7 @@ def self.global; @global if defined?(@global) end
141142
# Net::IMAP::Config[0.5] == Net::IMAP::Config[0.5r] # => true
142143
# Net::IMAP::Config["current"] == Net::IMAP::Config[:current] # => true
143144
# Net::IMAP::Config["0.5.6"] == Net::IMAP::Config[0.5r] # => true
144-
def self.version_defaults; @version_defaults end
145-
@version_defaults = Hash.new {|h, k|
146-
# NOTE: String responds to both so the order is significant.
147-
# And ignore non-numeric conversion to zero, because: "wat!?".to_r == 0
148-
(h.fetch(k.to_r, nil) || h.fetch(k.to_f, nil) if k.is_a?(Numeric)) ||
149-
(h.fetch(k.to_sym, nil) if k.respond_to?(:to_sym)) ||
150-
(h.fetch(k.to_r, nil) if k.respond_to?(:to_r) && k.to_r != 0r) ||
151-
(h.fetch(k.to_f, nil) if k.respond_to?(:to_f) && k.to_f != 0.0)
152-
}
145+
def self.version_defaults; AttrVersionDefaults.version_defaults end
153146

154147
# :call-seq:
155148
# Net::IMAP::Config[number] -> versioned config
@@ -189,6 +182,7 @@ def self.[](config)
189182
include AttrAccessors
190183
include AttrInheritance
191184
include AttrTypeCoercion
185+
extend AttrVersionDefaults
192186

193187
# The debug mode (boolean). The default value is +false+.
194188
#
@@ -200,7 +194,7 @@ def self.[](config)
200194
#
201195
# *NOTE:* Versioned default configs inherit #debug from Config.global, and
202196
# #load_defaults will not override #debug.
203-
attr_accessor :debug, type: :boolean
197+
attr_accessor :debug, type: :boolean, default: false
204198

205199
# method: debug?
206200
# :call-seq: debug? -> boolean
@@ -218,15 +212,15 @@ def self.[](config)
218212
# See Net::IMAP.new and Net::IMAP#starttls.
219213
#
220214
# The default value is +30+ seconds.
221-
attr_accessor :open_timeout, type: Integer
215+
attr_accessor :open_timeout, type: Integer, default: 30
222216

223217
# Seconds to wait until an IDLE response is received, after
224218
# the client asks to leave the IDLE state.
225219
#
226220
# See Net::IMAP#idle and Net::IMAP#idle_done.
227221
#
228222
# The default value is +5+ seconds.
229-
attr_accessor :idle_response_timeout, type: Integer
223+
attr_accessor :idle_response_timeout, type: Integer, default: 5
230224

231225
# Whether to use the +SASL-IR+ extension when the server and \SASL
232226
# mechanism both support it. Can be overridden by the +sasl_ir+ keyword
@@ -242,7 +236,10 @@ def self.[](config)
242236
#
243237
# [+true+ <em>(default since +v0.4+)</em>]
244238
# Use +SASL-IR+ when it is supported by the server and the mechanism.
245-
attr_accessor :sasl_ir, type: :boolean
239+
attr_accessor :sasl_ir, type: :boolean, defaults: {
240+
0.0r => false,
241+
0.4r => true,
242+
}
246243

247244
# Controls the behavior of Net::IMAP#login when the +LOGINDISABLED+
248245
# capability is present. When enforced, Net::IMAP will raise a
@@ -266,7 +263,10 @@ def self.[](config)
266263
#
267264
attr_accessor :enforce_logindisabled, type: Enum[
268265
false, :when_capabilities_cached, true
269-
]
266+
], defaults: {
267+
0.0r => false,
268+
0.5r => true,
269+
}
270270

271271
# The maximum allowed server response size. When +nil+, there is no limit
272272
# on response size.
@@ -300,7 +300,10 @@ def self.[](config)
300300
#
301301
# * original: +nil+ <em>(no limit)</em>
302302
# * +0.5+: 512 MiB
303-
attr_accessor :max_response_size, type: Integer?
303+
attr_accessor :max_response_size, type: Integer?, defaults: {
304+
0.0r => nil,
305+
0.5r => 512 << 20, # 512 MiB
306+
}
304307

305308
# Controls the behavior of Net::IMAP#responses when called without any
306309
# arguments (+type+ or +block+).
@@ -330,7 +333,11 @@ def self.[](config)
330333
# Note: #responses_without_args is an alias for #responses_without_block.
331334
attr_accessor :responses_without_block, type: Enum[
332335
:silence_deprecation_warning, :warn, :frozen_dup, :raise,
333-
]
336+
], defaults: {
337+
0.0r => :silence_deprecation_warning,
338+
0.5r => :warn,
339+
0.6r => :frozen_dup,
340+
}
334341

335342
alias responses_without_args responses_without_block # :nodoc:
336343
alias responses_without_args= responses_without_block= # :nodoc:
@@ -375,7 +382,11 @@ def self.[](config)
375382
# ResponseParser _only_ uses AppendUIDData and CopyUIDData.
376383
attr_accessor :parser_use_deprecated_uidplus_data, type: Enum[
377384
true, :up_to_max_size, false
378-
]
385+
], defaults: {
386+
0.0r => true,
387+
0.5r => :up_to_max_size,
388+
0.6r => false,
389+
}
379390

380391
# The maximum +uid-set+ size that ResponseParser will parse into
381392
# deprecated UIDPlusData. This limit only applies when
@@ -399,7 +410,13 @@ def self.[](config)
399410
# * +0.5+: <tt>100</tt>
400411
# * +0.6+: <tt>0</tt>
401412
#
402-
attr_accessor :parser_max_deprecated_uidplus_data_size, type: Integer
413+
attr_accessor :parser_max_deprecated_uidplus_data_size, type: Integer,
414+
defaults: {
415+
0.0r => 10_000,
416+
0.4r => 1_000,
417+
0.5r => 100,
418+
0.6r => 0,
419+
}
403420

404421
# Creates a new config object and initialize its attribute with +attrs+.
405422
#
@@ -474,82 +491,10 @@ def defaults_hash
474491
to_h.reject {|k,v| DEFAULT_TO_INHERIT.include?(k) }
475492
end
476493

477-
@default = new(
478-
debug: false,
479-
open_timeout: 30,
480-
idle_response_timeout: 5,
481-
sasl_ir: true,
482-
enforce_logindisabled: true,
483-
max_response_size: 512 << 20, # 512 MiB
484-
responses_without_block: :frozen_dup,
485-
parser_use_deprecated_uidplus_data: false,
486-
parser_max_deprecated_uidplus_data_size: 0,
487-
).freeze
488-
489-
@global = default.new
490-
491-
version_defaults[:default] = Config[default.send(:defaults_hash)]
492-
493-
version_defaults[0r] = Config[:default].dup.update(
494-
sasl_ir: false,
495-
responses_without_block: :silence_deprecation_warning,
496-
enforce_logindisabled: false,
497-
max_response_size: nil,
498-
parser_use_deprecated_uidplus_data: true,
499-
parser_max_deprecated_uidplus_data_size: 10_000,
500-
).freeze
501-
version_defaults[0.0r] = Config[0r]
502-
version_defaults[0.1r] = Config[0r]
503-
version_defaults[0.2r] = Config[0r]
504-
version_defaults[0.3r] = Config[0r]
505-
506-
version_defaults[0.4r] = Config[0.3r].dup.update(
507-
sasl_ir: true,
508-
parser_max_deprecated_uidplus_data_size: 1000,
509-
).freeze
510-
511-
version_defaults[0.5r] = Config[0.4r].dup.update(
512-
enforce_logindisabled: true,
513-
max_response_size: 512 << 20, # 512 MiB
514-
responses_without_block: :warn,
515-
parser_use_deprecated_uidplus_data: :up_to_max_size,
516-
parser_max_deprecated_uidplus_data_size: 100,
517-
).freeze
518-
519-
version_defaults[0.6r] = Config[0.5r].dup.update(
520-
responses_without_block: :frozen_dup,
521-
parser_use_deprecated_uidplus_data: false,
522-
parser_max_deprecated_uidplus_data_size: 0,
523-
).freeze
524-
525-
version_defaults[0.7r] = Config[0.6r].dup.update(
526-
).freeze
527-
528-
version_defaults[0.8r] = Config[0.7r].dup.update(
529-
).freeze
530-
531-
# Safe conversions one way only:
532-
# 0.6r.to_f == 0.6 # => true
533-
# 0.6 .to_r == 0.6r # => false
534-
version_defaults.to_a.each do |k, v|
535-
next unless k in Rational
536-
version_defaults[k.to_f] = v
537-
end
538-
539-
current = VERSION.to_r
540-
version_defaults[:original] = Config[0]
541-
version_defaults[:current] = Config[current]
542-
version_defaults[:next] = Config[current + 0.1r]
543-
version_defaults[:future] = Config[current + 0.2r]
544-
545-
version_defaults.freeze
494+
@default = AttrVersionDefaults.compile_default!
495+
@global = default.new
496+
AttrVersionDefaults.compile_version_defaults!
546497

547-
if ($VERBOSE || $DEBUG) && self[:current].to_h != self[:default].to_h
548-
warn "Misconfigured Net::IMAP::Config[:current] => %p,\n" \
549-
" not equal to Net::IMAP::Config[:default] => %p" % [
550-
self[:current].to_h, self[:default].to_h
551-
]
552-
end
553498
end
554499
end
555500
end
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# frozen_string_literal: true
2+
3+
require "forwardable"
4+
5+
module Net
6+
class IMAP
7+
class Config
8+
# >>>
9+
# *NOTE:* This module is an internal implementation detail, with no
10+
# guarantee of backward compatibility.
11+
#
12+
# Adds a +defaults+ parameter to +attr_accessor+, which is used to compile
13+
# Config.version_defaults.
14+
module AttrVersionDefaults
15+
# The <tt>x.y</tt> part of Net::IMAP::VERSION, as a Rational number.
16+
CURRENT_VERSION = VERSION.to_r
17+
18+
# The config version used for <tt>Config[:next]</tt>.
19+
NEXT_VERSION = CURRENT_VERSION + 0.1r
20+
21+
# The config version used for <tt>Config[:future]</tt>.
22+
FUTURE_VERSION = 1.0r
23+
24+
VERSIONS = ((0.0r..FUTURE_VERSION) % 0.1r).to_a.freeze
25+
26+
# See Config.version_defaults.
27+
singleton_class.attr_accessor :version_defaults
28+
29+
@version_defaults = Hash.new {|h, k|
30+
# NOTE: String responds to both so the order is significant.
31+
# And ignore non-numeric conversion to zero, because: "wat!?".to_r == 0
32+
(h.fetch(k.to_r, nil) || h.fetch(k.to_f, nil) if k.is_a?(Numeric)) ||
33+
(h.fetch(k.to_sym, nil) if k.respond_to?(:to_sym)) ||
34+
(h.fetch(k.to_r, nil) if k.respond_to?(:to_r) && k.to_r != 0r) ||
35+
(h.fetch(k.to_f, nil) if k.respond_to?(:to_f) && k.to_f != 0.0)
36+
}
37+
38+
# :stopdoc: internal APIs only
39+
40+
def attr_accessor(name, defaults: nil, default: (unset = true), **kw)
41+
unless unset
42+
version = DEFAULT_TO_INHERIT.include?(name) ? nil : 0.0r
43+
defaults = { version => default }
44+
end
45+
defaults&.each_pair do |version, default|
46+
AttrVersionDefaults.version_defaults[version] ||= {}
47+
AttrVersionDefaults.version_defaults[version][name] = default
48+
end
49+
super(name, **kw)
50+
end
51+
52+
def self.compile_default!
53+
raise "Config.default already compiled" if Config.default
54+
default = VERSIONS.select { _1 <= CURRENT_VERSION }
55+
.filter_map { version_defaults[_1] }
56+
.prepend(version_defaults.delete(nil))
57+
.inject(&:merge)
58+
Config.new(**default).freeze
59+
end
60+
61+
def self.compile_version_defaults!
62+
# Temporarily assign Config.default, enabling #load_defaults(:default)
63+
version_defaults[:default] = Config.default
64+
# Use #load_defaults so some attributes are inherited from global.
65+
version_defaults[:default] = Config.new.load_defaults(:default).freeze
66+
version_defaults[0.0r] = Config[version_defaults.fetch(0.0r)]
67+
68+
VERSIONS.each_cons(2) do |prior, version|
69+
updates = version_defaults[version]
70+
version_defaults[version] = version_defaults[prior]
71+
.then { updates ? _1.dup.update(**updates).freeze : _1 }
72+
end
73+
74+
# Safe conversions one way only:
75+
# 0.6r.to_f == 0.6 # => true
76+
# 0.6 .to_r == 0.6r # => false
77+
version_defaults.to_a.each do |k, v|
78+
next unless k in Rational
79+
version_defaults[k.to_f] = v
80+
end
81+
82+
version_defaults[:original] = Config[0.0r]
83+
version_defaults[:current] = Config[CURRENT_VERSION]
84+
version_defaults[:next] = Config[NEXT_VERSION]
85+
version_defaults[:future] = Config[FUTURE_VERSION]
86+
87+
version_defaults.freeze
88+
end
89+
90+
end
91+
end
92+
end
93+
end

test/net/imap/test_config.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ class ConfigTest < Net::IMAP::TestCase
77
Config = Net::IMAP::Config
88
THIS_VERSION = Net::IMAP::VERSION.to_f
99
NEXT_VERSION = THIS_VERSION + 0.1
10-
FUTURE_VERSION = THIS_VERSION + 0.2
10+
FUTURE_VERSION = 1.0
1111

1212
setup do
1313
Config.global.reset
@@ -72,6 +72,7 @@ class ConfigTest < Net::IMAP::TestCase
7272
test ".default" do
7373
default = Config.default
7474
assert default.equal?(Config.default)
75+
assert_nil default.parent
7576
assert default.is_a?(Config)
7677
assert default.frozen?
7778
refute default.debug?
@@ -184,8 +185,7 @@ class ConfigTest < Net::IMAP::TestCase
184185
assert_raise(RangeError) do Config[0.01] end
185186
assert_raise(RangeError) do Config[0.11] end
186187
assert_raise(RangeError) do Config[0.111] end
187-
assert_raise(RangeError) do Config[0.9] end
188-
assert_raise(RangeError) do Config[1] end
188+
assert_raise(RangeError) do Config[1.1] end
189189
end
190190

191191
test ".[] key errors" do

0 commit comments

Comments
 (0)