Skip to content

Commit 381c885

Browse files
committed
🔧 Add Config#update for multiple assignment
Although `#update` is not atomic, an ArgumentError will be raised unless all of the kwargs names are valid. In that case, none of the attrs are updated.
1 parent 2caa59d commit 381c885

File tree

2 files changed

+50
-1
lines changed

2 files changed

+50
-1
lines changed

lib/net/imap/config.rb

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,28 @@ def self.[](config) # :nodoc: unfinished API
143143
# If a block is given, the new config object is yielded to it.
144144
def initialize(parent = Config.global, **attrs)
145145
super(parent)
146-
attrs.each do send(:"#{_1}=", _2) end
146+
update(**attrs)
147147
yield self if block_given?
148148
end
149149

150+
# :call-seq: update(**attrs) -> self
151+
#
152+
# Assigns all of the provided +attrs+ to this config, and returns +self+.
153+
#
154+
# An ArgumentError is raised unless every key in +attrs+ matches an
155+
# assignment method on Config.
156+
#
157+
# >>>
158+
# *NOTE:* #update is not atomic. If an exception is raised due to an
159+
# invalid attribute value, +attrs+ may be partially applied.
160+
def update(**attrs)
161+
unless (bad = attrs.keys.reject { respond_to?(:"#{_1}=") }).empty?
162+
raise ArgumentError, "invalid config options: #{bad.join(", ")}"
163+
end
164+
attrs.each do send(:"#{_1}=", _2) end
165+
self
166+
end
167+
150168
# :call-seq: to_h -> hash
151169
#
152170
# Returns all config attributes in a hash.

test/net/imap/test_config.rb

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,4 +222,35 @@ class ConfigTest < Test::Unit::TestCase
222222
assert_equal expected, global_hash.slice(*expected.keys)
223223
end
224224

225+
test "#update" do
226+
config = Config.global.update(debug: true, sasl_ir: false, open_timeout: 2)
227+
assert_same Config.global, config
228+
assert_same true, config.debug
229+
assert_same false, config.sasl_ir
230+
assert_same 2, config.open_timeout
231+
end
232+
233+
# It's simple to check first that the names are valid, so we do.
234+
test "#update with invalid key name" do
235+
config = Config.new(debug: true, sasl_ir: false, open_timeout: 2)
236+
assert_raise(ArgumentError) do
237+
config.update(debug: false, sasl_ir: true, bogus: :invalid)
238+
end
239+
assert_same true, config.debug?
240+
assert_same false, config.sasl_ir?
241+
assert_same 2, config.open_timeout
242+
end
243+
244+
# Current behavior: partial updates are applied, in order they're received.
245+
# We could make #update atomic, but the complexity probably isn't worth it.
246+
test "#update with invalid value" do
247+
config = Config.new(debug: true, sasl_ir: false, open_timeout: 2)
248+
assert_raise(TypeError) do
249+
config.update(debug: false, open_timeout: :bogus, sasl_ir: true)
250+
end
251+
assert_same false, config.debug? # updated
252+
assert_same 2, config.open_timeout # unchanged
253+
assert_same false, config.sasl_ir? # unchanged
254+
end
255+
225256
end

0 commit comments

Comments
 (0)