class Puppet::Pops::Loader::PuppetPlanInstantiator
The PuppetPlanInstantiator
instantiates a Puppet::Functions::PuppetFunction
given a Puppet
Programming language source that when called evaluates the Puppet
logic it contains.
Public Class Methods
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.
@param loader [Loader] The loader the function is associated with @param typed_name [TypedName] the type / name of the function to load @param source_ref [URI, String] a reference to the source / origin of the puppet code to evaluate @param pp_code_string [String] puppet code in a string
@return [Functions::Function] - an instantiated function with global scope closure associated with the given loader
# File lib/puppet/pops/loader/puppet_plan_instantiator.rb 17 def self.create(loader, typed_name, source_ref, pp_code_string) 18 parser = Parser::EvaluatingParser.new() 19 20 # parse and validate 21 result = parser.parse_string(pp_code_string, source_ref) 22 23 # The parser attaches all definitions, including those nested in apply 24 # blocks, to the Program object. Node definitions in apply blocks are 25 # perfectly legal and don't count as the file containing multiple 26 # definitions for this purpose. By this point, we've already validated that 27 # there are no node definitions *outside* apply blocks, so we simply ignore 28 # them here. 29 definitions = result.definitions.reject { |definition| definition.is_a?(Puppet::Pops::Model::NodeDefinition) } 30 31 # Only one plan is allowed (and no other definitions) 32 case definitions.size 33 when 0 34 raise ArgumentError, _("The code loaded from %{source_ref} does not define the plan '%{plan_name}' - it is empty.") % { source_ref: source_ref, plan_name: typed_name.name } 35 when 1 36 # ok 37 else 38 raise ArgumentError, _("The code loaded from %{source_ref} must contain only the plan '%{plan_name}' - it has additional definitions.") % { source_ref: source_ref, plan_name: typed_name.name } 39 end 40 the_plan_definition = definitions[0] 41 42 unless the_plan_definition.is_a?(Model::PlanDefinition) 43 raise ArgumentError, _("The code loaded from %{source_ref} does not define the plan '%{plan_name}' - no plan found.") % { source_ref: source_ref, plan_name: typed_name.name } 44 end 45 unless the_plan_definition.name == typed_name.name 46 expected = typed_name.name 47 actual = the_plan_definition.name 48 raise ArgumentError, _("The code loaded from %{source_ref} produced plan with the wrong name, expected %{expected}, actual %{actual}") % { source_ref: source_ref, expected: expected, actual: actual } 49 end 50 unless result.body == the_plan_definition 51 raise ArgumentError, _("The code loaded from %{source} contains additional logic - can only contain the plan %{plan_name}") % { source: source_ref, plan_name: typed_name.name } 52 end 53 54 # Adapt the function definition with loader - this is used from logic contained in it body to find the 55 # loader to use when making calls to the new function API. Such logic have a hard time finding the closure (where 56 # the loader is known - hence this mechanism 57 private_loader = loader.private_loader 58 Adapters::LoaderAdapter.adapt(the_plan_definition).loader_name = private_loader.loader_name 59 60 # Cannot bind loaded functions to global scope, that must be done without binding that scope as 61 # loaders survive a compilation. 62 closure_scope = nil 63 64 created = create_function_class(the_plan_definition) 65 # create the function instance - it needs closure (scope), and loader (i.e. where it should start searching for things 66 # when calling functions etc. 67 # It should be bound to global scope 68 69 created.new(closure_scope, private_loader) 70 end
Creates Function class and instantiates it based on a FunctionDefinition model @return [Array<TypedName, Functions
.Function>] - array of
typed name, and an instantiated function with global scope closure associated with the given loader
# File lib/puppet/pops/loader/puppet_plan_instantiator.rb 76 def self.create_from_model(plan_definition, loader) 77 created = create_function_class(plan_definition) 78 typed_name = TypedName.new(:plan, plan_definition.name) 79 [typed_name, created.new(nil, loader)] 80 end
# File lib/puppet/pops/loader/puppet_plan_instantiator.rb 82 def self.create_function_class(plan_definition) 83 # Create a 4x function wrapper around a named closure 84 Puppet::Functions.create_function(plan_definition.name, Puppet::Functions::PuppetFunction) do 85 # TODO: should not create a new evaluator per function 86 init_dispatch(Evaluator::Closure::Named.new( 87 plan_definition.name, 88 Evaluator::EvaluatorImpl.new(), plan_definition)) 89 end 90 end