summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNoah Kantrowitz <noah@coderanger.net>2017-04-03 23:57:32 -0700
committerNoah Kantrowitz <noah@coderanger.net>2017-04-03 23:57:32 -0700
commitaab17cd7c33992b1f4138ebd309919be4473f217 (patch)
treed271ac6e6730053c897018a31d222af3be2e8d9d
parent55feb2b55bd37a895fcb3d3291c60b1e05334ad7 (diff)
downloadchef-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.rb15
-rw-r--r--spec/unit/property_spec.rb17
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