Class: Puppet::Pops::Loader::ModuleLoaders::AbstractPathBasedModuleLoader Private

Inherits:
BaseLoader show all
Defined in:
lib/puppet/pops/loader/module_loaders.rb

This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.

Direct Known Subclasses

FileBased

Constant Summary

Constants inherited from Loader

Loader::LOADABLE_KINDS

Instance Attribute Summary collapse

Attributes inherited from BaseLoader

#parent

Attributes inherited from Loader

#environment, #loader_name

Instance Method Summary collapse

Methods inherited from BaseLoader

#add_entry, #get_entry, #load_typed, #loaded_entry, #promote_entry, #remove_entry, #set_entry

Methods inherited from Loader

#[], #get_entry, #inspect, #load, #load_typed, #loaded_entry, #parent, #set_entry, #synchronize, #to_s

Constructor Details

#initialize(parent_loader, loaders, module_name, path, loader_name, loadables) ⇒ AbstractPathBasedModuleLoader

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Initialize a kind of ModuleLoader for one module

Parameters:

  • parent_loader (Loader)

    loader with higher priority

  • loaders (Loaders)

    the container for this loader

  • module_name (String)

    the name of the module (non qualified name), may be nil for a global “component”

  • path (String)

    the path to the root of the module (semantics defined by subclass)

  • loader_name (String)

    a name that is used for human identification (useful when module_name is nil)

Raises:

  • (ArgumentError)


121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/puppet/pops/loader/module_loaders.rb', line 121

def initialize(parent_loader, loaders, module_name, path, loader_name, loadables)
  super(parent_loader, loader_name, loaders.environment)

  raise ArgumentError, 'path based loader cannot be instantiated without a path' if path.nil? || path.empty?

  @module_name = module_name
  @path = path
  @smart_paths = LoaderPaths::SmartPaths.new(self)
  @loaders = loaders
  @loadables = loadables
  unless (loadables - LOADABLE_KINDS).empty?
    # TRANSLATORS 'loadables' is a variable containing loadable modules and should not be translated
    raise ArgumentError, _('given loadables are not of supported loadable kind')
  end

  loaders.add_loader_by_name(self)
end

Instance Attribute Details

#module_nameObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

The name of the module, or nil, if this is a global “component”, or “any module” if set to the ‘NAMESPACE_WILDCARD` (*)



99
100
101
# File 'lib/puppet/pops/loader/module_loaders.rb', line 99

def module_name
  @module_name
end

#pathObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

The path to the location of the module/component - semantics determined by subclass



102
103
104
# File 'lib/puppet/pops/loader/module_loaders.rb', line 102

def path
  @path
end

#private_loaderObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Produces the private loader for the module. If this module is not already resolved, this will trigger resolution



361
362
363
364
365
# File 'lib/puppet/pops/loader/module_loaders.rb', line 361

def private_loader
  # The system loader has a nil module_name and it does not have a private_loader as there are no functions
  # that can only by called by puppet runtime - if so, it acts as the private loader directly.
  @private_loader ||= (global? ? self : @loaders.private_loader_for_module(module_name))
end

#smart_pathsObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

A map of type to smart-paths that help with minimizing the number of paths to scan



105
106
107
# File 'lib/puppet/pops/loader/module_loaders.rb', line 105

def smart_paths
  @smart_paths
end

Instance Method Details

#candidate_paths(resolved_path) ⇒ Array<String>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Abstract method that subclasses override to return an array of paths that may be associated with the resolved path.

Parameters:

  • resolved_path (String)

    a path, without extension, resolved by a smart path against the loader’s root (if it has one)

Returns:

  • (Array<String>)

Raises:

  • (NotImplementedError)


317
318
319
# File 'lib/puppet/pops/loader/module_loaders.rb', line 317

def candidate_paths(resolved_path)
  raise NotImplementedError
end

#discover(type, error_collector = nil, name_authority = Pcore::RUNTIME_NAME_AUTHORITY, &block) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/puppet/pops/loader/module_loaders.rb', line 143

def discover(type, error_collector = nil, name_authority = Pcore::RUNTIME_NAME_AUTHORITY, &block)
  global = global?
  if name_authority == Pcore::RUNTIME_NAME_AUTHORITY
    smart_paths.effective_paths(type).each do |sp|
      relative_paths(sp).each do |rp|
        tp = sp.typed_name(type, name_authority, rp, global ? nil : @module_name)
        next unless sp.valid_name?(tp)

        begin
          load_typed(tp) unless block_given? && !block.yield(tp)
        rescue StandardError => e
          if error_collector.nil?
            Puppet.warn_once(:unloadable_entity, tp.to_s, e.message)
          else
            err = Puppet::DataTypes::Error.new(
              Issues::LOADER_FAILURE.format(:type => type),
              'PUPPET_LOADER_FAILURE',
              { 'original_error' => e.message },
              Issues::LOADER_FAILURE.issue_code
            )
            error_collector << err unless error_collector.include?(err)
          end
        end
      end
    end
  end
  super
end

#existing_path(resolved_path) ⇒ String?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Abstract method that subclasses override to answer if the given relative path exists, and if so returns that path

Parameters:

  • resolved_path (String)

    a path resolved by a smart path against the loader’s root (if it has one)

Returns:

  • (String, nil)

    the found path or nil if no such path was found

Raises:

  • (NotImplementedError)


308
309
310
# File 'lib/puppet/pops/loader/module_loaders.rb', line 308

def existing_path(resolved_path)
  raise NotImplementedError
end

#find(typed_name) ⇒ Loader::NamedEntry, ...

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Finds typed/named entity in this module

Parameters:

  • typed_name (TypedName)

    the type/name to find

Returns:

  • (Loader::NamedEntry, nil found/created entry, or nil if not found)

    Loader::NamedEntry, nil found/created entry, or nil if not found



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/puppet/pops/loader/module_loaders.rb', line 176

def find(typed_name)
  # This loader is tailored to only find entries in the current runtime
  return nil unless typed_name.name_authority == Pcore::RUNTIME_NAME_AUTHORITY

  # Assume it is a global name, and that all parts of the name should be used when looking up
  name_parts = typed_name.name_parts

  # Certain types and names can be disqualified up front
  if name_parts.size > 1
    # The name is in a name space.

    # Then entity cannot possible be in this module unless the name starts with the module name.
    # Note:
    # * If "module" represents a "global component", the module_name is nil and cannot match which is
    #   ok since such a "module" cannot have namespaced content).
    # * If this loader is allowed to have namespaced content, the module_name can be set to NAMESPACE_WILDCARD `*`
    #
    return nil unless name_parts[0] == module_name || module_name == NAMESPACE_WILDCARD
  else
    # The name is in the global name space.

    case typed_name.type
    when :function, :resource_type, :resource_type_pp
      # Can be defined in module using a global name. No action required

    when :plan
      unless global?
        # Global name must be the name of the module
        return nil unless name_parts[0] == module_name

        # Look for the special 'init' plan.
        origin, smart_path = find_existing_path(init_plan_name)
        return smart_path.nil? ? nil : instantiate(smart_path, typed_name, origin)
      end

    when :task
      unless global?
        # Global name must be the name of the module
        return nil unless name_parts[0] == module_name

        # Look for the special 'init' Task
        origin, smart_path = find_existing_path(init_task_name)
        return smart_path.nil? ? nil : instantiate(smart_path, typed_name, origin)
      end

    when :type
      unless global?
        # Global name must be the name of the module
        unless name_parts[0] == module_name || module_name == NAMESPACE_WILDCARD
          # Check for ruby defined data type in global namespace before giving up
          origin, smart_path = find_existing_path(typed_name)
          return smart_path.is_a?(LoaderPaths::DataTypePath) ? instantiate(smart_path, typed_name, origin) : nil
        end

        # Look for the special 'init_typeset' TypeSet
        origin, smart_path = find_existing_path(init_typeset_name)
        return nil if smart_path.nil?

        value = smart_path.instantiator.create(self, typed_name, origin, get_contents(origin))
        if value.is_a?(Types::PTypeSetType)
          # cache the entry and return it
          return set_entry(typed_name, value, origin)
        end

        # TRANSLATORS 'TypeSet' should not be translated
        raise ArgumentError, _("The code loaded from %{origin} does not define the TypeSet '%{module_name}'") %
                             { origin: origin, module_name: name_parts[0].capitalize }
      end
    else
      # anything else cannot possibly be in this module
      # TODO: should not be allowed anyway... may have to revisit this decision
      return nil
    end
  end

  # Get the paths that actually exist in this module (they are lazily processed once and cached).
  # The result is an array (that may be empty).
  # Find the file to instantiate, and instantiate the entity if file is found
  origin, smart_path = find_existing_path(typed_name)
  return instantiate(smart_path, typed_name, origin) unless smart_path.nil?

  return nil unless typed_name.type == :type && typed_name.qualified?

  # Search for TypeSet using parent name
  ts_name = typed_name.parent
  while ts_name
    # Do not traverse parents here. This search must be confined to this loader
    tse = get_entry(ts_name)
    tse = find(ts_name) if tse.nil? || tse.value.nil?
    if tse && (ts = tse.value).is_a?(Types::PTypeSetType)
      # The TypeSet might be unresolved at this point. If so, it must be resolved using
      # this loader. That in turn, adds all contained types to this loader.
      ts.resolve(self)
      te = get_entry(typed_name)
      return te unless te.nil?
    end
    ts_name = ts_name.parent
  end
  nil
end

#get_contents(effective_path) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

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.

Parameters:

  • effective_path (String)

    a path as resolved by a smart path

Returns:

  • (String)

    the content of the file

Raises:

  • (NotImplementedError)


327
328
329
# File 'lib/puppet/pops/loader/module_loaders.rb', line 327

def get_contents(effective_path)
  raise NotImplementedError
end

#get_source_ref(relative_path) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Abstract method that subclasses override to produce a source reference String used to identify the system resource (resource in the URI sense).

Parameters:

  • relative_path (String)

    a path relative to the module’s root

Returns:

  • (String)

    a reference to the source file (in file system, zip file, or elsewhere).

Raises:

  • (NotImplementedError)


337
338
339
# File 'lib/puppet/pops/loader/module_loaders.rb', line 337

def get_source_ref(relative_path)
  raise NotImplementedError
end

#global?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Answers the question if this loader represents a global component (true for resource type loader and environment loader)

Returns:

  • (Boolean)

    ‘true` if this loader represents a global component



345
346
347
# File 'lib/puppet/pops/loader/module_loaders.rb', line 345

def global?
  module_name.nil? || module_name == NAMESPACE_WILDCARD || module_name == ENVIRONMENT
end

#instantiate(smart_path, typed_name, origin) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



277
278
279
280
281
282
283
284
285
# File 'lib/puppet/pops/loader/module_loaders.rb', line 277

def instantiate(smart_path, typed_name, origin)
  if origin.is_a?(Array)
    value = smart_path.instantiator.create(self, typed_name, origin)
  else
    value = smart_path.instantiator.create(self, typed_name, origin, get_contents(origin))
  end
  # cache the entry and return it
  set_entry(typed_name, value, origin)
end

#lib_root?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Answers ‘true` if the loader used by this instance is rooted beneath ’lib’. This is typically true for 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.

Returns:

  • (Boolean)

    a boolean answering if the loader is rooted beneath ‘lib’.



355
356
357
# File 'lib/puppet/pops/loader/module_loaders.rb', line 355

def lib_root?
  false
end

#loadablesObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



139
140
141
# File 'lib/puppet/pops/loader/module_loaders.rb', line 139

def loadables
  @loadables
end

#meaningful_to_search?(smart_path) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

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).

Parameters:

  • smart_path (String)

    a path relative to the module’s root

Returns:

  • (Boolean)

    true if there is content in the directory appointed by the relative path

Raises:

  • (NotImplementedError)


299
300
301
# File 'lib/puppet/pops/loader/module_loaders.rb', line 299

def meaningful_to_search?(smart_path)
  raise NotImplementedError
end

#relative_paths(smart_path) ⇒ Array<String>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Return all paths that matches the given smart path. The returned paths are relative to the ‘#generic_path` of the given smart path.

Parameters:

  • smart_path (SmartPath)

    the path to find relative paths for

Returns:

  • (Array<String>)

    found paths

Raises:

  • (NotImplementedError)


372
373
374
# File 'lib/puppet/pops/loader/module_loaders.rb', line 372

def relative_paths(smart_path)
  raise NotImplementedError
end