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

loader[R]

The loader that loaded this function. Should be used if function wants to load other things.

Public Class Methods

dispatcher() click to toggle source

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
new(closure_scope, loader) click to toggle source
   # File lib/puppet/pops/functions/function.rb
20 def initialize(closure_scope, loader)
21   @closure_scope = closure_scope
22   @loader = loader
23 end
signatures() click to toggle source

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

call(scope, *args, &block) click to toggle source

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
call_function(function_name, *args, &block) click to toggle source

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
closure_scope() click to toggle source

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

internal_call_function(scope, function_name, args, &block) click to toggle source

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