From e94b70f20600d460c5123f5f9324fa36822ea69a Mon Sep 17 00:00:00 2001 From: Vasiliy Ermolovich Date: Sun, 25 Mar 2012 16:05:32 +0300 Subject: add DeepMerge extension --- README.markdown | 25 +++++++++++++++++++------ lib/hashie/extensions/deep_merge.rb | 16 +++++++++++++++- spec/hashie/extensions/deep_merge_spec.rb | 20 ++++++++++++++++++++ 3 files changed, 54 insertions(+), 7 deletions(-) create mode 100644 spec/hashie/extensions/deep_merge_spec.rb diff --git a/README.markdown b/README.markdown index 30504c7..80061c4 100644 --- a/README.markdown +++ b/README.markdown @@ -34,7 +34,7 @@ applications such as an API client: include Hashie::Extensions::Coercion coerce_key :user, User end - + user_hash = {:name => "Bob"} Tweet.new(:user => user_hash) # => automatically calls User.coerce(user_hash) or @@ -47,7 +47,7 @@ Hash-like class that is self-propagating. class SpecialHash < Hash include Hashie::Extensions::Coercion coerce_value Hash, SpecialHash - + def initialize(hash = {}) super hash.each_pair do |k,v| @@ -78,7 +78,7 @@ included as individual modules, i.e. `Hashie::Extensions::MethodReader`, class MyHash < Hash include Hashie::Extensions::MethodAccess end - + h = MyHash.new h.abc = 'def' h.abc # => 'def' @@ -97,10 +97,23 @@ hash in question. This means you can safely merge together indifferent and non-indifferent hashes arbitrarily deeply without worrying about whether you'll be able to `hash[:other][:another]` properly. -### DeepMerge (Unimplemented) +### DeepMerge + +This extension allow you to easily include a recursive merging +system to any Hash descendant: + + class MyHash < Hash + include Hashie::Extensions::DeepMerge + end + + h1 = MyHash.new + h2 = MyHash.new + + h1 = {:x => {:y => [4,5,6]}, :z => [7,8,9]} + h2 = {:x => {:y => [7,8,9]}, :z => "xyz"} -This extension *will* allow you to easily include a recursive merging -system to any Hash descendant. + h1.deep_merge(h2) #=> { :x => {:y => [7, 8, 9]}, :z => "xyz" } + h2.deep_merge(h1) #=> { :x => {:y => [4, 5, 6]}, :z => [7, 8, 9] } ## Mash diff --git a/lib/hashie/extensions/deep_merge.rb b/lib/hashie/extensions/deep_merge.rb index f019be3..ce821de 100644 --- a/lib/hashie/extensions/deep_merge.rb +++ b/lib/hashie/extensions/deep_merge.rb @@ -1,7 +1,21 @@ module Hashie module Extensions module DeepMerge - # TODO: Implement deep merging. + # Returns a new hash with +self+ and +other_hash+ merged recursively. + def deep_merge(other_hash) + (class << (h = dup); self; end).send :include, Hashie::Extensions::DeepMerge + h.deep_merge!(other_hash) + end + + # Returns a new hash with +self+ and +other_hash+ merged recursively. + # Modifies the receiver in place. + def deep_merge!(other_hash) + other_hash.each do |k,v| + (class << (tv = self[k]); self; end).send :include, Hashie::Extensions::DeepMerge + self[k] = tv.is_a?(::Hash) && v.is_a?(::Hash) ? tv.deep_merge(v) : v + end + self + end end end end diff --git a/spec/hashie/extensions/deep_merge_spec.rb b/spec/hashie/extensions/deep_merge_spec.rb new file mode 100644 index 0000000..d835381 --- /dev/null +++ b/spec/hashie/extensions/deep_merge_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe Hashie::Extensions::DeepMerge do + class DeepMergeHash < Hash; include Hashie::Extensions::DeepMerge end + + subject{ DeepMergeHash } + + let(:h1) { subject.new.merge(:a => "a", :b => "b", :c => { :c1 => "c1", :c2 => "c2", :c3 => { :d1 => "d1" } }) } + let(:h2) { { :a => 1, :c => { :c1 => 2, :c3 => { :d2 => "d2" } } } } + let(:expected_hash) { { :a => 1, :b => "b", :c => { :c1 => 2, :c2 => "c2", :c3 => { :d1 => "d1", :d2 => "d2" } } } } + + it 'should deep merge two hashes' do + h1.deep_merge(h2).should == expected_hash + end + + it 'should deep merge two hashes with bang method' do + h1.deep_merge!(h2) + h1.should == expected_hash + end +end -- cgit v1.2.1