summaryrefslogtreecommitdiff
path: root/lib/chef/node
diff options
context:
space:
mode:
authorLamont Granquist <lamont@scriptkiddie.org>2016-09-26 19:07:26 -0700
committerLamont Granquist <lamont@scriptkiddie.org>2016-10-25 09:29:29 -0700
commit6859b99ca4b79077c99ddeadc91333ce86b40454 (patch)
tree9e32863445d61487f5a1ef25e9994a2c280f19a4 /lib/chef/node
parent16a08bcf2a78aa1353ec88f0a8e0282973434914 (diff)
downloadchef-6859b99ca4b79077c99ddeadc91333ce86b40454.tar.gz
add a deep_merge_cache mixin
gets the inheritence chain correct Signed-off-by: Lamont Granquist <lamont@scriptkiddie.org>
Diffstat (limited to 'lib/chef/node')
-rw-r--r--lib/chef/node/attribute.rb128
-rw-r--r--lib/chef/node/mixin/deep_merge_cache.rb70
2 files changed, 115 insertions, 83 deletions
diff --git a/lib/chef/node/attribute.rb b/lib/chef/node/attribute.rb
index 2f498ff84c..d67571a6a8 100644
--- a/lib/chef/node/attribute.rb
+++ b/lib/chef/node/attribute.rb
@@ -17,6 +17,7 @@
# limitations under the License.
#
+require "chef/node/mixin/deep_merge_cache"
require "chef/node/mixin/immutablize_hash"
require "chef/node/mixin/state_tracking"
require "chef/node/immutable_collections"
@@ -36,9 +37,18 @@ class Chef
class Attribute < Mash
include Immutablize
-
+ # FIXME: what is include Enumerable doing up here, when down below we delegate
+ # most of the Enumerable/Hash things to the underlying merged ImmutableHash. That
+ # is, in fact, the correct, thing to do, while including Enumerable to try to create
+ # a hash-like API gets lots of things wrong because of the difference between the
+ # Hash `each do |key, value|` vs the Array-like `each do |value|` API that Enumerable
+ # expects. This include should probably be deleted?
include Enumerable
+ include Chef::Node::Mixin::DeepMergeCache
+ include Chef::Node::Mixin::StateTracking
+ include Chef::Node::Mixin::ImmutablizeHash
+
# List of the component attribute hashes, in order of precedence, low to
# high.
COMPONENTS = [
@@ -177,19 +187,6 @@ class Chef
# return the automatic level attribute component
attr_reader :automatic
- # 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.
- attr_accessor :deep_merge_cache
-
def initialize(normal, default, override, automatic)
@default = VividMash.new(self, default)
@env_default = VividMash.new(self, {})
@@ -204,12 +201,7 @@ class Chef
@force_override = VividMash.new(self, {})
@automatic = VividMash.new(self, automatic)
-
- @merged_attributes = nil
- @combined_override = nil
- @combined_default = nil
- @top_level_breadcrumb = nil
- @deep_merge_cache = {}
+ super()
end
# Debug what's going on with an attribute. +args+ is a path spec to the
@@ -237,20 +229,6 @@ class Chef
end
end
- # Invalidate a key in the deep_merge_cache. If called with nil, or no arg, this will invalidate
- # the entire deep_merge cache. In the case of the user doing node.default['foo']['bar']['baz']=
- # that eventually results in a call to reset_cache('foo') here. A node.default=hash_thing call
- # must invalidate the entire cache and re-deep-merge the entire node object.
- def reset_cache(path = nil)
- if path.nil?
- @deep_merge_cache = {}
- else
- deep_merge_cache.delete(path.to_s)
- end
- end
-
- alias :reset :reset_cache
-
# Set the cookbook level default attribute component to +new_data+.
def default=(new_data)
reset
@@ -415,36 +393,6 @@ class Chef
write(:force_override, *args, value)
end
- # method-style access to attributes
-
- def read(*path)
- merged_attributes.read(*path)
- end
-
- def read!(*path)
- merged_attributes.read!(*path)
- end
-
- def exist?(*path)
- merged_attributes.exist?(*path)
- end
-
- def write(level, *args, &block)
- self.send(level).write(*args, &block)
- end
-
- def write!(level, *args, &block)
- self.send(level).write!(*args, &block)
- end
-
- def unlink(level, *path)
- self.send(level).unlink(*path)
- end
-
- def unlink!(level, *path)
- self.send(level).unlink!(*path)
- end
-
#
# Accessing merged attributes.
#
@@ -486,24 +434,39 @@ class Chef
write(:normal, *args) if read(*args[0...-1]).nil?
end
- def [](key)
- if deep_merge_cache.has_key?(key.to_s)
- # return the cache of the deep merged values by top-level key
- deep_merge_cache[key.to_s]
- else
- # save all the work of computing node[key]
- deep_merge_cache[key.to_s] = merged_attributes(key)
+ def has_key?(key)
+ COMPONENTS.any? do |component_ivar|
+ instance_variable_get(component_ivar).has_key?(key)
end
end
+ # method-style access to attributes (has to come after the prepended ImmutablizeHash)
- def []=(key, value)
- raise Exceptions::ImmutableAttributeModification
+ def read(*path)
+ merged_attributes.read(*path)
end
- def has_key?(key)
- COMPONENTS.any? do |component_ivar|
- instance_variable_get(component_ivar).has_key?(key)
- end
+ def read!(*path)
+ merged_attributes.read!(*path)
+ end
+
+ def exist?(*path)
+ merged_attributes.exist?(*path)
+ end
+
+ def write(level, *args, &block)
+ self.send(level).write(*args, &block)
+ end
+
+ def write!(level, *args, &block)
+ self.send(level).write!(*args, &block)
+ end
+
+ def unlink(level, *path)
+ self.send(level).unlink(*path)
+ end
+
+ def unlink!(level, *path)
+ self.send(level).unlink!(*path)
end
alias :attribute? :has_key?
@@ -632,14 +595,13 @@ class Chef
Chef::Mixin::DeepMerge.deep_merge(component_value, merged)
end
end
- end
- # needed for __path
- def convert_key(key)
- key.kind_of?(Symbol) ? key.to_s : key
+ # needed for __path
+ def convert_key(key)
+ key.kind_of?(Symbol) ? key.to_s : key
+ end
+
end
- prepend Chef::Node::Mixin::StateTracking
- prepend Chef::Node::Mixin::ImmutablizeHash
end
end
diff --git a/lib/chef/node/mixin/deep_merge_cache.rb b/lib/chef/node/mixin/deep_merge_cache.rb
new file mode 100644
index 0000000000..d6a2149c52
--- /dev/null
+++ b/lib/chef/node/mixin/deep_merge_cache.rb
@@ -0,0 +1,70 @@
+#--
+# Copyright:: Copyright 2016, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+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.
+ attr_accessor :deep_merge_cache
+
+ def initialize
+ @merged_attributes = nil
+ @combined_override = nil
+ @combined_default = nil
+ @top_level_breadcrumb = nil
+ @deep_merge_cache = {}
+ end
+
+ # Invalidate a key in the deep_merge_cache. If called with nil, or no arg, this will invalidate
+ # the entire deep_merge cache. In the case of the user doing node.default['foo']['bar']['baz']=
+ # that eventually results in a call to reset_cache('foo') here. A node.default=hash_thing call
+ # must invalidate the entire cache and re-deep-merge the entire node object.
+ def reset_cache(path = nil)
+ if path.nil?
+ @deep_merge_cache = {}
+ else
+ deep_merge_cache.delete(path.to_s)
+ end
+ end
+
+ alias :reset :reset_cache
+
+ def [](key)
+ if deep_merge_cache.has_key?(key.to_s)
+ # return the cache of the deep merged values by top-level key
+ deep_merge_cache[key.to_s]
+ else
+ # save all the work of computing node[key]
+ deep_merge_cache[key.to_s] = merged_attributes(key)
+ end
+ end
+
+ end
+ end
+ end
+end