Class: Puppet::Util::Autoload

Inherits:
Object
  • Object
show all
Defined in:
lib/puppet/util/autoload.rb

Overview

Autoload paths, either based on names or all at once.

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(obj, path) ⇒ Autoload

Returns a new instance of Autoload

Raises:

  • (ArgumentError)

194
195
196
197
198
# File 'lib/puppet/util/autoload.rb', line 194

def initialize(obj, path)
  @path = path.to_s
  raise ArgumentError, _("Autoload paths cannot be fully qualified") if Puppet::Util.absolute_path?(@path)
  @object = obj
end

Class Attribute Details

.loadedObject

Returns the value of attribute loaded


19
20
21
# File 'lib/puppet/util/autoload.rb', line 19

def loaded
  @loaded
end

Instance Attribute Details

#objectObject

Returns the value of attribute object


192
193
194
# File 'lib/puppet/util/autoload.rb', line 192

def object
  @object
end

#pathObject

Returns the value of attribute path


192
193
194
# File 'lib/puppet/util/autoload.rb', line 192

def path
  @path
end

Class Method Details

.changed?(name, env) ⇒ 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.


47
48
49
50
51
52
53
54
55
56
57
# File 'lib/puppet/util/autoload.rb', line 47

def changed?(name, env)
  name = cleanpath(name).chomp('.rb')
  return true unless loaded.include?(name)
  file, old_mtime = loaded[name]
  return true unless file == get_file(name, env)
  begin
    old_mtime.to_i != File.mtime(file).to_i
  rescue Errno::ENOENT
    true
  end
end

.cleanpath(path) ⇒ Object

Normalize a path. This converts ALT_SEPARATOR to SEPARATOR on Windows and eliminates unnecessary parts of a path.


180
181
182
183
184
185
186
187
188
189
# File 'lib/puppet/util/autoload.rb', line 180

def cleanpath(path)
  # There are two cases here because cleanpath does not handle absolute
  # paths correctly on windows (c:\ and c:/ are treated as distinct) but
  # we don't want to convert relative paths to absolute
  if Puppet::Util.absolute_path?(path)
    File.expand_path(path)
  else
    Pathname.new(path).cleanpath.to_s
  end
end

.files_in_dir(dir, path) ⇒ 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.


107
108
109
110
111
112
# File 'lib/puppet/util/autoload.rb', line 107

def files_in_dir(dir, path)
  dir = Pathname.new(File.expand_path(dir))
  Dir.glob(File.join(dir, path, "*.rb")).collect do |file|
    Pathname.new(file).relative_path_from(dir).to_s
  end
end

.files_to_load(path, env) ⇒ Object


102
103
104
# File 'lib/puppet/util/autoload.rb', line 102

def files_to_load(path, env)
  search_directories(env).map {|dir| files_in_dir(dir, path) }.flatten.uniq
end

.gem_directoriesObject

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.


141
142
143
# File 'lib/puppet/util/autoload.rb', line 141

def gem_directories
  gem_source.directories
end

.gem_sourceObject


21
22
23
# File 'lib/puppet/util/autoload.rb', line 21

def gem_source
  @gem_source ||= Puppet::Util::RubyGems::Source.new
end

.get_file(name, env) ⇒ 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.

Get the correct file to load for a given path returns nil if no file is found


96
97
98
99
100
# File 'lib/puppet/util/autoload.rb', line 96

def get_file(name, env)
  name = name + '.rb' unless name =~ /\.rb$/
  path = search_directories(env).find { |dir| Puppet::FileSystem.exist?(File.join(dir, name)) }
  path and File.join(path, name)
end

.load_file(name, env) ⇒ Object

Load a single plugin by name. We use 'load' here so we can reload a given plugin.


61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/puppet/util/autoload.rb', line 61

def load_file(name, env)
  file = get_file(name.to_s, env)
  return false unless file
  begin
    mark_loaded(name, file)
    Kernel.load file
    return true
  rescue SystemExit,NoMemoryError
    raise
  rescue Exception => detail
    message = _("Could not autoload %{name}: %{detail}") % { name: name, detail: detail }
    Puppet.log_exception(detail, message)
    raise Puppet::Error, message, detail.backtrace
  end
end

.loadall(path, env) ⇒ Object


77
78
79
80
81
82
83
# File 'lib/puppet/util/autoload.rb', line 77

def loadall(path, env)
  # Load every instance of everything we can find.
  files_to_load(path, env).each do |file|
    name = file.chomp(".rb")
    load_file(name, env) unless loaded?(name)
  end
end

.loaded?(path) ⇒ Boolean

Has a given path been loaded? This is used for testing whether a changed file should be loaded or just ignored. This is only used in network/client/master, when downloading plugins, to see if a given plugin is currently loaded and thus should be reloaded.


30
31
32
33
# File 'lib/puppet/util/autoload.rb', line 30

def loaded?(path)
  path = cleanpath(path).chomp('.rb')
  loaded.include?(path)
end

.mark_loaded(name, file) ⇒ 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.

Save the fact that a given path has been loaded. This is so we can load downloaded plugins if they've already been loaded into memory.


39
40
41
42
43
44
# File 'lib/puppet/util/autoload.rb', line 39

def mark_loaded(name, file)
  name = cleanpath(name).chomp('.rb')
  file = File.expand_path(file)
  $LOADED_FEATURES << file unless $LOADED_FEATURES.include?(file)
  loaded[name] = [file, File.mtime(file)]
end

.module_directories(env) ⇒ 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.

Raises:

  • (ArgumentError)

115
116
117
118
119
120
121
122
123
124
125
# File 'lib/puppet/util/autoload.rb', line 115

def module_directories(env)
  raise ArgumentError, "Autoloader requires an environment" unless env

  Puppet::Util::ModuleDirectoriesAdapter.adapt(env) do |a|
    a.directories ||= env.modulepath.collect do |dir|
      Dir.entries(dir).reject { |f| f =~ /^\./ }.collect { |f| File.join(dir, f, "lib") }
    end.flatten.find_all do |d|
      FileTest.directory?(d)
    end
  end.directories
end

.reload_changed(env) ⇒ Object


85
86
87
88
89
90
91
# File 'lib/puppet/util/autoload.rb', line 85

def reload_changed(env)
  loaded.keys.each do |file|
    if changed?(file, env)
      load_file(file, env)
    end
  end
end

.search_directories(env) ⇒ 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.


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
# File 'lib/puppet/util/autoload.rb', line 146

def search_directories(env)
  # This is a little bit of a hack.  Basically, the autoloader is being
  # called indirectly during application bootstrapping when we do things
  # such as check "features".  However, during bootstrapping, we haven't
  # yet parsed all of the command line parameters nor the config files,
  # and thus we don't yet know with certainty what the module path is.
  # This should be irrelevant during bootstrapping, because anything that
  # we are attempting to load during bootstrapping should be something
  # that we ship with puppet, and thus the module path is irrelevant.
  #
  # In the long term, I think the way that we want to handle this is to
  # have the autoloader ignore the module path in all cases where it is
  # not specifically requested (e.g., by a constructor param or
  # something)... because there are very few cases where we should
  # actually be loading code from the module path.  However, until that
  # happens, we at least need a way to prevent the autoloader from
  # attempting to access the module path before it is initialized.  For
  # now we are accomplishing that by calling the
  # "app_defaults_initialized?" method on the main puppet Settings object.
  # --cprice 2012-03-16
  if Puppet.settings.app_defaults_initialized?
    unless @initialized
      $LOAD_PATH.unshift(Puppet[:libdir])
      $LOAD_PATH.concat(vendored_modules)
      @initialized = true
    end
    gem_directories + module_directories(env) + $LOAD_PATH
  else
    gem_directories + $LOAD_PATH
  end
end

.vendored_modulesObject

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.


128
129
130
131
132
133
134
135
136
137
138
# File 'lib/puppet/util/autoload.rb', line 128

def vendored_modules
  dir = Puppet[:vendormoduledir]
  if dir && File.directory?(dir)
    Dir.entries(dir)
      .reject { |f| f =~ /^\./ }
      .collect { |f| File.join(dir, f, "lib") }
      .find_all { |d| FileTest.directory?(d) }
  else
    []
  end
end

Instance Method Details

#changed?(name, env) ⇒ 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.


223
224
225
# File 'lib/puppet/util/autoload.rb', line 223

def changed?(name, env)
  self.class.changed?(expand(name), env)
end

#expand(name) ⇒ Object


231
232
233
# File 'lib/puppet/util/autoload.rb', line 231

def expand(name)
  ::File.join(@path, name.to_s)
end

#files_to_load(env) ⇒ Object


227
228
229
# File 'lib/puppet/util/autoload.rb', line 227

def files_to_load(env)
  self.class.files_to_load(@path, env)
end

#load(name, env) ⇒ Object


200
201
202
# File 'lib/puppet/util/autoload.rb', line 200

def load(name, env)
  self.class.load_file(expand(name), env)
end

#loadall(env) ⇒ Object

Load all instances from a path of Autoload.search_directories matching the relative path this Autoloader was initialized with. For example, if we have created a Puppet::Util::Autoload for Puppet::Type::User with a path of 'puppet/provider/user', the search_directories path will be searched for all ruby files matching puppet/provider/user/*.rb and they will then be loaded from the first directory in the search path providing them. So earlier entries in the search path may shadow later entries.

This uses require, rather than load, so that already-loaded files don't get reloaded unnecessarily.


214
215
216
# File 'lib/puppet/util/autoload.rb', line 214

def loadall(env)
  self.class.loadall(@path, env)
end

#loaded?(name) ⇒ Boolean


218
219
220
# File 'lib/puppet/util/autoload.rb', line 218

def loaded?(name)
  self.class.loaded?(expand(name))
end