class Puppet::Pops::Functions::Function
@note WARNING: This new function API is still under development and may change at
any time
A function in the puppet evaluator.
Functions are normally defined by another system, which produces subclasses of this class as well as constructing delegations to call the appropriate methods.
This class should rarely be used directly. Instead functions should be constructed using {Puppet::Functions.create_function}.
@api public
Attributes
The loader that loaded this function. Should be used if function wants to load other things.
Public Class Methods
The dispatcher for the function
@api private
# File lib/puppet/pops/functions/function.rb 84 def self.dispatcher 85 @dispatcher ||= Puppet::Pops::Functions::Dispatcher.new 86 end
# File lib/puppet/pops/functions/function.rb 20 def initialize(closure_scope, loader) 21 @closure_scope = closure_scope 22 @loader = loader 23 end
Produces information about parameters in a way that is compatible with Closure
@api private
# File lib/puppet/pops/functions/function.rb 91 def self.signatures 92 @dispatcher.signatures 93 end
Public Instance Methods
Invokes the function via the dispatching logic that performs type check and weaving. A specialized function may override this method to do its own dispatching and checking of the raw arguments. A specialized implementation can rearrange arguments, add or remove arguments and then delegate to the dispatching logic by calling:
@example Delegating to the dispatcher
def call(scope, *args) manipulated_args = args + ['easter_egg'] self.class.dispatcher.dispatch(self, scope, manipulated_args) end
System functions that must have access to the calling scope can use this technique. Functions in general should not need the calling scope. (The closure scope; what is visible where the function is defined) is available via the method `closure_scope`).
@api public
# File lib/puppet/pops/functions/function.rb 41 def call(scope, *args, &block) 42 begin 43 result = catch(:return) do 44 return self.class.dispatcher.dispatch(self, scope, args, &block) 45 end 46 return result.value 47 rescue Puppet::Pops::Evaluator::Next => jumper 48 begin 49 throw :next, jumper.value 50 rescue Puppet::Parser::Scope::UNCAUGHT_THROW_EXCEPTION 51 raise Puppet::ParseError.new("next() from context where this is illegal", jumper.file, jumper.line) 52 end 53 rescue Puppet::Pops::Evaluator::Return => jumper 54 begin 55 throw :return, jumper 56 rescue Puppet::Parser::Scope::UNCAUGHT_THROW_EXCEPTION 57 raise Puppet::ParseError.new("return() from context where this is illegal", jumper.file, jumper.line) 58 end 59 end 60 end
Allows the implementation of a function to call other functions by name. The callable functions are those visible to the same loader that loaded this function (the calling function). The referenced function is called with the calling functions closure scope as the caller's scope.
@param function_name [String] The name of the function @param *args [Object] splat of arguments @return [Object] The result returned by the called function
@api public
# File lib/puppet/pops/functions/function.rb 71 def call_function(function_name, *args, &block) 72 internal_call_function(closure_scope, function_name, args, &block) 73 end
The scope where the function was defined
# File lib/puppet/pops/functions/function.rb 76 def closure_scope 77 # If closure scope is explicitly set to not nil, if there is a global scope, otherwise an empty hash 78 @closure_scope || Puppet.lookup(:global_scope) { {} } 79 end
Protected Instance Methods
Allows the implementation of a function to call other functions by name and pass the caller scope. The callable functions are those visible to the same loader that loaded this function (the calling function).
@param scope [Puppet::Parser::Scope] The caller scope @param function_name [String] The name of the function @param args [Array] array of arguments @return [Object] The result returned by the called function
@api public
# File lib/puppet/pops/functions/function.rb 107 def internal_call_function(scope, function_name, args, &block) 108 109 the_loader = loader 110 unless the_loader 111 raise ArgumentError, _("Function %{class_name}(): cannot call function '%{function_name}' - no loader specified") % 112 { class_name: self.class.name, function_name: function_name } 113 end 114 115 func = the_loader.load(:function, function_name) 116 if func 117 Puppet::Util::Profiler.profile(function_name, [:functions, function_name]) do 118 return func.call(scope, *args, &block) 119 end 120 end 121 122 # Check if a 3x function is present. Raise a generic error if it's not to allow upper layers to fill in the details 123 # about where in a puppet manifest this error originates. (Such information is not available here). 124 loader_scope = closure_scope 125 func_3x = Puppet::Parser::Functions.function(function_name, loader_scope.environment) if loader_scope.is_a?(Puppet::Parser::Scope) 126 unless func_3x 127 raise ArgumentError, _("Function %{class_name}(): Unknown function: '%{function_name}'") % 128 { class_name: self.class.name, function_name: function_name } 129 end 130 131 # Call via 3x API 132 # Arguments must be mapped since functions are unaware of the new and magical creatures in 4x. 133 # NOTE: Passing an empty string last converts nil/:undef to empty string 134 result = scope.send(func_3x, Puppet::Pops::Evaluator::Runtime3FunctionArgumentConverter.map_args(args, loader_scope, ''), &block) 135 136 # Prevent non r-value functions from leaking their result (they are not written to care about this) 137 Puppet::Parser::Functions.rvalue?(function_name) ? result : nil 138 end