Class: Chef::Node::Attribute
- Inherits:
-
Object
- Object
- Chef::Node::Attribute
- Includes:
- Enumerable
- Defined in:
- lib/chef/node/attribute.rb
Constant Summary collapse
- HIDDEN_ATTRIBUES =
[:@override, :@attribute, :@default, :@normal, :@automatic]
Instance Attribute Summary collapse
-
#auto_vivifiy_on_read ⇒ Object
Returns the value of attribute auto_vivifiy_on_read.
-
#automatic ⇒ Object
Returns the value of attribute automatic.
-
#current_automatic ⇒ Object
Returns the value of attribute current_automatic.
-
#current_default ⇒ Object
Returns the value of attribute current_default.
-
#current_normal ⇒ Object
Returns the value of attribute current_normal.
-
#current_override ⇒ Object
Returns the value of attribute current_override.
-
#default ⇒ Object
Returns the value of attribute default.
-
#normal ⇒ Object
Returns the value of attribute normal.
-
#override ⇒ Object
Returns the value of attribute override.
-
#set_type ⇒ Object
Returns the value of attribute set_type.
-
#set_unless_value_present ⇒ Object
Returns the value of attribute set_unless_value_present.
Instance Method Summary collapse
- #[](key) ⇒ Object
- #[]=(key, value) ⇒ Object
- #attribute ⇒ Object
- #attribute=(value) ⇒ Object
- #auto_vivifiy(data_hash, key) ⇒ Object
- #auto_vivifiy_on_read? ⇒ Boolean
- #component_has_key?(component_attrs, key) ⇒ Boolean
- #delete(key) ⇒ Object
- #delete_from_component(component_attrs, key) ⇒ Object
- #determine_value(a_value, o_value, n_value, d_value) ⇒ Object
- #each(&block) ⇒ Object
- #each_attribute(&block) ⇒ Object
- #each_key(&block) ⇒ Object
- #each_pair(&block) ⇒ Object
- #each_value(&block) ⇒ Object
- #empty? ⇒ Boolean
- #fetch(key, default_value = nil, &block) ⇒ Object
- #get_keys ⇒ Object
- #get_value(data_hash, key) ⇒ Object
- #has_key?(key) ⇒ Boolean (also: #attribute?, #include?, #key?, #member?)
- #has_value?(value) ⇒ Boolean (also: #value?)
- #hash_and_not_cna?(to_check) ⇒ Boolean
- #index(value) ⇒ Object
-
#initialize(normal, default, override, automatic, state = []) ⇒ Attribute
constructor
A new instance of Attribute.
- #inspect ⇒ Object
- #keys ⇒ Object
-
#kind_of?(klass) ⇒ Boolean
Writing this method hurts me a little bit.
-
#method_missing(symbol, *args) ⇒ Object
Fetches or sets the value, depending on if any arguments are given.
-
#reset ⇒ Object
Reset our internal current_nesting_level to the top of every tree.
- #set_type_hash ⇒ Object
- #set_value(data_hash, key, value) ⇒ Object
- #size ⇒ Object (also: #length)
- #to_hash ⇒ Object
- #value_at_current_nesting(component_attrs, key) ⇒ Object
- #value_or_descend(data_hash, key, auto_vivifiy = false) ⇒ Object
- #values ⇒ Object
Constructor Details
#initialize(normal, default, override, automatic, state = []) ⇒ Attribute
Returns a new instance of Attribute.
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/chef/node/attribute.rb', line 42 def initialize(normal, default, override, automatic, state=[]) @normal = normal @current_normal = normal @default = default @current_default = default @override = override @current_override = override @automatic = automatic @current_automatic = automatic @current_nesting_level = state @auto_vivifiy_on_read = false @set_unless_value_present = false @set_type = nil @has_been_read = false end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(symbol, *args) ⇒ Object
Fetches or sets the value, depending on if any arguments are given.
Fetching
If no arguments are given, fetches the value:
node.network
=> {network data}
Getters will find either a string or symbol key.
Setting
If arguments are given, a value will be set. Both normal setter and DSL style setters are allowed:
node.foo = "bar"
node.foo("bar")
Both set node = “bar”
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 |
# File 'lib/chef/node/attribute.rb', line 418 def method_missing(symbol, *args) if args.empty? if key?(symbol) self[symbol] elsif key?(symbol.to_s) self[symbol.to_s] elsif auto_vivifiy_on_read? self[symbol] = Mash.new self[symbol] else raise ArgumentError, "Attribute #{symbol} is not defined!" unless auto_vivifiy_on_read end else key_to_set = symbol.to_s[/^(.+)=$/, 1] || symbol self[key_to_set] = (args.length == 1 ? args[0] : args) end end |
Instance Attribute Details
#auto_vivifiy_on_read ⇒ Object
Returns the value of attribute auto_vivifiy_on_read.
28 29 30 |
# File 'lib/chef/node/attribute.rb', line 28 def auto_vivifiy_on_read @auto_vivifiy_on_read end |
#automatic ⇒ Object
Returns the value of attribute automatic.
28 29 30 |
# File 'lib/chef/node/attribute.rb', line 28 def automatic @automatic end |
#current_automatic ⇒ Object
Returns the value of attribute current_automatic.
28 29 30 |
# File 'lib/chef/node/attribute.rb', line 28 def current_automatic @current_automatic end |
#current_default ⇒ Object
Returns the value of attribute current_default.
28 29 30 |
# File 'lib/chef/node/attribute.rb', line 28 def current_default @current_default end |
#current_normal ⇒ Object
Returns the value of attribute current_normal.
28 29 30 |
# File 'lib/chef/node/attribute.rb', line 28 def current_normal @current_normal end |
#current_override ⇒ Object
Returns the value of attribute current_override.
28 29 30 |
# File 'lib/chef/node/attribute.rb', line 28 def current_override @current_override end |
#default ⇒ Object
Returns the value of attribute default.
28 29 30 |
# File 'lib/chef/node/attribute.rb', line 28 def default @default end |
#normal ⇒ Object
Returns the value of attribute normal.
28 29 30 |
# File 'lib/chef/node/attribute.rb', line 28 def normal @normal end |
#override ⇒ Object
Returns the value of attribute override.
28 29 30 |
# File 'lib/chef/node/attribute.rb', line 28 def override @override end |
#set_type ⇒ Object
Returns the value of attribute set_type.
28 29 30 |
# File 'lib/chef/node/attribute.rb', line 28 def set_type @set_type end |
#set_unless_value_present ⇒ Object
Returns the value of attribute set_unless_value_present.
28 29 30 |
# File 'lib/chef/node/attribute.rb', line 28 def set_unless_value_present @set_unless_value_present end |
Instance Method Details
#[](key) ⇒ Object
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/chef/node/attribute.rb', line 89 def [](key) @current_nesting_level << key # We set this to so that we can cope with ||= as a setting. # See the comments in []= for more details. @has_been_read = true # If we have a set type, our destiny is to write if @set_type a_value = @set_type == :automatic ? value_or_descend(current_automatic, key, auto_vivifiy_on_read) : nil o_value = @set_type == :override ? value_or_descend(current_override, key, auto_vivifiy_on_read) : nil n_value = @set_type == :normal ? value_or_descend(current_normal, key, auto_vivifiy_on_read) : nil d_value = @set_type == :default ? value_or_descend(current_default, key, auto_vivifiy_on_read) : nil determine_value(a_value, o_value, n_value, d_value) # Our destiny is only to read, so we get the full list. else a_value = value_or_descend(current_automatic, key) o_value = value_or_descend(current_override, key) n_value = value_or_descend(current_normal, key) d_value = value_or_descend(current_default, key) determine_value(a_value, o_value, n_value, d_value) end end |
#[]=(key, value) ⇒ Object
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 |
# File 'lib/chef/node/attribute.rb', line 319 def []=(key, value) # If we don't have one, then we'll pretend we're normal @set_type ||= :normal if set_unless_value_present if get_value(set_type_hash, key) != nil Chef::Log.debug("Not setting #{@current_nesting_level.join("/")}/#{key} to #{value.inspect} because it has a #{@set_type} value already") return false end end # If we have been read, and the key we are writing is the same # as our parent, we have most like been ||='ed. So we need to # just rewind a bit. # # In practice, these objects are single use - this is just # supporting one more single-use style. @current_nesting_level.pop if @has_been_read && @current_nesting_level.last == key set_value(set_type_hash, key, value) value end |
#attribute ⇒ Object
58 59 60 |
# File 'lib/chef/node/attribute.rb', line 58 def attribute normal end |
#attribute=(value) ⇒ Object
62 63 64 |
# File 'lib/chef/node/attribute.rb', line 62 def attribute=(value) normal = value end |
#auto_vivifiy(data_hash, key) ⇒ Object
371 372 373 374 375 376 377 378 379 380 |
# File 'lib/chef/node/attribute.rb', line 371 def auto_vivifiy(data_hash, key) if data_hash.has_key?(key) unless data_hash[key].respond_to?(:has_key?) raise ArgumentError, "You tried to set a nested key, where the parent is not a hash-like object: #{@current_nesting_level.join("/")}/#{key} " unless auto_vivifiy_on_read end else data_hash[key] = Mash.new end data_hash end |
#auto_vivifiy_on_read? ⇒ Boolean
367 368 369 |
# File 'lib/chef/node/attribute.rb', line 367 def auto_vivifiy_on_read? auto_vivifiy_on_read end |
#component_has_key?(component_attrs, key) ⇒ Boolean
471 472 473 474 475 |
# File 'lib/chef/node/attribute.rb', line 471 def component_has_key?(component_attrs,key) # get the Hash-like object at the current nesting level: nested_attrs = value_at_current_nesting(component_attrs, key) nested_attrs.respond_to?(:key?) && nested_attrs.key?(key) end |
#delete(key) ⇒ Object
453 454 455 456 457 458 |
# File 'lib/chef/node/attribute.rb', line 453 def delete(key) [@automatic, @override, @normal, @default].inject(nil) do |return_value, attrs| deleted_value = delete_from_component(attrs, key) return_value || deleted_value end end |
#delete_from_component(component_attrs, key) ⇒ Object
460 461 462 463 464 465 466 467 468 469 |
# File 'lib/chef/node/attribute.rb', line 460 def delete_from_component(component_attrs, key) # get the Hash-like object at the current nesting level: nested_attrs = value_at_current_nesting(component_attrs, key) if nested_attrs.respond_to?(:delete) nested_attrs.delete(key) else nil end end |
#determine_value(a_value, o_value, n_value, d_value) ⇒ Object
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 |
# File 'lib/chef/node/attribute.rb', line 289 def determine_value(a_value, o_value, n_value, d_value) if hash_and_not_cna?(a_value) value = {} value = Chef::Mixin::DeepMerge.merge(value, d_value) if hash_and_not_cna?(d_value) value = Chef::Mixin::DeepMerge.merge(value, n_value) if hash_and_not_cna?(n_value) value = Chef::Mixin::DeepMerge.merge(value, o_value) if hash_and_not_cna?(o_value) value = Chef::Mixin::DeepMerge.merge(value, a_value) value elsif hash_and_not_cna?(o_value) value = {} value = Chef::Mixin::DeepMerge.merge(value, d_value) if hash_and_not_cna?(d_value) value = Chef::Mixin::DeepMerge.merge(value, n_value) if hash_and_not_cna?(n_value) value = Chef::Mixin::DeepMerge.merge(value, o_value) value elsif hash_and_not_cna?(n_value) value = {} value = Chef::Mixin::DeepMerge.merge(value, d_value) if hash_and_not_cna?(d_value) value = Chef::Mixin::DeepMerge.merge(value, n_value) value elsif hash_and_not_cna?(d_value) d_value else return a_value if ! a_value.nil? return o_value if ! o_value.nil? return n_value if ! n_value.nil? return d_value if ! d_value.nil? return nil end end |
#each(&block) ⇒ Object
128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/chef/node/attribute.rb', line 128 def each(&block) get_keys.each do |key| value = determine_value( get_value(automatic, key), get_value(override, key), get_value(normal, key), get_value(default, key) ) block.call([key, value]) end end |
#each_attribute(&block) ⇒ Object
152 153 154 155 156 157 158 159 160 161 162 |
# File 'lib/chef/node/attribute.rb', line 152 def each_attribute(&block) get_keys.each do |key| value = determine_value( get_value(automatic, key), get_value(override, key), get_value(normal, key), get_value(default, key) ) block.call(key, value) end end |
#each_key(&block) ⇒ Object
164 165 166 167 168 |
# File 'lib/chef/node/attribute.rb', line 164 def each_key(&block) get_keys.each do |key| block.call(key) end end |
#each_pair(&block) ⇒ Object
140 141 142 143 144 145 146 147 148 149 150 |
# File 'lib/chef/node/attribute.rb', line 140 def each_pair(&block) get_keys.each do |key| value = determine_value( get_value(automatic, key), get_value(override, key), get_value(normal, key), get_value(default, key) ) block.call(key, value) end end |
#each_value(&block) ⇒ Object
170 171 172 173 174 175 176 177 178 179 180 |
# File 'lib/chef/node/attribute.rb', line 170 def each_value(&block) get_keys.each do |key| value = determine_value( get_value(automatic, key), get_value(override, key), get_value(normal, key), get_value(default, key) ) block.call(value) end end |
#empty? ⇒ Boolean
182 183 184 |
# File 'lib/chef/node/attribute.rb', line 182 def empty? get_keys.empty? end |
#fetch(key, default_value = nil, &block) ⇒ Object
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
# File 'lib/chef/node/attribute.rb', line 186 def fetch(key, default_value=nil, &block) if get_keys.include? key determine_value( get_value(automatic, key), get_value(override, key), get_value(normal, key), get_value(default, key) ) elsif default_value default_value elsif block_given? block.call(key) else raise IndexError, "Key #{key} does not exist" end end |
#get_keys ⇒ Object
242 243 244 |
# File 'lib/chef/node/attribute.rb', line 242 def get_keys keys end |
#get_value(data_hash, key) ⇒ Object
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 |
# File 'lib/chef/node/attribute.rb', line 258 def get_value(data_hash, key) last = nil if @current_nesting_level.length == 0 if data_hash.has_key?(key) && ! data_hash[key].nil? return data_hash[key] else return nil end end 0.upto(@current_nesting_level.length) do |i| if i == 0 last = auto_vivifiy(data_hash, @current_nesting_level[i]) elsif i == @current_nesting_level.length fk = last[@current_nesting_level[i - 1]] if fk.has_key?(key) && ! fk[key].nil? return fk[key] else return nil end else last = auto_vivifiy(last[@current_nesting_level[i - 1]], @current_nesting_level[i]) end end end |
#has_key?(key) ⇒ Boolean Also known as: attribute?, include?, key?, member?
115 116 117 118 119 120 121 |
# File 'lib/chef/node/attribute.rb', line 115 def has_key?(key) return true if component_has_key?(@default,key) return true if component_has_key?(@automatic,key) return true if component_has_key?(@normal,key) return true if component_has_key?(@override,key) false end |
#has_value?(value) ⇒ Boolean Also known as: value?
217 218 219 220 221 |
# File 'lib/chef/node/attribute.rb', line 217 def has_value?(value) self.any? do |k,v| value == v end end |
#hash_and_not_cna?(to_check) ⇒ Boolean
285 286 287 |
# File 'lib/chef/node/attribute.rb', line 285 def hash_and_not_cna?(to_check) (! to_check.kind_of?(Chef::Node::Attribute)) && to_check.respond_to?(:has_key?) end |
#index(value) ⇒ Object
225 226 227 228 229 230 |
# File 'lib/chef/node/attribute.rb', line 225 def index(value) index = self.find do |h| value == h[1] end index.first if index.is_a? Array || nil end |
#inspect ⇒ Object
436 437 438 439 440 441 442 |
# File 'lib/chef/node/attribute.rb', line 436 def inspect determine_value(current_automatic, current_override, current_normal, current_default) "#<#{self.class} " << instance_variables.map{|iv| iv.to_s + '=' + (HIDDEN_ATTRIBUES.include?(iv.to_sym) ? "{...}" : instance_variable_get(iv).inspect) }.join(', ') << ">" end |
#keys ⇒ Object
246 247 248 249 250 251 252 253 254 255 256 |
# File 'lib/chef/node/attribute.rb', line 246 def keys tkeys = current_automatic ? current_automatic.keys : [] [ current_override, current_normal, current_default ].each do |attr_hash| if attr_hash attr_hash.keys.each do |key| tkeys << key unless tkeys.include?(key) end end end tkeys end |
#kind_of?(klass) ⇒ Boolean
Writing this method hurts me a little bit.
TODO: Refactor all this stuff so this kind of horror is no longer needed
We have invented a new kind of duck-typing, we call it Madoff typing. We just lie and hope we die before you recognize our scheme. :)
209 210 211 212 213 214 215 |
# File 'lib/chef/node/attribute.rb', line 209 def kind_of?(klass) if klass == Hash || klass == Mash || klass == Chef::Node::Attribute true else false end end |
#reset ⇒ Object
Reset our internal current_nesting_level to the top of every tree
80 81 82 83 84 85 86 87 |
# File 'lib/chef/node/attribute.rb', line 80 def reset @current_normal = @normal @current_default = @default @current_override = @override @current_automatic = @automatic @has_been_read = false @current_nesting_level = [] end |
#set_type_hash ⇒ Object
66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/chef/node/attribute.rb', line 66 def set_type_hash case @set_type when :normal @normal when :override @override when :default @default when :automatic @automatic end end |
#set_value(data_hash, key, value) ⇒ Object
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 |
# File 'lib/chef/node/attribute.rb', line 342 def set_value(data_hash, key, value) last = nil # If there is no current_nesting_level, just set the value if @current_nesting_level.length == 0 data_hash[key] = value return data_hash end # Walk all the previous places we have been 0.upto(@current_nesting_level.length) do |i| # If we are the first, we are top level, and should vivifiy the data_hash if i == 0 last = auto_vivifiy(data_hash, @current_nesting_level[i]) # If we are one past the last current_nesting_level, we are adding a key to that hash with a value elsif i == @current_nesting_level.length last[@current_nesting_level[i - 1]][key] = value # Otherwise, we're auto-vivifiy-ing an interim mash else last = auto_vivifiy(last[@current_nesting_level[i - 1]], @current_nesting_level[i]) end end data_hash end |
#size ⇒ Object Also known as: length
236 237 238 |
# File 'lib/chef/node/attribute.rb', line 236 def size self.collect{}.length end |
#to_hash ⇒ Object
444 445 446 447 448 449 450 451 |
# File 'lib/chef/node/attribute.rb', line 444 def to_hash result = determine_value(current_automatic, current_override, current_normal, current_default) if result.class == Hash result else result.to_hash end end |
#value_at_current_nesting(component_attrs, key) ⇒ Object
477 478 479 480 481 482 483 |
# File 'lib/chef/node/attribute.rb', line 477 def value_at_current_nesting(component_attrs, key) @current_nesting_level.inject(component_attrs) do |subtree, intermediate_key| # if the intermediate value isn't a hash or doesn't have the intermediate key, # it can't have the bottom-level key we're looking for. (subtree.respond_to?(:key?) && subtree[intermediate_key]) or (return false) end end |
#value_or_descend(data_hash, key, auto_vivifiy = false) ⇒ Object
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 |
# File 'lib/chef/node/attribute.rb', line 382 def value_or_descend(data_hash, key, auto_vivifiy=false) if auto_vivifiy hash_to_vivifiy = auto_vivifiy(data_hash, key) data_hash[key] = hash_to_vivifiy[key] else return nil if data_hash == nil return nil unless data_hash.has_key?(key) end if data_hash[key].respond_to?(:has_key?) cna = Chef::Node::Attribute.new(@normal, @default, @override, @automatic, @current_nesting_level) cna.current_normal = current_normal.nil? ? Mash.new : current_normal[key] cna.current_default = current_default.nil? ? Mash.new : current_default[key] cna.current_override = current_override.nil? ? Mash.new : current_override[key] cna.current_automatic = current_automatic.nil? ? Mash.new : current_automatic[key] cna.auto_vivifiy_on_read = auto_vivifiy_on_read cna.set_unless_value_present = set_unless_value_present cna.set_type = set_type cna else data_hash[key] end end |
#values ⇒ Object
232 233 234 |
# File 'lib/chef/node/attribute.rb', line 232 def values self.collect { |h| h[1] } end |