A configurable multibind producer for Array type multibindings.
This implementation collects all contributions to the multibind and then combines them using the following rules:
all unnamed entries are added unless the option `:#priority_on_unnamed` is set to true, in which case the unnamed contribution with the highest priority is added, and the rest are ignored (unless they have the same priority in which case an error is raised).
all named entries are handled the same way as unnamed but the option `:#priority_on_named` controls their handling.
the option `:uniq` post processes the result to only contain unique entries
the option `:flatten` post processes the result by flattening all nested arrays.
If both `:flatten` and `:uniq` are true, flattening is done first.
@note
Collection accepts elements that comply with the array's element type, or the entire type (i.e. Array[element_type]). If the type is restrictive - e.g. Array[String] and an Array[String] is contributed, the result will not be type compliant without also using the `:flatten` option, and a type error will be raised. For an array with relaxed typing i.e. Array[Data], it is valid to produce a result such as `['a', ['b', 'c'], 'd']` and no flattening is required and no error is raised (but using the array needs to be aware of potential array, non-array entries. The use of the option `:flatten` controls how the result is flattened.
@api public
@return [Boolean, Integer] If result should be flattened (true), or not (false), or flattened to given level (0 = none, -1 = all) @api public
@return [Boolean] whether priority should be considered for named contributions @api public
@return [Boolean] whether priority should be considered for unnamed contributions @api public
@return [Boolean] whether the result should be made contain unique (non-equal) entries or not @api public
@api private
# File lib/puppet/pops/binder/producers.rb, line 655 def assert_type(binding, tc, value) infered = tc.infer(value) unless tc.assignable?(binding.type.element_type, infered) || tc.assignable?(binding.type, infered) raise ArgumentError, ["Type Error: contribution to '#{binding.name}' does not match type of multibind, ", "#{type_error_detail([binding.type.element_type, binding.type], value)}"].join() end end
@api private
# File lib/puppet/pops/binder/producers.rb, line 615 def internal_produce(scope) seen = {} included_keys = [] injector.get_contributions(scope, contributions_key).each do |element| key = element[0] entry = element[1] name = entry.binding.name existing = seen[name] empty_name = name.nil? || name.empty? if existing if empty_name && priority_on_unnamed if (seen[name] <=> entry) >= 0 raise ArgumentError, "Duplicate key (same priority) contributed to Array Multibinding '#{binding.name}' with unnamed entry." end next elsif !empty_name && priority_on_named if (seen[name] <=> entry) >= 0 raise ArgumentError, "Duplicate key (same priority) contributed to Array Multibinding '#{binding.name}', key: '#{name}'." end next end else seen[name] = entry end included_keys << key end result = included_keys.collect do |k| x = injector.lookup_key(scope, k) assert_type(binding(), injector.type_calculator(), x) x end result.flatten!(flatten) if flatten result.uniq! if uniq result end
@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 [Boolean] :uniq (false) if collected result should be post-processed to contain only unique entries @option options [Boolean, Integer] :flatten (false) if collected result should be post-processed so all contained arrays
are flattened. May be set to an Integer value to indicate the level of recursion (-1 is endless, 0 is none).
@option options [Boolean] :#priority_on_named (true) if highest precedented named element should win or if all should be included @option options [Boolean] :#priority_on_unnamed (false) if highest precedented unnamed element should win or if all should be included @api public
# File lib/puppet/pops/binder/producers.rb, line 592 def initialize(injector, binding, scope, options) super @uniq = !!options[:uniq] @flatten = options[:flatten] @priority_on_named = options[:priority_on_named].nil? ? true : options[:priority_on_name] @priority_on_unnamed = !!options[:priority_on_unnamed] 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 end