diff options
-rw-r--r-- | chef-utils/lib/chef-utils/mash.rb | 15 | ||||
-rw-r--r-- | lib/chef/node/attribute.rb | 4 | ||||
-rw-r--r-- | spec/unit/node_spec.rb | 78 |
3 files changed, 95 insertions, 2 deletions
diff --git a/chef-utils/lib/chef-utils/mash.rb b/chef-utils/lib/chef-utils/mash.rb index 484e172b33..bb48064aa3 100644 --- a/chef-utils/lib/chef-utils/mash.rb +++ b/chef-utils/lib/chef-utils/mash.rb @@ -94,6 +94,10 @@ module ChefUtils end end + unless method_defined?(:regular_reader) + alias_method :regular_reader, :[] + end + unless method_defined?(:regular_writer) alias_method :regular_writer, :[]= end @@ -102,6 +106,11 @@ module ChefUtils alias_method :regular_update, :update end + # @param key<Object> The key to get. + def [](key) + regular_reader(key) + end + # @param key<Object> The key to set. # @param value<Object> # The value to set the key to. @@ -114,6 +123,12 @@ module ChefUtils # internal API for use by Chef's deep merge cache # @api private + def internal_get(key) + regular_reader(key) + end + + # internal API for use by Chef's deep merge cache + # @api private def internal_set(key, value) regular_writer(key, convert_value(value)) end diff --git a/lib/chef/node/attribute.rb b/lib/chef/node/attribute.rb index 29b60a98d5..3383b3c7e5 100644 --- a/lib/chef/node/attribute.rb +++ b/lib/chef/node/attribute.rb @@ -596,7 +596,7 @@ class Chef merge_with.each do |key, merge_with_value| value = if merge_onto.key?(key) - deep_merge!(safe_dup(merge_onto[key]), merge_with_value) + deep_merge!(safe_dup(merge_onto.internal_get(key)), merge_with_value) else merge_with_value end @@ -632,7 +632,7 @@ class Chef merge_with.each do |key, merge_with_value| value = if merge_onto.key?(key) - hash_only_merge!(safe_dup(merge_onto[key]), merge_with_value) + hash_only_merge!(safe_dup(merge_onto.internal_get(key)), merge_with_value) else merge_with_value end diff --git a/spec/unit/node_spec.rb b/spec/unit/node_spec.rb index ec7beb9a50..284a993f5c 100644 --- a/spec/unit/node_spec.rb +++ b/spec/unit/node_spec.rb @@ -1951,4 +1951,82 @@ describe Chef::Node do expect(node["foo"]["bar"]["test"]).to eql("right") end end + + describe "lazy values" do + it "supports lazy values in attributes" do + node.instance_eval do + default["foo"]["bar"] = lazy { node["fizz"]["buzz"] } + default["fizz"]["buzz"] = "works" + end + expect(node["foo"]["bar"]).to eql("works") + end + + it "lazy values maintain laziness" do + node.instance_eval do + default["foo"]["bar"] = lazy { node["fizz"]["buzz"] } + default["fizz"]["buzz"] = "works" + end + expect(node["foo"]["bar"]).to eql("works") + node.default["fizz"]["buzz"] = "still works" + expect(node["foo"]["bar"]).to eql("still works") + end + + it "supports recursive lazy values in attributes" do + node.instance_eval do + default["cool"]["beans"] = lazy { node["foo"]["bar"] } + default["foo"]["bar"] = lazy { node["fizz"]["buzz"] } + default["fizz"]["buzz"] = "works" + end + expect(node["cool"]["beans"]).to eql("works") + node.default["fizz"]["buzz"] = "still works" + expect(node["cool"]["beans"]).to eql("still works") + end + + it "supports top level lazy values in attributes" do + # due to the top level deep merge cache these are special cases + node.instance_eval do + default["cool"] = lazy { node["foo"] } + default["foo"] = lazy { node["fizz"] } + default["fizz"] = "works" + end + expect(node["cool"]).to eql("works") + node.default["fizz"] = "still works" + expect(node["cool"]).to eql("still works") + end + + it "supports deep merged values in attributes" do + node.instance_eval do + override["bar"]["cool"] = lazy { node["bar"]["foo"] } + override["bar"]["foo"] = lazy { node["bar"]["fizz"] } + override["bar"]["fizz"] = "works" + end + expect(node["bar"]["cool"]).to eql("works") + node.override["bar"]["fizz"] = "still works" + expect(node["bar"]["cool"]).to eql("still works") + end + + it "supports overridden deep merged values in attributes (deep_merge)" do + node.instance_eval do + role_override["bar"] = { "cool" => "bad", "foo" => "bad", "fizz" => "bad" } + force_override["bar"]["cool"] = lazy { node["bar"]["foo"] } + force_override["bar"]["foo"] = lazy { node["bar"]["fizz"] } + force_override["bar"]["fizz"] = "works" + end + expect(node["bar"]["cool"]).to eql("works") + node.force_override["bar"]["fizz"] = "still works" + expect(node["bar"]["cool"]).to eql("still works") + end + + it "supports overridden deep merged values in attributes (hash_only_merge)" do + node.instance_eval do + default["bar"] = { "cool" => "bad", "foo" => "bad", "fizz" => "bad" } + override["bar"]["cool"] = lazy { node["bar"]["foo"] } + override["bar"]["foo"] = lazy { node["bar"]["fizz"] } + override["bar"]["fizz"] = "works" + end + expect(node["bar"]["cool"]).to eql("works") + node.override["bar"]["fizz"] = "still works" + expect(node["bar"]["cool"]).to eql("still works") + end + end end |