Class: Puppet::Pops::Evaluator::Closure
- Inherits:
-
CallableSignature
- Object
- CallableSignature
- Puppet::Pops::Evaluator::Closure
- 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
Defined Under Namespace
Classes: Named
Constant Summary collapse
- CLOSURE_NAME =
'lambda'.freeze
Instance Attribute Summary collapse
- #enclosing_scope ⇒ Object readonly
- #evaluator ⇒ Object readonly
- #model ⇒ Object readonly
Instance Method Summary collapse
- #block_name ⇒ Object
-
#call(*args) ⇒ Object
Evaluates a closure in its enclosing scope after having matched given arguments with parameters (from left to right).
-
#call_by_name(args_hash, enforce_parameters) ⇒ Object
Call closure with argument assignment by name.
- #closure_name ⇒ Object
-
#initialize(evaluator, model, scope) ⇒ Closure
constructor
A new instance of Closure.
-
#invoke(instance, calling_scope, args, &block) ⇒ Object
private
This method makes a Closure compatible with a Dispatch.
- #last_captures_rest? ⇒ Boolean
-
#parameter_count ⇒ Integer
Returns the number of parameters (required and optional).
- #parameter_names ⇒ Object
- #parameters ⇒ Object
- #type ⇒ Object
Methods inherited from CallableSignature
#args_range, #block_range, #block_type, #infinity?
Constructor Details
#initialize(evaluator, model, scope) ⇒ Closure
Returns a new instance of Closure.
19 20 21 22 23 |
# File 'lib/puppet/pops/evaluator/closure.rb', line 19 def initialize(evaluator, model, scope) @evaluator = evaluator @model = model @enclosing_scope = scope end |
Instance Attribute Details
#enclosing_scope ⇒ Object (readonly)
17 18 19 |
# File 'lib/puppet/pops/evaluator/closure.rb', line 17 def enclosing_scope @enclosing_scope end |
#evaluator ⇒ Object (readonly)
15 16 17 |
# File 'lib/puppet/pops/evaluator/closure.rb', line 15 def evaluator @evaluator end |
#model ⇒ Object (readonly)
16 17 18 |
# File 'lib/puppet/pops/evaluator/closure.rb', line 16 def model @model end |
Instance Method Details
#block_name ⇒ Object
121 122 123 124 |
# File 'lib/puppet/pops/evaluator/closure.rb', line 121 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)
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/puppet/pops/evaluator/closure.rb', line 27 def call(*args) variable_bindings = combine_values_with_parameters(args) tc = Puppet::Pops::Types::TypeCalculator final_args = tc.infer_set(parameters.inject([]) do |tmp_args, param| if param.captures_rest tmp_args.concat(variable_bindings[param.name]) else tmp_args << variable_bindings[param.name] end end) if tc.callable?(type, final_args) @evaluator.evaluate_block_with_bindings(@enclosing_scope, variable_bindings, @model.body) else raise ArgumentError, "#{closure_name} called with mis-matched arguments\n#{Puppet::Pops::Evaluator::CallableMismatchDescriber.diff_string(closure_name, final_args, [self])}" end end |
#call_by_name(args_hash, enforce_parameters) ⇒ Object
Call closure with argument assignment by name
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/puppet/pops/evaluator/closure.rb', line 55 def call_by_name(args_hash, enforce_parameters) if enforce_parameters if args_hash.size > parameters.size raise ArgumentError, "Too many arguments: #{args_hash.size} for #{parameters.size}" end # associate values with parameters scope_hash = {} parameters.each do |p| name = p.name if (arg_value = args_hash[name]).nil? # only set result of default expr if it is defined (it is otherwise not possible to differentiate # between explicit undef and no default expression unless p.value.nil? scope_hash[name] = @evaluator.evaluate(p.value, @enclosing_scope) end else scope_hash[name] = arg_value end end missing = parameters.select { |p| !scope_hash.include?(p.name) } if missing.any? raise ArgumentError, "Too few arguments; no value given for required parameters #{missing.collect(&:name).join(" ,")}" end tc = Puppet::Pops::Types::TypeCalculator final_args = tc.infer_set(parameter_names.collect { |param| scope_hash[param] }) if !tc.callable?(type, final_args) raise ArgumentError, "#{closure_name} called with mis-matched arguments\n#{Puppet::Pops::Evaluator::CallableMismatchDescriber.diff_string(closure_name, final_args, [self])}" end else scope_hash = args_hash end @evaluator.evaluate_block_with_bindings(@enclosing_scope, scope_hash, @model.body) end |
#closure_name ⇒ Object
129 130 131 |
# File 'lib/puppet/pops/evaluator/closure.rb', line 129 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).
50 51 52 |
# File 'lib/puppet/pops/evaluator/closure.rb', line 50 def invoke(instance, calling_scope, args, &block) call(*args, &block) end |
#last_captures_rest? ⇒ Boolean
115 116 117 118 |
# File 'lib/puppet/pops/evaluator/closure.rb', line 115 def last_captures_rest? last = @model.parameters[-1] last && last.captures_rest end |
#parameter_count ⇒ Integer
Returns the number of parameters (required and optional)
99 100 101 102 |
# File 'lib/puppet/pops/evaluator/closure.rb', line 99 def parameter_count # yes, this is duplication of code, but it saves a method call @model.parameters.size end |
#parameter_names ⇒ Object
105 106 107 |
# File 'lib/puppet/pops/evaluator/closure.rb', line 105 def parameter_names @model.parameters.collect(&:name) end |
#parameters ⇒ Object
93 94 95 |
# File 'lib/puppet/pops/evaluator/closure.rb', line 93 def parameters @model.parameters end |
#type ⇒ Object
110 111 112 |
# File 'lib/puppet/pops/evaluator/closure.rb', line 110 def type @callable ||= create_callable_type end |