Class: Puppet::Pops::Loader::PuppetFunctionInstantiator

Inherits:
Object
  • Object
show all
Defined in:
lib/puppet/pops/loader/puppet_function_instantiator.rb

Overview

source that when called evaluates the Puppet logic it contains.

Class Method Summary collapse

Class Method Details

.create(loader, typed_name, source_ref, pp_code_string) ⇒ Functions::Function

Produces an instance of the Function class with the given typed_name, or fails with an error if the given puppet source does not produce this instance when evaluated.

Parameters:

  • loader (Loader)

    The loader the function is associated with

  • typed_name (TypedName)

    the type / name of the function to load

  • source_ref (URI, String)

    a reference to the source / origin of the puppet code to evaluate

  • pp_code_string (String)

    puppet code in a string

Returns:

  • (Functions::Function)
    • an instantiated function with global scope closure associated with the given loader



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/puppet/pops/loader/puppet_function_instantiator.rb', line 19

def self.create(loader, typed_name, source_ref, pp_code_string)
  parser = Parser::EvaluatingParser.new()

  # parse and validate
  result = parser.parse_string(pp_code_string, source_ref)
  # Only one function is allowed (and no other definitions)
  case result.definitions.size
  when 0
    raise ArgumentError, _("The code loaded from %{source_ref} does not define the function '%{func_name}' - it is empty.") % { source_ref: source_ref, func_name: typed_name.name }
  when 1
    # ok
  else
    raise ArgumentError, _("The code loaded from %{source_ref} must contain only the function '%{type_name}' - it has additional definitions.") % { source_ref: source_ref, type_name: typed_name.name }
  end
  the_function_definition = result.definitions[0]

  unless the_function_definition.is_a?(Model::FunctionDefinition)
    raise ArgumentError, _("The code loaded from %{source_ref} does not define the function '%{type_name}' - no function found.") % { source_ref: source_ref, type_name: typed_name.name }
  end

  unless the_function_definition.name == typed_name.name
    expected = typed_name.name
    actual = the_function_definition.name
    raise ArgumentError, _("The code loaded from %{source_ref} produced function with the wrong name, expected %{expected}, actual %{actual}") % { source_ref: source_ref, expected: expected, actual: actual }
  end
  unless result.body == the_function_definition
    raise ArgumentError, _("The code loaded from %{source} contains additional logic - can only contain the function %{name}") % { source: source_ref, name: typed_name.name }
  end

  # Adapt the function definition with loader - this is used from logic contained in it body to find the
  # loader to use when making calls to the new function API. Such logic have a hard time finding the closure (where
  # the loader is known - hence this mechanism
  private_loader = loader.private_loader
  Adapters::LoaderAdapter.adapt(the_function_definition).loader_name = private_loader.loader_name

  # Cannot bind loaded functions to global scope, that must be done without binding that scope as
  # loaders survive a compilation.
  closure_scope = nil # Puppet.lookup(:global_scope) { {} }

  created = create_function_class(the_function_definition)
  # create the function instance - it needs closure (scope), and loader (i.e. where it should start searching for things
  # when calling functions etc.
  # It should be bound to global scope

  created.new(closure_scope, private_loader)
end

.create_from_model(function_definition, loader) ⇒ Array<TypedName, Functions.Function>

Creates Function class and instantiates it based on a FunctionDefinition model

Returns:

  • (Array<TypedName, Functions.Function>)
    • array of

    typed name, and an instantiated function with global scope closure associated with the given loader



70
71
72
73
74
# File 'lib/puppet/pops/loader/puppet_function_instantiator.rb', line 70

def self.create_from_model(function_definition, loader)
  created = create_function_class(function_definition)
  typed_name = TypedName.new(:function, function_definition.name)
  [typed_name, created.new(nil, loader)]
end

.create_function_class(function_definition) ⇒ Object



76
77
78
79
80
81
82
83
84
85
# File 'lib/puppet/pops/loader/puppet_function_instantiator.rb', line 76

def self.create_function_class(function_definition)
  # Create a 4x function wrapper around a named closure
  Puppet::Functions.create_function(function_definition.name, Puppet::Functions::PuppetFunction) do
    # TODO: should not create a new evaluator per function
    init_dispatch(Evaluator::Closure::Named.new(
                    function_definition.name,
                    Evaluator::EvaluatorImpl.new(), function_definition
                  ))
  end
end