# The Adapters module contains adapters for Documentation, Origin, SourcePosition, and Loader.
#
module Puppet::Pops
module Adapters
class ObjectIdCacheAdapter < Puppet::Pops::Adaptable::Adapter
attr_accessor :cache
def initialize
@cache = {}
end
# Retrieves a mutable hash with all stored values
def retrieve(o)
@cache[o.__id__] ||= {}
end
end
# A documentation adapter adapts an object with a documentation string.
# (The intended use is for a source text parser to extract documentation and store this
# in DocumentationAdapter instances).
#
class DocumentationAdapter < Adaptable::Adapter
# @return [String] The documentation associated with an object
attr_accessor :documentation
end
# An empty alternative adapter is used when there is the need to
# attach a value to be used if the original is empty. This is used
# when a lazy evaluation takes place, and the decision how to handle an
# empty case must be delayed.
#
class EmptyAlternativeAdapter < Adaptable::Adapter
# @return [Object] The alternative value associated with an object
attr_accessor :empty_alternative
end
# This class is for backward compatibility only. It's not really an adapter but it is
# needed for the puppetlabs-strings gem
# @deprecated
class SourcePosAdapter
def self.adapt(object)
new(object)
end
def initialize(object)
@object = object
end
def file
@object.file
end
def line
@object.line
end
def pos
@object.pos
end
def extract_text
@object.locator.extract_text(@object.offset, @object.length)
end
end
# A LoaderAdapter adapts an object with a {Loader}. This is used to make further loading from the
# perspective of the adapted object take place in the perspective of this Loader.
#
# It is typically enough to adapt the root of a model as a search is made towards the root of the model
# until a loader is found, but there is no harm in duplicating this information provided a contained
# object is adapted with the correct loader.
#
# @see Utils#find_adapter
# @api private
class LoaderAdapter < Adaptable::Adapter
attr_accessor :loader_name
# Finds the loader to use when loading originates from the source position of the given argument.
#
# @param instance [Model::PopsObject] The model object
# @param file [String] the file from where the model was parsed
# @param default_loader [Loader] the loader to return if no loader is found for the model
# @return [Loader] the found loader or default_loader if it could not be found
#
def self.loader_for_model_object(model, file = nil, default_loader = nil)
loaders = Puppet.lookup(:loaders) { nil }
if loaders.nil?
default_loader || Loaders.static_loader
else
loader_name = loader_name_by_source(loaders.environment, model, file)
if loader_name.nil?
default_loader || loaders[Loader::ENVIRONMENT_PRIVATE]
else
loaders[loader_name]
end
end
end
class PathsAndNameCacheAdapter < Puppet::Pops::Adaptable::Adapter
attr_accessor :cache, :paths
def self.create_adapter(env)
adapter = super(env)
adapter.paths = env.modulepath.map { |p| Pathname.new(p) }
adapter.cache = {}
adapter
end
end
# Attempts to find the module that `instance` originates from by looking at it's {SourcePosAdapter} and
# compare the `locator.file` found there with the module paths given in the environment found in the
# given `scope`. If the file is found to be relative to a path, then the first segment of the relative
# path is interpreted as the name of a module. The object that the {SourcePosAdapter} is adapted to
# will then be adapted to the private loader for that module and that adapter is returned.
#
# The method returns `nil` when no module could be found.
#
# @param environment [Puppet::Node::Environment] the current environment
# @param instance [Model::PopsObject] the AST for the code
# @param file [String] the path to the file for the code or `nil`
# @return [String] the name of the loader associated with the source
# @api private
def self.loader_name_by_source(environment, instance, file)
file = instance.file if file.nil?
return nil if file.nil? || EMPTY_STRING == file
pn_adapter = PathsAndNameCacheAdapter.adapt(environment)
dir = File.dirname(file)
pn_adapter.cache.fetch(dir) do |key|
mod = find_module_for_dir(environment, pn_adapter.paths, dir)
loader_name = mod.nil? ? nil : "#{mod.name} private"
pn_adapter.cache[key] = loader_name
end
end
# @api private
def self.find_module_for_dir(environment, paths, dir)
return nil if dir.nil?
file_path = Pathname.new(dir)
paths.each do |path|
begin
relative_path = file_path.relative_path_from(path).to_s.split(File::SEPARATOR)
rescue ArgumentError
# file_path was not relative to the module_path. That's OK.
next
end
if relative_path.length > 1
mod = environment.module(relative_path[0])
return mod unless mod.nil?
end
end
nil
end
end
end
end
Copyright 2K16 - 2K18 Indonesian Hacker Rulez