diff options
author | Lamont Granquist <lamont@scriptkiddie.org> | 2016-09-26 19:07:26 -0700 |
---|---|---|
committer | Lamont Granquist <lamont@scriptkiddie.org> | 2016-10-25 09:29:29 -0700 |
commit | 6859b99ca4b79077c99ddeadc91333ce86b40454 (patch) | |
tree | 9e32863445d61487f5a1ef25e9994a2c280f19a4 /lib/chef/node | |
parent | 16a08bcf2a78aa1353ec88f0a8e0282973434914 (diff) | |
download | chef-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.rb | 128 | ||||
-rw-r--r-- | lib/chef/node/mixin/deep_merge_cache.rb | 70 |
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 |