module Poise::Helpers::Subresources::Child

A resource mixin for child subresources.

@since 1.0.0

Public Instance Methods

after_created() click to toggle source

Register ourself with parents in case this is not a nested resource.

@api private

Calls superclass method
# File lib/poise/helpers/subresources/child.rb, line 74
def after_created
  super
  self.class.parent_attributes.each_key do |name|
    parent = self.send(name)
    parent.register_subresource(self) if parent && parent.respond_to?(:register_subresource)
  end
end
parent(*args) click to toggle source

@overload parent()

Get the parent resource for this child. This may be nil if the
resource is set to parent_optional = true.
@return [Chef::Resource, nil]

@overload parent(val)

Set the parent resource. The parent can be set as  resource
object, a string (either a bare resource name or a type[name]
string), or a type:name hash.
@param val [String, Hash, Chef::Resource] Parent resource to set.
@return [Chef::Resource, nil]
# File lib/poise/helpers/subresources/child.rb, line 63
def parent(*args)
  # Lie about this method if the parent type is true.
  if self.class.parent_type == true
    raise NoMethodError.new("undefined method `parent' for #{self}")
  end
  _parent(:parent, self.class.parent_type, self.class.parent_optional, self.class.parent_auto, self.class.parent_default, *args)
end

Private Instance Methods

_parent(name, parent_type, parent_optional, parent_auto, parent_default, *args) click to toggle source

Generic form of the parent getter/setter.

@since 2.0.0 @see parent

# File lib/poise/helpers/subresources/child.rb, line 88
def _parent(name, parent_type, parent_optional, parent_auto, parent_default, *args)
  # Allow using a DSL symbol as the parent type.
  if parent_type.is_a?(Symbol)
    parent_type = Chef::Resource.resource_for_node(parent_type, node)
  end
  # Grab the ivar for local use.
  parent_ref = instance_variable_get(:"@#{name}")
  if !args.empty?
    val = args.first
    if val.nil?
      # Unsetting the parent.
      parent = parent_ref = nil
    else
      if val.is_a?(String) && !val.include?('[')
        raise Poise::Error.new("Cannot use a string #{name} without defining a parent type") if parent_type == Chef::Resource
        # Try to find the most recent instance of parent_type with a
        # matching name. This takes subclassing parent_type into account.
        found_val = nil
        iterator = run_context.resource_collection.respond_to?(:recursive_each) ? :recursive_each : :each
        # This will find the last matching value due to overwriting
        # found_val as it goes. Will be the nearest match.
        run_context.resource_collection.public_send(iterator) do |res|
          found_val = res if res.is_a?(parent_type) && res.name == val
        end
        # If found_val is nil, fall back to using lookup even though
        # it won't work with subclassing, better than nothing?
        val = found_val || "#{parent_type.resource_name}[#{val}]"
      end
      if val.is_a?(String) || val.is_a?(Hash)
        parent = @run_context.resource_collection.find(val)
      else
        parent = val
      end
      if !parent.is_a?(parent_type)
        raise Poise::Error.new("Parent resource is not an instance of #{parent_type.name}: #{val.inspect}")
      end
      parent_ref = ParentRef.new(parent)
    end
  elsif !parent_ref || !parent_ref.resource
    if parent_default
      parent = if parent_default.is_a?(Chef::DelayedEvaluator)
        instance_eval(&parent_default)
      else
        parent_default
      end
    end
    # The @parent_ref means we won't run this if we previously set
    # ParentRef.new(nil). This means auto-lookup only happens during
    # after_created.
    if !parent && !parent_ref && parent_auto
      # Automatic sibling lookup for sequential composition.
      # Find the last instance of the parent class as the default parent.
      # This is super flaky and should only be a last resort.
      parent = Poise::Helpers::Subresources::DefaultContainers.find(parent_type, run_context, self_resource: self)
    end
    # Can't find a valid parent, if it wasn't optional raise an error.
    raise Poise::Error.new("No #{name} found for #{self}") unless parent || parent_optional
    parent_ref = ParentRef.new(parent)
  else
    parent = parent_ref.resource
  end
  raise Poise::Error.new("Cannot set the #{name} of #{self} to itself") if parent.equal?(self)
  # Store the ivar back.
  instance_variable_set(:"@#{name}", parent_ref)
  # Return the actual resource.
  parent
end