Functions can specify how many arguments they expect, and can specify a data type for each argument. The rule set for a function’s arguments is called a signature.
Since Puppet functions support more advanced argument checking than Ruby does, the functions API uses a lightweight domain-specific language (DSL) to specify signatures.
Note: This is one of several pages describing the Ruby functions API. Before reading it, make sure you understand the overview of this API.
A function written in Ruby can have more than one signature.
Using multiple signatures is an easy way to have a function behave differently when passed different types or quantities of arguments — instead of writing complex logic to decide what to do, you can write separate implementations and let Puppet figure out which one to use.
If a function has multiple signatures, Puppet checks them in the order they’re written and uses the first one to match the provided arguments.
If your function only needs one signature, and you’re willing to skip the API’s data type checking, you can use an automatic signature. To do so:
dispatch
block.Puppet::Functions.create_function(:'stdlib::camelcase') do
def camelcase(str)
str.split('_').map{|e| e.capitalize}.join
end
end
In this case, since the last segment of stdlib::camelcase
is camelcase
, we must define a method named camelcase
.
Although functions with automatic signatures are simpler to write, they give worse error messages when called incorrectly. Users will get a useful error if they call the function with a wrong number of arguments, but if they give the wrong type of argument, they’ll get something unhelpful. (For example, if you pass the function above a number instead of a string, it reports Error: Evaluation Error: Error while evaluating a Function Call, undefined method 'split' for 5:Fixnum at /Users/nick/Desktop/test2.pp:7:8 on node magpie.lan
.)
If your function might be used by anyone other than yourself, you should support your users by writing a signature with dispatch
.
dispatch
To write a signature, use the dispatch
method.
# A signature that takes a single string argument
dispatch :camelcase do
param 'String', :input_string
return_type 'String' # optional
end
dispatch
takes:
create_function
block, usually after all the signatures.In the code block of a dispatch
statement, you can specify arguments with special parameter methods. All of these methods take two arguments:
The order in which you call these methods is important: the function’s first argument should go first, the second one second, etc.
The following parameter methods are available:
Method name | Description |
---|---|
param or required_param |
A mandatory argument. You can use any number of these. Position: All mandatory arguments must come first. |
optional_param |
An argument that can be omitted. You can use any number of these. When there are multiple optional arguments, users can only pass latter ones if they also provide values for the prior ones. This also applies to repeated arguments. Position: Must come after any required arguments. |
repeated_param or optional_repeated_param |
A repeatable argument, which can receive zero or more values. A signature can only use one repeatable argument. Position: Must come after any non-repeating arguments. |
required_repeated_param |
A repeatable argument, which must receive one or more values. A signature can only use one repeatable argument. Position: Must come after any non-repeating arguments. |
block_param or required_block_param |
A mandatory lambda (block of Puppet code). A signature can only use one block. Position: Must come after all other arguments. |
optional_block_param |
An optional lambda (block of Puppet code). A signature can only use one block. Position: Must come after all other arguments. |
When specifying a repeatable argument, note that:
repeated_param 'Numeric', :values_to_average
, not repeated_param 'Array[Numeric]', :values_to_average
.Functions can receive blocks of Puppet code, as described in the docs on calling functions.
The data type for a block argument should always be Callable
, or a Variant
that only contains Callable
s.
The Callable
type can optionally specify the type and quantity of parameters that the lambda should accept; for example, Callable[String, String]
matches any lambda that can be called with a pair of strings. For more details, see the docs on the Callable
type.
For details on how to execute a provided block in your implementation method, see Using special features in implementation methods.
The implementation method that corresponds to a signature must be able to accept any combination of arguments that the signature might allow.
Most notably, this means:
If the signature has optional arguments, the corresponding method arguments need default values. Otherwise, the function will fail if the arguments are omitted.
For example:
dispatch :epp do
required_param 'String', :template_file
optional_param 'Hash', :parameters_hash
end
def epp(template_file, parameters_hash = {})
# Note that parameters_hash defaults to an empty hash.
end
If the signature has a repeatable argument, the method must use a splat parameter (like *args
) as its final argument.
For example:
dispatch :average do
required_repeated_param 'Numeric', :values_to_average
end
def average(*values)
# Inside the method, the `values` variable will be an array of numbers.
end
return_type
methodNote:
return_type
only works with Puppet 4.7 and later. In earlier versions of Puppet, it will cause an evaluation error.
After specifying a signature’s arguments, you can use the return_type
method to specify the data type of its return value. This method takes one argument: a Puppet data type, specified as a string.
dispatch :camelcase do
param 'String', :input_string
return_type 'String'
end
The return type serves two purposes: documentation, and insurance.
nil
when a string is expected), it will fail early with an informative error instead of allowing compilation to continue with an incorrect value.Note: Local type aliases only work with Puppet 4.5 and later. In earlier versions of Puppet, they will cause an evaluation error.
If you are using complicated [abstract data types][] to validate arguments, and if you need to use these types in multiple signatures, they can sometimes become difficult to work with.
In these cases, you can specify short aliases for your complex types and use the short names in your signatures. Centralizing the complex part like this can make your function more maintainable by reducing copy-pasted code.
To specify aliases, use the local_types
method.
local_types
only once, before any signatures.local_types
takes a block, which should only contain calls to the type
method.type
method takes a single [string][] argument, of the form '<NAME> = <TYPE>'
.
Example:
local_types do
type 'PartColor = Enum[blue, red, green, mauve, teal, white, pine]'
type 'Part = Enum[cubicle_wall, chair, wall, desk, carpet]'
type 'PartToColorMap = Hash[Part, PartColor]'
end
dispatch :define_colors do
param 'PartToColorMap', :part_color_map
end
def define_colors(part_color_map)
# etc
end
To make this API reference easier to use, we’ve split some of its larger topics into separate pages. Please read the following pages to learn the remainder of the Ruby functions API: