File: //usr/lib/ruby/site_ruby/1.8/puppet/pops/binder/injector.rb
# The injector is the "lookup service" class
#
# Initialization
# --------------
# The injector is initialized with a configured {Puppet::Pops::Binder::Binder Binder}. The Binder instance contains a resolved set of
# `key => "binding information"` that is used to setup the injector.
#
# Lookup
# ------
# It is possible to lookup either the value, or a producer of the value. The {#lookup} method looks up a value, and the
# {#lookup_producer} looks up a producer.
# Both of these methods can be called with three different signatures; `lookup(key)`, `lookup(type, name)`, and `lookup(name)`,
# with the corresponding calls to obtain a producer; `lookup_producer(key)`, `lookup_producer(type, name)`, and `lookup_producer(name)`.
#
# It is possible to pass a block to {#lookup} and {#lookup_producer}, the block is passed the result of the lookup
# and the result of the block is returned as the value of the lookup. This is useful in order to provide a default value.
#
# @example Lookup with default value
# injector.lookup('favourite_food') {|x| x.nil? ? 'bacon' : x }
#
# Singleton or Not
# ----------------
# The lookup of a value is always based on the lookup of a producer. For *singleton producers* this means that the value is
# determined by the first value lookup. Subsequent lookups via `lookup` or `lookup_producer` will produce the same instance.
#
# *Non singleton producers* will produce a new instance on each request for a value. For constant value producers this
# means that a new deep-clone is produced for mutable objects (but not for immutable objects as this is not needed).
# Custom producers should have non singleton behavior, or if this is not possible ensure that the produced result is
# immutable. (The behavior if a custom producer hands out a mutable value and this is mutated is undefined).
#
# Custom bound producers capable of producing a series of objects when bound as a singleton means that the producer
# is a singleton, not the value it produces. If such a producer is bound as non singleton, each `lookup` will get a new
# producer (hence, typically, restarting the series). However, the producer returned from `lookup_producer` will not
# recreate the producer on each call to `produce`; i.e. each `lookup_producer` returns a producer capable of returning
# a series of objects.
#
# @see Puppet::Pops::Binder::Binder Binder, for details about how to bind keys to producers
# @see Puppet::Pops::Binder::BindingsFactory BindingsFactory, for a convenient way to create a Binder and bindings
#
# Assisted Inject
# ---------------
# The injector supports lookup of instances of classes *even if the requested class is not explicitly bound*.
# This is possible for classes that have a zero argument `initialize` method, or that has a class method called
# `inject` that takes two arguments; `injector`, and `scope`.
# This is useful in ruby logic as a class can then use the given injector to inject details.
# An `inject` class method wins over a zero argument `initialize` in all cases.
#
# @example Using assisted inject
# # Class with assisted inject support
# class Duck
# attr_reader :name, :year_of_birth
#
# def self.inject(injector, scope, binding, *args)
# # lookup default name and year of birth, and use defaults if not present
# name = injector.lookup(scope,'default-duck-name') {|x| x ? x : 'Donald Duck' }
# year_of_birth = injector.lookup(scope,'default-duck-year_of_birth') {|x| x ? x : 1934 }
# self.new(name, year_of_birth)
# end
#
# def initialize(name, year_of_birth)
# @name = name
# @year_of_birth = year_of_birth
# end
# end
#
# injector.lookup(scope, Duck)
# # Produces a Duck named 'Donald Duck' or named after the binding 'default-duck-name' (and with similar treatment of
# # year_of_birth).
# @see Puppet::Pops::Binder::Producers::AssistedInjectProducer AssistedInjectProducer, for more details on assisted injection
#
# Access to key factory and type calculator
# -----------------------------------------
# It is important to use the same key factory, and type calculator as the binder. It is therefor possible to obtain
# these with the methods {#key_factory}, and {#type_calculator}.
#
# Special support for producers
# -----------------------------
# There is one method specially designed for producers. The {#get_contributions} method returns an array of all contributions
# to a given *contributions key*. This key is obtained from the {#key_factory} for a given multibinding. The returned set of
# contributed bindings is sorted in descending precedence order. Any conflicts, merges, etc. is performed by the multibinding
# producer configured for a multibinding.
#
# @api public
#
class Puppet::Pops::Binder::Injector
Producers = Puppet::Pops::Binder::Producers
def self.create_from_model(layered_bindings_model)
self.new(Puppet::Pops::Binder::Binder.new(layered_bindings_model))
end
def self.create_from_hash(name, key_value_hash)
factory = Puppet::Pops::Binder::BindingsFactory
named_bindings = factory.named_bindings(name) { key_value_hash.each {|k,v| bind.name(k).to(v) }}
layered_bindings = factory.layered_bindings(factory.named_layer(name+'-layer',named_bindings.model))
self.new(Puppet::Pops::Binder::Binder.new(layered_bindings))
end
# Creates an injector with a single bindings layer created with the given name, and the bindings
# produced by the given block. The block is evaluated with self bound to a BindingsContainerBuilder.
#
# @example
# Injector.create('mysettings') do
# bind('name').to(42)
# end
#
# @api public
#
def self.create(name, &block)
factory = Puppet::Pops::Binder::BindingsFactory
layered_bindings = factory.layered_bindings(factory.named_layer(name+'-layer',factory.named_bindings(name, &block).model))
self.new(Puppet::Pops::Binder::Binder.new(layered_bindings))
end
# Creates an overriding injector with a single bindings layer
# created with the given name, and the bindings produced by the given block.
# The block is evaluated with self bound to a BindingsContainerBuilder.
#
# @example
# an_injector.override('myoverrides') do
# bind('name').to(43)
# end
#
# @api public
#
def override(name, &block)
factory = Puppet::Pops::Binder::BindingsFactory
layered_bindings = factory.layered_bindings(factory.named_layer(name+'-layer',factory.named_bindings(name, &block).model))
self.class.new(Puppet::Pops::Binder::Binder.new(layered_bindings, @impl.binder))
end
# Creates an overriding injector with bindings from a bindings model (a LayeredBindings) which
# may consists of multiple layers of bindings.
#
# @api public
#
def override_with_model(layered_bindings)
unless layered_bindings.is_a?(Puppet::Pops::Binder::Bindings::LayeredBindings)
raise ArgumentError, "Expected a LayeredBindings model, got '#{bindings_model.class}'"
end
self.class.new(Puppet::Pops::Binder::Binder.new(layered_bindings, @impl.binder))
end
# Creates an overriding injector with a single bindings layer
# created with the given name, and the bindings given in the key_value_hash
# @api public
#
def override_with_hash(name, key_value_hash)
factory = Puppet::Pops::Binder::BindingsFactory
named_bindings = factory.named_bindings(name) { key_value_hash.each {|k,v| bind.name(k).to(v) }}
layered_bindings = factory.layered_bindings(factory.named_layer(name+'-layer',named_bindings.model))
self.class.new(Puppet::Pops::Binder::Binder.new(layered_bindings, @impl.binder))
end
# An Injector is initialized with a configured {Puppet::Pops::Binder::Binder Binder}.
#
# @param configured_binder [Puppet::Pops::Binder::Binder,nil] The configured binder containing effective bindings. A given value
# of nil creates an injector that returns or yields nil on all lookup requests.
# @raise ArgumentError if the given binder is not fully configured
#
# @api public
#
def initialize(configured_binder, parent_injector = nil)
if configured_binder.nil?
@impl = Private::NullInjectorImpl.new()
else
@impl = Private::InjectorImpl.new(configured_binder, parent_injector)
end
end
# The KeyFactory used to produce keys in this injector.
# The factory is shared with the Binder to ensure consistent translation to keys.
# A compatible type calculator can also be obtained from the key factory.
# @return [Puppet::Pops::Binder::KeyFactory] the key factory in use
#
# @api public
#
def key_factory()
@impl.key_factory
end
# Returns the TypeCalculator in use for keys. The same calculator (as used for keys) should be used if there is a need
# to check type conformance, or infer the type of Ruby objects.
#
# @return [Puppet::Pops::Types::TypeCalculator] the type calculator that is in use for keys
# @api public
#
def type_calculator()
@impl.type_calculator()
end
# Lookup (a.k.a "inject") of a value given a key.
# The lookup may be called with different parameters. This method is a convenience method that
# dispatches to one of #lookup_key or #lookup_type depending on the arguments. It also provides
# the ability to use an optional block that is called with the looked up value, or scope and value if the
# block takes two parameters. This is useful to provide a default value or other transformations, calculations
# based on the result of the lookup.
#
# @overload lookup(scope, key)
# (see #lookup_key)
# @param scope [Puppet::Parser::Scope] the scope to use for evaluation
# @param key [Object] an opaque object being the full key
#
# @overload lookup(scope, type, name = '')
# (see #lookup_type)
# @param scope [Puppet::Parser::Scope] the scope to use for evaluation
# @param type [Puppet::Pops::Types::PAnyType] the type of what to lookup
# @param name [String] the name to use, defaults to empty string (for unnamed)
#
# @overload lookup(scope, name)
# Lookup of Data type with given name.
# @see #lookup_type
# @param scope [Puppet::Parser::Scope] the scope to use for evaluation
# @param name [String] the Data/name to lookup
#
# @yield [value] passes the looked up value to an optional block and returns what this block returns
# @yield [scope, value] passes scope and value to the block and returns what this block returns
# @yieldparam scope [Puppet::Parser::Scope] the scope given to lookup
# @yieldparam value [Object, nil] the looked up value or nil if nothing was found
#
# @raise [ArgumentError] if the block has an arity that is not 1 or 2
#
# @api public
#
def lookup(scope, *args, &block)
@impl.lookup(scope, *args, &block)
end
# Looks up a (typesafe) value based on a type/name combination.
# Creates a key for the type/name combination using a KeyFactory. Specialization of the Data type are transformed
# to a Data key, and the result is type checked to conform with the given key.
#
# @param type [Puppet::Pops::Types::PAnyType] the type to lookup as defined by Puppet::Pops::Types::TypeFactory
# @param name [String] the (optional for non `Data` types) name of the entry to lookup.
# The name may be an empty String (the default), but not nil. The name is required for lookup for subtypes of
# `Data`.
# @return [Object, nil] the looked up bound object, or nil if not found (type conformance with given type is guaranteed)
# @raise [ArgumentError] if the produced value does not conform with the given type
#
# @api public
#
def lookup_type(scope, type, name='')
@impl.lookup_type(scope, type, name)
end
# Looks up the key and returns the entry, or nil if no entry is found.
# Produced type is checked for type conformance with its binding, but not with the lookup key.
# (This since all subtypes of PDataType are looked up using a key based on PDataType).
# Use the Puppet::Pops::Types::TypeCalculator#instance? method to check for conformance of the result
# if this is wanted, or use #lookup_type.
#
# @param key [Object] lookup of key as produced by the key factory
# @return [Object, nil] produced value of type that conforms with bound type (type conformance with key not guaranteed).
# @raise [ArgumentError] if the produced value does not conform with the bound type
#
# @api public
#
def lookup_key(scope, key)
@impl.lookup_key(scope, key)
end
# Lookup (a.k.a "inject") producer of a value given a key.
# The producer lookup may be called with different parameters. This method is a convenience method that
# dispatches to one of #lookup_producer_key or #lookup_producer_type depending on the arguments. It also provides
# the ability to use an optional block that is called with the looked up producer, or scope and producer if the
# block takes two parameters. This is useful to provide a default value, call a custom producer method,
# or other transformations, calculations based on the result of the lookup.
#
# @overload lookup_producer(scope, key)
# (see #lookup_proudcer_key)
# @param scope [Puppet::Parser::Scope] the scope to use for evaluation
# @param key [Object] an opaque object being the full key
#
# @overload lookup_producer(scope, type, name = '')
# (see #lookup_type)
# @param scope [Puppet::Parser::Scope] the scope to use for evaluation
# @param type [Puppet::Pops::Types::PAnyType], the type of what to lookup
# @param name [String], the name to use, defaults to empty string (for unnamed)
#
# @overload lookup_producer(scope, name)
# Lookup of Data type with given name.
# @see #lookup_type
# @param scope [Puppet::Parser::Scope] the scope to use for evaluation
# @param name [String], the Data/name to lookup
#
# @return [Puppet::Pops::Binder::Producers::Producer, Object, nil] a producer, or what the optional block returns
#
# @yield [producer] passes the looked up producer to an optional block and returns what this block returns
# @yield [scope, producer] passes scope and producer to the block and returns what this block returns
# @yieldparam producer [Puppet::Pops::Binder::Producers::Producer, nil] the looked up producer or nil if nothing was bound
# @yieldparam scope [Puppet::Parser::Scope] the scope given to lookup
#
# @raise [ArgumentError] if the block has an arity that is not 1 or 2
#
# @api public
#
def lookup_producer(scope, *args, &block)
@impl.lookup_producer(scope, *args, &block)
end
# Looks up a Producer given an opaque binder key.
# @return [Puppet::Pops::Binder::Producers::Producer, nil] the bound producer, or nil if no such producer was found.
#
# @api public
#
def lookup_producer_key(scope, key)
@impl.lookup_producer_key(scope, key)
end
# Looks up a Producer given a type/name key.
# @note The result is not type checked (it cannot be until the producer has produced an instance).
# @return [Puppet::Pops::Binder::Producers::Producer, nil] the bound producer, or nil if no such producer was found
#
# @api public
#
def lookup_producer_type(scope, type, name='')
@impl.lookup_producer_type(scope, type, name)
end
# Returns the contributions to a multibind given its contribution key (as produced by the KeyFactory).
# This method is typically used by multibind value producers, but may be used for introspection of the injector's state.
#
# @param scope [Puppet::Parser::Scope] the scope to use
# @param contributions_key [Object] Opaque key as produced by KeyFactory as the contributions key for a multibinding
# @return [Array<Puppet::Pops::Binder::InjectorEntry>] the contributions sorted in deecending order of precedence
#
# @api public
#
def get_contributions(scope, contributions_key)
@impl.get_contributions(scope, contributions_key)
end
# Returns an Injector that returns (or yields) nil on all lookups, and produces an empty structure for contributions
# This method is intended for testing purposes.
#
def self.null_injector
self.new(nil)
end
# The implementation of the Injector is private.
# @see Puppet::Pops::Binder::Injector The public API this module implements.
# @api private
#
module Private
# This is a mocking "Null" implementation of Injector. It never finds anything
# @api private
class NullInjectorImpl
attr_reader :entries
attr_reader :key_factory
attr_reader :type_calculator
def initialize
@entries = []
@key_factory = Puppet::Pops::Binder::KeyFactory.new()
@type_calculator = @key_factory.type_calculator
end
def lookup(scope, *args, &block)
raise ArgumentError, "lookup should be called with two or three arguments, got: #{args.size()+1}" unless args.size.between?(1,2)
# call block with result if given
if block
case block.arity
when 1
block.call(nil)
when 2
block.call(scope, nil)
else
raise ArgumentError, "The block should have arity 1 or 2"
end
else
val
end
end
# @api private
def binder
nil
end
# @api private
def lookup_key(scope, key)
nil
end
# @api private
def lookup_producer(scope, *args, &block)
lookup(scope, *args, &block)
end
# @api private
def lookup_producer_key(scope, key)
nil
end
# @api private
def lookup_producer_type(scope, type, name='')
nil
end
def get_contributions()
[]
end
end
# @api private
#
class InjectorImpl
# Hash of key => InjectorEntry
# @api private
#
attr_reader :entries
attr_reader :key_factory
attr_reader :type_calculator
attr_reader :binder
def initialize(configured_binder, parent_injector = nil)
@binder = configured_binder
@parent = parent_injector
# TODO: Different error message
raise ArgumentError, "Given Binder is not configured" unless configured_binder #&& configured_binder.configured?()
@entries = configured_binder.injector_entries()
# It is essential that the injector uses the same key factory as the binder since keys must be
# represented the same (but still opaque) way.
#
@key_factory = configured_binder.key_factory()
@type_calculator = key_factory.type_calculator()
@@transform_visitor ||= Puppet::Pops::Visitor.new(nil,"transform", 2, 2)
@recursion_lock = [ ]
end
# @api private
def lookup(scope, *args, &block)
raise ArgumentError, "lookup should be called with two or three arguments, got: #{args.size()+1}" unless args.size.between?(1,2)
val = case args[ 0 ]
when Puppet::Pops::Types::PAnyType
lookup_type(scope, *args)
when String
raise ArgumentError, "lookup of name should only pass the name" unless args.size == 1
lookup_key(scope, key_factory.data_key(args[ 0 ]))
else
raise ArgumentError, 'lookup using a key should only pass a single key' unless args.size == 1
lookup_key(scope, args[ 0 ])
end
# call block with result if given
if block
case block.arity
when 1
block.call(val)
when 2
block.call(scope, val)
else
raise ArgumentError, "The block should have arity 1 or 2"
end
else
val
end
end
# Produces a key for a type/name combination.
# @api private
def named_key(type, name)
key_factory.named_key(type, name)
end
# Produces a key for a PDataType/name combination
# @api private
def data_key(name)
key_factory.data_key(name)
end
# @api private
def lookup_type(scope, type, name='')
val = lookup_key(scope, named_key(type, name))
return nil if val.nil?
unless key_factory.type_calculator.instance?(type, val)
raise ArgumentError, "Type error: incompatible type, #{type_error_detail(type, val)}"
end
val
end
# @api private
def type_error_detail(expected, actual)
actual_t = type_calculator.infer(actual)
"expected: #{type_calculator.string(expected)}, got: #{type_calculator.string(actual_t)}"
end
# @api private
def lookup_key(scope, key)
if @recursion_lock.include?(key)
raise ArgumentError, "Lookup loop detected for key: #{key}"
end
begin
@recursion_lock.push(key)
case entry = get_entry(key)
when NilClass
@parent ? @parent.lookup_key(scope, key) : nil
when Puppet::Pops::Binder::InjectorEntry
val = produce(scope, entry)
return nil if val.nil?
unless key_factory.type_calculator.instance?(entry.binding.type, val)
raise "Type error: incompatible type returned by producer, #{type_error_detail(entry.binding.type, val)}"
end
val
when Producers::AssistedInjectProducer
entry.produce(scope)
else
# internal, direct entries
entry
end
ensure
@recursion_lock.pop()
end
end
# Should be used to get entries as it converts missing entries to NotFound entries or AssistedInject entries
#
# @api private
def get_entry(key)
case entry = entries[ key ]
when NilClass
# not found, is this an assisted inject?
if clazz = assistable_injected_class(key)
entry = Producers::AssistedInjectProducer.new(self, clazz)
entries[ key ] = entry
else
entries[ key ] = NotFound.new()
entry = nil
end
when NotFound
entry = nil
end
entry
end
# Returns contributions to a multibind in precedence order; highest first.
# Returns an Array on the form [ [key, entry], [key, entry]] where the key is intended to be used to lookup the value
# (or a producer) for that entry.
# @api private
def get_contributions(scope, contributions_key)
result = {}
return [] unless contributions = lookup_key(scope, contributions_key)
contributions.each { |k| result[k] = get_entry(k) }
result.sort {|a, b| a[0] <=> b[0] }
#result.sort_by {|key, entry| entry }
end
# Produces an injectable class given a key, or nil if key does not represent an injectable class
# @api private
#
def assistable_injected_class(key)
kt = key_factory.get_type(key)
return nil unless kt.is_a?(Puppet::Pops::Types::PRuntimeType) && kt.runtime == :ruby && !key_factory.is_named?(key)
type_calculator.injectable_class(kt)
end
def lookup_producer(scope, *args, &block)
raise ArgumentError, "lookup_producer should be called with two or three arguments, got: #{args.size()+1}" unless args.size <= 2
p = case args[ 0 ]
when Puppet::Pops::Types::PAnyType
lookup_producer_type(scope, *args)
when String
raise ArgumentError, "lookup_producer of name should only pass the name" unless args.size == 1
lookup_producer_key(scope, key_factory.data_key(args[ 0 ]))
else
raise ArgumentError, "lookup_producer using a key should only pass a single key" unless args.size == 1
lookup_producer_key(scope, args[ 0 ])
end
# call block with result if given
if block
case block.arity
when 1
block.call(p)
when 2
block.call(scope, p)
else
raise ArgumentError, "The block should have arity 1 or 2"
end
else
p
end
end
# @api private
def lookup_producer_key(scope, key)
if @recursion_lock.include?(key)
raise ArgumentError, "Lookup loop detected for key: #{key}"
end
begin
@recursion_lock.push(key)
producer(scope, get_entry(key), :multiple_use)
ensure
@recursion_lock.pop()
end
end
# @api private
def lookup_producer_type(scope, type, name='')
lookup_producer_key(scope, named_key(type, name))
end
# Returns the producer for the entry
# @return [Puppet::Pops::Binder::Producers::Producer] the entry's producer.
#
# @api private
#
def producer(scope, entry, use)
return nil unless entry # not found
return entry.producer(scope) if entry.is_a?(Producers::AssistedInjectProducer)
unless entry.cached_producer
entry.cached_producer = transform(entry.binding.producer, scope, entry)
end
unless entry.cached_producer
raise ArgumentError, "Injector entry without a producer #{format_binding(entry.binding)}"
end
entry.cached_producer.producer(scope)
end
# @api private
def transform(producer_descriptor, scope, entry)
@@transform_visitor.visit_this_2(self, producer_descriptor, scope, entry)
end
# Returns the produced instance
# @return [Object] the produced instance
# @api private
#
def produce(scope, entry)
return nil unless entry # not found
producer(scope, entry, :single_use).produce(scope)
end
# @api private
def named_arguments_to_hash(named_args)
nb = named_args.nil? ? [] : named_args
result = {}
nb.each {|arg| result[ :"#{arg.name}" ] = arg.value }
result
end
# @api private
def merge_producer_options(binding, options)
named_arguments_to_hash(binding.producer_args).merge(options)
end
# @api private
def format_binding(b)
Puppet::Pops::Binder::Binder.format_binding(b)
end
# Handles a missing producer (which is valid for a Multibinding where one is selected automatically)
# @api private
#
def transform_NilClass(descriptor, scope, entry)
unless entry.binding.is_a?(Puppet::Pops::Binder::Bindings::Multibinding)
raise ArgumentError, "Binding without producer detected, #{format_binding(entry.binding)}"
end
case entry.binding.type
when Puppet::Pops::Types::PArrayType
transform(Puppet::Pops::Binder::Bindings::ArrayMultibindProducerDescriptor.new(), scope, entry)
when Puppet::Pops::Types::PHashType
transform(Puppet::Pops::Binder::Bindings::HashMultibindProducerDescriptor.new(), scope, entry)
else
raise ArgumentError, "Unsupported multibind type, must be an array or hash type, #{format_binding(entry.binding)}"
end
end
# @api private
def transform_ArrayMultibindProducerDescriptor(descriptor, scope, entry)
make_producer(Producers::ArrayMultibindProducer, descriptor, scope, entry, named_arguments_to_hash(entry.binding.producer_args))
end
# @api private
def transform_HashMultibindProducerDescriptor(descriptor, scope, entry)
make_producer(Producers::HashMultibindProducer, descriptor, scope, entry, named_arguments_to_hash(entry.binding.producer_args))
end
# @api private
def transform_ConstantProducerDescriptor(descriptor, scope, entry)
producer_class = singleton?(descriptor) ? Producers::SingletonProducer : Producers::DeepCloningProducer
producer_class.new(self, entry.binding, scope, merge_producer_options(entry.binding, {:value => descriptor.value}))
end
# @api private
def transform_InstanceProducerDescriptor(descriptor, scope, entry)
make_producer(Producers::InstantiatingProducer, descriptor, scope, entry,
merge_producer_options(entry.binding, {:class_name => descriptor.class_name, :init_args => descriptor.arguments}))
end
# @api private
def transform_EvaluatingProducerDescriptor(descriptor, scope, entry)
make_producer(Producers::EvaluatingProducer, descriptor, scope, entry,
merge_producer_options(entry.binding, {:expression => descriptor.expression}))
end
# @api private
def make_producer(clazz, descriptor, scope, entry, options)
singleton_wrapped(descriptor, scope, entry, clazz.new(self, entry.binding, scope, options))
end
# @api private
def singleton_wrapped(descriptor, scope, entry, producer)
return producer unless singleton?(descriptor)
Producers::SingletonProducer.new(self, entry.binding, scope,
merge_producer_options(entry.binding, {:value => producer.produce(scope)}))
end
# @api private
def transform_ProducerProducerDescriptor(descriptor, scope, entry)
p = transform(descriptor.producer, scope, entry)
clazz = singleton?(descriptor) ? Producers::SingletonProducerProducer : Producers::ProducerProducer
clazz.new(self, entry.binding, scope, merge_producer_options(entry.binding,
merge_producer_options(entry.binding, { :producer_producer => p })))
end
# @api private
def transform_LookupProducerDescriptor(descriptor, scope, entry)
make_producer(Producers::LookupProducer, descriptor, scope, entry,
merge_producer_options(entry.binding, {:type => descriptor.type, :name => descriptor.name}))
end
# @api private
def transform_HashLookupProducerDescriptor(descriptor, scope, entry)
make_producer(Producers::LookupKeyProducer, descriptor, scope, entry,
merge_producer_options(entry.binding, {:type => descriptor.type, :name => descriptor.name, :key => descriptor.key}))
end
# @api private
def transform_NonCachingProducerDescriptor(descriptor, scope, entry)
# simply delegates to the wrapped producer
transform(descriptor.producer, scope, entry)
end
# @api private
def transform_FirstFoundProducerDescriptor(descriptor, scope, entry)
make_producer(Producers::FirstFoundProducer, descriptor, scope, entry,
merge_producer_options(entry.binding, {:producers => descriptor.producers.collect {|p| transform(p, scope, entry) }}))
end
# @api private
def singleton?(descriptor)
! descriptor.eContainer().is_a?(Puppet::Pops::Binder::Bindings::NonCachingProducerDescriptor)
end
# Special marker class used in entries
# @api private
class NotFound
end
end
end
end