1 2 3 4 5 6 7 8 9 10 11 12 13 14 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 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
|
# 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
|