class Puppet::Pops::Loader::ModuleLoaders::AbstractPathBasedModuleLoader
Attributes
The name of the module, or nil, if this is a global “component”, or “any module” if set to the `NAMESPACE_WILDCARD` (*)
The path to the location of the module/component - semantics determined by subclass
A Module
Loader
has a private loader, it is lazily obtained on request to provide the visibility for entities contained in the module. Since a ModuleLoader also represents an environment and it is created a different way, this loader can be set explicitly by the loaders bootstrap logic.
@api private
A map of type to smart-paths that help with minimizing the number of paths to scan
Public Class Methods
Initialize a kind of ModuleLoader for one module @param parent_loader [Loader] loader with higher priority @param loaders [Loaders] the container for this loader @param module_name
[String] the name of the module (non qualified name), may be nil for a global “component” @param path [String] the path to the root of the module (semantics defined by subclass) @param loader_name [String] a name that is used for human identification (useful when module_name
is nil)
# File lib/puppet/pops/loader/module_loaders.rb 127 def initialize(parent_loader, loaders, module_name, path, loader_name, loadables) 128 super(parent_loader, loader_name, loaders.environment) 129 130 raise ArgumentError, 'path based loader cannot be instantiated without a path' if path.nil? || path.empty? 131 132 @module_name = module_name 133 @path = path 134 @smart_paths = LoaderPaths::SmartPaths.new(self) 135 @loaders = loaders 136 @loadables = loadables 137 unless (loadables - LOADABLE_KINDS).empty? 138 #TRANSLATORS 'loadables' is a variable containing loadable modules and should not be translated 139 raise ArgumentError, _('given loadables are not of supported loadable kind') 140 end 141 loaders.add_loader_by_name(self) 142 end
Public Instance Methods
Abstract method that subclasses override to return an array of paths that may be associated with the resolved path.
@param resolved_path [String] a path, without extension, resolved by a smart path against the loader's root (if it has one) @return [Array<String>]
# File lib/puppet/pops/loader/module_loaders.rb 320 def candidate_paths(resolved_path) 321 raise NotImplementedError.new 322 end
# File lib/puppet/pops/loader/module_loaders.rb 148 def discover(type, error_collector = nil, name_authority = Pcore::RUNTIME_NAME_AUTHORITY, &block) 149 global = global? 150 if name_authority == Pcore::RUNTIME_NAME_AUTHORITY 151 smart_paths.effective_paths(type).each do |sp| 152 relative_paths(sp).each do |rp| 153 tp = sp.typed_name(type, name_authority, rp, global ? nil : @module_name) 154 next unless sp.valid_name?(tp) 155 begin 156 load_typed(tp) unless block_given? && !block.yield(tp) 157 rescue StandardError => e 158 if error_collector.nil? 159 Puppet.warn_once(:unloadable_entity, tp.to_s, e.message) 160 else 161 err = Puppet::DataTypes::Error.new( 162 Issues::LOADER_FAILURE.format(:type => type), 163 'PUPPET_LOADER_FAILURE', 164 { 'original_error' => e.message }, 165 Issues::LOADER_FAILURE.issue_code) 166 error_collector << err unless error_collector.include?(err) 167 end 168 end 169 end 170 end 171 end 172 super 173 end
Abstract method that subclasses override to answer if the given relative path exists, and if so returns that path
@param resolved_path [String] a path resolved by a smart path against the loader's root (if it has one) @return [String, nil] the found path or nil if no such path was found
# File lib/puppet/pops/loader/module_loaders.rb 311 def existing_path(resolved_path) 312 raise NotImplementedError.new 313 end
Finds typed/named entity in this module @param typed_name [TypedName] the type/name to find @return [Loader::NamedEntry, nil found/created entry, or nil if not found
# File lib/puppet/pops/loader/module_loaders.rb 179 def find(typed_name) 180 # This loader is tailored to only find entries in the current runtime 181 return nil unless typed_name.name_authority == Pcore::RUNTIME_NAME_AUTHORITY 182 183 # Assume it is a global name, and that all parts of the name should be used when looking up 184 name_parts = typed_name.name_parts 185 186 # Certain types and names can be disqualified up front 187 if name_parts.size > 1 188 # The name is in a name space. 189 190 # Then entity cannot possible be in this module unless the name starts with the module name. 191 # Note: 192 # * If "module" represents a "global component", the module_name is nil and cannot match which is 193 # ok since such a "module" cannot have namespaced content). 194 # * If this loader is allowed to have namespaced content, the module_name can be set to NAMESPACE_WILDCARD `*` 195 # 196 return nil unless name_parts[0] == module_name || module_name == NAMESPACE_WILDCARD 197 else 198 # The name is in the global name space. 199 200 case typed_name.type 201 when :function, :resource_type, :resource_type_pp 202 # Can be defined in module using a global name. No action required 203 204 when :plan 205 if !global? 206 # Global name must be the name of the module 207 return nil unless name_parts[0] == module_name 208 209 # Look for the special 'init' plan. 210 origin, smart_path = find_existing_path(init_plan_name) 211 return smart_path.nil? ? nil : instantiate(smart_path, typed_name, origin) 212 end 213 214 when :task 215 if !global? 216 # Global name must be the name of the module 217 return nil unless name_parts[0] == module_name 218 219 # Look for the special 'init' Task 220 origin, smart_path = find_existing_path(init_task_name) 221 return smart_path.nil? ? nil : instantiate(smart_path, typed_name, origin) 222 end 223 224 when :type 225 if !global? 226 # Global name must be the name of the module 227 unless name_parts[0] == module_name || module_name == NAMESPACE_WILDCARD 228 # Check for ruby defined data type in global namespace before giving up 229 origin, smart_path = find_existing_path(typed_name) 230 return smart_path.is_a?(LoaderPaths::DataTypePath) ? instantiate(smart_path, typed_name, origin) : nil 231 end 232 233 # Look for the special 'init_typeset' TypeSet 234 origin, smart_path = find_existing_path(init_typeset_name) 235 return nil if smart_path.nil? 236 237 value = smart_path.instantiator.create(self, typed_name, origin, get_contents(origin)) 238 if value.is_a?(Types::PTypeSetType) 239 # cache the entry and return it 240 return set_entry(typed_name, value, origin) 241 end 242 243 # TRANSLATORS 'TypeSet' should not be translated 244 raise ArgumentError, _("The code loaded from %{origin} does not define the TypeSet '%{module_name}'") % 245 { origin: origin, module_name: name_parts[0].capitalize } 246 end 247 else 248 # anything else cannot possibly be in this module 249 # TODO: should not be allowed anyway... may have to revisit this decision 250 return nil 251 end 252 end 253 254 # Get the paths that actually exist in this module (they are lazily processed once and cached). 255 # The result is an array (that may be empty). 256 # Find the file to instantiate, and instantiate the entity if file is found 257 origin, smart_path = find_existing_path(typed_name) 258 return instantiate(smart_path, typed_name, origin) unless smart_path.nil? 259 260 return nil unless typed_name.type == :type && typed_name.qualified? 261 262 # Search for TypeSet using parent name 263 ts_name = typed_name.parent 264 while ts_name 265 # Do not traverse parents here. This search must be confined to this loader 266 tse = get_entry(ts_name) 267 tse = find(ts_name) if tse.nil? || tse.value.nil? 268 if tse && (ts = tse.value).is_a?(Types::PTypeSetType) 269 # The TypeSet might be unresolved at this point. If so, it must be resolved using 270 # this loader. That in turn, adds all contained types to this loader. 271 ts.resolve(self) 272 te = get_entry(typed_name) 273 return te unless te.nil? 274 end 275 ts_name = ts_name.parent 276 end 277 nil 278 end
Abstract method that subclasses override to produce the content of the effective path. It should either succeed and return a String or fail with an exception.
@param effective_path [String] a path as resolved by a smart path @return [String] the content of the file
# File lib/puppet/pops/loader/module_loaders.rb 330 def get_contents(effective_path) 331 raise NotImplementedError.new 332 end
Abstract method that subclasses override to produce a source reference String used to identify the system resource (resource in the URI sense).
@param relative_path [String] a path relative to the module's root @return [String] a reference to the source file (in file system, zip file, or elsewhere).
# File lib/puppet/pops/loader/module_loaders.rb 340 def get_source_ref(relative_path) 341 raise NotImplementedError.new 342 end
Answers the question if this loader represents a global component (true for resource type loader and environment loader)
@return [Boolean] `true` if this loader represents a global component
# File lib/puppet/pops/loader/module_loaders.rb 348 def global? 349 module_name.nil? || module_name == NAMESPACE_WILDCARD || module_name == ENVIRONMENT 350 end
# File lib/puppet/pops/loader/module_loaders.rb 280 def instantiate(smart_path, typed_name, origin) 281 if origin.is_a?(Array) 282 value = smart_path.instantiator.create(self, typed_name, origin) 283 else 284 value = smart_path.instantiator.create(self, typed_name, origin, get_contents(origin)) 285 end 286 # cache the entry and return it 287 set_entry(typed_name, value, origin) 288 end
Answers `true` if the loader used by this instance is rooted beneath 'lib'. This is typically true for the the system_loader. It will have a path relative to the parent of 'puppet' instead of the parent of 'lib/puppet' since the 'lib' directory of puppet is renamed during install. This is significant for loaders that load ruby code.
@return [Boolean] a boolean answering if the loader is rooted beneath 'lib'.
# File lib/puppet/pops/loader/module_loaders.rb 358 def lib_root? 359 false 360 end
# File lib/puppet/pops/loader/module_loaders.rb 144 def loadables 145 @loadables 146 end
Abstract method that subclasses override that checks if it is meaningful to search using a generic smart path. This optimization is performed to not be tricked into searching an empty directory over and over again. The implementation may perform a deep search for file content other than directories and cache this in and index. It is guaranteed that a call to meaningful_to_search? takes place before checking any other path with relative_path_exists?.
This optimization exists because many modules have been created from a template and they have empty directories for functions, types, etc. (It is also the place to create a cached index of the content).
@param smart_path [String] a path relative to the module's root @return [Boolean] true if there is content in the directory appointed by the relative path
# File lib/puppet/pops/loader/module_loaders.rb 302 def meaningful_to_search?(smart_path) 303 raise NotImplementedError.new 304 end
Return all paths that matches the given smart path. The returned paths are relative to the `#generic_path` of the given smart path.
@param smart_path [SmartPath] the path to find relative paths for @return [Array<String>] found paths
# File lib/puppet/pops/loader/module_loaders.rb 375 def relative_paths(smart_path) 376 raise NotImplementedError.new 377 end
Private Instance Methods
Find an existing path or paths for the given `typed_name`. Return `nil` if no path is found @param typed_name [TypedName] the `typed_name` to find a path for @return [Array,nil] `nil`or a two element array where the first element is an effective path or array of paths
(depending on the `SmartPath`) and the second element is the `SmartPath` that produced the effective path or paths. A path is a String
# File lib/puppet/pops/loader/module_loaders.rb 403 def find_existing_path(typed_name) 404 is_global = global? 405 smart_paths.effective_paths(typed_name.type).each do |sp| 406 next unless sp.valid_name?(typed_name) 407 origin = sp.effective_path(typed_name, is_global ? 0 : 1) 408 unless origin.nil? 409 if sp.fuzzy_matching? 410 # If there are multiple *specific* paths for the file, find 411 # whichever ones exist. Otherwise, find all paths that *might* be 412 # related to origin 413 if origin.is_a?(Array) 414 origins = origin.map { |ori| existing_path(ori) }.compact 415 return [origins, sp] unless origins.empty? 416 else 417 origins = candidate_paths(origin) 418 return [origins, sp] unless origins.empty? 419 end 420 else 421 existing = existing_path(origin) 422 return [origin, sp] unless existing.nil? 423 end 424 end 425 end 426 nil 427 end
@return [TypedName] the fake typed name that maps to the path of an init.pp file that represents
a plan named after the module
# File lib/puppet/pops/loader/module_loaders.rb 394 def init_plan_name 395 @init_plan_name ||= TypedName.new(:plan, "#{module_name}::init") 396 end
@return [TypedName] the fake typed name that maps to the path of an init[arbitrary extension]
file that represents a task named after the module
# File lib/puppet/pops/loader/module_loaders.rb 388 def init_task_name 389 @init_task_name ||= TypedName.new(:task, "#{module_name}::init") 390 end
@return [TypedName] the fake typed name that maps to the init_typeset path for this module
# File lib/puppet/pops/loader/module_loaders.rb 382 def init_typeset_name 383 @init_typeset_name ||= TypedName.new(:type, "#{module_name}::init_typeset") 384 end