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

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

Overview

The PuppetFunctionInstantiator instantiates a Puppet::Functions::PuppetFunction given a Puppet Programming language 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) ⇒ Puppet::Pops::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 (Puppet::Pops::Loader::Loader)

    The loader the function is associated with

  • typed_name (Puppet::Pops::Loader::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:



15
16
17
18
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
# File 'lib/puppet/pops/loader/puppet_function_instantiator.rb', line 15

def self.create(loader, typed_name, source_ref, pp_code_string)
  parser = Puppet::Pops::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.model.definitions.size
  when 0
    raise ArgumentError, "The code loaded from #{source_ref} does not define the function '#{typed_name.name}' - it is empty."
  when 1
    # ok
  else
    raise ArgumentError, "The code loaded from #{source_ref} must contain only the function '#{typed_name.name}' - it has additional definitions."
  end
  the_function_definition = result.model.definitions[0]

  unless the_function_definition.is_a?(Puppet::Pops::Model::FunctionDefinition)
    raise ArgumentError, "The code loaded from #{source_ref} does not define the function '#{typed_name.name}' - no function found."
  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}"
  end
  unless result.model().body == the_function_definition
    raise ArgumentError, "The code loaded from #{source_ref} contains additional logic - can only contain the function #{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
  Puppet::Pops::Adapters::LoaderAdapter.adapt(the_function_definition).loader = loader

  # TODO: Cheating wrt. scope - assuming it is found in the context
  closure_scope = Puppet.lookup(:global_scope) { {} }

  created = create_function_class(the_function_definition, closure_scope)
  # 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, loader)
end

.create_from_model(function_definition, loader) ⇒ Array<Puppet::Pops::Loader::Loader::TypedName, Puppet::Pops::Functions.Function>

Creates Function class and instantiates it based on a FunctionDefinition model

Returns:



63
64
65
66
67
68
# File 'lib/puppet/pops/loader/puppet_function_instantiator.rb', line 63

def self.create_from_model(function_definition, loader)
  closure_scope = Puppet.lookup(:global_scope) { {} }
  created = create_function_class(function_definition, closure_scope)
  typed_name = Puppet::Pops::Loader::Loader::TypedName.new(:function, function_definition.name)
  [typed_name, created.new(closure_scope, loader)]
end

.create_function_class(function_definition, closure_scope) ⇒ Object



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

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