Add block to Extensions::DeepMerge

This commit is contained in:
Ilya Kamenko 2014-12-30 00:12:14 +03:00
parent 337a980e35
commit 2846b3e623
4 changed files with 65 additions and 21 deletions

View File

@ -6,6 +6,7 @@
* [#252](https://github.com/intridia/hashie/pull/252): Add support for conditionally required Hashie::Dash attributes - [@ccashwell](https://github.com/ccashwell).
* [#256](https://github.com/intridia/hashie/pull/256): Inherit key coercions - [@Erol](https://github.com/Erol).
* [#259](https://github.com/intridia/hashie/pull/259): Fix handling of default proc values in Mash - [@Erol](https://github.com/Erol).
* [#260](https://github.com/intridia/hashie/pull/260): Add block to Extensions::DeepMerge - [@galathius](https://github.com/galathius).
* Your contribution here.
## 3.3.2 (11/26/2014)

View File

@ -230,6 +230,21 @@ h1.deep_merge(h2) # => { x: { y: [7, 8, 9] }, z: "xyz" }
h2.deep_merge(h1) # => { x: { y: [4, 5, 6] }, z: [7, 8, 9] }
```
Like with Hash#merge in the standard library, a block can be provided to merge values:
```ruby
class MyHash < Hash
include Hashie::Extensions::DeepMerge
end
h1 = MyHash[{ a: 100, b: 200, c: { c1: 100 } }]
h2 = MyHash[{ b: 250, c: { c1: 200 } }]
h1.deep_merge(h2) { |key, this_val, other_val| this_val + other_val }
# => { a: 100, b: 450, c: { c1: 300 } }
```
### DeepFetch
This extension can be mixed in to provide for safe and concise retrieval of deeply nested hash values. In the event that the requested key does not exist a block can be provided and its value will be returned.

View File

@ -2,28 +2,33 @@ module Hashie
module Extensions
module DeepMerge
# Returns a new hash with +self+ and +other_hash+ merged recursively.
def deep_merge(other_hash)
dup.deep_merge!(other_hash)
def deep_merge(other_hash, &block)
dup.deep_merge!(other_hash, &block)
end
# Returns a new hash with +self+ and +other_hash+ merged recursively.
# Modifies the receiver in place.
def deep_merge!(other_hash)
_recursive_merge(self, other_hash)
def deep_merge!(other_hash, &block)
return self unless other_hash.is_a?(::Hash)
_recursive_merge(self, other_hash, &block)
self
end
private
def _recursive_merge(hash, other_hash)
if other_hash.is_a?(::Hash) && hash.is_a?(::Hash)
other_hash.each do |k, v|
hash[k] = hash.key?(k) ? _recursive_merge(hash[k], v) : v
end
hash
else
other_hash
def _recursive_merge(hash, other_hash, &block)
other_hash.each do |k, v|
hash[k] = if hash.key?(k) && hash[k].is_a?(::Hash) && v.is_a?(::Hash)
_recursive_merge(hash[k], v, &block)
else
if hash.key?(k) && block_given?
block.call(k, hash[k], v)
else
v
end
end
end
hash
end
end
end

View File

@ -7,16 +7,39 @@ describe Hashie::Extensions::DeepMerge do
subject { DeepMergeHash }
let(:h1) { subject.new.merge(a: 'a', a1: 42, b: 'b', c: { c1: 'c1', c2: { a: 'b' }, c3: { d1: 'd1' } }) }
let(:h2) { { a: 1, a1: 1, c: { c1: 2, c2: 'c2', c3: { d2: 'd2' } } } }
let(:expected_hash) { { a: 1, a1: 1, b: 'b', c: { c1: 2, c2: 'c2', c3: { d1: 'd1', d2: 'd2' } } } }
it 'deep merges two hashes' do
expect(h1.deep_merge(h2)).to eq expected_hash
it 'should return initial hash for arguments that are not hash' do
hash = subject.new.merge(a: 'a')
expect(hash.deep_merge('abc')).to eq(hash)
end
it 'deep merges another hash in place via bang method' do
h1.deep_merge!(h2)
expect(h1).to eq expected_hash
context 'without &block' do
let(:h1) { subject.new.merge(a: 'a', a1: 42, b: 'b', c: { c1: 'c1', c2: { a: 'b' }, c3: { d1: 'd1' } }) }
let(:h2) { { a: 1, a1: 1, c: { c1: 2, c2: 'c2', c3: { d2: 'd2' } } } }
let(:expected_hash) { { a: 1, a1: 1, b: 'b', c: { c1: 2, c2: 'c2', c3: { d1: 'd1', d2: 'd2' } } } }
it 'deep merges two hashes' do
expect(h1.deep_merge(h2)).to eq expected_hash
end
it 'deep merges another hash in place via bang method' do
h1.deep_merge!(h2)
expect(h1).to eq expected_hash
end
end
context 'with &block' do
let(:h1) { subject.new.merge(a: 100, b: 200, c: { c1: 100 }) }
let(:h2) { { b: 250, c: { c1: 200 } } }
let(:expected_hash) { { a: 100, b: 450, c: { c1: 300 } } }
let(:block) { proc { |_, this_val, other_val| this_val + other_val } }
it 'deep merges two hashes' do
expect(h1.deep_merge(h2, &block)).to eq expected_hash
end
it 'deep merges another hash in place via bang method' do
h1.deep_merge!(h2, &block)
expect(h1).to eq expected_hash
end
end
end