summaryrefslogtreecommitdiff
path: root/lib/chef/node/attribute.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/chef/node/attribute.rb')
-rw-r--r--lib/chef/node/attribute.rb254
1 files changed, 254 insertions, 0 deletions
diff --git a/lib/chef/node/attribute.rb b/lib/chef/node/attribute.rb
new file mode 100644
index 0000000000..22075533b4
--- /dev/null
+++ b/lib/chef/node/attribute.rb
@@ -0,0 +1,254 @@
+#--
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Author:: AJ Christensen (<aj@opscode.com>)
+# Copyright:: Copyright (c) 2008 Opscode, 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.
+#
+
+require 'chef/node/immutable_collections'
+require 'chef/node/attribute_collections'
+require 'chef/mixin/deep_merge'
+require 'chef/log'
+
+class Chef
+ class Node
+
+ # == Attribute
+ # Attribute implements a nested key-value (Hash) and flat collection
+ # (Array) data structure supporting multiple levels of precedence, such
+ # that a given key may have multiple values internally, but will only
+ # return the highest precedence value when reading.
+ class Attribute < Mash
+
+ include Immutablize
+
+ include Enumerable
+
+ COMPONENTS = [:@default, :@normal, :@override, :@automatic].freeze
+ COMPONENT_ACCESSORS = {:default => :@default,
+ :normal => :@normal,
+ :override => :@override,
+ :automatic => :@automatic
+ }
+
+ attr_accessor :properties
+ attr_reader :serial_number
+
+ [:all?,
+ :any?,
+ :assoc,
+ :chunk,
+ :collect,
+ :collect_concat,
+ :compare_by_identity,
+ :compare_by_identity?,
+ :count,
+ :cycle,
+ :detect,
+ :drop,
+ :drop_while,
+ :each,
+ :each_cons,
+ :each_entry,
+ :each_key,
+ :each_pair,
+ :each_slice,
+ :each_value,
+ :each_with_index,
+ :each_with_object,
+ :empty?,
+ :entries,
+ :except,
+ :fetch,
+ :find,
+ :find_all,
+ :find_index,
+ :first,
+ :flat_map,
+ :flatten,
+ :grep,
+ :group_by,
+ :has_value?,
+ :include?,
+ :index,
+ :inject,
+ :invert,
+ :key,
+ :keys,
+ :length,
+ :map,
+ :max,
+ :max_by,
+ :merge,
+ :min,
+ :min_by,
+ :minmax,
+ :minmax_by,
+ :none?,
+ :one?,
+ :partition,
+ :rassoc,
+ :reduce,
+ :reject,
+ :reverse_each,
+ :select,
+ :size,
+ :slice_before,
+ :sort,
+ :sort_by,
+ :store,
+ :symbolize_keys,
+ :take,
+ :take_while,
+ :to_a,
+ :to_hash,
+ :to_set,
+ :value?,
+ :values,
+ :values_at,
+ :zip].each do |delegated_method|
+ class_eval(<<-METHOD_DEFN)
+ def #{delegated_method}(*args, &block)
+ merged_attributes.send(:#{delegated_method}, *args, &block)
+ end
+ METHOD_DEFN
+ end
+
+ def initialize(normal, default, override, automatic)
+ @serial_number = 0
+ @set_unless_present = false
+
+ @normal = VividMash.new(self, normal)
+ @default = VividMash.new(self, default)
+ @override = VividMash.new(self, override)
+ @automatic = VividMash.new(self, automatic)
+
+ @merged_attributes = nil
+ end
+
+ def set_unless_value_present=(setting)
+ @set_unless_present = setting
+ end
+
+ def reset_cache
+ @serial_number += 1
+ @merged_attributes = nil
+ end
+
+ def reset
+ @serial_number += 1
+ @merged_attributes = nil
+ end
+
+ def default
+ @default
+ end
+
+ def default=(new_data)
+ reset
+ @default = VividMash.new(self, new_data)
+ end
+
+ def normal
+ @normal
+ end
+
+ def normal=(new_data)
+ reset
+ @normal = VividMash.new(self, new_data)
+ end
+
+ def override
+ @override
+ end
+
+ def override=(new_data)
+ reset
+ @override = VividMash.new(self, new_data)
+ end
+
+ def automatic
+ @automatic
+ end
+
+ def automatic=(new_data)
+ reset
+ @automatic = VividMash.new(self, new_data)
+ end
+
+ def merged_attributes
+ @merged_attributes ||= begin
+ resolved_attrs = COMPONENTS.inject(Mash.new) do |merged, component_ivar|
+ component_value = instance_variable_get(component_ivar)
+ Chef::Mixin::DeepMerge.merge(merged, component_value)
+ end
+ immutablize(self, resolved_attrs)
+ end
+ end
+
+ def [](key)
+ merged_attributes[key]
+ end
+
+ def []=(key, value)
+ merged_attributes[key] = value
+ end
+
+ def has_key?(key)
+ COMPONENTS.any? do |component_ivar|
+ instance_variable_get(component_ivar).has_key?(key)
+ end
+ end
+
+ alias :attribute? :has_key?
+ alias :member? :has_key?
+ alias :include? :has_key?
+ alias :key? :has_key?
+
+ alias :each_attribute :each
+
+ def method_missing(symbol, *args)
+ if args.empty?
+ if key?(symbol)
+ self[symbol]
+ else
+ raise NoMethodError, "Undefined method or attribute `#{symbol}' on `node'"
+ end
+ elsif symbol.to_s =~ /=$/
+ key_to_set = symbol.to_s[/^(.+)=$/, 1]
+ self[key_to_set] = (args.length == 1 ? args[0] : args)
+ else
+ raise NoMethodError, "Undefined node attribute or method `#{symbol}' on `node'"
+ end
+ end
+
+ def inspect
+ "#<#{self.class} " << (COMPONENTS + [:@merged_attributes, :@properties]).map{|iv|
+ "#{iv}=#{instance_variable_get(iv).inspect}"
+ }.join(', ') << ">"
+ end
+
+ def set_unless?
+ @set_unless_present
+ end
+
+ def stale_subtree?(serial_number)
+ serial_number != @serial_number
+ end
+
+ end
+
+ end
+end