# 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 ||= {} raise(Puppet::DevError, "Feature %s is already defined" % name) if @features.include?(name) 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 determine if a provided list of # features are satisfied by the curred provider. @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 (is_a?(Class) ? declared_feature?(name) : self.class.declared_feature?(name)) or feature.available?(self) 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 # Return the actual provider feature instance. Really only used for testing. def provider_feature(name) return nil unless defined?(@features) @features[name] end end