class Puppet::Pops::Binder::Binder::LayerProcessor

Processes the information in a layer, aggregating it to the injector_entries hash in its parent binder. A LayerProcessor holds the intermediate state required while processing one layer.

@api private

Attributes

binder[R]
binder_precedence[R]
bindings[R]
contributions[R]
key_factory[R]

Public Instance Methods

add(b) click to toggle source

Add the binding to the list of potentially effective bindings from this layer @api private

# File lib/puppet/pops/binder/binder.rb, line 190
def add(b)
  bindings << Puppet::Pops::Binder::InjectorEntry.new(b, binder_precedence)
end
add_contribution(b) click to toggle source

Add a multibind contribution @api private

# File lib/puppet/pops/binder/binder.rb, line 197
def add_contribution(b)
  contributions << Puppet::Pops::Binder::InjectorEntry.new(b, binder_precedence)
end
bind(binding) click to toggle source

Bind given abstract binding @api private

# File lib/puppet/pops/binder/binder.rb, line 204
def bind(binding)
  @@bind_visitor.visit_this(self, binding)
end
bind_Binding(o) click to toggle source

@api private

# File lib/puppet/pops/binder/binder.rb, line 290
def bind_Binding(o)
  if is_contribution?(o)
    add_contribution(o)
  else
    add(o)
  end
end
bind_Bindings(o) click to toggle source

@api private

# File lib/puppet/pops/binder/binder.rb, line 299
def bind_Bindings(o)
  o.bindings.each {|b| bind(b) }
end
bind_LayeredBindings(o) click to toggle source

Process layered bindings from highest to lowest layer @api private

# File lib/puppet/pops/binder/binder.rb, line 312
def bind_LayeredBindings(o)
  o.layers.each do |layer|
    processor = LayerProcessor.new(binder, key_factory)
    # All except abstract (==error) are transfered to injector_entries

    processor.bind(layer).each do |k, v|
      entry = binder.injector_entries[k]
      unless key_factory.is_contributions_key?(k)
        if v.is_abstract?()
          layer_name, bindings_name = Puppet::Pops::Binder::Binder.get_named_binding_layer_and_name(v.binding)
          type_name = key_factory.type_calculator.string(v.binding.type)
          raise ArgumentError, "The abstract binding '#{type_name}/#{v.binding.name}' in '#{bindings_name}' in layer '#{layer_name}' was not overridden"
        end
        raise ArgumentError, "Internal Error - redefinition of key: #{k}, (should never happen)" if entry
        binder.injector_entries[k] = v
      else
        entry ? entry << v : binder.injector_entries[k] = v
      end
    end
  end
end
bind_NamedBindings(o) click to toggle source

@api private

# File lib/puppet/pops/binder/binder.rb, line 304
def bind_NamedBindings(o)
  # Name is ignored here, it should be introspected when needed (in case of errors)
  o.bindings.each {|b| bind(b) }
end
bind_NamedLayer(o) click to toggle source

Processes one named (“top level”) layer consisting of a list of NamedBindings @api private

# File lib/puppet/pops/binder/binder.rb, line 337
def bind_NamedLayer(o)
  o.bindings.each {|b| bind(b) }
  this_layer = {}

  # process regular bindings
  bindings.each do |b|
    bkey = key(b.binding)

    # ignore if a higher layer defined it (unless the lower is final), but ensure override gets resolved
    # (override is not resolved across binders)
    if x = binder.injector_entries[bkey]
      if b.is_final?
        raise_conflicting_binding(x, b)
      end
      x.mark_override_resolved()
      next
    end

    # If a lower (parent) binder exposes a final binding it may not be overridden
    #
    if (x = binder.lookup_in_parent(bkey)) && x.is_final?
      raise_conflicting_binding(x, b)
    end

    # if already found in this layer, one wins (and resolves override), or it is an error
    existing = this_layer[bkey]
    winner = existing ? highest(existing, b) : b
    this_layer[bkey] = winner
    if existing
      winner.mark_override_resolved()
    end
  end

  # Process contributions
  # - organize map multibind_id to bindings with this id
  # - for each id, create an array with the unique anonymous keys to the contributed bindings
  # - bind the index to a special multibind contributions key (these are aggregated)
  #
  c_hash = Hash.new {|hash, key| hash[ key ] = [] }
  contributions.each {|b| c_hash[ b.binding.multibind_id ] << b }
  # - for each id
  c_hash.each do |k, v|
    index = v.collect do |b|
      bkey = key(b.binding)
      this_layer[bkey] = b
      bkey
    end
    contributions_key = key_factory.multibind_contributions(k)
    unless this_layer[contributions_key]
      this_layer[contributions_key] = []
    end
    this_layer[contributions_key] += index
  end
  this_layer
end
highest(b1, b2) click to toggle source

@return [Puppet::Pops::Binder::InjectorEntry] the entry with the highest precedence @api private

# File lib/puppet/pops/binder/binder.rb, line 210
def highest(b1, b2)
  if b1.is_abstract? != b2.is_abstract?
    # if one is abstract and the other is not, the non abstract wins
    b1.is_abstract? ? b2 : b1
  else
    case b1.precedence <=> b2.precedence
    when 1
      b1
    when -1
      b2
    when 0
      raise_conflicting_binding(b1, b2)
    end
  end
end
is_contribution?(binding) click to toggle source

@api private

# File lib/puppet/pops/binder/binder.rb, line 285
def is_contribution?(binding)
  ! binding.multibind_id.nil?
end
key(binding) click to toggle source

Produces the key for the given Binding. @param binding [Puppet::Pops::Binder::Bindings::Binding] the binding to get a key for @return [Object] an opaque key @api private

# File lib/puppet/pops/binder/binder.rb, line 275
def key(binding)
  k = if is_contribution?(binding)
    # contributions get a unique (sequential) key
    binder.next_anonymous_key()
  else
    key_factory.binding_key(binding)
  end
end
raise_conflicting_binding(b1, b2) click to toggle source

Raises a conflicting bindings error given two InjectorEntry’s with same precedence in the same layer (if they are in different layers, something is seriously wrong)

# File lib/puppet/pops/binder/binder.rb, line 228
def raise_conflicting_binding(b1, b2)
  b1_layer_name, b1_bindings_name = binder.class.get_named_binding_layer_and_name(b1.binding)
  b2_layer_name, b2_bindings_name = binder.class.get_named_binding_layer_and_name(b2.binding)

  finality_msg = (b1.is_final? || b2.is_final?) ? ". Override of final binding not allowed" : ''

  # TODO: Use of layer_name is not very good, it is not guaranteed to be unique
  unless b1_layer_name == b2_layer_name
    raise ArgumentError, [
      'Conflicting binding for',
      "'#{b1.binding.name}'",
      'being resolved across layers',
      "'#{b1_layer_name}' and",
      "'#{b2_layer_name}'"
      ].join(' ')+finality_msg
  end

  # Conflicting bindings made from the same source
  if b1_bindings_name == b2_bindings_name
    raise ArgumentError, [
      'Conflicting binding for name:',
      "'#{b1.binding.name}'",
      'in layer:',
      "'#{b1_layer_name}', ",
      'both from:',
      "'#{b1_bindings_name}'"
      ].join(' ')+finality_msg
  end

  # Conflicting bindings from different sources
  raise ArgumentError, [
    'Conflicting binding for name:',
    "'#{b1.binding.name}'",
    'in layer:',
    "'#{b1_layer_name}',",
    'from:',
    "'#{b1_bindings_name}', and",
    "'#{b2_bindings_name}'"
    ].join(' ')+finality_msg
end

Public Class Methods

new(binder, key_factory) click to toggle source
# File lib/puppet/pops/binder/binder.rb, line 178
def initialize(binder, key_factory)
  @binder = binder
  @binder_precedence = binder.binder_precedence
  @key_factory = key_factory
  @bindings = []
  @contributions = []
  @@bind_visitor ||= Puppet::Pops::Visitor.new(nil,"bind",0,0)
end