Skip to content

Commit 891975e

Browse files
committed
♻️ Add default_proc to Config.version_defaults
This enables more flexible lookups. So we can now use something like `Config["0.4.11"]` (I do _not_ intend to store version defaults for `x.y.z` releases). I had to add a special-case for zero, to avoid `Config["missing"] == Config[0r]`
1 parent 3e4476c commit 891975e

2 files changed

Lines changed: 74 additions & 8 deletions

File tree

lib/net/imap/config.rb

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,25 @@ def self.default; @default end
129129
def self.global; @global if defined?(@global) end
130130

131131
# A hash of hard-coded configurations, indexed by version number or name.
132+
# Values can be accessed with any object that responds to +to_sym+ or
133+
# +to_r+/+to_f+ with a non-zero number.
134+
#
135+
# Config::[] gets named or numbered versions from this hash.
136+
#
137+
# For example:
138+
# Net::IMAP::Config.version_defaults[0.5] == Net::IMAP::Config[0.5]
139+
# Net::IMAP::Config[0.5] == Net::IMAP::Config[0.5r] # => true
140+
# Net::IMAP::Config["current"] == Net::IMAP::Config[:current] # => true
141+
# Net::IMAP::Config["0.5.6"] == Net::IMAP::Config[0.5r] # => true
132142
def self.version_defaults; @version_defaults end
133-
@version_defaults = {}
143+
@version_defaults = Hash.new {|h, k|
144+
# NOTE: String responds to both so the order is significant.
145+
# And ignore non-numeric conversion to zero, because: "wat!?".to_r == 0
146+
(h.fetch(k.to_r, nil) || h.fetch(k.to_f, nil) if k.is_a?(Numeric)) ||
147+
(h.fetch(k.to_sym, nil) if k.respond_to?(:to_sym)) ||
148+
(h.fetch(k.to_r, nil) if k.respond_to?(:to_r) && k.to_r != 0r) ||
149+
(h.fetch(k.to_f, nil) if k.respond_to?(:to_f) && k.to_f != 0.0)
150+
}
134151

135152
# :call-seq:
136153
# Net::IMAP::Config[number] -> versioned config
@@ -153,18 +170,17 @@ def self.[](config)
153170
elsif config.nil? && global.nil? then nil
154171
elsif config.respond_to?(:to_hash) then new(global, **config).freeze
155172
else
156-
version_defaults.fetch(config) do
173+
version_defaults[config] or
157174
case config
158175
when Numeric
159176
raise RangeError, "unknown config version: %p" % [config]
160-
when Symbol
177+
when String, Symbol
161178
raise KeyError, "unknown config name: %p" % [config]
162179
else
163180
raise TypeError, "no implicit conversion of %s to %s" % [
164181
config.class, Config
165182
]
166183
end
167-
end
168184
end
169185
end
170186

@@ -449,8 +465,6 @@ def defaults_hash
449465
version_defaults.to_a.each do |k, v|
450466
next unless k in Rational
451467
version_defaults[k.to_f] = v
452-
next unless k.to_i.to_r == k
453-
version_defaults[k.to_i] = v
454468
end
455469

456470
current = VERSION.to_r

test/net/imap/test_config.rb

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,14 +165,18 @@ class ConfigTest < Test::Unit::TestCase
165165
end
166166

167167
test ".[] for all x.y versions" do
168-
original = Config[0]
168+
original = Config[0r]
169169
assert_kind_of Config, original
170+
assert_same original, Config[0]
170171
assert_same original, Config[0.0]
171172
assert_same original, Config[0.1]
172173
assert_same original, Config[0.2]
173174
assert_same original, Config[0.3]
174175
((0.4r..FUTURE_VERSION.to_r) % 0.1r).each do |version|
175-
assert_kind_of Config, Config[version.to_f]
176+
config = Config[version]
177+
assert_kind_of Config, config
178+
assert_same config, Config[version.to_f]
179+
assert_same config, Config[version.to_f.to_r]
176180
end
177181
end
178182

@@ -186,6 +190,8 @@ class ConfigTest < Test::Unit::TestCase
186190

187191
test ".[] key errors" do
188192
assert_raise(KeyError) do Config[:nonexistent] end
193+
assert_raise(KeyError) do Config["nonexistent"] end
194+
assert_raise(KeyError) do Config["0.01"] end
189195
end
190196

191197
test ".[] with symbol names" do
@@ -195,6 +201,52 @@ class ConfigTest < Test::Unit::TestCase
195201
assert_same Config[FUTURE_VERSION], Config[:future]
196202
end
197203

204+
test ".[] with string names" do
205+
assert_same Config[:original], Config["original"]
206+
assert_same Config[:current], Config["current"]
207+
assert_same Config[0.4r], Config["0.4.11"]
208+
assert_same Config[0.5r], Config["0.5.6"]
209+
assert_same Config[:current], Config[Net::IMAP::VERSION]
210+
end
211+
212+
test ".[] with object responding to to_sym, to_r, or to_f" do
213+
# responds to none of the methods
214+
duck = Object.new
215+
assert_raise TypeError do Config[duck] end
216+
217+
# to_sym
218+
duck = Object.new
219+
def duck.to_sym = :current
220+
assert_same Config[:current], Config[duck]
221+
222+
# to_r
223+
duck = Object.new
224+
def duck.to_r = 0.6r
225+
assert_same Config[0.6r], Config[duck]
226+
227+
# to_f
228+
duck = Object.new
229+
def duck.to_f = 0.4
230+
assert_same Config[0.4], Config[duck]
231+
232+
# prefer to_r over to_f
233+
def duck.to_r = 0.5r
234+
assert_same Config[0.5r], Config[duck]
235+
236+
# prefer to_sym over to_r
237+
def duck.to_sym = :original
238+
assert_same Config[:original], Config[duck]
239+
240+
# keeps trying if to_sym finds nothing
241+
duck = Object.new
242+
def duck.to_sym = :nope
243+
def duck.to_f = 0.5
244+
assert_same Config[0.5], Config[duck]
245+
# keeps trying if to_sym and to_r both find nothing
246+
def duck.to_r = 1/11111
247+
assert_same Config[0.5], Config[duck]
248+
end
249+
198250
test ".[] with a hash" do
199251
config = Config[{responses_without_block: :raise, sasl_ir: false}]
200252
assert config.frozen?

0 commit comments

Comments
 (0)