Module: Puppet::Functions
- Defined in:
- lib/puppet/functions.rb
Overview
Documentation for individual instances of these new functions is not yet tied into the puppet doc system.
Functions in the puppet language can be written in Ruby and distributed in puppet modules. The function is written by creating a file in the module’s ‘lib/puppet/functions/<modulename>` directory, where `<modulename>` is replaced with the module’s name. The file should have the name of the function. For example, to create a function named ‘min` in a module named `math` create a file named `lib/puppet/functions/math/min.rb` in the module.
A function is implemented by calling Functions.create_function, and passing it a block that defines the implementation of the function.
Functions are namespaced inside the module that contains them. The name of the function is prefixed with the name of the module. For example, ‘math::min`.
Anatomy of a function
Functions are composed of four parts: the name, the implementation methods, the signatures, and the dispatches.
The name is the string given to the Functions.create_function method. It specifies the name to use when calling the function in the puppet language, or from other functions.
The implementation methods are ruby methods (there can be one or more) that provide that actual implementation of the function’s behavior. In the simplest case the name of the function (excluding any namespace) and the name of the method are the same. When that is done no other parts (signatures and dispatches) need to be used.
Signatures are a way of specifying the types of the function’s parameters. The types of any arguments will be checked against the types declared in the signature and an error will be produced if they don’t match. The types are defined by using the same syntax for types as in the puppet language.
Dispatches are how signatures and implementation methods are tied together. When the function is called, puppet searches the signatures for one that matches the supplied arguments. Each signature is part of a dispatch, which specifies the method that should be called for that signature. When a matching signature is found, the corrosponding method is called.
Documentation for the function should be placed as comments to the implementation method(s).
Specifying Signatures
If nothing is specified, the number of arguments given to the function must be the same as the number of parameters, and all of the parameters are of type ‘Any’.
The following methods can be used to define a parameter
- _param_ - the argument must be given in the call.
- _optional_param_ - the argument may be missing in the call. May not be followed by a required parameter
- _repeated_param_ - the type specifies a repeating type that occurs 0 to "infinite" number of times. It may only appear last or just before a block parameter.
- _block_param_ - a block must be given in the call. May only appear last.
- _optional_block_param_ - a block may be given in the call. May only appear last.
The method name required_param is an alias for param and required_block_param is an alias for block_param
A parameter definition takes 2 arguments:
- _type_ A string that must conform to a type in the puppet language
- _name_ A symbol denoting the parameter name
Both arguments are optional when defining a block parameter. The type defaults to “Callable” and the name to :block.
Note that the dispatch definition is used to match arguments given in a call to the function with the defined parameters. It then dispatches the call to the implementation method simply passing the given arguments on to that method without any further processing and it is the responsibility of that method’s implementor to ensure that it can handle those arguments.
There is no requirement for direct mapping between parameter definitions and the parameters in the receiving implementation method so the following example is also legal. Here the dispatch will ensure that ‘*values` in the receiver will be an array with at least one entry of type String and that any remaining entries are of type Numeric:
Access to Scope
In general, functions should not need access to scope; they should be written to act on their given input only. If they absolutely must look up variable values, they should do so via the closure scope (the scope where they are defined) - this is done by calling ‘closure_scope()`.
Calling other Functions
Calling other functions by name is directly supported via Pops::Functions::Function#call_function. This allows a function to call other functions visible from its loader.
Defined Under Namespace
Classes: DispatcherBuilder, Function, InternalDispatchBuilder, InternalFunction, PuppetFunction
Class Method Summary collapse
-
.any_signature(from, to, names) ⇒ Object
private
Construct a signature consisting of Object type, with min, and max, and given names.
-
.create_function(func_name, function_base = Function, &block) ⇒ Class<Function>
The newly created Function class.
-
.default_dispatcher(the_class, func_name) ⇒ Object
private
Creates a default dispatcher configured from a method with the same name as the function.
- .min_max_param(method) ⇒ Object private
Class Method Details
.any_signature(from, to, names) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Construct a signature consisting of Object type, with min, and max, and given names. (there is only one type entry).
222 223 224 225 226 227 |
# File 'lib/puppet/functions.rb', line 222 def self.any_signature(from, to, names) # Construct the type for the signature # Tuple[Object, from, to] factory = Puppet::Pops::Types::TypeFactory [factory.callable(factory.any, from, to), names] end |
.create_function(func_name, function_base = Function, &block) ⇒ Class<Function>
Returns the newly created Function class.
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
# File 'lib/puppet/functions.rb', line 155 def self.create_function(func_name, function_base = Function, &block) if function_base.ancestors.none? { |s| s == Puppet::Pops::Functions::Function } raise ArgumentError, "Functions must be based on Puppet::Pops::Functions::Function. Got #{function_base}" end func_name = func_name.to_s # Creates an anonymous class to represent the function # The idea being that it is garbage collected when there are no more # references to it. # the_class = Class.new(function_base, &block) # Make the anonymous class appear to have the class-name <func_name> # Even if this class is not bound to such a symbol in a global ruby scope and # must be resolved via the loader. # This also overrides any attempt to define a name method in the given block # (Since it redefines it) # # TODO, enforce name in lower case (to further make it stand out since Ruby # class names are upper case) # the_class.instance_eval do @func_name = func_name def name @func_name end end # Automatically create an object dispatcher based on introspection if the # loaded user code did not define any dispatchers. Fail if function name # does not match a given method name in user code. # if the_class.dispatcher.empty? simple_name = func_name.split(/::/)[-1] type, names = default_dispatcher(the_class, simple_name) last_captures_rest = (type.size_range[1] == Float::INFINITY) the_class.dispatcher.add_dispatch(type, simple_name, names, nil, nil, nil, last_captures_rest) end # The function class is returned as the result of the create function method the_class end |
.default_dispatcher(the_class, func_name) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Creates a default dispatcher configured from a method with the same name as the function
201 202 203 204 205 206 |
# File 'lib/puppet/functions.rb', line 201 def self.default_dispatcher(the_class, func_name) unless the_class.method_defined?(func_name) raise ArgumentError, "Function Creation Error, cannot create a default dispatcher for function '#{func_name}', no method with this name found" end any_signature(*min_max_param(the_class.instance_method(func_name))) end |
.min_max_param(method) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
209 210 211 212 213 214 215 216 |
# File 'lib/puppet/functions.rb', line 209 def self.min_max_param(method) result = {:req => 0, :opt => 0, :rest => 0 } # count per parameter kind, and get array of names names = method.parameters.map { |p| result[p[0]] += 1 ; p[1].to_s } from = result[:req] to = result[:rest] > 0 ? :default : from + result[:opt] [from, to, names] end |