# Provides feature definitions.
require 'puppet/util/methodhelper'
require 'puppet/util/docs'
require 'puppet/util'
module Puppet::Util::ProviderFeatures
include Puppet::Util::Docs
# The class that models the features and handles checking whether the features
# are present.
class ProviderFeature
include Puppet::Util
include Puppet::Util::MethodHelper
include Puppet::Util::Docs
attr_accessor :name, :docs, :methods
# Are all of the requirements met?
def available?(obj)
if self.methods
if methods_available?(obj)
return true
else
return false
end
else
# In this case, the provider has to declare support for this
# feature, and that's been checked before we ever get to the
# method checks.
return false
end
end
def initialize(name, docs, hash)
self.name = symbolize(name)
self.docs = docs
hash = symbolize_options(hash)
set_options(hash)
end
private
# Are all of the required methods available?
def methods_available?(obj)
methods.each do |m|
if obj.is_a?(Class)
return false unless obj.public_method_defined?(m)
else
return false unless obj.respond_to?(m)
end
end
return true
end
end
# Define one or more features. At a minimum, features require a name
# and docs, and at this point they should also specify a list of methods
# required to determine if the feature is present.
def feature(name, docs, hash = {})
@features ||= {}
if @features.include?(name)
raise Puppet::DevError, "Feature %s is already defined" % name
end
begin
obj = ProviderFeature.new(name, docs, hash)
@features[obj.name] = obj
rescue ArgumentError => detail
error = ArgumentError.new(
"Could not create feature %s: %s" % [name, detail]
)
error.set_backtrace(detail.backtrace)
raise error
end
end
# Return a hash of all feature documentation.
def featuredocs
str = ""
@features ||= {}
return nil if @features.empty?
names = @features.keys.sort { |a,b| a.to_s <=> b.to_s }
names.each do |name|
doc = @features[name].docs.gsub(/\n\s+/, " ")
str += "- **%s**: %s\n" % [name, doc]
end
if providers.length > 0
headers = ["Provider", names].flatten
data = {}
providers.each do |provname|
data[provname] = []
prov = provider(provname)
names.each do |name|
if prov.feature?(name)
data[provname] << "**X**"
else
data[provname] << ""
end
end
end
str += doctable(headers, data)
end
str
end
# Return a list of features.
def features
@features ||= {}
@features.keys
end
# Generate a module that sets up the boolean methods to test for given
# features.
def feature_module
unless defined? @feature_module
@features ||= {}
@feature_module = ::Module.new
const_set("FeatureModule", @feature_module)
features = @features
# Create a feature? method that can be passed a feature name and
# determine if the feature is present.
@feature_module.send(:define_method, :feature?) do |name|
method = name.to_s + "?"
if respond_to?(method) and send(method)
return true
else
return false
end
end
# Create a method that will list all functional features.
@feature_module.send(:define_method, :features) do
return false unless defined?(features)
features.keys.find_all { |n| feature?(n) }.sort { |a,b|
a.to_s <=> b.to_s
}
end
# Create a method that will list all functional features.
@feature_module.send(:define_method, :satisfies?) do |*needed|
ret = true
needed.flatten.each do |feature|
unless feature?(feature)
ret = false
break
end
end
ret
end
# Create a boolean method for each feature so you can test them
# individually as you might need.
@features.each do |name, feature|
method = name.to_s + "?"
@feature_module.send(:define_method, method) do
if defined? @declared_features and @declared_features.include?(name)
true
elsif feature.available?(self)
true
else
false
end
end
end
# Allow the provider to declare that it has a given feature.
@feature_module.send(:define_method, :has_features) do |*names|
@declared_features ||= []
names.each do |name|
name = symbolize(name)
@declared_features << name
end
end
# Aaah, grammatical correctness
@feature_module.send(:alias_method, :has_feature, :has_features)
end
@feature_module
end
end
# $Id: provider_features.rb 2542 2007-05-30 20:36:29Z mpalmer $
syntax highlighted by Code2HTML, v. 0.9.1