diff --git a/lib/puppet/loaders.rb b/lib/puppet/loaders.rb index 1880fabb2..e85b5e3fc 100644 --- a/lib/puppet/loaders.rb +++ b/lib/puppet/loaders.rb @@ -1,21 +1,20 @@ module Puppet module Pops require 'puppet/pops/loaders' module Loader require 'puppet/pops/loader/loader' require 'puppet/pops/loader/base_loader' require 'puppet/pops/loader/gem_support' require 'puppet/pops/loader/module_loaders' require 'puppet/pops/loader/dependency_loader' require 'puppet/pops/loader/null_loader' require 'puppet/pops/loader/static_loader' - require 'puppet/pops/loader/puppet_function_instantiator' require 'puppet/pops/loader/ruby_function_instantiator' require 'puppet/pops/loader/ruby_legacy_function_instantiator' require 'puppet/pops/loader/loader_paths' require 'puppet/pops/loader/simple_environment_loader' end end -end \ No newline at end of file +end diff --git a/lib/puppet/pops/loader/loader_paths.rb b/lib/puppet/pops/loader/loader_paths.rb index fed0eecf7..a431a4801 100644 --- a/lib/puppet/pops/loader/loader_paths.rb +++ b/lib/puppet/pops/loader/loader_paths.rb @@ -1,165 +1,137 @@ # LoaderPaths # === # The central loader knowledge about paths, what they represent and how to instantiate from them. # Contains helpers (*smart paths*) to deal with lazy resolution of paths. # # TODO: Currently only supports loading of functions (3 kinds) # module Puppet::Pops::Loader::LoaderPaths # Returns an array of SmartPath, each instantiated with a reference to the given loader (for root path resolution # and existence checks). The smart paths in the array appear in precedence order. The returned array may be # mutated. # def self.relative_paths_for_type(type, loader) #, start_index_in_name) result = case type # typed_name.type when :function if Puppet[:biff] == true - [FunctionPath4x.new(loader), FunctionPath3x.new(loader), FunctionPathPP.new(loader)] + [FunctionPath4x.new(loader), FunctionPath3x.new(loader)] else - [FunctionPath4x.new(loader), FunctionPathPP.new(loader)] + [FunctionPath4x.new(loader)] end # when :xxx # TODO: Add all other types else # unknown types, simply produce an empty result; no paths to check, nothing to find... move along... [] end result end # # DO NOT REMOVE YET. needed later? when there is the need to decamel a classname # def de_camel(fq_name) # fq_name.to_s.gsub(/::/, '/'). # gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2'). # gsub(/([a-z\d])([A-Z])/,'\1_\2'). # tr("-", "_"). # downcase # end class SmartPath # Generic path, in the sense of "if there are any entities of this kind to load, where are they?" attr_reader :generic_path # Creates SmartPath for the given loader (loader knows how to check for existence etc.) def initialize(loader) @loader = loader end def generic_path() return @generic_path unless @generic_path.nil? root_path = @loader.path @generic_path = (root_path.nil? ? relative_path : File.join(root_path, relative_path)) end # Effective path is the generic path + the name part(s) + extension. # def effective_path(typed_name, start_index_in_name) -# "#{File.join(generic_path, typed_name.name_parts[ start_index_in_name..-1 ])}#{extension}" "#{File.join(generic_path, typed_name.name_parts)}#{extension}" end def relative_path() raise NotImplementedError.new end def instantiator() raise NotImplementedError.new end end class RubySmartPath < SmartPath def extension ".rb" end # Duplication of extension information, but avoids one call def effective_path(typed_name, start_index_in_name) -# "#{File.join(generic_path, typed_name.name_parts[ start_index_in_name..-1 ])}.rb" "#{File.join(generic_path, typed_name.name_parts)}.rb" end end - class PuppetSmartPath < SmartPath - def extension - ".pp" - end - - # Duplication of extension information, but avoids one call - def effective_path(typed_name, start_index_in_name) - # Puppet name to path always skips the name-space as that is part of the generic path - # i.e. /mumodule/functions/foo.pp is the function mymodule::foo - "#{File.join(generic_path, typed_name.name_parts[ 1..-1 ])}.pp" -# "#{File.join(generic_path, typed_name.name_parts)}.pp" - end - end - class FunctionPath4x < RubySmartPath FUNCTION_PATH_4X = File.join('lib', 'puppet', 'functions') def relative_path FUNCTION_PATH_4X end def instantiator() Puppet::Pops::Loader::RubyFunctionInstantiator end end class FunctionPath3x < RubySmartPath FUNCTION_PATH_3X = File.join('lib', 'puppet', 'parser', 'functions') def relative_path FUNCTION_PATH_3X end def instantiator() Puppet::Pops::Loader::RubyLegacyFunctionInstantiator end end - class FunctionPathPP < PuppetSmartPath - FUNCTION_PATH_PP = 'functions' - - def relative_path - FUNCTION_PATH_PP - end - - def instantiator() - Puppet::Pops::Loader::PuppetFunctionInstantiator - end - end - # SmartPaths # === # Holds effective SmartPath instances per type # class SmartPaths def initialize(path_based_loader) @loader = path_based_loader @smart_paths = {} end # Ensures that the paths for the type have been probed and pruned to what is existing relative to # the given root. # # @param type [Symbol] the entity type to load # @return [Array] array of effective paths for type (may be empty) # def effective_paths(type) smart_paths = @smart_paths loader = @loader unless effective_paths = smart_paths[type] # type not yet processed, does the various directories for the type exist ? # Get the relative dirs for the type paths_for_type = Puppet::Pops::Loader::LoaderPaths.relative_paths_for_type(type, loader) # Check which directories exist in the loader's content/index effective_paths = smart_paths[type] = paths_for_type.select { |sp| loader.meaningful_to_search?(sp) } end effective_paths end end end diff --git a/lib/puppet/pops/loader/puppet_function_instantiator.rb b/lib/puppet/pops/loader/puppet_function_instantiator.rb deleted file mode 100644 index b39477f52..000000000 --- a/lib/puppet/pops/loader/puppet_function_instantiator.rb +++ /dev/null @@ -1,113 +0,0 @@ -# The PuppetFunctionInstantiator instantiates a Puppet::Functions::Function given a Puppet Programming language -# source that when called evaluates the Puppet logic it contains. -# -class Puppet::Pops::Loader::PuppetFunctionInstantiator - # 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 [Puppet::Pops::Loader::Loader] The loader the function is associated with - # @param typed_name [Puppet::Pops::Loader::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 [Puppet::Pops::Functions.Function] - an instantiated function with global scope closure associated with the given loader - # - def self.create(loader, typed_name, source_ref, pp_code_string) - parser = Puppet::Pops::Parser::EvaluatingParser::Transitional.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 - - # Creates Function class and instantiates it based on a FunctionDefinition model - # @return [Array] - array of - # typed name, and an instantiated function with global scope closure associated with the given loader - # - 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 - - def self.create_function_class(function_definition, closure_scope) - method_name = :"#{function_definition.name.split(/::/).slice(-1)}" - closure = Puppet::Pops::Evaluator::Closure.new( - Puppet::Pops::Evaluator::EvaluatorImpl.new(), - function_definition, - closure_scope) - required_optional = function_definition.parameters.reduce([0, 0]) do |memo, p| - if p.value.nil? - memo[0] += 1 - else - memo[1] += 1 - end - memo - end - min_arg_count = required_optional[0] - max_arg_count = required_optional[0] + required_optional[1] - - # Create a 4x function wrapper around the Puppet Function - created_function_class = Puppet::Functions.create_function(function_definition.name) do - # Define the method that is called from dispatch - this method just changes a call - # with multiple unknown arguments to passing all in an array (since this is expected in the closure API. - # - # TODO: The closure will call the evaluator.call method which will again match args with parameters. - # This can be done a better way later - unifying the two concepts - a function instance is really the same - # as the current evaluator closure for lambdas, only that it also binds an evaluator. This could perhaps - # be a specialization of Function... with a special dispatch - # - define_method(:__relay__call__) do |*args| - closure.call(nil, *args) - end - - # Define a dispatch that performs argument type/count checking - # - dispatch :__relay__call__ do - # Use Puppet Type Object (not Optional[Object] since the 3x API passes undef as empty string). - param(optional(object), 'args') - # Specify arg count (transformed from FunctionDefinition.parameters, no types, or varargs yet) - arg_count(min_arg_count, max_arg_count) - end - end - created_function_class - - end -end \ No newline at end of file diff --git a/lib/puppet/pops/loader/ruby_function_instantiator.rb b/lib/puppet/pops/loader/ruby_function_instantiator.rb index 0b66ac126..d298eec2e 100644 --- a/lib/puppet/pops/loader/ruby_function_instantiator.rb +++ b/lib/puppet/pops/loader/ruby_function_instantiator.rb @@ -1,34 +1,34 @@ # The RubyFunctionInstantiator instantiates a Puppet::Functions::Function given the ruby source # that calls Puppet::Functions.create_function. # class Puppet::Pops::Loader::RubyFunctionInstantiator # Produces an instance of the Function class with the given typed_name, or fails with an error if the # given ruby source does not produce this instance when evaluated. # # @param loader [Puppet::Pops::Loader::Loader] The loader the function is associated with # @param typed_name [Puppet::Pops::Loader::TypedName] the type / name of the function to load # @param source_ref [URI, String] a reference to the source / origin of the ruby code to evaluate # @param ruby_code_string [String] ruby code in a string # # @return [Puppet::Pops::Functions.Function] - an instantiated function with global scope closure associated with the given loader # def self.create(loader, typed_name, source_ref, ruby_code_string) unless ruby_code_string.is_a?(String) && ruby_code_string =~ /Puppet\:\:Functions\.create_function/ raise ArgumentError, "The code loaded from #{source_ref} does not seem to be a Puppet 4x API function - no create_function call." end created = eval(ruby_code_string) unless created.is_a?(Class) raise ArgumentError, "The code loaded from #{source_ref} did not produce a Function class when evaluated. Got '#{created.class}'" end unless created.name.to_s == typed_name.name() raise ArgumentError, "The code loaded from #{source_ref} produced mis-matched name, expected '#{typed_name.name}', got #{created.name}" end # 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 # TODO: Cheating wrt. scope - assuming it is found in the context closure_scope = Puppet.lookup(:global_scope) { {} } created.new(closure_scope, loader) end -end \ No newline at end of file +end diff --git a/spec/fixtures/unit/pops/loaders/loaders/single_module/modules/modulea/functions/func_a.pp b/spec/fixtures/unit/pops/loaders/loaders/single_module/modules/modulea/functions/func_a.pp deleted file mode 100644 index fb4238340..000000000 --- a/spec/fixtures/unit/pops/loaders/loaders/single_module/modules/modulea/functions/func_a.pp +++ /dev/null @@ -1 +0,0 @@ -function modulea::func_a() { "I am modulea::func_a()" } \ No newline at end of file diff --git a/spec/fixtures/unit/pops/loaders/loaders/single_module/modules/modulea/functions/func_b.pp b/spec/fixtures/unit/pops/loaders/loaders/single_module/modules/modulea/functions/func_b.pp deleted file mode 100644 index c0ebd62d7..000000000 --- a/spec/fixtures/unit/pops/loaders/loaders/single_module/modules/modulea/functions/func_b.pp +++ /dev/null @@ -1,2 +0,0 @@ -# Is not namespaces on purpose to trigger error -function func_b() { "I am func_a()" } \ No newline at end of file diff --git a/spec/fixtures/unit/pops/loaders/loaders/single_module/modules/modulea/functions/nested/func_a.pp b/spec/fixtures/unit/pops/loaders/loaders/single_module/modules/modulea/functions/nested/func_a.pp deleted file mode 100644 index 17ed2ac58..000000000 --- a/spec/fixtures/unit/pops/loaders/loaders/single_module/modules/modulea/functions/nested/func_a.pp +++ /dev/null @@ -1 +0,0 @@ -function modulea::nested::func_a() { "I am modulea::nested::func_a()" } \ No newline at end of file diff --git a/spec/unit/pops/loaders/dependency_loader_spec.rb b/spec/unit/pops/loaders/dependency_loader_spec.rb index de4289e7a..5f12528a2 100644 --- a/spec/unit/pops/loaders/dependency_loader_spec.rb +++ b/spec/unit/pops/loaders/dependency_loader_spec.rb @@ -1,40 +1,43 @@ require 'spec_helper' require 'puppet_spec/files' require 'puppet/pops' require 'puppet/loaders' describe 'dependency loader' do include PuppetSpec::Files let(:static_loader) { Puppet::Pops::Loader::StaticLoader.new() } describe 'FileBased module loader' do it 'load something in global name space raises an error' do module_dir = dir_containing('testmodule', { - 'functions' => { - 'foo.pp' => 'function foo() { yay }'}}) + 'lib' => { 'puppet' => { 'functions' => { 'testmodule' => { + 'foo.rb' => 'Puppet::Functions.create_function("foo") { def foo; end; }' + }}}}}) module_loader = Puppet::Pops::Loader::ModuleLoaders::FileBased.new(static_loader, 'testmodule', module_dir, 'test1') dep_loader = Puppet::Pops::Loader::DependencyLoader.new(static_loader, 'test-dep', [module_loader]) expect do dep_loader.load_typed(typed_name(:function, 'testmodule::foo')).value - end.to raise_error(ArgumentError, /produced function with the wrong name, expected testmodule::foo, actual foo/) + end.to raise_error(ArgumentError, /produced mis-matched name, expected 'testmodule::foo', got foo/) end it 'can load something in a qualified name space' do module_dir = dir_containing('testmodule', { - 'functions' => { - 'foo.pp' => 'function testmodule::foo() { yay }'}}) - + 'lib' => { 'puppet' => { 'functions' => { 'testmodule' => { + 'foo.rb' => 'Puppet::Functions.create_function("testmodule::foo") { def foo; end; }' + }}}}}) module_loader = Puppet::Pops::Loader::ModuleLoaders::FileBased.new(static_loader, 'testmodule', module_dir, 'test1') dep_loader = Puppet::Pops::Loader::DependencyLoader.new(static_loader, 'test-dep', [module_loader]) + function = dep_loader.load_typed(typed_name(:function, 'testmodule::foo')).value + expect(function.class.name).to eq('testmodule::foo') expect(function.is_a?(Puppet::Functions::Function)).to eq(true) end end def typed_name(type, name) Puppet::Pops::Loader::Loader::TypedName.new(type, name) end end diff --git a/spec/unit/pops/loaders/loader_paths_spec.rb b/spec/unit/pops/loaders/loader_paths_spec.rb index c17861e59..4e098a097 100644 --- a/spec/unit/pops/loaders/loader_paths_spec.rb +++ b/spec/unit/pops/loaders/loader_paths_spec.rb @@ -1,74 +1,71 @@ require 'spec_helper' require 'puppet_spec/files' require 'puppet/pops' require 'puppet/loaders' describe 'loader paths' do include PuppetSpec::Files before(:each) { Puppet[:biff] = true } let(:static_loader) { Puppet::Pops::Loader::StaticLoader.new() } it 'expects dir_containing to create a temp directory structure from a hash' do module_dir = dir_containing('testmodule', { 'test.txt' => 'Hello world', 'sub' => { 'foo.txt' => 'foo'}}) expect(File.read(File.join(module_dir, 'test.txt'))).to be_eql('Hello world') expect(File.read(File.join(module_dir, 'sub', 'foo.txt'))).to be_eql('foo') end describe 'the relative_path_for_types method' do it 'produces paths to load in precendence order' do module_dir = dir_containing('testmodule', { 'functions' => {}, 'lib' => { 'puppet' => { 'functions' => {}, 'parser' => { 'functions' => {}, } }}}) # Must have a File/Path based loader to talk to module_loader = Puppet::Pops::Loader::ModuleLoaders::FileBased.new(static_loader, 'testmodule', module_dir, 'test1') effective_paths = Puppet::Pops::Loader::LoaderPaths.relative_paths_for_type(:function, module_loader) - expect(effective_paths.size).to be_eql(3) + expect(effective_paths.size).to be_eql(2) # 4x expect(effective_paths[0].generic_path).to be_eql(File.join(module_dir, 'lib', 'puppet', 'functions')) # 3x expect(effective_paths[1].generic_path).to be_eql(File.join(module_dir, 'lib', 'puppet','parser', 'functions')) - # .pp - expect(effective_paths[2].generic_path).to be_eql(File.join(module_dir, 'functions')) end it 'module loader has smart-paths that prunes unavailable paths' do - module_dir = dir_containing('testmodule', {'functions' => {'foo.pp' => 'function foo() { yay }'} }) - # Must have a File/Path based loader to talk to + module_dir = dir_containing('testmodule', {'lib' => {'puppet' => {'functions' => {'foo.rb' => 'Puppet::Functions.create_function("testmodule::foo") { def foo; end; }' }}}}) module_loader = Puppet::Pops::Loader::ModuleLoaders::FileBased.new(static_loader, 'testmodule', module_dir, 'test1') + effective_paths = module_loader.smart_paths.effective_paths(:function) + expect(effective_paths.size).to be_eql(1) - expect(effective_paths[0].generic_path).to be_eql(File.join(module_dir, 'functions')) + expect(effective_paths[0].generic_path).to be_eql(File.join(module_dir, 'lib', 'puppet', 'functions')) expect(module_loader.path_index.size).to be_eql(1) - expect(module_loader.path_index.include?(File.join(module_dir, 'functions', 'foo.pp'))).to be(true) + expect(module_loader.path_index.include?(File.join(module_dir, 'lib', 'puppet', 'functions', 'foo.rb'))).to be(true) end it 'all function smart-paths produces entries if they exist' do module_dir = dir_containing('testmodule', { - 'functions' => {'foo.pp' => 'function foo() { yay }'}, 'lib' => { 'puppet' => { 'functions' => {'foo4x.rb' => 'ignored in this test'}, 'parser' => { 'functions' => {'foo3x.rb' => 'ignored in this test'}, } }}}) # Must have a File/Path based loader to talk to module_loader = Puppet::Pops::Loader::ModuleLoaders::FileBased.new(static_loader, 'testmodule', module_dir, 'test1') effective_paths = module_loader.smart_paths.effective_paths(:function) - expect(effective_paths.size).to eq(3) - expect(module_loader.path_index.size).to eq(3) + expect(effective_paths.size).to eq(2) + expect(module_loader.path_index.size).to eq(2) path_index = module_loader.path_index - expect(path_index.include?(File.join(module_dir, 'functions', 'foo.pp'))).to eq(true) expect(path_index.include?(File.join(module_dir, 'lib', 'puppet', 'functions', 'foo4x.rb'))).to eq(true) expect(path_index.include?(File.join(module_dir, 'lib', 'puppet', 'parser', 'functions', 'foo3x.rb'))).to eq(true) end end end diff --git a/spec/unit/pops/loaders/loaders_spec.rb b/spec/unit/pops/loaders/loaders_spec.rb index e5d12f217..a3ee91ac3 100644 --- a/spec/unit/pops/loaders/loaders_spec.rb +++ b/spec/unit/pops/loaders/loaders_spec.rb @@ -1,86 +1,78 @@ require 'spec_helper' require 'puppet_spec/files' require 'puppet/pops' require 'puppet/loaders' describe 'loaders' do include PuppetSpec::Files def config_dir(config_name) my_fixture(config_name) end # Loaders caches the puppet_system_loader, must reset between tests # before(:each) { Puppet::Pops::Loaders.clear() } it 'creates a puppet_system loader' do loaders = Puppet::Pops::Loaders.new() expect(loaders.puppet_system_loader().class).to be(Puppet::Pops::Loader::ModuleLoaders::FileBased) end it 'creates an environment loader' do loaders = Puppet::Pops::Loaders.new() # When this test is running, there is no environments dir configured, and a NullLoader is therefore used a.t.m expect(loaders.public_environment_loader().class).to be(Puppet::Pops::Loader::SimpleEnvironmentLoader) # The default name of the enironment is '*root*', and the loader should identify itself that way expect(loaders.public_environment_loader().to_s).to eql("(SimpleEnvironmentLoader 'environment:*root*')") expect(loaders.private_environment_loader().class).to be(Puppet::Pops::Loader::DependencyLoader) expect(loaders.private_environment_loader().to_s).to eql("(DependencyLoader 'environment' [])") end context 'when delegating 3x to 4x' do before(:each) { Puppet[:biff] = true } it 'the puppet system loader can load 3x functions' do loaders = Puppet::Pops::Loaders.new() puppet_loader = loaders.puppet_system_loader() function = puppet_loader.load_typed(typed_name(:function, 'sprintf')).value expect(function.class.name).to eq('sprintf') expect(function.is_a?(Puppet::Functions::Function)).to eq(true) end end # TODO: LOADING OF MODULES ON MODULEPATH context 'loading from path with single module' do before do env = Puppet::Node::Environment.create(:'*test*', [File.join(config_dir('single_module'), 'modules')], '') overrides = { :current_environment => env } Puppet.push_context(overrides, "single-module-test-loaders") end after do Puppet.pop_context() end it 'can load from a module path' do loaders = Puppet::Pops::Loaders.new() modulea_loader = loaders.public_loader_for_module('modulea') expect(modulea_loader.class).to eql(Puppet::Pops::Loader::ModuleLoaders::FileBased) - function = modulea_loader.load_typed(typed_name(:function, 'modulea::func_a')).value - expect(function.is_a?(Puppet::Functions::Function)).to eq(true) - expect(function.class.name).to eq('modulea::func_a') - - function = modulea_loader.load_typed(typed_name(:function, 'modulea::nested::func_a')).value - expect(function.is_a?(Puppet::Functions::Function)).to eq(true) - expect(function.class.name).to eq('modulea::nested::func_a') - function = modulea_loader.load_typed(typed_name(:function, 'rb_func_a')).value expect(function.is_a?(Puppet::Functions::Function)).to eq(true) expect(function.class.name).to eq('rb_func_a') function = modulea_loader.load_typed(typed_name(:function, 'modulea::rb_func_a')).value expect(function.is_a?(Puppet::Functions::Function)).to eq(true) expect(function.class.name).to eq('modulea::rb_func_a') end end def typed_name(type, name) Puppet::Pops::Loader::Loader::TypedName.new(type, name) end -end \ No newline at end of file +end diff --git a/spec/unit/pops/loaders/module_loaders_spec.rb b/spec/unit/pops/loaders/module_loaders_spec.rb index 9c73ab858..24bad0cf4 100644 --- a/spec/unit/pops/loaders/module_loaders_spec.rb +++ b/spec/unit/pops/loaders/module_loaders_spec.rb @@ -1,134 +1,122 @@ require 'spec_helper' require 'puppet_spec/files' require 'puppet/pops' require 'puppet/loaders' describe 'module loaders' do include PuppetSpec::Files let(:static_loader) { Puppet::Pops::Loader::StaticLoader.new() } describe 'FileBased module loader' do - it 'loading a .pp function in global name space raises an error' do - module_dir = dir_containing('testmodule', { - 'functions' => { - 'foo.pp' => 'function foo() { yay }'}}) - - module_loader = Puppet::Pops::Loader::ModuleLoaders::FileBased.new(static_loader, 'testmodule', module_dir, 'test1') - expect do - module_loader.load_typed(typed_name(:function, 'testmodule::foo')).value - end.to raise_error(ArgumentError, /produced function with the wrong name, expected testmodule::foo, actual foo/) - - end - - it 'can load a .pp function in a qualified name space' do - module_dir = dir_containing('testmodule', { - 'functions' => { - 'foo.pp' => 'function testmodule::foo() { yay }'}}) - - module_loader = Puppet::Pops::Loader::ModuleLoaders::FileBased.new(static_loader, 'testmodule', module_dir, 'test1') - function = module_loader.load_typed(typed_name(:function, 'testmodule::foo')).value - expect(function.class.name).to eq('testmodule::foo') - expect(function.is_a?(Puppet::Functions::Function)).to eq(true) - end - it 'can load a 4x function API ruby function in global name space' do module_dir = dir_containing('testmodule', { 'lib' => { 'puppet' => { 'functions' => { 'foo4x.rb' => <<-CODE Puppet::Functions.create_function(:foo4x) do def foo4x() 'yay' end end CODE } } } }) module_loader = Puppet::Pops::Loader::ModuleLoaders::FileBased.new(static_loader, 'testmodule', module_dir, 'test1') function = module_loader.load_typed(typed_name(:function, 'foo4x')).value expect(function.class.name).to eq('foo4x') expect(function.is_a?(Puppet::Functions::Function)).to eq(true) end it 'can load a 4x function API ruby function in qualified name space' do module_dir = dir_containing('testmodule', { 'lib' => { 'puppet' => { 'functions' => { 'testmodule' => { 'foo4x.rb' => <<-CODE Puppet::Functions.create_function('testmodule::foo4x') do def foo4x() 'yay' end end CODE } } } }}) module_loader = Puppet::Pops::Loader::ModuleLoaders::FileBased.new(static_loader, 'testmodule', module_dir, 'test1') function = module_loader.load_typed(typed_name(:function, 'testmodule::foo4x')).value expect(function.class.name).to eq('testmodule::foo4x') expect(function.is_a?(Puppet::Functions::Function)).to eq(true) end it 'makes parent loader win over entries in child' do module_dir = dir_containing('testmodule', { - 'functions' => { - 'foo.pp' => 'function testmodule::foo() { yay }'}}) - + 'lib' => { 'puppet' => { 'functions' => { 'testmodule' => { + 'foo.rb' => <<-CODE + Puppet::Functions.create_function('testmodule::foo') do + def foo() + 'yay' + end + end + CODE + }}}}}) module_loader = Puppet::Pops::Loader::ModuleLoaders::FileBased.new(static_loader, 'testmodule', module_dir, 'test1') - module_dir2 = dir_containing('testmodule2', { - 'functions' => { - 'foo.pp' => 'fail(should not happen)'}}) + module_dir2 = dir_containing('testmodule2', { + 'lib' => { 'puppet' => { 'functions' => { 'testmodule2' => { + 'foo.rb' => <<-CODE + raise "should not get here" + CODE + }}}}}) module_loader2 = Puppet::Pops::Loader::ModuleLoaders::FileBased.new(module_loader, 'testmodule2', module_dir2, 'test2') + function = module_loader2.load_typed(typed_name(:function, 'testmodule::foo')).value + expect(function.class.name).to eq('testmodule::foo') expect(function.is_a?(Puppet::Functions::Function)).to eq(true) end context 'when delegating 3x to 4x' do before(:each) { Puppet[:biff] = true } it 'can load a 3x function API ruby function in global name space' do module_dir = dir_containing('testmodule', { 'lib' => { 'puppet' => { 'parser' => { 'functions' => { 'foo3x.rb' => <<-CODE Puppet::Parser::Functions::newfunction( :foo3x, :type => :rvalue, :arity => 1 ) do |args| args[0] end CODE } } } }}) module_loader = Puppet::Pops::Loader::ModuleLoaders::FileBased.new(static_loader, 'testmodule', module_dir, 'test1') function = module_loader.load_typed(typed_name(:function, 'foo3x')).value expect(function.class.name).to eq('foo3x') expect(function.is_a?(Puppet::Functions::Function)).to eq(true) end end # Gives error when loading something with mismatched name end def typed_name(type, name) Puppet::Pops::Loader::Loader::TypedName.new(type, name) end end