Permalink
Browse files

Merge pull request #7519 from leocassarani/merge-with-block

Add an optional block to HashWithIndifferentAccess#update and #merge
  • Loading branch information...
2 parents 7995c03 + edab820 commit ac5089d769d612a596c437085acd3c4ef257ab4e @rafaelfranca rafaelfranca committed Sep 5, 2012
@@ -1,5 +1,11 @@
## Rails 4.0.0 (unreleased) ##
+* An optional block can be passed to `HashWithIndifferentAccess#update` and `#merge`.
+ The block will be invoked for each duplicated key, and used to resolve the conflict,
+ thus replicating the behaviour of the corresponding methods on the `Hash` class.
+
+ *Leo Cassarani*
+
* Remove `j` alias for `ERB::Util#json_escape`.
The `j` alias is already used for `ActionView::Helpers::JavaScriptHelper#escape_javascript`
and both modules are included in the view context that would confuse the developers.
@@ -95,10 +95,10 @@ def []=(key, value)
alias_method :store, :[]=
- # Updates the receiver in-place merging in the hash passed as argument:
+ # Updates the receiver in-place, merging in the hash passed as argument:
#
# hash_1 = ActiveSupport::HashWithIndifferentAccess.new
- # hash_2[:key] = "value"
+ # hash_1[:key] = "value"
#
# hash_2 = ActiveSupport::HashWithIndifferentAccess.new
# hash_2[:key] = "New Value!"
@@ -110,12 +110,27 @@ def []=(key, value)
# In either case the merge respects the semantics of indifferent access.
#
# If the argument is a regular hash with keys +:key+ and +"key"+ only one
- # of the values end up in the receiver, but which was is unespecified.
+ # of the values end up in the receiver, but which one is unspecified.
+ #
+ # When given a block, the value for duplicated keys will be determined
+ # by the result of invoking the block with the duplicated key, the value
+ # in the receiver, and the value in +other_hash+. The rules for duplicated
+ # keys follow the semantics of indifferent access:
+ #
+ # hash_1[:key] = 10
+ # hash_2['key'] = 12
+ # hash_1.update(hash_2) { |key, old, new| old + new } # => {"key"=>22}
+ #
def update(other_hash)
if other_hash.is_a? HashWithIndifferentAccess
super(other_hash)
else
- other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
+ other_hash.each_pair do |key, value|
+ if block_given? && key?(key)
+ value = yield(convert_key(key), self[key], value)
+ end
+ regular_writer(convert_key(key), convert_value(value))
+ end
self
end
end
@@ -173,8 +188,8 @@ def dup
# This method has the same semantics of +update+, except it does not
# modify the receiver but rather returns a new hash with indifferent
# access with the result of the merge.
- def merge(hash)
- self.dup.update(hash)
+ def merge(hash, &block)
+ self.dup.update(hash, &block)
end
# Like +merge+ but the other way around: Merges the receiver into the
@@ -428,6 +428,29 @@ def test_indifferent_merging
assert_equal 2, hash['b']
end
+ def test_indifferent_merging_with_block
+ hash = HashWithIndifferentAccess.new
+ hash[:a] = 1
+ hash['b'] = 3
+
+ other = { 'a' => 4, :b => 2, 'c' => 10 }
+
+ merged = hash.merge(other) { |key, old, new| old > new ? old : new }
+
+ assert_equal HashWithIndifferentAccess, merged.class
+ assert_equal 4, merged[:a]
+ assert_equal 3, merged['b']
+ assert_equal 10, merged[:c]
+
+ other_indifferent = HashWithIndifferentAccess.new('a' => 9, :b => 2)
+
+ merged = hash.merge(other_indifferent) { |key, old, new| old + new }
+
+ assert_equal HashWithIndifferentAccess, merged.class
+ assert_equal 10, merged[:a]
+ assert_equal 5, merged[:b]
+ end
+
def test_indifferent_reverse_merging
hash = HashWithIndifferentAccess.new('some' => 'value', 'other' => 'value')
hash.reverse_merge!(:some => 'noclobber', :another => 'clobber')

0 comments on commit ac5089d

Please sign in to comment.