diff options
author | Noah Kantrowitz <noah@coderanger.net> | 2017-04-03 23:57:32 -0700 |
---|---|---|
committer | Noah Kantrowitz <noah@coderanger.net> | 2017-04-03 23:57:32 -0700 |
commit | aab17cd7c33992b1f4138ebd309919be4473f217 (patch) | |
tree | d271ac6e6730053c897018a31d222af3be2e8d9d | |
parent | 55feb2b55bd37a895fcb3d3291c60b1e05334ad7 (diff) | |
download | chef-aab17cd7c33992b1f4138ebd309919be4473f217.tar.gz |
Switch to recursive freeze, if you want to footgun you need to work for it.
Signed-off-by: Noah Kantrowitz <noah@coderanger.net>
-rw-r--r-- | lib/chef/property.rb | 15 | ||||
-rw-r--r-- | spec/unit/property_spec.rb | 17 |
2 files changed, 30 insertions, 2 deletions
diff --git a/lib/chef/property.rb b/lib/chef/property.rb index ecc23b701c..3c3fd44d24 100644 --- a/lib/chef/property.rb +++ b/lib/chef/property.rb @@ -110,8 +110,19 @@ class Chef raise ArgumentError, "Cannot specify both default and name_property/name_attribute together on property #{self}" end - # Freeze the default if it isn't a lazy value. - default.freeze unless default.is_a?(DelayedEvaluator) + # Recursively freeze the default if it isn't a lazy value. + unless default.is_a?(DelayedEvaluator) + visitor = lambda do |obj| + case obj + when Hash + obj.each_value {|value| visitor.call(value) } + when Array + obj.each {|value| visitor.call(value) } + end + obj.freeze + end + visitor.call(default) + end # Validate the default early, so the user gets a good error message, and # cache it so we don't do it again if so diff --git a/spec/unit/property_spec.rb b/spec/unit/property_spec.rb index 70937bb53c..31408e8209 100644 --- a/spec/unit/property_spec.rb +++ b/spec/unit/property_spec.rb @@ -575,6 +575,23 @@ describe "Chef::Resource.property" do end end + context "complex, nested default" do + with_property ":x, default: [{foo: 'bar'}]" do + it "when x is not set, it returns [{foo: 'bar'}]" do + expect(resource.x).to eq([{foo: 'bar'}]) + end + it "x is immutable" do + expect { resource.x << :other }.to raise_error(RuntimeError, "can't modify frozen Array") + end + it "x.first is immutable" do + expect { resource.x.first[:foo] = "other" }.to raise_error(RuntimeError, "can't modify frozen Hash") + end + it "x.first[:foo] is immutable" do + expect { resource.x.first[:foo] << "other" }.to raise_error(RuntimeError, "can't modify frozen String") + end + end + end + context "with a class with 'blah' as both class and instance methods" do before do resource_class.class_eval do |