diff --git a/lib/puppet/functions/reduce.rb b/lib/puppet/functions/reduce.rb index b23c94dde..b941a88c8 100644 --- a/lib/puppet/functions/reduce.rb +++ b/lib/puppet/functions/reduce.rb @@ -1,102 +1,102 @@ # Applies a parameterized block to each element in a sequence of entries from the first # argument (_the enumerable_) and returns the last result of the invocation of the parameterized block. # # This function takes two mandatory arguments: the first should be an Array, Hash, or something of # enumerable type, and the last a parameterized block as produced by the puppet syntax: # # $a.reduce |$memo, $x| { ... } # reduce($a) |$memo, $x| { ... } # # When the first argument is an Array or someting of an enumerable type, the block is called with each entry in turn. # When the first argument is a hash each entry is converted to an array with `[key, value]` before being # fed to the block. An optional 'start memo' value may be supplied as an argument between the array/hash # and mandatory block. # # $a.reduce(start) |$memo, $x| { ... } # reduce($a, start) |$memo, $x| { ... } # # If no 'start memo' is given, the first invocation of the parameterized block will be given the first and second # elements of the enumeration, and if the enumerable has fewer than 2 elements, the first # element is produced as the result of the reduction without invocation of the block. -# +# # On each subsequent invocation, the produced value of the invoked parameterized block is given as the memo in the # next invocation. # # @example Using reduce # # # Reduce an array # $a = [1,2,3] # $a.reduce |$memo, $entry| { $memo + $entry } # #=> 6 -# +# # # Reduce hash values # $a = {a => 1, b => 2, c => 3} # $a.reduce |$memo, $entry| { [sum, $memo[1]+$entry[1]] } # #=> [sum, 6] -# +# # # reverse a string # "abc".reduce |$memo, $char| { "$char$memo" } # #=>"cbe" # # It is possible to provide a starting 'memo' as an argument. # # @example Using reduce with given start 'memo' # # # Reduce an array # $a = [1,2,3] # $a.reduce(4) |$memo, $entry| { $memo + $entry } # #=> 10 # # # Reduce hash values # $a = {a => 1, b => 2, c => 3} # $a.reduce([na, 4]) |$memo, $entry| { [sum, $memo[1]+$entry[1]] } # #=> [sum, 10] # # @example Using reduce with an Integer range # # Integer[1,4].reduce |$memo, $x| { $memo + $x } # #=> 10 # # @since 3.2 for Array and Hash # @since 3.5 for additional enumerable types # @note requires `parser = future`. # Puppet::Functions.create_function(:reduce) do dispatch :reduce_without_memo do param 'Object', :enumerable required_block_param end dispatch :reduce_with_memo do param 'Object', :enumerable param 'Object', :memo required_block_param end require 'puppet/util/functions/iterative_support' include Puppet::Util::Functions::IterativeSupport def reduce_without_memo(enumerable, pblock) assert_serving_size(pblock) enum = asserted_enumerable(enumerable) enum.reduce {|memo, x| pblock.call(nil, memo, x) } end def reduce_with_memo(enumerable, given_memo, pblock) assert_serving_size(pblock) enum = asserted_enumerable(enumerable) enum.reduce(given_memo) {|memo, x| pblock.call(nil, memo, x) } end # Asserts number of lambda parameters with more specific error message than the generic # mis-matched arguments message that is produced by the dispatcher's type checking. # def assert_serving_size(pblock) serving_size = pblock.parameter_count unless serving_size == 2 || pblock.last_captures_rest? && serving_size <= 2 raise ArgumentError, "reduce(): block must define 2 parameters; memo, value. Block has #{serving_size}; "+ pblock.parameter_names.join(', ') end end end diff --git a/lib/puppet/parser/functions/assert_type.rb b/lib/puppet/parser/functions/assert_type.rb index 4536cf9aa..577697420 100644 --- a/lib/puppet/parser/functions/assert_type.rb +++ b/lib/puppet/parser/functions/assert_type.rb @@ -1,30 +1,31 @@ Puppet::Parser::Functions::newfunction( :assert_type, + :type => :rvalue, :arity => -3, :doc => "Returns the given value if it is an instance of the given type, and raises an error otherwise. Optionally, if a block is given (accepting two parameters), it will be called instead of raising an error. This to enable giving the user richer feedback, or to supply a default value. Example: assert that `$b` is a non empty `String` and assign to `$a`: $a = assert_type(String[1], $b) Example using custom error message: $a = assert_type(String[1], $b) |$expected, $actual| { fail('The name cannot be empty') } Example, using a warning and a default: $a = assert_type(String[1], $b) |$expected, $actual| { warning('Name is empty, using default') 'anonymous' } See the documentation for 'The Puppet Type System' for more information about types. - since Puppet 3.7 - requires future parser/evaluator ") do |args| function_fail(["assert_type() is only available when parser/evaluator future is in effect"]) end diff --git a/lib/puppet/parser/functions/each.rb b/lib/puppet/parser/functions/each.rb index a76f03fd6..3f4437eef 100644 --- a/lib/puppet/parser/functions/each.rb +++ b/lib/puppet/parser/functions/each.rb @@ -1,47 +1,48 @@ Puppet::Parser::Functions::newfunction( :each, + :type => :rvalue, :arity => -3, :doc => <<-DOC 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 or something that is of enumerable type (integer, Integer range, or String), and the second a parameterized block as produced by the puppet syntax: $a.each |$x| { ... } each($a) |$x| { ... } When the first argument is an Array (or of enumerable type other than Hash), 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| { ... } each($a) |$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}" } Example using each: [1,2,3].each |$val| { ... } # 1, 2, 3 [5,6,7].each |$index, $val| { ... } # (0, 5), (1, 6), (2, 7) {a=>1, b=>2, c=>3}].each |$val| { ... } # ['a', 1], ['b', 2], ['c', 3] {a=>1, b=>2, c=>3}.each |$key, $val| { ... } # ('a', 1), ('b', 2), ('c', 3) Integer[ 10, 20 ].each |$index, $value| { ... } # (0, 10), (1, 11) ... "hello".each |$char| { ... } # 'h', 'e', 'l', 'l', 'o' 3.each |$number| { ... } # 0, 1, 2 - since 3.2 for Array and Hash - since 3.5 for other enumerables - note requires `parser = future` DOC ) do |args| function_fail(["each() is only available when parser/evaluator future is in effect"]) end diff --git a/lib/puppet/parser/functions/lookup.rb b/lib/puppet/parser/functions/lookup.rb index 55d56f452..e36c4c378 100644 --- a/lib/puppet/parser/functions/lookup.rb +++ b/lib/puppet/parser/functions/lookup.rb @@ -1,144 +1,144 @@ Puppet::Parser::Functions.newfunction(:lookup, :type => :rvalue, :arity => -2, :doc => <<-'ENDHEREDOC') do |args| Looks up data defined using Puppet Bindings and Hiera. The function is callable with one to three arguments and optionally with a code block to further process the result. The lookup function can be called in one of these ways: lookup(name) lookup(name, type) lookup(name, type, default) lookup(options_hash) lookup(name, options_hash) The function may optionally be called with a code block / lambda with the following signatures: lookup(...) |$result| { ... } lookup(...) |$name, $result| { ... } lookup(...) |$name, $result, $default| { ... } The longer signatures are useful when the block needs to raise an error (it can report the name), or if it needs to know if the given default value was selected. The code block receives the following three arguments: * The `$name` is the last name that was looked up (*the* name if only one name was looked up) * The `$result` is the looked up value (or the default value if not found). * The `$default` is the given default value (`undef` if not given). The block, if present, is called with the result from the lookup. The value produced by the block is also what is produced by the `lookup` function. When a block is used, it is the users responsibility to call `error` if the result does not meet additional criteria, or if an undef value is not acceptable. If a value is not found, and a default has been specified, the default value is given to the block. The content of the options hash is: * `name` - The name or array of names to lookup (first found is returned) * `type` - The type to assert (a Type or a type specification in string form) * `default` - The default value if there was no value found (must comply with the data type) * `accept_undef` - (default `false`) An `undef` result is accepted if this options is set to `true`. * `override` - a hash with map from names to values that are used instead of the underlying bindings. If the name is found here it wins. Defaults to an empty hash. * `extra` - a hash with map from names to values that are used as a last resort to obtain a value. Defaults to an empty hash. When the call is on the form `lookup(name, options_hash)`, or `lookup(name, type, options_hash)`, the given name argument wins over the `options_hash['name']`. The search order is `override` (if given), then `binder`, then `hiera` and finally `extra` (if given). The first to produce a value other than undef for a given name wins. The type specification is one of: * A type in the Puppet Type System, e.g.: * `Integer`, an integral value with optional range e.g.: * `Integer[0, default]` - 0 or positive * `Integer[default, -1]` - negative, * `Integer[1,100]` - value between 1 and 100 inclusive * `String`- any string * `Float` - floating point number (same signature as for Integer for `Integer` ranges) * `Boolean` - true of false (strict) * `Array` - an array (of Data by default), or parameterized as `Array[]`, where `` is the expected type of elements * `Hash`, - a hash (of default `Literal` keys and `Data` values), or parameterized as `Hash[]`, `Hash[, ]`, where ``, and `` are the types of the keys and values respectively (key is `Literal` by default). * `Data` - abstract type representing any `Literal`, `Array[Data]`, or `Hash[Literal, Data]` * `Pattern[, , ..., ]` - an enumeration of valid patterns (one or more) where a pattern is a regular expression string or regular expression, e.g. `Pattern['.com$', '.net$']`, `Pattern[/[a-z]+[0-9]+/]` * `Enum[, , ..., ]`, - an enumeration of exact string values (one or more) e.g. `Enum[blue, red, green]`. * `Variant[, ,...]` - matches one of the listed types (at least one must be given) e.g. `Variant[Integer[8000,8999], Integer[20000, 99999]]` to accept a value in either range * `Regexp`- a regular expression (i.e. the result is a regular expression, not a string matching a regular expression). * A string containing a type description - one of the types as shown above but in string form. If the function is called without specifying a default value, and nothing is bound to the given name an error is raised unless the option `accept_undef` is true. If a block is given it must produce an acceptable value (or call `error`). If the block does not produce an acceptable value an error is raised. Examples: When called with one argument; **the name**, it returns the bound value with the given name after having asserted it has the default datatype `Data`: lookup('the_name') When called with two arguments; **the name**, and **the expected type**, it returns the bound value with the given name after having asserted it has the given data type ('String' in the example): lookup('the_name', 'String') # 3.x lookup('the_name', String) # parser future When called with three arguments, **the name**, the **expected type**, and a **default**, it returns the bound value with the given name, or the default after having asserted the value has the given data type (`String` in the example above): lookup('the_name', 'String', 'Fred') # 3x lookup('the_name', String, 'Fred') # parser future Using a lambda to process the looked up result - asserting that it starts with an upper case letter: # only with parser future lookup('the_size', Integer[1,100]) |$result| { if $large_value_allowed and $result > 10 { error 'Values larger than 10 are not allowed'} $result } Including the name in the error # only with parser future lookup('the_size', Integer[1,100]) |$name, $result| { if $large_value_allowed and $result > 10 { error 'The bound value for '${name}' can not be larger than 10 in this configuration'} $result } When using a block, the value it produces is also asserted against the given type, and it may not be `undef` unless the option `'accept_undef'` is `true`. All options work as the corresponding (direct) argument. The `first_found` option and `accept_undef` are however only available as options. Using first_found semantics option to return the first name that has a bound value: lookup(['apache::port', 'nginx::port'], 'Integer', 80) If you want to make lookup return undef when no value was found instead of raising an error: $are_you_there = lookup('peekaboo', { accept_undef => true} ) $are_you_there = lookup('peekaboo', { accept_undef => true}) |$result| { $result } ENDHEREDOC unless Puppet[:binder] || Puppet[:parser] == 'future' - raise Puppet::ParseError, "The lookup function is only available with settings --binder true, or --parser future" + raise Puppet::ParseError, "The lookup function is only available with settings --binder true, or --parser future" end Puppet::Pops::Binder::Lookup.lookup(self, args) end diff --git a/lib/puppet/parser/functions/map.rb b/lib/puppet/parser/functions/map.rb index 6a8ba029f..c2ca3aae6 100644 --- a/lib/puppet/parser/functions/map.rb +++ b/lib/puppet/parser/functions/map.rb @@ -1,42 +1,43 @@ Puppet::Parser::Functions::newfunction( :map, + :type => :rvalue, :arity => -3, :doc => <<-DOC Applies a parameterized block to each element in a sequence of entries from the first argument and returns an array with the result of each invocation of the parameterized block. This function takes two mandatory arguments: the first should be an Array, Hash, or of Enumerable type (integer, Integer range, or String), and the second a parameterized block as produced by the puppet syntax: $a.map |$x| { ... } map($a) |$x| { ... } When the first argument `$a` is an Array or of enumerable type, the block is called with each entry in turn. When the first argument is a hash the entry is an array with `[key, value]`. Example Using map with two arguments # Turns hash into array of values $a.map |$x|{ $x[1] } # Turns hash into array of keys $a.map |$x| { $x[0] } When using a block with 2 parameters, the element's index (starting from 0) for an array, and the key for a hash is given to the block's first parameter, and the value is given to the block's second parameter.args. Example Using map with two arguments # Turns hash into array of values $a.map |$key,$val|{ $val } # Turns hash into array of keys $a.map |$key,$val|{ $key } - since 3.4 for Array and Hash - since 3.5 for other enumerables, and support for blocks with 2 parameters - note requires `parser = future` DOC ) do |args| function_fail(["map() is only available when parser/evaluator future is in effect"]) end diff --git a/lib/puppet/parser/functions/reduce.rb b/lib/puppet/parser/functions/reduce.rb index b4e39c5ba..4fe90a239 100644 --- a/lib/puppet/parser/functions/reduce.rb +++ b/lib/puppet/parser/functions/reduce.rb @@ -1,70 +1,71 @@ Puppet::Parser::Functions::newfunction( :reduce, + :type => :rvalue, :arity => -3, :doc => <<-DOC Applies a parameterized block to each element in a sequence of entries from the first argument (_the enumerable_) and returns the last result of the invocation of the parameterized block. This function takes two mandatory arguments: the first should be an Array, Hash, or something of enumerable type, and the last a parameterized block as produced by the puppet syntax: $a.reduce |$memo, $x| { ... } reduce($a) |$memo, $x| { ... } When the first argument is an Array or someting of an enumerable type, the block is called with each entry in turn. When the first argument is a hash each entry is converted to an array with `[key, value]` before being fed to the block. An optional 'start memo' value may be supplied as an argument between the array/hash and mandatory block. $a.reduce(start) |$memo, $x| { ... } reduce($a, start) |$memo, $x| { ... } If no 'start memo' is given, the first invocation of the parameterized block will be given the first and second elements of the enumeration, and if the enumerable has fewer than 2 elements, the first element is produced as the result of the reduction without invocation of the block. On each subsequent invocation, the produced value of the invoked parameterized block is given as the memo in the next invocation. Example Using reduce # Reduce an array $a = [1,2,3] $a.reduce |$memo, $entry| { $memo + $entry } #=> 6 # Reduce hash values $a = {a => 1, b => 2, c => 3} $a.reduce |$memo, $entry| { [sum, $memo[1]+$entry[1]] } #=> [sum, 6] # reverse a string "abc".reduce |$memo, $char| { "$char$memo" } #=>"cbe" It is possible to provide a starting 'memo' as an argument. Example Using reduce with given start 'memo' # Reduce an array $a = [1,2,3] $a.reduce(4) |$memo, $entry| { $memo + $entry } #=> 10 # Reduce hash values $a = {a => 1, b => 2, c => 3} $a.reduce([na, 4]) |$memo, $entry| { [sum, $memo[1]+$entry[1]] } #=> [sum, 10] Example Using reduce with an Integer range Integer[1,4].reduce |$memo, $x| { $memo + $x } #=> 10 - since 3.2 for Array and Hash - since 3.5 for additional enumerable types - note requires `parser = future`. DOC ) do |args| function_fail(["reduce() is only available when parser/evaluator future is in effect"]) end diff --git a/lib/puppet/parser/functions/slice.rb b/lib/puppet/parser/functions/slice.rb index 810ac2a29..b6e8c5ff7 100644 --- a/lib/puppet/parser/functions/slice.rb +++ b/lib/puppet/parser/functions/slice.rb @@ -1,47 +1,48 @@ Puppet::Parser::Functions::newfunction( :slice, + :type => :rvalue, :arity => -3, :doc => <<-DOC Applies a parameterized block to each _slice_ of elements in a sequence of selected entries from the first argument and returns the first argument, or if no block is given returns a new array with a concatenation of the slices. This function takes two mandatory arguments: the first, `$a`, should be an Array, Hash, or something of enumerable type (integer, Integer range, or String), and the second, `$n`, the number of elements to include in each slice. The optional third argument should be a a parameterized block as produced by the puppet syntax: $a.slice($n) |$x| { ... } slice($a) |$x| { ... } The parameterized block should have either one parameter (receiving an array with the slice), or the same number of parameters as specified by the slice size (each parameter receiving its part of the slice). In case there are fewer remaining elements than the slice size for the last slice it will contain the remaining elements. When the block has multiple parameters, excess parameters are set to :undef for an array or enumerable type, and to empty arrays for a Hash. $a.slice(2) |$first, $second| { ... } When the first argument is a Hash, each `key,value` entry is counted as one, e.g, a slice size of 2 will produce an array of two arrays with key, and value. Example Using slice with Hash $a.slice(2) |$entry| { notice "first ${$entry[0]}, second ${$entry[1]}" } $a.slice(2) |$first, $second| { notice "first ${first}, second ${second}" } When called without a block, the function produces a concatenated result of the slices. Example Using slice without a block slice([1,2,3,4,5,6], 2) # produces [[1,2], [3,4], [5,6]] slice(Integer[1,6], 2) # produces [[1,2], [3,4], [5,6]] slice(4,2) # produces [[0,1], [2,3]] slice('hello',2) # produces [[h, e], [l, l], [o]] - since 3.2 for Array and Hash - since 3.5 for additional enumerable types - note requires `parser = future`. DOC ) do |args| function_fail(["slice() is only available when parser/evaluator future is in effect"]) end diff --git a/lib/puppet/parser/functions/with.rb b/lib/puppet/parser/functions/with.rb index bf3b499d8..07beff6fd 100644 --- a/lib/puppet/parser/functions/with.rb +++ b/lib/puppet/parser/functions/with.rb @@ -1,20 +1,21 @@ Puppet::Parser::Functions::newfunction( :with, + :type => :rvalue, :arity => -1, :doc => <<-DOC Call a lambda code block with the given arguments. Since the parameters of the lambda are local to the lambda's scope, this can be used to create private sections of logic in a class so that the variables are not visible outside of the class. Example: # notices the array [1, 2, 'foo'] with(1, 2, 'foo') |$x, $y, $z| { notice [$x, $y, $z] } - since 3.7.0 - note requires future parser DOC ) do |args| function_fail(["with() is only available when parser/evaluator future is in effect"]) end