Class: Puppet::Pops::Evaluator::Closure

Inherits:
CallableSignature show all
Defined in:
lib/puppet/pops/evaluator/closure.rb

Overview

A Closure represents logic bound to a particular scope. As long as the runtime (basically the scope implementation) has the behavior of Puppet 3x it is not safe to return and later use this closure.

The 3x scope is essentially a named scope with an additional internal local/ephemeral nested scope state. In 3x there is no way to directly refer to the nested scopes, instead, the named scope must be in a particular state. Specifically, closures that require a local/ephemeral scope to exist at a later point will fail. It is safe to call a closure (even with 3x scope) from the very same place it was defined, but not returning it and expecting the closure to reference the scope’s state at the point it was created.

Note that this class is a CallableSignature, and the methods defined there should be used as the API for obtaining information in a callable-implementation agnostic way.

Direct Known Subclasses

Dynamic, Named

Defined Under Namespace

Classes: Dynamic, Named

Constant Summary collapse

CLOSURE_NAME =
'lambda'.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from CallableSignature

#args_range, #argument_mismatch_handler?, #block_range, #block_type, #infinity?

Constructor Details

#initialize(evaluator, model) ⇒ Closure

Returns a new instance of Closure.



60
61
62
63
# File 'lib/puppet/pops/evaluator/closure.rb', line 60

def initialize(evaluator, model)
  @evaluator = evaluator
  @model = model
end

Instance Attribute Details

#enclosing_scopeObject (readonly)



58
59
60
# File 'lib/puppet/pops/evaluator/closure.rb', line 58

def enclosing_scope
  @enclosing_scope
end

#evaluatorObject (readonly)



56
57
58
# File 'lib/puppet/pops/evaluator/closure.rb', line 56

def evaluator
  @evaluator
end

#modelObject (readonly)



57
58
59
# File 'lib/puppet/pops/evaluator/closure.rb', line 57

def model
  @model
end

Instance Method Details

#block_nameObject



153
154
155
156
# File 'lib/puppet/pops/evaluator/closure.rb', line 153

def block_name
  # TODO: Lambda's does not support blocks yet. This is a placeholder
  'unsupported_block'
end

#call(*args) ⇒ Object

Evaluates a closure in its enclosing scope after having matched given arguments with parameters (from left to right)



67
68
69
# File 'lib/puppet/pops/evaluator/closure.rb', line 67

def call(*args)
  call_with_scope(enclosing_scope, args)
end

#call_by_name(args_hash, enforce_parameters) ⇒ Object

Call closure with argument assignment by name



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/puppet/pops/evaluator/closure.rb', line 82

def call_by_name(args_hash, enforce_parameters)
  closure_scope = enclosing_scope
  if enforce_parameters
    # Push a temporary parameter scope used while resolving the parameter defaults
    closure_scope.with_parameter_scope(closure_name, parameter_names) do |param_scope|
      # Assign all non-nil values, even those that represent non-existent paramaters.
      args_hash.each { |k, v| param_scope[k] = v unless v.nil? }
      parameters.each do |p|
        name = p.name
        arg = args_hash[name]
        if arg.nil?
          # Arg either wasn't given, or it was undef
          if p.value.nil?
            # No default. Assign nil if the args_hash included it
            param_scope[name] = nil if args_hash.include?(name)
          else
            param_scope[name] = param_scope.evaluate(name, p.value, closure_scope, @evaluator)
          end
        end
      end
      args_hash = param_scope.to_hash
    end
    Types::TypeMismatchDescriber.validate_parameters(closure_name, params_struct, args_hash)
    result = catch(:next) do
      @evaluator.evaluate_block_with_bindings(closure_scope, args_hash, @model.body)
    end
    Types::TypeAsserter.assert_instance_of(nil, return_type, result) do
      "value returned from #{closure_name}"
    end
  else
    @evaluator.evaluate_block_with_bindings(closure_scope, args_hash, @model.body)
  end
end

#closure_nameObject



161
162
163
# File 'lib/puppet/pops/evaluator/closure.rb', line 161

def closure_name()
  CLOSURE_NAME
end

#invoke(instance, calling_scope, args, &block) ⇒ 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.

This method makes a Closure compatible with a Dispatch. This is used when the closure is wrapped in a Function and the function is called. (Saves an extra Dispatch that just delegates to a Closure and avoids having two checks of the argument type/arity validity).



75
76
77
78
79
# File 'lib/puppet/pops/evaluator/closure.rb', line 75

def invoke(instance, calling_scope, args, &block)
  enclosing_scope.with_global_scope do |global_scope|
    call_with_scope(global_scope, args, &block)
  end
end

#last_captures_rest?Boolean

Returns:

  • (Boolean)


147
148
149
150
# File 'lib/puppet/pops/evaluator/closure.rb', line 147

def last_captures_rest?
  last = @model.parameters[-1]
  last && last.captures_rest
end

#parameter_countInteger

Returns the number of parameters (required and optional)

Returns:

  • (Integer)

    the total number of accepted parameters



122
123
124
125
# File 'lib/puppet/pops/evaluator/closure.rb', line 122

def parameter_count
  # yes, this is duplication of code, but it saves a method call
  @model.parameters.size
end

#parameter_namesObject



128
129
130
# File 'lib/puppet/pops/evaluator/closure.rb', line 128

def parameter_names
  @model.parameters.collect(&:name)
end

#parametersObject



116
117
118
# File 'lib/puppet/pops/evaluator/closure.rb', line 116

def parameters
  @model.parameters
end

#params_structObject



142
143
144
# File 'lib/puppet/pops/evaluator/closure.rb', line 142

def params_struct
  @params_struct ||= create_params_struct
end

#return_typeObject



132
133
134
# File 'lib/puppet/pops/evaluator/closure.rb', line 132

def return_type
  @return_type ||= create_return_type
end

#typeObject



137
138
139
# File 'lib/puppet/pops/evaluator/closure.rb', line 137

def type
  @callable ||= create_callable_type
end