File: loader.rb

package info (click to toggle)
puppet-agent 7.23.0-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 19,092 kB
  • sloc: ruby: 245,074; sh: 456; makefile: 38; xml: 33
file content (221 lines) | stat: -rw-r--r-- 8,381 bytes parent folder | download
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
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
# Loader
# ===
# A Loader is responsible for loading "entities" ("instantiable and executable objects in the puppet language" which
# are type, hostclass, definition, function, and bindings.
#
# The main method for users of a Loader is the `load` or `load_typed methods`, which returns a previously loaded entity
# of a given type/name, and searches and loads the entity if not already loaded.
#
# private entities
# ---
# TODO: handle loading of entities that are private. Suggest that all calls pass an origin_loader (the loader
# where request originated (or symbol :public). A module loader has one (or possibly a list) of what is
# considered to represent private loader - i.e. the dependency loader for a module. If an entity is private
# it should be stored with this status, and an error should be raised if the origin_loader is not on the list
# of accepted "private" loaders.
# The private loaders can not be given at creation time (they are parented by the loader in question). Another
# alternative is to check if the origin_loader is a child loader, but this requires bidirectional links
# between loaders or a search if loader with private entity is a parent of the origin_loader).
#
# @api public
#
module Puppet::Pops
module Loader

ENVIRONMENT = 'environment'.freeze
ENVIRONMENT_PRIVATE = 'environment private'.freeze

class Loader
  attr_reader :environment, :loader_name

  # Describes the kinds of things that loaders can load
  LOADABLE_KINDS = [:func_4x, :func_4xpp, :func_3x, :datatype, :type_pp, :resource_type_pp, :plan, :task].freeze

  # @param [String] name the name of the loader. Must be unique among all loaders maintained by a {Loader} instance
  def initialize(loader_name, environment)
    @loader_name = loader_name.freeze
    @environment = environment
  end

  # Search all places where this loader would find values of a given type and return a list the
  # found values for which the given block returns true. All found entries will be returned if no
  # block is given.
  #
  # Errors that occur function discovery will either be logged as warnings or collected by the optional
  # `error_collector` array. When provided, it will receive {Puppet::DataTypes::Error} instances describing
  # each error in detail and no warnings will be logged.
  #
  # @param type [Symbol] the type of values to search for
  # @param error_collector [Array<Puppet::DataTypes::Error>] an optional array that will receive errors during load
  # @param name_authority [String] the name authority, defaults to the pcore runtime
  # @yield [typed_name] optional block to filter the results
  # @yieldparam [TypedName] typed_name the typed name of a found entry
  # @yieldreturn [Boolean] `true` to keep the entry, `false` to discard it.
  # @return [Array<TypedName>] the list of names of discovered values
  def discover(type, error_collector = nil, name_authority = Pcore::RUNTIME_NAME_AUTHORITY, &block)
    return EMPTY_ARRAY
  end

  # Produces the value associated with the given name if already loaded, or available for loading
  # by this loader, one of its parents, or other loaders visible to this loader.
  # This is the method an external party should use to "get" the named element.
  #
  # An implementor of this method should first check if the given name is already loaded by self, or a parent
  # loader, and if so return that result. If not, it should call `find` to perform the loading.
  #
  # @param type [:Symbol] the type to load
  # @param name [String, Symbol]  the name of the entity to load
  # @return [Object, nil] the value or nil if not found
  #
  # @api public
  #
  def load(type, name)
    synchronize do
      result = load_typed(TypedName.new(type, name.to_s))
      if result
        result.value
      end
    end
  end

  # Loads the given typed name, and returns a NamedEntry if found, else returns nil.
  # This the same a `load`, but returns a NamedEntry with origin/value information.
  #
  # @param typed_name [TypedName] - the type, name combination to lookup
  # @return [NamedEntry, nil] the entry containing the loaded value, or nil if not found
  #
  # @api public
  #
  def load_typed(typed_name)
    raise NotImplementedError, "Class #{self.class.name} must implement method #load_typed"
  end

  # Returns an already loaded entry if one exists, or nil. This does not trigger loading
  # of the given type/name.
  #
  # @param typed_name [TypedName] - the type, name combination to lookup
  # @param check_dependencies [Boolean] - if dependencies should be checked in addition to here and parent
  # @return [NamedEntry, nil] the entry containing the loaded value, or nil if not found
  # @api public
  #
  def loaded_entry(typed_name, check_dependencies = false)
    raise NotImplementedError, "Class #{self.class.name} must implement method #loaded_entry"
  end

  # Produces the value associated with the given name if defined **in this loader**, or nil if not defined.
  # This lookup does not trigger any loading, or search of the given name.
  # An implementor of this method may not search or look up in any other loader, and it may not
  # define the name.
  #
  # @param typed_name [TypedName] - the type, name combination to lookup
  #
  # @api private
  #
  def [](typed_name)
    found = get_entry(typed_name)
    if found
      found.value
    else
      nil
    end
  end

  # Searches for the given name in this loader's context (parents should already have searched their context(s) without
  # producing a result when this method is called).
  # An implementation of find typically caches the result.
  #
  # @param typed_name [TypedName] the type, name combination to lookup
  # @return [NamedEntry, nil] the entry for the loaded entry, or nil if not found
  #
  # @api private
  #
  def find(typed_name)
    raise NotImplementedError, "Class #{self.class.name} must implement method #find"
  end

  # Returns the parent of the loader, or nil, if this is the top most loader. This implementation returns nil.
  def parent
    nil
  end

  # Produces the private loader for loaders that have a one (the visibility given to loaded entities).
  # For loaders that does not provide a private loader, self is returned.
  #
  # @api private
  def private_loader
    self
  end

  # Lock around a block
  # This exists so some subclasses that are set up statically and don't actually
  # load can override it
  def synchronize(&block)
    @environment.lock.synchronize(&block)
  end

  # Binds a value to a name. The name should not start with '::', but may contain multiple segments.
  #
  # @param type [:Symbol] the type of the entity being set
  # @param name [String, Symbol] the name of the entity being set
  # @param origin [URI, #uri, String] the origin of the set entity, a URI, or provider of URI, or URI in string form
  # @return [NamedEntry, nil] the created entry
  #
  # @api private
  #
  def set_entry(type, name, value, origin = nil)
    raise NotImplementedError.new
  end

  # Produces a NamedEntry if a value is bound to the given name, or nil if nothing is bound.
  #
  # @param typed_name [TypedName] the type, name combination to lookup
  # @return [NamedEntry, nil] the value bound in an entry
  #
  # @api private
  #
  def get_entry(typed_name)
    raise NotImplementedError.new
  end

  # A loader is by default a loader for all kinds of loadables. An implementation may override
  # if it cannot load all kinds.
  #
  # @api private
  def loadables
    LOADABLE_KINDS
  end

  # A loader may want to implement its own version with more detailed information.
  def to_s
    loader_name
  end

  # Loaders may contain references to the environment they load items within.
  # Consequently, calling Kernel#inspect may return strings that are large
  # enough to cause OutOfMemoryErrors on some platforms.
  #
  # We do not call alias_method here as that would copy the content of to_s
  # at this point to inspect (ie children would print out `loader_name`
  # rather than their version of to_s if they chose to implement it).
  def inspect
    self.to_s
  end


  # An entry for one entity loaded by the loader.
  #
  class NamedEntry
    attr_reader :typed_name
    attr_reader :value
    attr_reader :origin

    def initialize(typed_name, value, origin)
      @typed_name = typed_name
      @value = value
      @origin = origin
      freeze()
    end
  end
end
end
end