class Puppet::Util::Feature

Attributes

path[R]

Public Class Methods

new(path) click to toggle source

Create a new feature collection.

   # File lib/puppet/util/feature.rb
64 def initialize(path)
65   @path = path
66   @results = {}
67   @loader = Puppet::Util::Autoload.new(self, @path)
68 end

Public Instance Methods

add(name, options = {}, &block) click to toggle source

Create a new feature test. You have to pass the feature name, and it must be unique. You can pass a block to determine if the feature is present:

Puppet.features.add(:myfeature) do
  # return true or false if feature is available
  # return nil if feature may become available later
end

The block should return true if the feature is available, false if it is not, or nil if the state is unknown. True and false values will be cached. A nil value will not be cached, and should be used if the feature may become true in the future.

Features are often used to detect if a ruby library is installed. To support that common case, you can pass one or more ruby libraries, and the feature will be true if all of the libraries load successfully:

Puppet.features.add(:myfeature, libs: 'mylib')
Puppet.features.add(:myfeature, libs: ['mylib', 'myotherlib'])

If the ruby library is not installed, then the failure is not cached, as it's assumed puppet may install the gem during catalog application.

If a feature is defined using `:libs` and a block, then the block is used and the `:libs` are ignored.

Puppet evaluates the feature test when the `Puppet.features.myfeature?` method is called. If the feature test was defined using a block and the block returns nil, then the feature test will be re-evaluated the next time `Puppet.features.myfeature?` is called.

@param [Symbol] name The unique feature name @param [Hash<Symbol,Array<String>>] options The libraries to load

   # File lib/puppet/util/feature.rb
42 def add(name, options = {}, &block)
43   method = name.to_s + "?"
44   @results.delete(name)
45 
46   meta_def(method) do
47     # we return a cached result if:
48     #  * if we've tested this feature before
49     #  AND
50     #    * the result was true/false
51     #    OR
52     #    * we're configured to never retry
53     if @results.has_key?(name) &&
54        (!@results[name].nil? || !Puppet[:always_retry_plugins])
55       !!@results[name]
56     else
57       @results[name] = test(name, options, &block)
58       !!@results[name]
59     end
60   end
61 end
load() click to toggle source
   # File lib/puppet/util/feature.rb
70 def load
71   @loader.loadall(Puppet.lookup(:current_environment))
72 end
method_missing(method, *args) click to toggle source
Calls superclass method
   # File lib/puppet/util/feature.rb
74 def method_missing(method, *args)
75   return super unless method.to_s =~ /\?$/
76 
77   feature = method.to_s.sub(/\?$/, '')
78   @loader.load(feature, Puppet.lookup(:current_environment))
79 
80   respond_to?(method) && self.send(method)
81 end
test(name, options) { || ... } click to toggle source

Actually test whether the feature is present. We only want to test when someone asks for the feature, so we don't unnecessarily load files.

    # File lib/puppet/util/feature.rb
 86 def test(name, options, &block)
 87   if block_given?
 88     begin
 89       result = yield
 90     rescue StandardError,ScriptError => detail
 91       warn _("Failed to load feature test for %{name}: %{detail}") % { name: name, detail: detail }
 92       result = nil
 93     end
 94     @results[name] = result
 95     result
 96   else
 97     libs = options[:libs]
 98     if libs
 99       libs = [libs] unless libs.is_a?(Array)
100       libs.all? { |lib| load_library(lib, name) } ? true : nil
101     else
102       true
103     end
104   end
105 end

Private Instance Methods

load_library(lib, name) click to toggle source
    # File lib/puppet/util/feature.rb
109 def load_library(lib, name)
110   raise ArgumentError, _("Libraries must be passed as strings not %{klass}") % { klass: lib.class } unless lib.is_a?(String)
111 
112   @rubygems ||= Puppet::Util::RubyGems::Source.new
113   @rubygems.clear_paths
114 
115   begin
116     require lib
117     true
118   rescue LoadError
119     # Expected case. Required library insn't installed.
120     debug_once(_("Could not find library '%{lib}' required to enable feature '%{name}'") %
121       {lib: lib, name: name})
122     false
123   rescue StandardError, ScriptError => detail
124     debug_once(_("Exception occurred while loading library '%{lib}' required to enable feature '%{name}': %{detail}") %
125       {lib: lib, name: name, detail: detail})
126     false
127   end
128 end