Skip to content

Commit c7732e6

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 66fdb1c commit c7732e6

File tree

2 files changed

+74
-8
lines changed

2 files changed

+74
-8
lines changed

lib/net/imap/config.rb

+20-6
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,25 @@ def self.default; @default end
131131
def self.global; @global if defined?(@global) end
132132

133133
# A hash of hard-coded configurations, indexed by version number or name.
134+
# Values can be accessed with any object that responds to +to_sym+ or
135+
# +to_r+/+to_f+ with a non-zero number.
136+
#
137+
# Config::[] gets named or numbered versions from this hash.
138+
#
139+
# For example:
140+
# Net::IMAP::Config.version_defaults[0.5] == Net::IMAP::Config[0.5]
141+
# Net::IMAP::Config[0.5] == Net::IMAP::Config[0.5r] # => true
142+
# Net::IMAP::Config["current"] == Net::IMAP::Config[:current] # => true
143+
# Net::IMAP::Config["0.5.6"] == Net::IMAP::Config[0.5r] # => true
134144
def self.version_defaults; @version_defaults end
135-
@version_defaults = {}
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+
}
136153

137154
# :call-seq:
138155
# Net::IMAP::Config[number] -> versioned config
@@ -155,18 +172,17 @@ def self.[](config)
155172
elsif config.nil? && global.nil? then nil
156173
elsif config.respond_to?(:to_hash) then new(global, **config).freeze
157174
else
158-
version_defaults.fetch(config) do
175+
version_defaults[config] or
159176
case config
160177
when Numeric
161178
raise RangeError, "unknown config version: %p" % [config]
162-
when Symbol
179+
when String, Symbol
163180
raise KeyError, "unknown config name: %p" % [config]
164181
else
165182
raise TypeError, "no implicit conversion of %s to %s" % [
166183
config.class, Config
167184
]
168185
end
169-
end
170186
end
171187
end
172188

@@ -478,8 +494,6 @@ def defaults_hash
478494
version_defaults.to_a.each do |k, v|
479495
next unless k in Rational
480496
version_defaults[k.to_f] = v
481-
next unless k.to_i.to_r == k
482-
version_defaults[k.to_i] = v
483497
end
484498

485499
current = VERSION.to_r

test/net/imap/test_config.rb

+54-2
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)