diff --git a/lib/puppet/parser/functions/each.rb b/lib/puppet/parser/functions/each.rb index d9014ca20..569a6c416 100644 --- a/lib/puppet/parser/functions/each.rb +++ b/lib/puppet/parser/functions/each.rb @@ -1,132 +1,140 @@ Puppet::Parser::Functions::newfunction( :each, :type => :rvalue, :arity => 2, :doc => <<-'ENDHEREDOC') do |args| Applies a parameterized block to each element in a sequence of selected entries from the first argument and returns the first argument. This function takes two mandatory arguments: the first should be an Array or a Hash, and the second a parameterized block as produced by the puppet syntax: $a.each {|$x| ... } When the first argument is an Array, the parameterized block should define one or two block parameters. For each application of the block, the next element from the array is selected, and it is passed to the block if the block has one parameter. If the block has two parameters, the first is the elements index, and the second the value. The index starts from 0. $a.each {|$index, $value| ... } When the first argument is a Hash, the parameterized block should define one or two parameters. When one parameter is defined, the iteration is performed with each entry as an array of `[key, value]`, and when two parameters are defined the iteration is performed with key and value. $a.each {|$entry| ..."key ${$entry[0]}, value ${$entry[1]}" } $a.each {|$key, $value| ..."key ${key}, value ${value}" } + When the first argument is a Type and the type is Enumerable, the parameterized block should define one + or two block parameters. + For each application of the block, the next element from the type enumerator is selected, and it is passed to + the block if the block has one parameter. If the block has two parameters, the first is the elements + index, and the second the value. The index starts from 0. + + Integer[ 10, 20 ].each {|$index, $value| ... } + - Since 3.2 - requires `parser = future`. ENDHEREDOC require 'puppet/parser/ast/lambda' def foreach_Array(o, scope, pblock) return nil unless pblock serving_size = pblock.parameter_count if serving_size == 0 raise ArgumentError, "Block must define at least one parameter; value." end if serving_size > 2 raise ArgumentError, "Block must define at most two parameters; index, value" end enumerator = o.each index = 0 if serving_size == 1 (o.size).times do pblock.call(scope, enumerator.next) end else (o.size).times do pblock.call(scope, index, enumerator.next) index = index +1 end end o end def foreach_Hash(o, scope, pblock) return nil unless pblock serving_size = pblock.parameter_count case serving_size when 0 raise ArgumentError, "Block must define at least one parameter (for hash entry key)." when 1 when 2 else raise ArgumentError, "Block must define at most two parameters (for hash entry key and value)." end enumerator = o.each_pair if serving_size == 1 (o.size).times do pblock.call(scope, enumerator.next) end else (o.size).times do pblock.call(scope, *enumerator.next) end end o end def foreach_Type(o, scope, pblock) return nil unless pblock tc = Puppet::Pops::Types::TypeCalculator.new() enumerable = tc.enumerable(o) if enumerable.nil? raise ArgumentError, ("each(): given type '#{tc.string(o)}' is not enumerable") end serving_size = pblock.parameter_count if serving_size == 0 raise ArgumentError, "Block must define at least one parameter; value." end if serving_size > 2 raise ArgumentError, "Block must define at most two parameters; index, value" end enumerator = enumerable.each index = 0 if serving_size == 1 begin loop { pblock.call(scope, enumerator.next) } rescue StopIteration end else begin loop do pblock.call(scope, index, enumerator.next) index = index +1 end rescue StopIteration end end o end raise ArgumentError, ("each(): wrong number of arguments (#{args.length}; must be 2)") if args.length != 2 receiver = args[0] pblock = args[1] raise ArgumentError, ("each(): wrong argument type (#{args[1].class}; must be a parameterized block.") unless pblock.respond_to?(:puppet_lambda) case receiver when Array foreach_Array(receiver, self, pblock) when Hash foreach_Hash(receiver, self, pblock) else if receiver.is_a?(Puppet::Pops::Types::PAbstractType) foreach_Type(receiver, self, pblock) else raise ArgumentError, ("each(): wrong argument type (#{args[0].class}; must be an Array, Hash, or Enumerable Type.") end end end