summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteven Danna <steve@opscode.com>2014-02-08 20:21:03 -0800
committerSteven Danna <steve@opscode.com>2014-02-08 20:26:04 -0800
commitc8b53719a266e0aff8f19d4a2313e97eee16834a (patch)
treeddb40edf54de420f32316197abdce41afd77f8a4
parent7fba480379ce69f7e16310684f7d66bfd422ae7b (diff)
downloadchef-ssd/CHEF-4918.tar.gz
[CHEF-4918] Don't destructively merge subhashes in hash_only_merge!ssd/CHEF-4918
hash_only_merge dups its inputs and then passes it to hash_only_merge!. Unfortunately, dup does not make a deep copy, leading hash_only_merge to mutate deeply nested structures.
-rw-r--r--lib/chef/mixin/deep_merge.rb8
-rw-r--r--spec/unit/mixin/deep_merge_spec.rb7
2 files changed, 12 insertions, 3 deletions
diff --git a/lib/chef/mixin/deep_merge.rb b/lib/chef/mixin/deep_merge.rb
index ad3e5803fd..5002f5dcc5 100644
--- a/lib/chef/mixin/deep_merge.rb
+++ b/lib/chef/mixin/deep_merge.rb
@@ -122,7 +122,11 @@ class Chef
# If there are two Hashes, recursively merge.
if merge_onto.kind_of?(Hash) && merge_with.kind_of?(Hash)
merge_with.each do |key, merge_with_value|
- merge_onto[key] = hash_only_merge!(merge_onto[key], merge_with_value)
+ merge_onto[key] = if merge_onto.has_key?(key)
+ hash_only_merge(merge_onto[key], merge_with_value)
+ else
+ merge_with_value
+ end
end
merge_onto
@@ -164,5 +168,3 @@ class Chef
end
end
end
-
-
diff --git a/spec/unit/mixin/deep_merge_spec.rb b/spec/unit/mixin/deep_merge_spec.rb
index 0a7bbffa41..b7828a3076 100644
--- a/spec/unit/mixin/deep_merge_spec.rb
+++ b/spec/unit/mixin/deep_merge_spec.rb
@@ -347,5 +347,12 @@ describe Chef::Mixin::DeepMerge do
merged_result["top_level_a"]["1_deep_b"].should == %w[B B B]
end
+ it "does not mutate deeply-nested original hashes by default" do
+ merge_ee_hash = {"top_level_a" => {"1_deep_a" => { "2_deep_a" => { "3_deep_a" => "foo" }}}}
+ merge_with_hash = {"top_level_a" => {"1_deep_a" => { "2_deep_a" => { "3_deep_b" => "bar" }}}}
+ @dm.hash_only_merge(merge_ee_hash, merge_with_hash)
+ merge_ee_hash.should == {"top_level_a" => {"1_deep_a" => { "2_deep_a" => { "3_deep_a" => "foo" }}}}
+ merge_with_hash.should == {"top_level_a" => {"1_deep_a" => { "2_deep_a" => { "3_deep_b" => "bar" }}}}
+ end
end
end