Class: Chef::Node::MultiMash

Inherits:
Object
  • Object
show all
Defined in:
lib/chef/node/attribute_collections.rb

Overview

MultiMash

This is a Hash-like object that contains multiple VividMashes in it. Its purpose is so that the user can descend into the mash and delete a subtree from all of the Mash objects (used to delete all values in a subtree from default, force_default, role_default and env_default at the same time). The assignment operator strictly does assignment (does no merging) and works by deleting the subtree and then assigning to the last mash which passed in the initializer.

A lot of the complexity of this class comes from the fact that at any key value some or all of the mashes may walk off their ends and become nil or true or something. The schema may change so that one precidence leve may be ‘true’ object and another may be a VividMash. It is also possible that one or many of them may transition from VividMashes to Hashes or Arrays.

It also supports the case where you may be deleting a key using node.rm in which case if intermediate keys all walk off into nil then you don’t want to be autovivifying keys as you go. On the other hand you may be using node.force_default! in which case you’ll wind up with a []= operator at the end and you want autovivification, so we conditionally have to support either operation.

@todo: can we have an autovivify class that decorates a class that doesn’t autovivify or something so that the code is less awful?

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(root, primary_mash, mashes, opts = {}) ⇒ MultiMash

Initialize with an array of mashes. For the delete return value to work properly the mashes must come from the same attribute level (i.e. all override or all default, but not a mix of both).



248
249
250
251
252
253
254
# File 'lib/chef/node/attribute_collections.rb', line 248

def initialize(root, primary_mash, mashes, opts={})
  @root = root
  @primary_mash = primary_mash
  @mashes = mashes
  @opts = opts
  @opts[:autovivify] = true if @opts[:autovivify].nil?
end

Instance Attribute Details

#mashesObject (readonly)

Returns the value of attribute mashes.



241
242
243
# File 'lib/chef/node/attribute_collections.rb', line 241

def mashes
  @mashes
end

#optsObject (readonly)

Returns the value of attribute opts.



242
243
244
# File 'lib/chef/node/attribute_collections.rb', line 242

def opts
  @opts
end

#primary_mashObject (readonly)

Returns the value of attribute primary_mash.



243
244
245
# File 'lib/chef/node/attribute_collections.rb', line 243

def primary_mash
  @primary_mash
end

#rootObject (readonly)

Returns the value of attribute root.



240
241
242
# File 'lib/chef/node/attribute_collections.rb', line 240

def root
  @root
end

Instance Method Details

#[](key) ⇒ Object



256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
# File 'lib/chef/node/attribute_collections.rb', line 256

def [](key)
  # handle the secondary mashes
  new_mashes = []
  mashes.each do |mash|
    new_mash = safe_evalute_key(mash, key)
    # secondary mashes never autovivify so once they fall into nil, we just stop tracking them
    new_mashes.push(new_mash) unless new_mash.nil?
  end

  new_primary_mash = safe_evalute_key(primary_mash, key)

  if new_primary_mash.nil? && @opts[:autovivify]
    primary_mash[key] = VividMash.new(root)
    new_primary_mash = primary_mash[key]
  end

  MultiMash.new(root, new_primary_mash, new_mashes, opts)
end

#[]=(key, value) ⇒ Object



275
276
277
278
279
280
281
282
283
284
# File 'lib/chef/node/attribute_collections.rb', line 275

def []=(key, value)
  if primary_mash.nil?
    # This theoretically should never happen since node#force_default! setter methods will autovivify and
    # node#rm methods do not end in #[]= operators.
    raise TypeError, "No autovivification was specified initially on a method chain ending in assignment"
  end
  ret = delete(key)
  primary_mash[key] = value
  ret
end

#delete(key) ⇒ Object



293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
# File 'lib/chef/node/attribute_collections.rb', line 293

def delete(key)
  # the return value is a deep merge which is correct semantics when
  # merging between attributes on the same level (this would be incorrect
  # if passed both override and default attributes which would need hash_only
  # merging).
  ret = mashes.inject(Mash.new) do |merged, mash|
    Chef::Mixin::DeepMerge.merge(merged, mash)
  end
  ret = Chef::Mixin::DeepMerge.merge(ret, primary_mash)
  mashes.each do |mash|
    mash.delete(key) if mash.respond_to?(:delete)
  end
  primary_mash.delete(key) if primary_mash.respond_to?(:delete)
  ret[key]
end

#element(key = nil, *subkeys) ⇒ Object

mash.element(‘foo’, ‘bar’) is the same as mash[‘bar’]



287
288
289
290
291
# File 'lib/chef/node/attribute_collections.rb', line 287

def element(key = nil, *subkeys)
  return self if key.nil?
  submash = self[key]
  subkeys.empty? ? submash : submash.element(*subkeys)
end