class Puppet::Pops::Binder::Producers::HashMultibindProducer

@api public

Attributes

conflict_resolution[R]

@return [Symbol] One of `:error`, `:merge`, `:append`, `:priority`, `:ignore` @api public

flatten[R]

@return [Boolean, Integer] Flatten all if true, or none if false, or to given level (0 = none, -1 = all) @api public

uniq[R]

@return [Boolean] @api public

Protected Instance Methods

assert_type(binding, tc, key, value) click to toggle source

@api private

# File lib/puppet/pops/binder/producers.rb, line 807
def assert_type(binding, tc, key, value)
  unless tc.instance?(binding.type.key_type, key)
    raise ArgumentError, ["Type Error: key contribution to #{binding.name}['#{key}'] ",
      "is incompatible with key type: #{tc.label(binding.type)}, ",
      type_error_detail(binding.type.key_type, key)].join()
  end

  if key.nil? || !key.is_a?(String) || key.empty?
    raise ArgumentError, "Entry contributing to multibind hash with id '#{binding.id}' must have a name."
  end

  unless tc.instance?(binding.type.element_type, value)
    raise ArgumentError, ["Type Error: value contribution to #{binding.name}['#{key}'] ",
      "is incompatible, ",
      type_error_detail(binding.type.element_type, value)].join()
  end
end
internal_produce(scope) click to toggle source

@api private

# File lib/puppet/pops/binder/producers.rb, line 737
def internal_produce(scope)
  seen = {}
  included_entries = []

  injector.get_contributions(scope, contributions_key).each do |element|
    key = element[0]
    entry = element[1]

    name = entry.binding.name
    raise ArgumentError, "A Hash Multibind contribution to '#{binding.name}' must have a name." if name.nil? || name.empty?

    existing = seen[name]
    if existing
      case conflict_resolution.to_s
      when 'priority'
        # skip if duplicate has lower prio
        if (comparison = (seen[name] <=> entry)) <= 0
          raise ArgumentError, "Internal Error: contributions not given in decreasing precedence order" unless comparison == 0
          raise ArgumentError, "Duplicate key (same priority) contributed to Hash Multibinding '#{binding.name}', key: '#{name}'."
        end
        next

      when 'ignore'
        # skip, ignore conflict if prio is the same
        next

      when 'error'
        raise ArgumentError, "Duplicate key contributed to Hash Multibinding '#{binding.name}', key: '#{name}'."

      end
    else
      seen[name] = entry
    end
    included_entries << [key, entry]
  end
  result = {}
  included_entries.each do |element|
    k = element[ 0 ]
    entry = element[ 1 ]
    x = injector.lookup_key(scope, k)
    name = entry.binding.name
    assert_type(binding(), injector.type_calculator(), name, x)
    if result[ name ]
      merge(result, name, result[ name ], x)
    else
      result[ name ] = conflict_resolution().to_s == 'append' ? [x] : x
    end
  end
  result
end
merge(result, name, higher, lower) click to toggle source

@api private

# File lib/puppet/pops/binder/producers.rb, line 789
def merge(result, name, higher, lower)
  case conflict_resolution.to_s
  when 'append'
    unless higher.is_a?(Array)
      higher = [higher]
    end
    tmp = higher + [lower]
    tmp.flatten!(flatten) if flatten
    tmp.uniq! if uniq
    result[name] = tmp

  when 'merge'
    result[name] = lower.merge(higher)

  end
end

Public Class Methods

new(injector, binding, scope, options) click to toggle source

The hash multibind producer provides options to control conflict resolution. By default, the hash is produced using `:priority` resolution - the highest entry is selected, the rest are ignored unless they have the same priority which is an error.

@param injector [Puppet::Pops::Binder::Injector] The injector where the lookup originates @param binding [Puppet::Pops::Binder::Bindings::Binding, nil] The binding using this producer @param scope [Puppet::Parser::Scope] The scope to use for evaluation @option options [Puppet::Pops::Model::LambdaExpression] :transformer (nil) a transformer of produced value @option options [Symbol, String] :#conflict_resolution (:priority) One of `:error`, `:merge`, `:append`, `:priority`, `:ignore`

<ul><li> `ignore` the first found highest priority contribution is used, the rest are ignored</li>
<li>`error` any duplicate key is an error</li>
<li>`append` element type must be compatible with Array, makes elements be arrays and appends all found</li>
<li>`merge` element type must be compatible with hash, merges hashes with retention of highest priority hash content</li>
<li>`priority` the first found highest priority contribution is used, duplicates with same priority raises and error, the rest are
  ignored.</li></ul>

@option options [Boolean, Integer] :flatten (false) If appended should be flattened. Also see {flatten}. @option options [Boolean] :uniq (false) If appended result should be made unique.

@api public

# File lib/puppet/pops/binder/producers.rb, line 699
def initialize(injector, binding, scope, options)
    super
    @conflict_resolution = options[:conflict_resolution].nil? ? :priority : options[:conflict_resolution]
    @uniq = !!options[:uniq]
    @flatten = options[:flatten]

    unless [:error, :merge, :append, :priority, :ignore].include?(@conflict_resolution)
      raise ArgumentError, "Unknown conflict_resolution for Multibind Hash: '#{@conflict_resolution}."
    end

    case @flatten
    when Integer
    when true
      @flatten = -1
    when false
      @flatten = nil
    when NilClass
      @flatten = nil
    else
      raise ArgumentError, "Option :flatten must be nil, Boolean, or an integer value" unless @flatten.is_a?(Integer)
    end

    if uniq || flatten || conflict_resolution.to_s == 'append'
      etype = binding.type.element_type
      unless etype.class == Puppet::Pops::Types::PDataType || etype.is_a?(Puppet::Pops::Types::PArrayType)
        detail = []
        detail << ":uniq" if uniq
        detail << ":flatten" if flatten
        detail << ":conflict_resolution => :append" if conflict_resolution.to_s == 'append'
        raise ArgumentError, ["Options #{detail.join(', and ')} cannot be used with a Multibind ",
          "of type #{injector.type_calculator.string(binding.type)}"].join()
      end
    end
  end