summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLamont Granquist <lamont@scriptkiddie.org>2016-10-07 15:04:47 -0700
committerLamont Granquist <lamont@scriptkiddie.org>2016-10-25 09:29:29 -0700
commit6536f15dcb53938fe9c77e7438b20fe6ac3cdffb (patch)
treee6f6a3b0f28914582e4be97c5ecb9e3b20a0e9c1
parent10904d6cf42a2a0ed69757aca3e80f512bb89379 (diff)
downloadchef-6536f15dcb53938fe9c77e7438b20fe6ac3cdffb.tar.gz
remove breadcrumb state
Signed-off-by: Lamont Granquist <lamont@scriptkiddie.org>
-rw-r--r--lib/chef/node.rb4
-rw-r--r--lib/chef/node/attribute_collections.rb22
-rw-r--r--lib/chef/node/common_api.rb6
-rw-r--r--lib/chef/node/immutable_collections.rb6
-rw-r--r--lib/chef/node/mixin/deep_merge_cache.rb11
-rw-r--r--lib/chef/node/mixin/state_tracking.rb18
-rw-r--r--spec/unit/node/vivid_mash_spec.rb111
7 files changed, 52 insertions, 126 deletions
diff --git a/lib/chef/node.rb b/lib/chef/node.rb
index 212b1ced14..34a92d325b 100644
--- a/lib/chef/node.rb
+++ b/lib/chef/node.rb
@@ -197,7 +197,6 @@ class Chef
# Set a normal attribute of this node, but auto-vivify any Mashes that
# might be missing
def normal
- attributes.top_level_breadcrumb = nil
attributes.normal
end
@@ -209,14 +208,12 @@ class Chef
# Set a default of this node, but auto-vivify any Mashes that might
# be missing
def default
- attributes.top_level_breadcrumb = nil
attributes.default
end
# Set an override attribute of this node, but auto-vivify any Mashes that
# might be missing
def override
- attributes.top_level_breadcrumb = nil
attributes.override
end
@@ -237,7 +234,6 @@ class Chef
end
def automatic_attrs
- attributes.top_level_breadcrumb = nil
attributes.automatic
end
diff --git a/lib/chef/node/attribute_collections.rb b/lib/chef/node/attribute_collections.rb
index be87c028cb..412fb52a48 100644
--- a/lib/chef/node/attribute_collections.rb
+++ b/lib/chef/node/attribute_collections.rb
@@ -64,13 +64,12 @@ class Chef
MUTATOR_METHODS.each do |mutator|
define_method(mutator) do |*args, &block|
ret = super(*args, &block)
- __root.reset_cache(__root.top_level_breadcrumb)
+ send_reset_cache
ret
end
end
- def initialize(data = [], root = self)
- @__root ||= root
+ def initialize(data = [])
super(data)
map! { |e| convert_value(e) }
end
@@ -129,7 +128,6 @@ class Chef
# object.
MUTATOR_METHODS = [
:clear,
- :delete,
:delete_if,
:keep_if,
:merge!,
@@ -143,21 +141,24 @@ class Chef
# For all of the mutating methods on Mash, override them so that they
# also invalidate the cached `merged_attributes` on the root Attribute
# object.
+
+ def delete(key, &block)
+ send_reset_cache(__path + [ key ])
+ super
+ end
+
MUTATOR_METHODS.each do |mutator|
define_method(mutator) do |*args, &block|
- __root.reset_cache(__root.top_level_breadcrumb)
+ send_reset_cache
super(*args, &block)
end
end
- def initialize(data = {}, root = self)
- puts caller unless root.class == Chef::Node::Attribute
- @__root ||= root
+ def initialize(data = {})
super(data)
end
def [](key)
- __root.top_level_breadcrumb ||= key
value = super
if !key?(key)
value = self.class.new({}, __root)
@@ -168,9 +169,8 @@ class Chef
end
def []=(key, value)
- __root.top_level_breadcrumb ||= key
ret = super
- __root.reset_cache(__root.top_level_breadcrumb)
+ send_reset_cache(__path + [ key ])
ret
end
diff --git a/lib/chef/node/common_api.rb b/lib/chef/node/common_api.rb
index 6aae880667..9bb83a5178 100644
--- a/lib/chef/node/common_api.rb
+++ b/lib/chef/node/common_api.rb
@@ -32,7 +32,6 @@ class Chef
# - autovivifying / autoreplacing writer
# - non-container-ey intermediate objects are replaced with hashes
def write(*args, &block)
- __root.top_level_breadcrumb = nil if respond_to?(:__root)
value = block_given? ? yield : args.pop
last = args.pop
prev_memo = prev_key = nil
@@ -56,7 +55,6 @@ class Chef
# something that is not a container ("schema violation" issues).
#
def write!(*args, &block)
- __root.top_level_breadcrumb = nil if respond_to?(:__root)
value = block_given? ? yield : args.pop
last = args.pop
obj = args.inject(self) do |memo, key|
@@ -71,7 +69,6 @@ class Chef
# return true or false based on if the attribute exists
def exist?(*path)
- __root.top_level_breadcrumb = nil if respond_to?(:__root)
path.inject(self) do |memo, key|
return false unless valid_container?(memo, key)
if memo.is_a?(Hash)
@@ -103,7 +100,6 @@ class Chef
# non-autovivifying reader that throws an exception if the attribute does not exist
def read!(*path)
raise Chef::Exceptions::NoSuchAttribute unless exist?(*path)
- __root.top_level_breadcrumb = nil if respond_to?(:__root)
path.inject(self) do |memo, key|
memo[key]
end
@@ -112,10 +108,8 @@ class Chef
# FIXME:(?) does anyone really like the autovivifying reader that we have and wants the same behavior? readers that write? ugh...
def unlink(*path, last)
- __root.top_level_breadcrumb = nil if respond_to?(:__root)
hash = path.empty? ? self : read(*path)
return nil unless hash.is_a?(Hash) || hash.is_a?(Array)
- __root.top_level_breadcrumb ||= last
hash.delete(last)
end
diff --git a/lib/chef/node/immutable_collections.rb b/lib/chef/node/immutable_collections.rb
index 50ac8daf93..623290f287 100644
--- a/lib/chef/node/immutable_collections.rb
+++ b/lib/chef/node/immutable_collections.rb
@@ -52,8 +52,7 @@ class Chef
alias :internal_push :<<
private :internal_push
- def initialize(array_data = [], root = self)
- @__root = root
+ def initialize(array_data = [])
array_data.each do |value|
internal_push(immutablize(value))
end
@@ -114,8 +113,7 @@ class Chef
alias :internal_set :[]=
private :internal_set
- def initialize(mash_data = {}, root = self)
- @__root = root
+ def initialize(mash_data = {})
mash_data.each do |key, value|
internal_set(key, immutablize(value))
end
diff --git a/lib/chef/node/mixin/deep_merge_cache.rb b/lib/chef/node/mixin/deep_merge_cache.rb
index d6a2149c52..b18d6b10cb 100644
--- a/lib/chef/node/mixin/deep_merge_cache.rb
+++ b/lib/chef/node/mixin/deep_merge_cache.rb
@@ -19,14 +19,6 @@ class Chef
class Node
module Mixin
module DeepMergeCache
- # This is used to track the top level key as we descend through method chaining into
- # a precedence level (e.g. node.default['foo']['bar']['baz']= results in 'foo' here). We
- # need this so that when we hit the end of a method chain which results in a mutator method
- # that we can invalidate the whole top-level deep merge cache for the top-level key. It is
- # the responsibility of the accessor on the Chef::Node object to reset this to nil, and then
- # the first VividMash#[] call can ||= and set this to the first key we encounter.
- attr_accessor :top_level_breadcrumb
-
# Cache of deep merged values by top-level key. This is a simple hash which has keys that are the
# top-level keys of the node object, and we save the computed deep-merge for that key here. There is
# no cache of subtrees.
@@ -36,7 +28,6 @@ class Chef
@merged_attributes = nil
@combined_override = nil
@combined_default = nil
- @top_level_breadcrumb = nil
@deep_merge_cache = {}
end
@@ -46,7 +37,7 @@ class Chef
# must invalidate the entire cache and re-deep-merge the entire node object.
def reset_cache(path = nil)
if path.nil?
- @deep_merge_cache = {}
+ deep_merge_cache.clear
else
deep_merge_cache.delete(path.to_s)
end
diff --git a/lib/chef/node/mixin/state_tracking.rb b/lib/chef/node/mixin/state_tracking.rb
index 7472d2a67d..2447de1c16 100644
--- a/lib/chef/node/mixin/state_tracking.rb
+++ b/lib/chef/node/mixin/state_tracking.rb
@@ -22,10 +22,14 @@ class Chef
attr_reader :__path
attr_reader :__root
- def initialize(*args)
- super(*args)
- @__path ||= []
- @__root ||= self
+ NULL = Object.new
+
+ def initialize(data = NULL, root = self)
+ # __path and __root must be nil when we call super so it knows
+ # to avoid resetting the cache on construction
+ data == NULL ? super() : super(data)
+ @__path = []
+ @__root = root
end
def [](key)
@@ -55,6 +59,12 @@ class Chef
def __root=(root)
@__root = root
end
+
+ private
+
+ def send_reset_cache(path = __path)
+ __root.reset_cache(path.first) if !__root.nil? && __root.respond_to?(:reset_cache) && !path.nil?
+ end
end
end
end
diff --git a/spec/unit/node/vivid_mash_spec.rb b/spec/unit/node/vivid_mash_spec.rb
index eb22929685..0955b6b1c3 100644
--- a/spec/unit/node/vivid_mash_spec.rb
+++ b/spec/unit/node/vivid_mash_spec.rb
@@ -19,37 +19,46 @@ require "spec_helper"
require "chef/node/attribute_collections"
describe Chef::Node::VividMash do
- class Root
- attr_accessor :top_level_breadcrumb
- end
-
- let(:root) { Root.new }
+ let(:root) { instance_double(Chef::Node::Attribute) }
let(:vivid) do
- expect(root).to receive(:reset_cache).at_least(:once).with(nil)
Chef::Node::VividMash.new(
{ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil },
root
)
end
- def with_breadcrumb(key)
- expect(root).to receive(:top_level_breadcrumb=).with(nil).at_least(:once).and_call_original
- expect(root).to receive(:top_level_breadcrumb=).with(key).at_least(:once).and_call_original
+ context "without a root node" do
+ let(:vivid) do
+ Chef::Node::VividMash.new(
+ { "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil }
+ )
+ end
+
+ it "sets the root to the root object" do
+ expect(vivid["one"]["two"].__root).to eql(vivid)
+ end
+
+ it "does not send reset cache" do
+ # if we setup the expectation here then the object winds up responding to :reset_cache and then it fails...
+ # expect(vivid).not_to receive(:reset_cache)
+ # but even so we expect to blow up here with NoMethodError if we screw up and send :reset_cache to a root VividMash
+ vivid["one"]["foo"] = "bar"
+ end
end
context "#[]=" do
it "deep converts values through arrays" do
- allow(root).to receive(:reset_cache)
- vivid[:foo] = [ { :bar => true } ]
+ expect(root).to receive(:reset_cache).with("foo")
+ vivid["foo"] = [ { :bar => true } ]
expect(vivid["foo"].class).to eql(Chef::Node::AttrArray)
expect(vivid["foo"][0].class).to eql(Chef::Node::VividMash)
expect(vivid["foo"][0]["bar"]).to be true
end
it "deep converts values through nested arrays" do
- allow(root).to receive(:reset_cache)
- vivid[:foo] = [ [ { :bar => true } ] ]
+ expect(root).to receive(:reset_cache).with("foo")
+ vivid["foo"] = [ [ { :bar => true } ] ]
expect(vivid["foo"].class).to eql(Chef::Node::AttrArray)
expect(vivid["foo"][0].class).to eql(Chef::Node::AttrArray)
expect(vivid["foo"][0][0].class).to eql(Chef::Node::VividMash)
@@ -57,8 +66,8 @@ describe Chef::Node::VividMash do
end
it "deep converts values through hashes" do
- allow(root).to receive(:reset_cache)
- vivid[:foo] = { baz: { :bar => true } }
+ expect(root).to receive(:reset_cache).with("foo")
+ vivid["foo"] = { baz: { :bar => true } }
expect(vivid["foo"]).to be_an_instance_of(Chef::Node::VividMash)
expect(vivid["foo"]["baz"]).to be_an_instance_of(Chef::Node::VividMash)
expect(vivid["foo"]["baz"]["bar"]).to be true
@@ -67,182 +76,144 @@ describe Chef::Node::VividMash do
context "#read" do
before do
- # vivify the vividmash, then we're read-only so the cache should never be cleared afterwards
- vivid
expect(root).not_to receive(:reset_cache)
end
it "reads hashes deeply" do
- with_breadcrumb("one")
expect(vivid.read("one", "two", "three")).to eql("four")
end
it "does not trainwreck when hitting hash keys that do not exist" do
- with_breadcrumb("one")
expect(vivid.read("one", "five", "six")).to eql(nil)
end
it "does not trainwreck when hitting an array with an out of bounds index" do
- with_breadcrumb("array")
expect(vivid.read("array", 5, "one")).to eql(nil)
end
it "does not trainwreck when hitting an array with a string key" do
- with_breadcrumb("array")
expect(vivid.read("array", "one", "two")).to eql(nil)
end
it "does not trainwreck when traversing a nil" do
- with_breadcrumb("nil")
expect(vivid.read("nil", "one", "two")).to eql(nil)
end
end
context "#exist?" do
before do
- # vivify the vividmash, then we're read-only so the cache should never be cleared afterwards
- vivid
expect(root).not_to receive(:reset_cache)
end
it "true if there's a hash key there" do
- with_breadcrumb("one")
expect(vivid.exist?("one", "two", "three")).to be true
end
it "true for intermediate hashes" do
- with_breadcrumb("one")
expect(vivid.exist?("one")).to be true
end
it "true for arrays that exist" do
- with_breadcrumb("array")
expect(vivid.exist?("array", 1)).to be true
end
it "true when the value of the key is nil" do
- with_breadcrumb("nil")
expect(vivid.exist?("nil")).to be true
end
it "false when attributes don't exist" do
- with_breadcrumb("one")
expect(vivid.exist?("one", "five", "six")).to be false
end
it "false when traversing a non-container" do
- with_breadcrumb("one")
expect(vivid.exist?("one", "two", "three", "four")).to be false
end
it "false when an array index does not exist" do
- with_breadcrumb("array")
expect(vivid.exist?("array", 3)).to be false
end
it "false when traversing a nil" do
- with_breadcrumb("nil")
expect(vivid.exist?("nil", "foo", "bar")).to be false
end
end
context "#read!" do
before do
- # vivify the vividmash, then we're read-only so the cache should never be cleared afterwards
- vivid
expect(root).not_to receive(:reset_cache)
end
it "reads hashes deeply" do
- with_breadcrumb("one")
expect(vivid.read!("one", "two", "three")).to eql("four")
end
it "reads arrays deeply" do
- with_breadcrumb("array")
expect(vivid.read!("array", 1)).to eql(1)
end
it "throws an exception when attributes do not exist" do
- with_breadcrumb("one")
expect { vivid.read!("one", "five", "six") }.to raise_error(Chef::Exceptions::NoSuchAttribute)
end
it "throws an exception when traversing a non-container" do
- with_breadcrumb("one")
expect { vivid.read!("one", "two", "three", "four") }.to raise_error(Chef::Exceptions::NoSuchAttribute)
end
it "throws an exception when an array element does not exist" do
- with_breadcrumb("array")
expect { vivid.read!("array", 3) }.to raise_error(Chef::Exceptions::NoSuchAttribute)
end
end
context "#write" do
- before do
- vivid
- expect(root).not_to receive(:reset_cache).with(nil)
- end
-
it "should write into hashes" do
- with_breadcrumb("one")
expect(root).to receive(:reset_cache).at_least(:once).with("one")
vivid.write("one", "five", "six")
expect(vivid["one"]["five"]).to eql("six")
end
it "should deeply autovivify" do
- with_breadcrumb("one")
expect(root).to receive(:reset_cache).at_least(:once).with("one")
vivid.write("one", "five", "six", "seven", "eight", "nine", "ten")
expect(vivid["one"]["five"]["six"]["seven"]["eight"]["nine"]).to eql("ten")
end
it "should raise an exception if you overwrite an array with a hash" do
- with_breadcrumb("array")
expect(root).to receive(:reset_cache).at_least(:once).with("array")
vivid.write("array", "five", "six")
expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => { "five" => "six" }, "nil" => nil })
end
it "should raise an exception if you traverse through an array with a hash" do
- with_breadcrumb("array")
expect(root).to receive(:reset_cache).at_least(:once).with("array")
vivid.write("array", "five", "six", "seven")
expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => { "five" => { "six" => "seven" } }, "nil" => nil })
end
it "should raise an exception if you overwrite a string with a hash" do
- with_breadcrumb("one")
expect(root).to receive(:reset_cache).at_least(:once).with("one")
vivid.write("one", "two", "three", "four", "five")
expect(vivid).to eql({ "one" => { "two" => { "three" => { "four" => "five" } } }, "array" => [ 0, 1, 2 ], "nil" => nil })
end
it "should raise an exception if you traverse through a string with a hash" do
- with_breadcrumb("one")
expect(root).to receive(:reset_cache).at_least(:once).with("one")
vivid.write("one", "two", "three", "four", "five", "six")
expect(vivid).to eql({ "one" => { "two" => { "three" => { "four" => { "five" => "six" } } } }, "array" => [ 0, 1, 2 ], "nil" => nil })
end
it "should raise an exception if you overwrite a nil with a hash" do
- with_breadcrumb("nil")
expect(root).to receive(:reset_cache).at_least(:once).with("nil")
vivid.write("nil", "one", "two")
expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => { "one" => "two" } })
end
it "should raise an exception if you traverse through a nil with a hash" do
- with_breadcrumb("nil")
expect(root).to receive(:reset_cache).at_least(:once).with("nil")
vivid.write("nil", "one", "two", "three")
expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => { "one" => { "two" => "three" } } })
end
it "writes with a block" do
- with_breadcrumb("one")
expect(root).to receive(:reset_cache).at_least(:once).with("one")
vivid.write("one", "five") { "six" }
expect(vivid["one"]["five"]).to eql("six")
@@ -250,69 +221,55 @@ describe Chef::Node::VividMash do
end
context "#write!" do
- before do
- vivid
- expect(root).not_to receive(:reset_cache).with(nil)
- end
-
it "should write into hashes" do
- with_breadcrumb("one")
expect(root).to receive(:reset_cache).at_least(:once).with("one")
vivid.write!("one", "five", "six")
expect(vivid["one"]["five"]).to eql("six")
end
it "should deeply autovivify" do
- with_breadcrumb("one")
expect(root).to receive(:reset_cache).at_least(:once).with("one")
vivid.write!("one", "five", "six", "seven", "eight", "nine", "ten")
expect(vivid["one"]["five"]["six"]["seven"]["eight"]["nine"]).to eql("ten")
end
it "should raise an exception if you overwrite an array with a hash" do
- with_breadcrumb("array")
expect(root).not_to receive(:reset_cache)
expect { vivid.write!("array", "five", "six") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
end
it "should raise an exception if you traverse through an array with a hash" do
- with_breadcrumb("array")
expect(root).not_to receive(:reset_cache)
expect { vivid.write!("array", "five", "six", "seven") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
end
it "should raise an exception if you overwrite a string with a hash" do
- with_breadcrumb("one")
expect(root).not_to receive(:reset_cache)
expect { vivid.write!("one", "two", "three", "four", "five") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
end
it "should raise an exception if you traverse through a string with a hash" do
- with_breadcrumb("one")
expect(root).not_to receive(:reset_cache)
expect { vivid.write!("one", "two", "three", "four", "five", "six") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
end
it "should raise an exception if you overwrite a nil with a hash" do
- with_breadcrumb("nil")
expect(root).not_to receive(:reset_cache)
expect { vivid.write!("nil", "one", "two") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
end
it "should raise an exception if you traverse through a nil with a hash" do
- with_breadcrumb("nil")
expect(root).not_to receive(:reset_cache)
expect { vivid.write!("nil", "one", "two", "three") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
end
it "writes with a block" do
- with_breadcrumb("one")
expect(root).to receive(:reset_cache).at_least(:once).with("one")
vivid.write!("one", "five") { "six" }
expect(vivid["one"]["five"]).to eql("six")
@@ -320,41 +277,31 @@ describe Chef::Node::VividMash do
end
context "#unlink" do
- before do
- vivid
- expect(root).not_to receive(:reset_cache).with(nil)
- end
-
it "should return nil if the keys don't already exist" do
- expect(root).to receive(:top_level_breadcrumb=).with(nil).at_least(:once).and_call_original
expect(root).not_to receive(:reset_cache)
expect(vivid.unlink("five", "six", "seven", "eight")).to eql(nil)
expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
end
it "should unlink hashes" do
- with_breadcrumb("one")
expect(root).to receive(:reset_cache).at_least(:once).with("one")
expect( vivid.unlink("one") ).to eql({ "two" => { "three" => "four" } })
expect(vivid).to eql({ "array" => [ 0, 1, 2 ], "nil" => nil })
end
it "should unlink array elements" do
- with_breadcrumb("array")
expect(root).to receive(:reset_cache).at_least(:once).with("array")
expect(vivid.unlink("array", 2)).to eql(2)
expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1 ], "nil" => nil })
end
it "should unlink nil" do
- with_breadcrumb("nil")
expect(root).to receive(:reset_cache).at_least(:once).with("nil")
expect(vivid.unlink("nil")).to eql(nil)
expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ] })
end
it "should traverse a nil and safely do nothing" do
- with_breadcrumb("nil")
expect(root).not_to receive(:reset_cache)
expect(vivid.unlink("nil", "foo")).to eql(nil)
expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
@@ -362,41 +309,31 @@ describe Chef::Node::VividMash do
end
context "#unlink!" do
- before do
- vivid
- expect(root).not_to receive(:reset_cache).with(nil)
- end
-
it "should raise an exception if the keys don't already exist" do
- expect(root).to receive(:top_level_breadcrumb=).with(nil).at_least(:once).and_call_original
expect(root).not_to receive(:reset_cache)
expect { vivid.unlink!("five", "six", "seven", "eight") }.to raise_error(Chef::Exceptions::NoSuchAttribute)
expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
end
it "should unlink! hashes" do
- with_breadcrumb("one")
expect(root).to receive(:reset_cache).at_least(:once).with("one")
expect( vivid.unlink!("one") ).to eql({ "two" => { "three" => "four" } })
expect(vivid).to eql({ "array" => [ 0, 1, 2 ], "nil" => nil })
end
it "should unlink! array elements" do
- with_breadcrumb("array")
expect(root).to receive(:reset_cache).at_least(:once).with("array")
expect(vivid.unlink!("array", 2)).to eql(2)
expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1 ], "nil" => nil })
end
it "should unlink! nil" do
- with_breadcrumb("nil")
expect(root).to receive(:reset_cache).at_least(:once).with("nil")
expect(vivid.unlink!("nil")).to eql(nil)
expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ] })
end
it "should raise an exception if it traverses a nil" do
- with_breadcrumb("nil")
expect(root).not_to receive(:reset_cache)
expect { vivid.unlink!("nil", "foo") }.to raise_error(Chef::Exceptions::NoSuchAttribute)
expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })