diff --git a/.yardopts b/.yardopts index a552a9bc425..4a8a9632367 100644 --- a/.yardopts +++ b/.yardopts @@ -7,5 +7,7 @@ --transitive-tag status --tag comment --hide-tag comment +--tag dsl:"DSL" --no-transitive-tag api +--template-path yardoc/templates lib/**/*.rb diff --git a/lib/puppet/metatype/manager.rb b/lib/puppet/metatype/manager.rb index 23da210c61d..727b89034b7 100644 --- a/lib/puppet/metatype/manager.rb +++ b/lib/puppet/metatype/manager.rb @@ -2,20 +2,29 @@ require 'puppet/util/classgen' require 'puppet/node/environment' -# Methods dealing with Type management. This module gets included into the -# Puppet::Type class, it's just split out here for clarity. +# This module defines methods dealing with Type management. +# This module gets included into the Puppet::Type class, it's just split out here for clarity. +# @api public +# module Puppet::MetaType module Manager include Puppet::Util::ClassGen - # remove all type instances; this is mostly only useful for testing + # An implementation specific method that removes all type instances during testing. + # @note Only use this method for testing purposes. + # @api private + # def allclear @types.each { |name, type| type.clear } end - # iterate across all of the subclasses of Type + # Iterates over all already loaded Type subclasses. + # @yield [t] a block receiving each type + # @yieldparam t [Puppet::Type] each defined type + # @yieldreturn [Object] the last returned object is also returned from this method + # @return [Object] the last returned value from the block. def eachtype @types.each do |name, type| # Only consider types that have names @@ -25,12 +34,31 @@ def eachtype end end - # Load all types. Only currently used for documentation. + # Loads all types. + # @note Should only be used for purposes such as generating documentation as this is potentially a very + # expensive operation. + # @return [void] + # def loadall typeloader.loadall end - # Define a new type. + # Defines a new type or redefines an existing type with the given name. + # A convenience method on the form `new` where name is the name of the type is also created. + # (If this generated method happens to clash with an existing method, a warning is issued and the original + # method is kept). + # + # @param name [String] the name of the type to create or redefine. + # @param options [Hash] options passed on to {Puppet::Util::ClassGen#genclass} as the option `:attributes` after + # first having removed any present `:parent` option. + # @option options [Puppet::Type] :parent the parent (super type) of this type. If nil, the default is + # Puppet::Type. This option is not passed on as an attribute to genclass. + # @yield [ ] a block evaluated in the context of the created class, thus allowing further detailing of + # that class. + # @return [Class] the created subclass + # @see Puppet::Util::ClassGen.genclass + # + # @dsl type # @api public def newtype(name, options = {}, &block) # Handle backward compatibility @@ -97,7 +125,9 @@ def newtype(name, options = {}, &block) klass end - # Remove an existing defined type. Largely used for testing. + # Removes an existing type. + # @note Only use this for testing. + # @api private def rmtype(name) # Then create the class. @@ -106,7 +136,11 @@ def rmtype(name) singleton_class.send(:remove_method, "new#{name}") if respond_to?("new#{name}") end - # Return a Type instance by name. + # Returns a Type instance by name. + # This will load the type if not already defined. + # @param [String, Symbol] name of the wanted Type + # @return [Puppet::Type, nil] the type or nil if the type was not defined and could not be loaded + # def type(name) @types ||= {} @@ -130,7 +164,10 @@ def type(name) return @types[name] end - # Create a loader for Puppet types. + # Creates a loader for Puppet types. + # Defaults to an instance of {Puppet::Util::Autoload} if no other auto loader has been set. + # @return [Puppet::Util::Autoload] the loader to use. + # @api private def typeloader unless defined?(@typeloader) @typeloader = Puppet::Util::Autoload.new(self, "puppet/type", :wrap => false) diff --git a/lib/puppet/parameter.rb b/lib/puppet/parameter.rb index 3969374cde1..d515c5e247a 100644 --- a/lib/puppet/parameter.rb +++ b/lib/puppet/parameter.rb @@ -3,6 +3,22 @@ require 'puppet/util/logging' require 'puppet/util/docs' +# The Parameter class is the implementation of a resource's attributes of _parameter_ kind. +# The Parameter class is also the base class for {Puppet::Property}, and is used to describe meta-parameters +# (parameters that apply to all resource types). +# A Parameter (in contrast to a Property) has a single value where a property has both a current and a wanted value. +# The Parameter class methods are used to configure and create an instance of Parameter that represents +# one particular attribute data type; its valid value(s), and conversion to/from internal form. +# +# The intention is that a new parameter is created by using the DSL method {Puppet::Type.newparam}, or +# {Puppet::Type.newmetaparam} if the parameter should be applicable to all resource types. +# +# A Parameter that does not specify and valid values (via {newvalues}) accepts any value. +# +# @see Puppet::Type +# @see Puppet::Property +# @api public +# class Puppet::Parameter include Puppet::Util include Puppet::Util::Errors @@ -15,12 +31,71 @@ class Puppet::Parameter class << self include Puppet::Util include Puppet::Util::Docs - attr_reader :validater, :munger, :name, :default, :required_features, :value_collection + # Unused? + # @todo The term "validater" only appears in this location in the Puppet code base. There is `validate` + # which seems to works fine without this attribute declaration. + # @api private + # + attr_reader :validater + + # Unused? + # @todo The term "munger" only appears in this location in the Puppet code base. There is munge and unmunge + # and they seem to work perfectly fine without this attribute declaration. + # @api private + # + attr_reader :munger + + # @return [Symbol] The parameter name as given when it was created. + attr_reader :name + + # @return [Object] The default value of the parameter as determined by the {defaultto} method, or nil if no + # default has been set. + attr_reader :default + + # @comment This somewhat odd documentation construct is because the getter and setter are not + # orthogonal; the setter uses varargs and this confuses yard. To overcome the problem both the + # getter and the setter are documented here. If this issues is fixed, a todo will be displayed + # for the setter method, and the setter documentation can be moved there. + # Since the attribute is actually RW it should perhaps instead just be implemented as a setter + # and a getter method (and no attr_xxx declaration). + # + # @!attribute [rw] required_features + # @return [Array] The names of the _provider features_ required for this parameter to work. + # the returned names are always all lower case symbols. + # @overload required_features + # Returns the required _provider features_ as an array of lower case symbols + # @overload required_features=(*args) + # @param *args [Symbol] one or more names of required provider features + # Sets the required_provider_features_ from one or more values, or array. The given arguments + # are flattened, and internalized. + # @api public + # @dsl type + # + attr_reader :required_features + + # @return [Puppet::Parameter::ValueCollection] The set of valid values (or an empty set that accepts any value). + # @api private + # + attr_reader :value_collection + + # @return [Boolean] Flag indicating whether this parameter is a meta-parameter or not. attr_accessor :metaparam - # Define the default value for a given parameter or parameter. This - # means that 'nil' is an invalid default value. This defines - # the 'default' instance method. + # Defines how the `default` value of a parameter is computed. + # The computation of the parameter's default value is defined by providing a value or a block. + # A default of `nil` can not be used. + # @overload defaultto(value) + # Defines the default value with a literal value + # @param value [Object] the literal value to use as the default value + # @overload defaultto({|| ... }) + # Defines that the default value is produced by the given block. The given block + # should produce the default value. + # @raise [Puppet::DevError] if value is nil, and no block is given. + # @return [void] + # @see Parameter.default + # @dsl type + # @api public + # def defaultto(value = nil, &block) if block define_method(:default, &block) @@ -33,8 +108,12 @@ def defaultto(value = nil, &block) end end - # Return a documentation string. If there are valid values, - # then tack them onto the string. + # Produces a documentation string. + # If an enumeration of _valid values_ has been defined, it is appended to the documentation + # for this parameter specified with the {desc} method. + # @return [String] Returns a documentation string. + # @api public + # def doc @doc ||= "" @@ -50,21 +129,49 @@ def doc @doc end + # Removes the `default` method if defined. + # Has no effect if the default method is not defined. + # This method is intended to be used in a DSL scenario where a parameter inherits from a parameter + # with a default value that is not wanted in the derived parameter (otherwise, simply do not define + # a default value method). + # + # @return [void] + # @see desc + # @api public + # @dsl type + # def nodefault undef_method :default if public_method_defined? :default end - # Store documentation for this parameter. + # Sets the documentation for this parameter. + # @param str [String] The documentation string to set + # @return [String] the given `str` parameter + # @see doc + # @dsl type + # @api public + # def desc(str) @doc = str end + # Initializes the instance variables. + # Clears the internal value collection (set of allowed values). + # @return [void] + # @api private + # def initvars @value_collection = ValueCollection.new end - # This is how we munge the value. Basically, this is our - # opportunity to convert the value from one form into another. + # @overload munge {|| ... } + # Defines an optional method used to convert the parameter value from DSL/string form to an internal form. + # If a munge method is not defined, the DSL/string value is used as is. + # @note This adds a method with the name `unsafe_munge` in the created parameter class. Later this method is + # called in a context where exceptions will be rescued and handled. + # @dsl type + # @api public + # def munge(&block) # I need to wrap the unsafe version in begin/rescue parameterments, # but if I directly call the block then it gets bound to the @@ -73,55 +180,105 @@ def munge(&block) define_method(:unsafe_munge, &block) end - # Does the parameter support reverse munging? - # This will be called when something wants to access the parameter - # in a canonical form different to what the storage form is. + # @overload unmunge {|| ... } + # Defines an optional method used to convert the parameter value to DSL/string form from an internal form. + # If an `unmunge` method is not defined, the internal form is used. + # @see munge + # @note This adds a method with the name `unmunge` in the created parameter class. + # @dsl type + # @api public + # def unmunge(&block) define_method(:unmunge, &block) end - # Mark whether we're the namevar. + # Sets a marker indicating that this parameter is the _namevar_ (unique identifier) of the type + # where the parameter is contained. + # This also makes the parameter a required value. The marker can not be unset once it has been set. + # @return [void] + # @dsl type + # @api public + # def isnamevar @isnamevar = true @required = true end - # Is this parameter the namevar? Defaults to false. + # @return [Boolean] Returns whether this parameter is the _namevar_ or not. + # @api public + # def isnamevar? @isnamevar end - # This parameter is required. + # Sets a marker indicating that this parameter is required. + # Once set, it is not possible to make a parameter optional. + # @return [void] + # @dsl type + # @api public + # def isrequired @required = true end - # Specify features that are required for this parameter to work. + # @comment This method is not picked up by yard as it has a different signature than + # expected for an attribute (varargs). Instead, this method is documented as an overload + # of the attribute required_features. (Not ideal, but better than nothing). + # @todo If this text appears in documentation - see comment in source and makes corrections - it means + # that an issue in yardoc has been fixed. + # def required_features=(*args) @required_features = args.flatten.collect { |a| a.to_s.downcase.intern } end - # Is this parameter required? Defaults to false. + # Returns whether this parameter is required or not. + # A parameter is required if a call has been made to the DSL method {isrequired}. + # @return [Boolean] Returns whether this parameter is required or not. + # @api public + # def required? @required end - # Verify that we got a good value + # @overload validate {|| ... } + # Defines an optional method that is used to validate the parameter's value. + # Validation should raise appropriate exceptions, the return value of the given block is ignored. + # @return [void] + # @dsl type + # @api public + # def validate(&block) define_method(:unsafe_validate, &block) end - # Define a new value for our parameter. + # Defines valid values for the parameter (enumeration or regular expressions). + # The set of valid values for the parameter can be limited to a (mix of) literal values and + # regular expression patterns. + # @note Each call to this method adds to the set of valid values + # @param names [Symbol, Regexp] The set of valid literal values and/or patterns for the parameter. + # @return [void] + # @dsl type + # @api public + # def newvalues(*names) @value_collection.newvalues(*names) end + # Makes the given `name` an alias for the given `other` name. + # Or said differently, the valid value `other` can now also be referred to via the given `name`. + # Aliasing may affect how the parameter's value is serialized/stored (it may store the `other` value + # instead of the alias). + # @api public + # @dsl type + # def aliasvalue(name, other) @value_collection.aliasvalue(name, other) end end - # Just a simple method to proxy instance methods to class methods + # Creates instance (proxy) methods that delegates to a class method with the same name. + # @api private + # def self.proxymethods(*values) values.each { |val| define_method(val) do @@ -130,21 +287,45 @@ def self.proxymethods(*values) } end - # And then define one of these proxies for each method in our - # ParamHandler class. + # @!method required? + # (see required?) + # @!method isnamevar? + # (see isnamevar?) + # proxymethods("required?", "isnamevar?") + # @return [Puppet::Resource] A reference to the resource this parameter is an attribute of (the _associated resource_). attr_accessor :resource - # LAK 2007-05-09: Keep the @parent around for backward compatibility. + + # @comment LAK 2007-05-09: Keep the @parent around for backward compatibility. + # @return [Puppet::Parameter] A reference to the parameter's parent kept for backwards compatibility. + # @api private + # attr_accessor :parent + # @!method line() + # @return [Integer] Returns the result of calling the same method on the associated resource. + # @!method file + # @return [Integer] Returns the result of calling the same method on the associated resource. + # @!method version + # @return [Integer] Returns the result of calling the same method on the associated resource. + # [:line, :file, :version].each do |param| define_method(param) do resource.send(param) end end - # Basic parameter initialization. + # Initializes the parameter with a required resource reference and optional attribute settings. + # The option `:resource` must be specified or an exception is raised. Any additional options passed + # are used to initialize the attributes of this parameter by treating each key in the `options` hash as + # the name of the attribute to set, and the value as the value to set. + # @param options [Hash{Symbol => Object]] Options, where `resource` is required + # @option options [Puppet::Resource] :resource The resource this parameter holds a value for. Required. + # @raise [Puppet::DevError] If resource is not specified in the options hash. + # @api public + # @note A parameter should be created via the DSL method {Puppet::Type::newparam} + # def initialize(options = {}) options = symbolize_options(options) if resource = options[:resource] @@ -157,24 +338,35 @@ def initialize(options = {}) set_options(options) end + # Writes the given `msg` to the log with the loglevel indicated by the associated resource's + # `loglevel` parameter. + # @todo is loglevel a metaparameter? it is looked up with `resource[:loglevel]` + # @return [void] + # @api public def log(msg) send_log(resource[:loglevel], msg) end - # Is this parameter a metaparam? + # @return [Boolean] Returns whether this parameter is a meta-parameter or not. def metaparam? self.class.metaparam end - # each parameter class must define the name method, and parameter - # instances do not change that name this implicitly means that a given - # object can only have one parameter instance of a given parameter - # class + # @!attribute [r] name + # @return [Symbol] The parameter's name as given when it was created. + # @note Since a Parameter defines the name at the class level, each Parameter class must be + # unique within a type's inheritance chain. + # @comment each parameter class must define the name method, and parameter + # instances do not change that name this implicitly means that a given + # object can only have one parameter instance of a given parameter + # class def name self.class.name end - # for testing whether we should actually do anything + # @return [Boolean] Returns true if this parameter, the associated resource, or overall puppet mode is `noop`. + # @todo How is noop mode set for a parameter? Is this of value in DSL to inhibit a parameter? + # def noop @noop ||= false tmp = @noop || self.resource.noop || Puppet[:noop] || false @@ -182,8 +374,11 @@ def noop tmp end - # return the full path to us, for logging and rollback; not currently - # used + # @todo Original comment = _return the full path to us, for logging and rollback; not currently + # used_ This is difficult to figure out (if it is used or not as calls are certainly made to "pathbuilder" + # method is several places, not just sure if it is this implementation or not. + # + # @api private def pathbuilder if @resource return [@resource.pathbuilder, self.name] @@ -192,18 +387,31 @@ def pathbuilder end end - # If the specified value is allowed, then munge appropriately. - # If the developer uses a 'munge' hook, this method will get overridden. + # This is the default implementation of `munge` that simply produces the value (if it is valid). + # The DSL method {munge} should be used to define an overriding method if munging is required. + # + # @api private + # def unsafe_munge(value) self.class.value_collection.munge(value) end - # no unmunge by default + # Unmunges the value by transforming it from internal form to DSL form. + # This is the default implementation of `unmunge` that simply returns the value without processing. + # The DSL method {unmunge} should be used to define an overriding method if required. + # @return [Object] the unmunged value + # def unmunge(value) value end - # A wrapper around our munging that makes sure we raise useful exceptions. + # Munges the value to internal form. + # This implementation of `munge` provides exception handling around the specified munging of this parameter. + # @note This method should not be overridden. Use the DSL method {munge} to define a munging method + # if required. + # @param value [Object] the DSL value to munge + # @return [Object] the munged (internal) value + # def munge(value) begin ret = unsafe_munge(value) @@ -216,13 +424,27 @@ def munge(value) ret end - # Verify that the passed value is valid. - # If the developer uses a 'validate' hook, this method will get overridden. + # This is the default implementation of `validate` that may be overridden by the DSL method {validate}. + # If no valid values have been defined, the given value is accepted, else it is validated against + # the literal values (enumerator) and/or patterns defined by calling {newvalues}. + # + # @param value [Object] the value to check for validity + # @raise [ArgumentError] if the value is not valid + # @return [void] + # @api private + # def unsafe_validate(value) self.class.value_collection.validate(value) end + # Performs validation of the given value against the rules defined by this parameter. + # @return [void] + # @todo Better description of when the various exceptions are raised.ArgumentError is rescued and + # changed into Puppet::Error. + # @raise [ArgumentError, TypeError, Puppet::DevError, Puppet::Error] under various conditions # A protected validation method that only ever raises useful exceptions. + # @api public + # def validate(value) begin unsafe_validate(value) @@ -235,30 +457,54 @@ def validate(value) end end + # Sets the associated resource to nil. + # @todo Why - what is the intent/purpose of this? + # @return [nil] + # def remove @resource = nil end + # @return [Object] Gets the value of this parameter after performing any specified unmunging. def value unmunge(@value) unless @value.nil? end - # Store the value provided. All of the checking should possibly be - # late-binding (e.g., users might not exist when the value is assigned - # but might when it is asked for). + # Sets the given value as the value of this parameter. + # @todo This original comment _"All of the checking should possibly be + # late-binding (e.g., users might not exist when the value is assigned + # but might when it is asked for)."_ does not seem to be correct, the implementation + # calls both validate an munge on the given value, so no late binding. + # + # The given value is validated and then munged (if munging has been specified). The result is store + # as the value of this arameter. + # @return [Object] The given `value` after munging. + # @raise (see #validate) + # def value=(value) validate(value) @value = munge(value) end - # Retrieve the resource's provider. Some types don't have providers, in which - # case we return the resource object itself. + # @return [Puppet::Provider] Returns the provider of the associated resource. + # @todo The original comment says = _"Retrieve the resource's provider. + # Some types don't have providers, in which case we return the resource object itself."_ + # This does not seem to be true, the default implementation that sets this value may be + # {Puppet::Type.provider=} which always gets either the name of a provider or an instance of one. + # def provider @resource.provider end - # The properties need to return tags so that logs correctly collect them. + # @return [Array] Returns an array of the associated resource's symbolic tags (including the parameter itself). + # Returns an array of the associated resource's symbolic tags (including the parameter itself). + # At a minimun, the array contains the name of the parameter. If the associated resource + # has tags, these tags are also included in the array. + # @todo The original comment says = _"The properties need to return tags so that logs correctly + # collect them."_ what if anything of that is of interest to document. Should tags and their relationship + # to logs be described. This is a more general concept. + # def tags unless defined?(@tags) @tags = [] @@ -269,10 +515,29 @@ def tags @tags end + # @return [String] The name of the parameter in string form. def to_s name.to_s end + # Produces a String with the value formatted for display to a human. + # When the parameter value is a: + # + # * **single valued parameter value** the result is produced on the + # form `'value'` where _value_ is the string form of the parameter's value. + # + # * **Array** the list of values is enclosed in `[]`, and + # each produced value is separated by a comma. + # + # * **Hash** value is output with keys in sorted order enclosed in `{}` with each entry formatted + # on the form `'k' => v` where + # `k` is the key in string form and _v_ is the value of the key. Entries are comma separated. + # + # For both Array and Hash this method is called recursively to format contained values. + # @note this method does not protect against infinite structures. + # + # @return [String] The formatted value in string form. + # def self.format_value_for_display(value) if value.is_a? Array formatted_values = value.collect {|value| format_value_for_display(value)}.join(', ') diff --git a/lib/puppet/parameter/package_options.rb b/lib/puppet/parameter/package_options.rb index 7482cb0489d..60b3c06c370 100644 --- a/lib/puppet/parameter/package_options.rb +++ b/lib/puppet/parameter/package_options.rb @@ -1,5 +1,9 @@ require 'puppet/parameter' +# This specialized {Puppet::Parameter} handles munging of package options. +# Package options are passed as an array of key value pairs. Special munging is +# required as the keys and values needs to be quoted in a safe way. +# class Puppet::Parameter::PackageOptions < Puppet::Parameter def unsafe_munge(values) values = [values] unless values.is_a? Array @@ -20,6 +24,7 @@ def unsafe_munge(values) end end + # @api private def quote(value) value.include?(' ') ? %Q["#{value.gsub(/"/, '\"')}"] : value end diff --git a/lib/puppet/parameter/path.rb b/lib/puppet/parameter/path.rb index 11d0969dbd1..430aca1de33 100644 --- a/lib/puppet/parameter/path.rb +++ b/lib/puppet/parameter/path.rb @@ -1,6 +1,13 @@ require 'puppet/parameter' +# This specialized {Puppet::Parameter} handles validation and munging of paths. +# By default, a single path is accepted, and by calling {accept_arrays} it is possible to +# allow an array of paths. +# class Puppet::Parameter::Path < Puppet::Parameter + # Specifies whether multiple paths are accepted or not. + # @dsl type + # def self.accept_arrays(bool = true) @accept_arrays = !!bool end @@ -8,6 +15,13 @@ def self.arrays? @accept_arrays end + # Performs validation of the given paths. + # If the concrete parameter defines a validation method, it may call this method to perform + # path validation. + # @raise [Puppet::Error] if this property is configured for single paths and an array is given + # @raise [Puppet::Error] if a path is not an absolute path + # @return [Array] the given paths + # def validate_path(paths) if paths.is_a?(Array) and ! self.class.arrays? then fail "#{name} only accepts a single path, not an array of paths" @@ -18,13 +32,22 @@ def validate_path(paths) paths end - # This will be overridden if someone uses the validate option, which is why - # it just delegates to the other, useful, method. + # This is the default implementation of the `validate` method. + # It will be overridden if the validate option is used when defining the parameter. + # @return [void] + # def unsafe_validate(paths) validate_path(paths) end - # Likewise, this might be overridden, but by default... + # This is the default implementation of `munge`. + # If the concrete parameter defines a `munge` method, this default implementation will be overridden. + # This default implementation does not perform any munging, it just checks the one/many paths + # constraints. A derived implementation can perform this check as: + # `paths.is_a?(Array) and ! self.class.arrays?` and raise a {Puppet::Error}. + # @param [String, Array] one of multiple paths + # @return [String, Array] the given paths + # @raise [Puppet::Error] if the given paths does not comply with the on/many paths rule. def unsafe_munge(paths) if paths.is_a?(Array) and ! self.class.arrays? then fail "#{name} only accepts a single path, not an array of paths" diff --git a/lib/puppet/parameter/value.rb b/lib/puppet/parameter/value.rb index 12c389d2fa1..c45c40e5efc 100644 --- a/lib/puppet/parameter/value.rb +++ b/lib/puppet/parameter/value.rb @@ -1,25 +1,42 @@ require 'puppet/parameter/value_collection' -# An individual Value class. +# Describes an acceptable value for a parameter or property. +# An acceptable value is either specified as a literal value or a regular expression. +# @note this class should be used via the api methods in {Puppet::Parameter} and {Puppet::Property} +# @api private +# class Puppet::Parameter::Value attr_reader :name, :options, :event attr_accessor :block, :call, :method, :required_features - # Add an alias for this value. + # Adds an alias for this value. + # Makes the given _name_ be an alias for this acceptable value. + # @param name [Symbol] the additonal alias this value should be known as + # @api private + # def alias(name) @aliases << convert(name) end - # Return all aliases. + # @return [Array] Returns all aliases (or an empty array). + # @api private + # def aliases @aliases.dup end - # Store the event that our value generates, if it does so. + # Stores the event that our value generates, if it does so. + # @api private + # def event=(value) @event = convert(value) end + # Initializes the instance with a literal accepted value, or a regular expression. + # If anything else is passed, it is turned into a String, and then made into a Symbol. + # @param [Symbol, Regexp, Object] the value to accept, Symbol, a regular expression, or object to convert. + # @api private + # def initialize(name) if name.is_a?(Regexp) @name = name @@ -34,7 +51,10 @@ def initialize(name) @call = :instead end - # Does a provided value match our value? + # Checks if the given value matches the acceptance rules (literal value, regular expression, or one + # of the aliases. + # @api private + # def match?(value) if regex? return true if name =~ value.to_s @@ -43,7 +63,9 @@ def match?(value) end end - # Is our value a regex? + # @return [Boolean] whether the accepted value is a regular expression or not. + # @api private + # def regex? @name.is_a?(Regexp) end @@ -52,6 +74,8 @@ def regex? # A standard way of converting all of our values, so we're always # comparing apples to apples. + # @api private + # def convert(value) case value when Symbol, '' # can't intern an empty string diff --git a/lib/puppet/parameter/value_collection.rb b/lib/puppet/parameter/value_collection.rb index 619e0731d94..c048fbdaa74 100644 --- a/lib/puppet/parameter/value_collection.rb +++ b/lib/puppet/parameter/value_collection.rb @@ -1,9 +1,21 @@ require 'puppet/parameter/value' -# A collection of values and regexes, used for specifying -# what values are allowed in a given parameter. +# A collection of values and regular expressions, used for specifying allowed values +# in a given parameter. +# @note This class is considered part of the internal implementation of {Puppet::Parameter}, and +# {Puppet::Property} and the functionality provided by this class should be used via their interfaces. +# @comment This class probably have several problems when trying to use it with a combination of +# regular expressions and aliases as it finds an acceptable value holder vi "name" which may be +# a regular expression... +# +# @api private +# class Puppet::Parameter::ValueCollection + # Aliases the given existing _other_ value with the additional given _name_. + # @return [void] + # @api private + # def aliasvalue(name, other) other = other.to_sym unless value = match?(other) @@ -13,7 +25,10 @@ def aliasvalue(name, other) value.alias(name) end - # Return a doc string for all of the values in this parameter/property. + # Returns a doc string (enumerating the acceptable values) for all of the values in this parameter/property. + # @return [String] a documentation string. + # @api private + # def doc unless defined?(@doc) @doc = "" @@ -34,11 +49,15 @@ def doc @doc end - # Does this collection contain any value definitions? + # @return [Boolean] Returns whether the set of allowed values is empty or not. + # @api private + # def empty? @values.empty? end + # @api private + # def initialize # We often look values up by name, so a hash makes more sense. @values = {} @@ -49,7 +68,15 @@ def initialize @strings = [] end - # Can we match a given value? + # Checks if the given value is acceptable (matches one of the literal values or patterns) and returns + # the "matcher" that matched. + # Literal string matchers are tested first, if both a literal and a regexp match would match, the literal + # match wins. + # + # @param test_value [Object] the value to test if it complies with the configured rules + # @return [Puppet::Parameter::Value, nil] The instance of Puppet::Parameter::Value that matched the given value, or nil if there was no match. + # @api private + # def match?(test_value) # First look for normal values if value = @strings.find { |v| v.match?(test_value) } @@ -60,7 +87,14 @@ def match?(test_value) @regexes.find { |v| v.match?(test_value) } end - # If the specified value is allowed, then munge appropriately. + # Munges the value if it is valid, else produces the same value. + # @param value [Object] the value to munge + # @return [Object] the munged value, or the given value + # @todo This method does not seem to do any munging. It just returns the value if it matches the + # regexp, or the (most likely Symbolic) allowed value if it matches (which is more of a replacement + # of one instance with an equal one. Is the intent that this method should be specialized? + # @api private + # def munge(value) return value if empty? @@ -75,17 +109,25 @@ def munge(value) end end - # Define a new valid value for a property. You must provide the value itself, - # usually as a symbol, or a regex to match the value. + # Defines a new valid value for a {Puppet::Property}. + # A valid value is specified as a literal (typically a Symbol), but can also be + # specified with a regexp. + # + # @param name [Symbol, Regexp] a valid literal value, or a regexp that matches a value + # @param options [Hash] a hash with options + # @option options [Symbol] :event The event that should be emitted when this value is set. + # @todo Option :event original comment says "event should be returned...", is "returned" the correct word + # to use? + # @option options [Symbol] :call When to call any associated block. The default value is `:instead` which + # means that the block should be called instead of the provider. In earlier versions (before 20081031) it + # was possible to specify a value of `:before` or `:after` for the purpose of calling + # both the block and the provider. Use of these deprecated options will now raise an exception later + # in the process when the _is_ value is set (see Puppet::Property#set). + # @option options [Object] _any_ Any other option is treated as a call to a setter having the given + # option name (e.g. `:required_features` calls `required_features=` with the option's value as an + # argument). + # @api private # - # The first argument to the method is either the value itself or a regex. - # The second argument is an option hash; valid options are: - # * :event: The event that should be returned when this value is set. - # * :call: When to call any associated block. The default value - # is ``instead``, which means to call the value instead of calling the - # provider. You can also specify ``before`` or ``after``, which will - # call both the block and the provider, according to the order you specify - # (the ``first`` refers to when the block is called, not the provider). def newvalue(name, options = {}, &block) value = Puppet::Parameter::Value.new(name) @values[value.name] = value @@ -107,16 +149,27 @@ def newvalue(name, options = {}, &block) value end - # Define one or more new values for our parameter. + # Defines one or more valid values (literal or regexp) for a parameter or property. + # @return [void] + # @dsl type + # @api private + # def newvalues(*names) names.each { |name| newvalue(name) } end + # @return [Array] An array of the regular expressions in string form, configured as matching valid values. + # @api private + # def regexes @regexes.collect { |r| r.name.inspect } end - # Verify that the passed value is valid. + # Validates the given value against the set of valid literal values and regular expressions. + # @raise [ArgumentError] if the value is not accepted + # @return [void] + # @api private + # def validate(value) return if empty? @@ -131,12 +184,21 @@ def validate(value) end end - # Return a single value instance. + # Returns a valid value matcher (a literal or regular expression) + # @todo This looks odd, asking for an instance that matches a symbol, or a instance that has + # a regexp. What is the intention here? Marking as api private... + # + # @return [Puppet::Parameter::Value] a valid valud matcher + # @api private + # def value(name) @values[name] end - # Return the list of valid values. + # @return [Array] Returns a list of valid literal values. + # @see regexes + # @api private + # def values @strings.collect { |s| s.name } end diff --git a/lib/puppet/property.rb b/lib/puppet/property.rb index 52e46fa27a1..083d68a9de9 100644 --- a/lib/puppet/property.rb +++ b/lib/puppet/property.rb @@ -4,27 +4,98 @@ require 'puppet' require 'puppet/parameter' +# The Property class is the implementation of a resource's attributes of _property_ kind. +# A Property is a specialized Resource Type Parameter that has both an 'is' (current) state, and +# a 'should' (wanted state). However, even if this is conceptually true, the current _is_ value is +# obtained by asking the associated provider for the value, and hence it is not actually part of a +# property's state, and only available when a provider has been selected and can obtain the value (i.e. when +# running on an agent). +# +# A Property (also in contrast to a parameter) is intended to describe a managed attribute of +# some system entity, such as the name or mode of a file. +# +# The current value _(is)_ is read and written with the methods {#retrieve} and {#set}, and the wanted +# value _(should)_ is read and written with the methods {#value} and {#value=} which delegate to +# {#should} and {#should=}, i.e. when a property is used like any other parameter, it is the _should_ value +# that is operated on. +# +# All resource type properties in the puppet system are derived from this class. +# +# The intention is that new parameters are created by using the DSL method {Puppet::Type.newproperty}. +# +# @abstract +# @note Properties of Types are expressed using subclasses of this class. Such a class describes one +# named property of a particular Type (as opposed to describing a type of property in general). This +# limits the use of one (concrete) property class instance to occur only once for a given type's inheritance +# chain. An instance of a Property class is the value holder of one instance of the resource type (e.g. the +# mode of a file resource instance). +# A Property class may server as the superclass _(parent)_ of another; e.g. a Size property that describes +# handling of measurements such as kb, mb, gb. If a type requires two different size measurements it requires +# one concrete class per such measure; e.g. MinSize (:parent => Size), and MaxSize (:parent => Size). +# +# @todo Describe meta-parameter shadowing. This concept can not be understood by just looking at the descriptions +# of the methods involved. +# +# @see Puppet::Type +# @see Puppet::Parameter +# +# @api public +# class Puppet::Property < Puppet::Parameter require 'puppet/property/ensure' - # Because 'should' uses an array, we have a special method for handling - # it. We also want to keep copies of the original values, so that - # they can be retrieved and compared later when merging. + # Returns the original wanted value(s) _(should)_ unprocessed by munging/unmunging. + # The original values are set by {#value=} or {#should=}. + # @return (see #should) + # attr_reader :shouldorig + # The noop mode for this property. + # By setting a property's noop mode to `true`, any management of this property is inhibited. Calculation + # and reporting still takes place, but if a change of the underlying managed entity's state + # should take place it will not be carried out. This noop + # setting overrides the overall `Puppet[:noop]` mode as well as the noop mode in the _associated resource_ + # attr_writer :noop class << self + # @todo Figure out what this is used for. Can not find any logic in the puppet code base that + # reads or writes this attribute. + # ??? Probably Unused attr_accessor :unmanaged + + # @return [Symbol] The name of the property as given when the property was created. + # attr_reader :name - # Return array matching info, defaulting to just matching - # the first value. + # @!attribute [rw] array_matching + # @comment note that $#46; is a period - char code require to not terminate sentence. + # The `is` vs. `should` array matching mode; `:first`, or `:all`. + # + # @comment there are two blank chars after the symbols to cause a break - do not remove these. + # * `:first` + # This is primarily used for single value properties. When matched against an array of values + # a match is true if the `is` value matches any of the values in the `should` array. When the `is` value + # is also an array, the matching is performed against the entire array as the `is` value. + # * `:all` + # : This is primarily used for multi-valued properties. When matched against an array of + # `should` values, the size of `is` and `should` must be the same, and all values in `is` must match + # a value in `should`. + # + # @note The semantics of these modes are implemented by the method {#insync?}. That method is the default + # implementation and it has a backwards compatible behavior that imposes additional constraints + # on what constitutes a positive match. A derived property may override that method. + # @return [Symbol] (:first) the mode in which matching is performed + # @see #insync? + # @dsl type + # @api public + # def array_matching @array_matching ||= :first end - # Set whether properties should match all values or just the first one. + # @comment This is documented as an attribute - see the {array_matching} method. + # def array_matching=(value) value = value.intern if value.is_a?(String) raise ArgumentError, "Supported values for Property#array_matching are 'first' and 'all'" unless [:first, :all].include?(value) @@ -32,33 +103,56 @@ def array_matching=(value) end end - # Look up a value's name, so we can find options and such. + # Looks up a value's name among valid values, to enable option lookup with result as a key. + # @param name [Object] the parameter value to match against valid values (names). + # @return {Symbol, Regexp} a value matching predicate + # @api private + # def self.value_name(name) if value = value_collection.match?(name) value.name end end - # Retrieve an option set when a value was defined. + # Returns the value of the given option (set when a valid value with the given "name" was defined). + # @param name [Symbol, Regexp] the valid value predicate as returned by {value_name} + # @param option [Symbol] the name of the wanted option + # @return [Object] value of the option + # @raise [NoMethodError] if the option is not supported + # @todo Guessing on result of passing a non supported option (it performs send(option)). + # @api private + # def self.value_option(name, option) if value = value_collection.value(name) value.send(option) end end - # Define a new valid value for a property. You must provide the value itself, - # usually as a symbol, or a regex to match the value. + # Defines a new valid value for this property. + # A valid value is specified as a literal (typically a Symbol), but can also be + # specified with a Regexp. # - # The first argument to the method is either the value itself or a regex. - # The second argument is an option hash; valid options are: - # * :method: The name of the method to define. Defaults to 'set_'. - # * :required_features: A list of features this value requires. - # * :event: The event that should be returned when this value is set. - # * :call: When to call any associated block. The default value - # is `instead`, which means to call the value instead of calling the - # provider. You can also specify `before` or `after`, which will - # call both the block and the provider, according to the order you specify - # (the `first` refers to when the block is called, not the provider). + # @param name [Symbol, Regexp] a valid literal value, or a regexp that matches a value + # @param options [Hash] a hash with options + # @option options [Symbol] :event The event that should be emitted when this value is set. + # @todo Option :event original comment says "event should be returned...", is "returned" the correct word + # to use? + # @option options [Symbol] :call When to call any associated block. The default value is `:instead` which + # means that the block should be called instead of the provider. In earlier versions (before 20081031) it + # was possible to specify a value of `:before` or `:after` for the purpose of calling + # both the block and the provider. Use of these deprecated options will now raise an exception later + # in the process when the _is_ value is set (see #set). + # @option options [Object] any Any other option is treated as a call to a setter having the given + # option name (e.g. `:required_features` calls `required_features=` with the option's value as an + # argument). + # @todo The original documentation states that the option `:method` will set the name of the generated + # setter method, but this is not implemented. Is the documentatin or the implementation in error? + # (The implementation is in Puppet::Parameter::ValueCollection#new_value). + # @todo verify that the use of :before and :after have been deprecated (or rather - never worked, and + # was never in use. (This means, that the option :call could be removed since calls are always :instead). + # + # @dsl type + # @api public def self.newvalue(name, options = {}, &block) value = value_collection.newvalue(name, options, &block) @@ -66,7 +160,12 @@ def self.newvalue(name, options = {}, &block) value end - # Call the provider method. + # Calls the provider setter method for this property with the given value as argument. + # @return [Object] what the provider returns when calling a setter for this property's name + # @raise [Puppet::Error] when the provider can not handle this property. + # @see #set + # @api private + # def call_provider(value) method = self.class.name.to_s + "=" unless provider.respond_to? method @@ -75,8 +174,19 @@ def call_provider(value) provider.send(method, value) end - # Call the dynamically-created method associated with our value, if - # there is one. + # Sets the value of this property to the given value by calling the dynamically created setter method associated with the "valid value" referenced by the given name. + # @param name [Symbol, Regexp] a valid value "name" as returned by {value_name} + # @param value [Object] the value to set as the value of the property + # @raise [Puppet::DevError] if there was no method to call + # @raise [Puppet::Error] if there were problems setting the value + # @raise [Puppet::ResourceError] if there was a problem setting the value and it was not raised + # as a Puppet::Error. The original exception is wrapped and logged. + # @todo The check for a valid value option called `:method` does not seem to be fully supported + # as it seems that this option is never consulted when the method is dynamically created. Needs to + # be investigated. (Bug, or documentation needs to be changed). + # @see #set + # @api private + # def call_valuemethod(name, value) if method = self.class.value_option(name, :method) and self.respond_to?(method) begin @@ -98,7 +208,11 @@ def call_valuemethod(name, value) end end - # How should a property change be printed as a string? + # Formats a message for a property change from the given `current_value` to the given `newvalue`. + # @return [String] a message describing the property change. + # @note If called with equal values, this is reported as a change. + # @raise [Puppet::DevError] if there were issues formatting the message + # def change_to_s(current_value, newvalue) begin if current_value == :absent @@ -117,7 +231,12 @@ def change_to_s(current_value, newvalue) end end - # Figure out which event to return. + # Produces the name of the event to use to describe a change of this property's value. + # The produced event name is either the event name configured for this property, or a generic + # event based on the name of the property with suffix `_changed`, or if the property is + # `:ensure`, the name of the resource type and one of the suffixes `_created`, `_removed`, or `_changed`. + # @return [String] the name of the event that describes the change + # def event_name value = self.should @@ -133,14 +252,35 @@ def event_name end).to_sym end - # Return a modified form of the resource event. + # Produces an event describing a change of this property. + # In addition to the event attributes set by the resource type, this method adds: + # + # * `:name` - the event_name + # * `:desired_value` - a.k.a _should_ or _wanted value_ + # * `:property` - reference to this property + # * `:source_description` - the _path_ (?? See todo) + # + # @todo What is the intent of this method? What is the meaning of the :source_description passed in the + # options to the created event? + # @return [Puppet::Transaction::Event] the created event + # @see Puppet::Type#event def event resource.event :name => event_name, :desired_value => should, :property => self, :source_description => path end + # @todo What is this? + # What is this used for? attr_reader :shadow - # initialize our property + # Initializes a Property the same way as a Parameter and handles the special case when a property is shadowing a meta-parameter. + # @todo There is some special initialization when a property is not a metaparameter but + # Puppet::Type.metaparamclass(for this class's name) is not nil - if that is the case a + # setup_shadow is performed for that class. + # + # @param hash [Hash] options passed to the super initializer {Puppet::Parameter#initialize} + # @note New properties of a type should be created via the DSL method {Puppet::Type.newproperty}. + # @see Puppet::Parameter#initialize description of Parameter initialize options. + # @api private def initialize(hash = {}) super @@ -149,14 +289,14 @@ def initialize(hash = {}) end end - # Determine whether the property is in-sync or not. If @should is - # not defined or is set to a non-true value, then we do not have - # a valid value for it and thus consider the property to be in-sync - # since we cannot fix it. Otherwise, we expect our should value - # to be an array, and if @is matches any of those values, then - # we consider it to be in-sync. + # Determines whether the property is in-sync or not in a way that is protected against missing value. + # @note If the wanted value _(should)_ is not defined or is set to a non-true value then this is + # a state that can not be fixed and the property is reported to be in sync. + # @return [Boolean] the protected result of `true` or the result of calling {#insync?}. + # + # @api private + # @note Do not override this method. # - # Don't override this method. def safe_insync?(is) # If there is no @should value, consider the property to be in sync. return true unless @should @@ -165,15 +305,34 @@ def safe_insync?(is) insync?(is) end + # Protects against override of the {#safe_insync?} method. + # @raise [RuntimeError] if the added method is `:safe_insync?` + # @api private + # def self.method_added(sym) raise "Puppet::Property#safe_insync? shouldn't be overridden; please override insync? instead" if sym == :safe_insync? end - # This method may be overridden by derived classes if necessary - # to provide extra logic to determine whether the property is in - # sync. In most cases, however, only `property_matches?` needs to be - # overridden to give the correct outcome - without reproducing all the array - # matching logic, etc, found here. + # Checks if the current _(is)_ value is in sync with the wanted _(should)_ value. + # The check if the two values are in sync is controlled by the result of {#match_all?} which + # specifies a match of `:first` or `:all`). The matching of the _is_ value against the entire _should_ value + # or each of the _should_ values (as controlled by {#match_all?} is performed by {#property_matches?}. + # + # A derived property typically only needs to override the {#property_matches?} method, but may also + # override this method if there is a need to have more control over the array matching logic. + # + # @note The array matching logic in this method contains backwards compatible logic that performs the + # comparison in `:all` mode by checking equality and equality of _is_ against _should_ converted to array of String, + # and that the lengths are equal, and in `:first` mode by checking if one of the _should_ values + # is included in the _is_ values. This means that the _is_ value needs to be carefully arranged to + # match the _should_. + # @todo The implementation should really do return is.zip(@should).all? {|a, b| property_matches?(a, b) } + # instead of using equality check and then check against an array with converted strings. + # @param is [Object] The current _(is)_ value to check if it is in sync with the wanted _(should)_ value(s) + # @return [Boolean] whether the values are in sync or not. + # @raise [Puppet::DevError] if wanted value _(should)_ is not an array. + # @api public + # def insync?(is) self.devfail "#{self.class.name}'s should is not array" unless @should.is_a?(Array) @@ -211,10 +370,16 @@ def insync?(is) end end - # Compare the current and desired value of a property in a property-specific - # way. Invoked by `insync?`; this should be overridden if your property - # has a different comparison type but does not actually differentiate the - # overall insync? logic. + # Checks if the given current and desired values are equal. + # This default implementation performs this check in a backwards compatible way where + # the equality of the two values is checked, and then the equality of current with desired + # converted to a string. + # + # A derived implementation may override this method to perform a property specific equality check. + # + # The intent of this method is to provide an equality check suitable for checking if the property + # value is in sync or not. It is typically called from {#insync?}. + # def property_matches?(current, desired) # This preserves the older Puppet behaviour of doing raw and string # equality comparisons for all equality. I am not clear this is globally @@ -222,15 +387,20 @@ def property_matches?(current, desired) current == desired or current == desired.to_s end - # because the @should and @is vars might be in weird formats, - # we need to set up a mechanism for pretty printing of the values - # default to just the values, but this way individual properties can - # override these methods + # Produces a pretty printing string for the given value. + # This default implementation simply returns the given argument. A derived implementation + # may perform property specific pretty printing when the _is_ and _should_ values are not + # already in suitable form. + # @return [String] a pretty printing string def is_to_s(currentvalue) currentvalue end - # Send a log message. + # Emits a log message at the log level specified for the associated resource. + # The log entry is associated with this property. + # @param msg [String] the message to log + # @return [void] + # def log(msg) Puppet::Util::Log.create( :level => resource[:loglevel], @@ -239,27 +409,36 @@ def log(msg) ) end - # Should we match all values, or just the first? + # @return [Boolean] whether the {array_matching} mode is set to `:all` or not def match_all? self.class.array_matching == :all end - # Execute our shadow's munge code, too, if we have one. + # (see Puppet::Parameter#munge) + # If this property is a meta-parameter shadow, the shadow's munge is also called. + # @todo Incomprehensible ! The concept of "meta-parameter-shadowing" needs to be explained. + # def munge(value) self.shadow.munge(value) if self.shadow super end - # each property class must define the name method, and property instances - # do not change that name - # this implicitly means that a given object can only have one property - # instance of a given property class + # @return [Symbol] the name of the property as stated when the property was created. + # @note A property class (just like a parameter class) describes one specific property and + # can only be used once within one type's inheritance chain. def name self.class.name end - # for testing whether we should actually do anything + # @return [Boolean] whether this property is in noop mode or not. + # Returns whether this property is in noop mode or not; if a difference between the + # _is_ and _should_ values should be acted on or not. + # The noop mode is a transitive setting. The mode is checked in this property, then in + # the _associated resource_ and finally in Puppet[:noop]. + # @todo This logic is different than Parameter#noop in that the resource noop mode overrides + # the property's mode - in parameter it is the other way around. Bug or feature? + # def noop # This is only here to make testing easier. if @resource.respond_to?(:noop?) @@ -273,14 +452,33 @@ def noop end end - # By default, call the method associated with the property name on our - # provider. In other words, if the property name is 'gid', we'll call - # 'provider.gid' to retrieve the current value. + # Retrieves the current value _(is)_ of this property from the provider. + # This implementation performs this operation by calling a provider method with the + # same name as this property (i.e. if the property name is 'gid', a call to the + # 'provider.gid' is expected to return the current value. + # @return [Object] what the provider returns as the current value of the property + # def retrieve provider.send(self.class.name) end - # Set our value, using the provider, an associated block, or both. + # Sets the current _(is)_ value of this property. + # The value is set using the provider's setter method for this property ({#call_provider}) if nothing + # else has been specified. If the _valid value_ for the given value defines a `:call` option with the + # value `:instead`, the + # value is set with {#call_valuemethod} which invokes a block specified for the valid value. + # + # @note In older versions (before 20081031) it was possible to specify the call types `:before` and `:after` + # which had the effect that both the provider method and the _valid value_ block were called. + # This is no longer supported. + # + # @param value [Object] the value to set as the value of this property + # @return [Object] returns what {#call_valuemethod} or {#call_provider} returns + # @raise [Puppet::Error] when the provider setter should be used but there is no provider set in the _associated + # resource_ + # @raise [Puppet::DevError] when a deprecated call form was specified (e.g. `:before` or `:after`). + # @api public + # def set(value) # Set a name for looking up associated options like the event. name = self.class.value_name(value) @@ -305,15 +503,29 @@ def set(value) end end - # If there's a shadowing metaparam, instantiate it now. - # This allows us to create a property or parameter with the - # same name as a metaparameter, and the metaparam will only be - # stored as a shadow. + # Sets up a shadow property for a shadowing meta-parameter. + # This construct allows the creation of a property with the + # same name as a meta-parameter. The metaparam will only be stored as a shadow. + # @param klass [Class] the class of the shadowed meta-parameter + # @return [Puppet::Parameter] an instance of the given class (a parameter or property) + # def setup_shadow(klass) @shadow = klass.new(:resource => self.resource) end - # Only return the first value + # Returns the wanted _(should)_ value of this property. + # If the _array matching mode_ {#match_all?} is true, an array of the wanted values in unmunged format + # is returned, else the first value in the array of wanted values in unmunged format is returned. + # @return [Array, Object, nil] Array of values if {#match_all?} else a single value, or nil if there are no + # wanted values. + # @raise [Puppet::DevError] if the wanted value is non nil and not an array + # + # @note This method will potentially return different values than the original values as they are + # converted via munging/unmunging. If the original values are wanted, call {#shouldorig}. + # + # @see #shouldorig + # @api public + # def should return nil unless defined?(@should) @@ -326,7 +538,14 @@ def should end end - # Set the should value. + # Sets the wanted _(should)_ value of this property. + # If the given value is not already an Array, it will be wrapped in one before being set. + # This method also sets the cached original _should_ values returned by {#shouldorig}. + # + # @param values [Array, Object] the value(s) to set as the wanted value(s) + # @raise [StandardError] when validation of a value fails (see {#validate}). + # @api public + # def should=(values) values = [values] unless values.is_a?(Array) @@ -336,23 +555,39 @@ def should=(values) @should = values.collect { |val| self.munge(val) } end + # Formats the given newvalue (following _should_ type conventions) for inclusion in a string describing a change. + # @return [String] Returns the given newvalue in string form with space separated entries if it is an array. + # @see #change_to_s + # def should_to_s(newvalue) [newvalue].flatten.join(" ") end + # Synchronizes the current value _(is)_ and the wanted value _(should)_ by calling {#set}. + # @raise [Puppet::DevError] if {#should} is nil + # @todo The implementation of this method is somewhat inefficient as it computes the should + # array twice. def sync devfail "Got a nil value for should" unless should set(should) end - # Verify that the passed value is valid. + # Asserts that the given value is valid. # If the developer uses a 'validate' hook, this method will get overridden. + # @raise [Exception] if the value is invalid, or value can not be handled. + # @return [void] + # @api private + # def unsafe_validate(value) super validate_features_per_value(value) end - # Make sure that we've got all of the required features for a given value. + # Asserts that all required provider features are present for the given property value. + # @raise [ArgumentError] if a required feature is not present + # @return [void] + # @api private + # def validate_features_per_value(value) if features = self.class.value_option(self.class.value_name(value), :required_features) features = Array(features) @@ -361,13 +596,12 @@ def validate_features_per_value(value) end end - # Just return any should value we might have. + # @return [Object, nil] Returns the wanted _(should)_ value of this property. def value self.should end - # Match the Parameter interface, but we really just use 'should' internally. - # Note that the should= method does all of the validation and such. + # (see #should=) def value=(value) self.should = value end diff --git a/lib/puppet/property/ensure.rb b/lib/puppet/property/ensure.rb index 8b97ddeab86..3c9f98d85a7 100644 --- a/lib/puppet/property/ensure.rb +++ b/lib/puppet/property/ensure.rb @@ -1,7 +1,12 @@ require 'puppet/property' -# This property will get automatically added to any type that responds +# This property is automatically added to any {Puppet::Type} that responds # to the methods 'exists?', 'create', and 'destroy'. +# +# Ensure defaults to having the wanted _(should)_ value `:present`. +# +# @api public +# class Puppet::Property::Ensure < Puppet::Property @name = :ensure @@ -58,6 +63,14 @@ def change_to_s(currentvalue, newvalue) end end + # Retrieves the _is_ value for the ensure property. + # The existence of the resource is checked by first consulting the provider (if it responds to + # `:exists`), and secondly the resource. A a value of `:present` or `:absent` is returned + # depending on if the managed entity exists or not. + # + # @return [Symbol] a value of `:present` or `:absent` depending on if it exists or not + # @raise [Puppet::DevError] if neither the provider nor the resource responds to `:exists` + # def retrieve # XXX This is a problem -- whether the object exists or not often # depends on the results of other properties, yet we're the first property diff --git a/lib/puppet/property/keyvalue.rb b/lib/puppet/property/keyvalue.rb index d66f16a326a..8184b77b3fc 100644 --- a/lib/puppet/property/keyvalue.rb +++ b/lib/puppet/property/keyvalue.rb @@ -1,16 +1,16 @@ -#This subclass of property manages string key value pairs. - -#In order to use this property: -# - the @should value must be an array of keyvalue pairs separated by the 'separator' -# - the retrieve method should return a hash with the keys as symbols -# IMPORTANT NOTE: In order for this property to work there must also be a 'membership' parameter -# The class that inherits from property should override that method with the symbol for the membership - - require 'puppet/property' module Puppet class Property + # This subclass of {Puppet::Property} manages string key value pairs. + # In order to use this property: + # + # * the _should_ value must be an array of key-value pairs separated by the 'separator' + # * the retrieve method should return a hash with the keys as symbols + # @note **IMPORTANT**: In order for this property to work there must also be a 'membership' parameter + # The class that inherits from property should override that method with the symbol for the membership + # @todo The node with an important message is not very clear. + # class KeyValue < Property def hash_to_key_value_s(hash) @@ -60,14 +60,19 @@ def should current.merge(members) end + # @return [String] Returns a default separator of "=" def separator "=" end + # @return [String] Returns a default delimiter of ";" def delimiter ";" end + # Retrieves the key-hash from the provider by invoking it's method named the same as this property. + # @return [Hash] the hash from the provider, or `:absent` + # def retrieve #ok, some 'convention' if the keyvalue property is named properties, provider should implement a properties method if key_hash = provider.send(name) and key_hash != :absent @@ -77,6 +82,9 @@ def retrieve end end + # Returns true if there is no _is_ value, else returns if _is_ is equal to _should_ using == as comparison. + # @return [Boolean] whether the property is in sync or not. + # def insync?(is) return true unless is diff --git a/lib/puppet/property/list.rb b/lib/puppet/property/list.rb index b86dc87f232..ed0566bddde 100644 --- a/lib/puppet/property/list.rb +++ b/lib/puppet/property/list.rb @@ -2,6 +2,9 @@ module Puppet class Property + # This subclass of {Puppet::Property} manages an unordered list of values. + # For an ordered list see {Puppet::Property::OrderedList}. + # class List < Property def should_to_s(should_value) diff --git a/lib/puppet/property/ordered_list.rb b/lib/puppet/property/ordered_list.rb index 7408b301916..65cd1695303 100644 --- a/lib/puppet/property/ordered_list.rb +++ b/lib/puppet/property/ordered_list.rb @@ -2,6 +2,13 @@ module Puppet class Property + # This subclass of {Puppet::Property} manages an ordered list of values. + # The maintained order is the order defined by the 'current' set of values (i.e. the + # original order is not disrupted). Any additions are added after the current values + # in their given order). + # + # For an unordered list see {Puppet::Property::List}. + # class OrderedList < List def add_should_with_current(should, current) diff --git a/lib/puppet/provider.rb b/lib/puppet/provider.rb index a8d2d3f9096..5edebb53efa 100644 --- a/lib/puppet/provider.rb +++ b/lib/puppet/provider.rb @@ -1,4 +1,40 @@ -# The container class for implementations. +# A Provider is an implementation of the actions that manage resources (of some type) on a system. +# This class is the base class for all implementation of a Puppet Provider. +# +# Concepts: +#-- +# * **Confinement** - confinement restricts providers to only be applicable under certain conditions. +# It is possible to confine a provider several different ways: +# * the included {#confine} method which provides filtering on fact, feature, existence of files, or a free form +# predicate. +# * the {commands} method that filters on the availability of given system commands. +# * **Property hash** - the important instance variable `@property_hash` contains all current state values +# for properties (it is lazily built). It is important that these values are managed appropriately in the +# methods {instances}, {prefetch}, and in methods that alters the current state (those that change the +# lifecycle (creates, destroys), or alters some value reflected backed by a property). +# * **Flush** - is a hook that is called once per resource when everything has been applied. The intent is +# that an implementation may defer modification of the current state typically done in property setters +# and instead record information that allows flush to perform the changes more efficiently. +# * **Execution Methods** - The execution methods provides access to execution of arbitrary commands. +# As a convenience execution methods are available on both the instance and the class of a provider since a +# lot of provider logic switch between these contexts fairly freely. +# * **System Entity/Resource** - this documentation uses the term "system entity" for system resources to make +# it clear if talking about a resource on the system being managed (e.g. a file in the file system) +# or about a description of such a resource (e.g. a Puppet Resource). +# * **Resource Type** - this is an instance of Type that describes a classification of instances of Resource (e.g. +# the `File` resource type describes all instances of `file` resources). +# (The term is used to contrast with "type" in general, and specifically to contrast with the implementation +# class of Resource or a specific Type). +# +# @note An instance of a Provider is associated with one resource. +# +# @note Class level methods are only called once to configure the provider (when the type is created), and not +# for each resource the provider is operating on. +# The instance methods are however called for each resource. +# +# +# @api public +# class Puppet::Provider include Puppet::Util include Puppet::Util::Errors @@ -16,53 +52,105 @@ class << self # Include the util module so we have access to things like 'which' include Puppet::Util, Puppet::Util::Docs include Puppet::Util::Logging + + # @return [String] The name of the provider attr_accessor :name - # The source parameter exists so that providers using the same - # source can specify this, so reading doesn't attempt to read the - # same package multiple times. + # + # @todo Original = _"The source parameter exists so that providers using the same + # source can specify this, so reading doesn't attempt to read the + # same package multiple times."_ This seems to be a package type specific attribute. Is this really + # used? + # + # @return [???] The source is WHAT? attr_writer :source - # LAK 2007-05-09: Keep the model stuff around for backward compatibility + # @todo Original = _"LAK 2007-05-09: Keep the model stuff around for backward compatibility"_ + # Is this really needed? The comment about backwards compatibility was made in 2007. + # + # @return [???] A model kept for backwards compatibility. + # @api private + # @deprecated This attribute is available for backwards compatibility reasons. attr_reader :model + + # @todo What is this type? A reference to a Puppet::Type ? + # @return [Puppet::Type] the resource type (that this provider is ... WHAT?) + # attr_accessor :resource_type + + # @!attribute [r] doc + # The (full) documentation for this provider class. The documentation for the provider class itself + # should be set with the DSL method {desc=}. Setting the documentation with with {doc=} has the same effect + # as setting it with {desc=} (only the class documentation part is set). In essence this means that + # there is no getter for the class documentation part (since the getter returns the full + # documentation when there are additional contributors). + # + # @return [String] Returns the full documentation for the provider. + # @see Puppet::Utils::Docs + # @comment This is puzzling ... a write only doc attribute??? The generated setter never seems to be + # used, instead the instance variable @doc is set in the `desc` method. This seems wrong. It is instead + # documented as a read only attribute (to get the full documentation). Also see doc below for + # desc. + # @!attribute [w] desc + # Sets the documentation of this provider class. (The full documentation is read via the + # {doc} attribute). + # + # @dsl type + # + # attr_writer :doc + end - # LAK 2007-05-09: Keep the model stuff around for backward compatibility + # @todo original = _"LAK 2007-05-09: Keep the model stuff around for backward compatibility"_, why is it + # both here (instance) and at class level? Is this a different model? + # @return [???] model is WHAT? attr_reader :model + + # @return [???] This resource is what? Is an instance of a provider attached to one particular Puppet::Resource? + # attr_accessor :resource - # Provide access to execution of arbitrary commands in providers. Execution methods are - # available on both the instance and the class of a provider because it seems that a lot of - # providers switch between these contexts fairly freely. - # - # @see Puppet::Util::Execution for how to use these methods + # Convenience methods - see class method with the same name. + # @see execute + # @return (see execute) def execute(*args) Puppet::Util::Execution.execute(*args) end + # (see Puppet::Util::Execution.execute) def self.execute(*args) Puppet::Util::Execution.execute(*args) end + # Convenience methods - see class method with the same name. + # @see execpipe + # @return (see execpipe) def execpipe(*args, &block) Puppet::Util::Execution.execpipe(*args, &block) end + # (see Puppet::Util::Execution.execpipe) def self.execpipe(*args, &block) Puppet::Util::Execution.execpipe(*args, &block) end + # Convenience methods - see class method with the same name. + # @see execfail + # @return (see execfail) def execfail(*args) Puppet::Util::Execution.execfail(*args) end + # (see Puppet::Util::Execution.execfail) def self.execfail(*args) Puppet::Util::Execution.execfail(*args) end - ######### + # Returns the absolute path to the executable for the command referenced by the given name. + # @raise [Puppet::DevError] if the name does not reference an existing command. + # @return [String] the absolute path to the found executable for the command + # @see which def self.command(name) name = name.intern @@ -77,22 +165,33 @@ def self.command(name) which(command) end - # Define commands that are not optional. + # Confines this provider to be suitable only on hosts where the given commands are present. + # Also see {Puppet::Provider::Confiner#confine} for other types of confinement of a provider by use of other types of + # predicates. + # + # @note It is preferred if the commands are not entered with absolute paths as this allows puppet + # to search for them using the PATH variable. # - # @param [Hash{String => String}] command_specs Named commands that the provider will + # @param command_specs [Hash{String => String}] Map of name to command that the provider will # be executing on the system. Each command is specified with a name and the path of the executable. - # (@see #has_command) + # @return [void] + # @see optional_commands + # def self.commands(command_specs) command_specs.each do |name, path| has_command(name, path) end end - # Define commands that are optional. - # + # Defines optional commands. + # Since Puppet 2.7.8 this is typically not needed as evaluation of provider suitability + # is lazy (when a resource is evaluated) and the absence of commands + # that will be present after other resources have been applied no longer needs to be specified as + # optional. # @param [Hash{String => String}] command_specs Named commands that the provider will # be executing on the system. Each command is specified with a name and the path of the executable. # (@see #has_command) + # @see commands def self.optional_commands(hash) hash.each do |name, target| has_command(name, target) do @@ -101,27 +200,31 @@ def self.optional_commands(hash) end end - # Define a single command - # - # A method will be generated on the provider that allows easy execution of the command. The generated - # method can take arguments that will be passed through to the executable as the command line arguments - # when it is run. + # Creates a convenience method for invocation of a command. # - # has_command(:echo, "/bin/echo") - # def some_method - # echo("arg 1", "arg 2") - # end + # This generates a Provider method that allows easy execution of the command. The generated + # method may take arguments that will be passed through to the executable as the command line arguments + # when it is invoked. # - # # or + # @example Use it like this: + # has_command(:echo, "/bin/echo") + # def some_method + # echo("arg 1", "arg 2") + # end + # @comment the . . . below is intentional to avoid the three dots to become an illegible ellipsis char. + # @example . . . or like this + # has_command(:echo, "/bin/echo") do + # is_optional + # environment :HOME => "/var/tmp", :PWD => "/tmp" + # end # - # has_command(:echo, "/bin/echo") do - # is_optional - # environment :HOME => "/var/tmp", :PWD => "/tmp" - # end + # @param name [Symbol] The name of the command (will become the name of the generated method that executes the command) + # @param path [String] The path to the executable for the command + # @yield [ ] A block that configures the command (see {Puppet::Provider::Command}) + # @comment a yield [ ] produces {|| ...} in the signature, do not remove the space. + # @note the name ´has_command´ looks odd in an API context, but makes more sense when seen in the internal + # DSL context where a Provider is declaratively defined. # - # @param [Symbol] name Name of the command (will be the name of the generated method to call the command) - # @param [String] path The path to the executable for the command - # @yield A block that configures the command (@see Puppet::Provider::Command) def self.has_command(name, path, &block) name = name.intern command = CommandDefiner.define(name, path, self, &block) @@ -134,6 +237,8 @@ def self.has_command(name, path, &block) end end + # Internal helper class when creating commands - undocumented. + # @api private class CommandDefiner private_class_method :new @@ -168,13 +273,15 @@ def command end end - # Is the provided feature a declared feature? + # @return [Boolean] Return whether the given feature has been declared or not. def self.declared_feature?(name) defined?(@declared_features) and @declared_features.include?(name) end - # Does this implementation match all of the default requirements? If - # defaults are empty, we return false. + # @return [Boolean] Returns whether this implementation satisfies all of the default requirements or not. + # Returns false If defaults are empty. + # @see Provider.defaultfor + # def self.default? return false if @defaults.empty? if @defaults.find do |fact, values| @@ -198,31 +305,79 @@ def self.default? end end - # Store how to determine defaults. + # Sets a facts filter that determine which of several suitable providers should be picked by default. + # This selection only kicks in if there is more than one suitable provider. + # To filter on multiple facts the given hash may contain more than one fact name/value entry. + # The filter picks the provider if all the fact/value entries match the current set of facts. (In case + # there are still more than one provider after this filtering, the first found is picked). + # @param hash [Hash<{String => Object}>] hash of fact name to fact value. + # @return [void] + # def self.defaultfor(hash) hash.each do |d,v| @defaults[d] = v end end + # @return [Integer] Returns a numeric specificity for this provider based on how many requirements it has + # and number of _ancestors_. The higher the number the more specific the provider. + # The number of requirements is based on the number of defaults set up with {Provider.defaultfor}. + # + # The _ancestors_ is the Ruby Module::ancestors method and the number of classes returned is used + # to boost the score. The intent is that if two providers are equal, but one is more "derived" than the other + # (i.e. includes more classes), it should win because it is more specific). + # @note Because of how this value is + # calculated there could be surprising side effects if a provider included an excessive amount of classes. + # def self.specificity + # This strange piece of logic attempts to figure out how many parent providers there + # are to increase the score. What is will actually do is count all classes that Ruby Module::ancestors + # returns (which can be other classes than those the parent chain) - in a way, an odd measure of the + # complexity of a provider). (@defaults.length * 100) + ancestors.select { |a| a.is_a? Class }.length end + # Initializes defaults and commands (i.e. clears them). + # @return [void] def self.initvars @defaults = {} @commands = {} end - # The method for returning a list of provider instances. Note that it returns providers, preferably with values already - # filled in, not resources. + # Returns a list of system resources (entities) this provider may/can manage. + # This is a query mechanism that lists entities that the provider may manage on a given system. It is + # is directly used in query services, but is also the foundation for other services; prefetching, and + # purging. + # + # As an example, a package provider lists all installed packages. (In contrast, the File provider does + # not list all files on the file-system as that would make execution incredibly slow). An implementation + # of this method should be made if it is possible to quickly (with a single system call) provide all + # instances. + # + # An implementation of this method should only cache the values of properties + # if they are discovered as part of the process for finding existing resources. + # Resource properties that require additional commands (than those used to determine existence/identity) + # should be implemented in their respective getter method. (This is important from a performance perspective; + # it may be expensive to compute, as well as wasteful as all discovered resources may perhaps not be managed). + # + # An implementation may return an empty list (naturally with the effect that it is not possible to query + # for manageable entities). + # + # By implementing this method, it is possible to use the `resources´ resource type to specify purging + # of all non managed entities. + # + # @note The returned instances are instance of some subclass of Provider, not resources. + # @return [Array] a list of providers referencing the system entities + # @abstract this method must be implemented by a subclass and this super method should never be called as it raises an exception. + # @raise [Puppet::DevError] Error indicating that the method should have been implemented by subclass. + # @see prefetch def self.instances raise Puppet::DevError, "Provider #{self.name} has not defined the 'instances' class method" end - # Create the methods for a given command. - # - # @deprecated Use {#commands}, {#optional_commands}, or {#has_command} instead. This was not meant to be part of a public API + # Creates the methods for a given command. + # @api private + # @deprecated Use {commands}, {optional_commands}, or {has_command} instead. This was not meant to be part of a public API def self.make_command_methods(name) Puppet.deprecation_warning "Provider.make_command_methods is deprecated; use Provider.commands or Provider.optional_commands instead for creating command methods" @@ -245,9 +400,19 @@ def self.make_command_methods(name) end end - # Create getter/setter methods for each property our resource type supports. - # They all get stored in @property_hash. This method is useful - # for those providers that use prefetch and flush. + # Creates getter- and setter- methods for each property supported by the resource type. + # Call this method to generate simple accessors for all properties supported by the + # resource type. These simple accessors lookup and sets values in the property hash. + # The generated methods may be overridden by more advanced implementations if something + # else than a straight forward getter/setter pair of methods is required. + # (i.e. define such overriding methods after this method has been called) + # + # An implementor of a provider that makes use of `prefetch` and `flush` can use this method since it uses + # the internal `@property_hash` variable to store values. An implementation would then update the system + # state on a call to `flush` based on the current values in the `@property_hash`. + # + # @return [void] + # def self.mk_resource_methods [resource_type.validproperties, resource_type.parameters].flatten.each do |attr| attr = attr.intern @@ -264,6 +429,10 @@ def self.mk_resource_methods self.initvars + # This method is used to generate a method for a command. + # @return [void] + # @api private + # def self.create_class_and_instance_method(name, &block) unless singleton_class.method_defined?(name) meta_def(name, &block) @@ -277,12 +446,20 @@ def self.create_class_and_instance_method(name, &block) end private_class_method :create_class_and_instance_method - # Retrieve the data source. Defaults to the provider name. + # @return [String] Returns the data source, which is the provider name if no other source has been set. + # @todo Unclear what "the source" is used for? def self.source @source ||= self.name end - # Does this provider support the specified parameter? + # Returns true if the given attribute/parameter is supported by the provider. + # The check is made that the parameter is a valid parameter for the resource type, and then + # if all its required features (if any) are supported by the provider. + # + # @param param [Class, Puppet::Parameter] the parameter class, or a parameter instance + # @return [Boolean] Returns whether this provider supports the given parameter or not. + # @raise [Puppet::DevError] if the given parameter is not valid for the resource type + # def self.supports_parameter?(param) if param.is_a?(Class) klass = param @@ -331,22 +508,34 @@ def self.supports_parameter?(param) end end - # Remove the reference to the resource, so GC can clean up. + # Clears this provider instance to allow GC to clean up. def clear @resource = nil @model = nil end - # Retrieve a named command. + # (see command) def command(name) self.class.command(name) end - # Get a parameter value. + # Returns the value of a parameter value, or `:absent` if it is not defined. + # @param param [Puppet::Parameter] the parameter to obtain the value of + # @return [Object] the value of the parameter or `:absent` if not defined. + # def get(param) @property_hash[param.intern] || :absent end + # Creates a new provider that is optionally initialized from a resource or a hash of properties. + # If no argument is specified, a new non specific provider is initialized. If a resource is given + # it is remembered for further operations. If a hash is used it becomes the internal `@property_hash` + # structure of the provider - this hash holds the current state property values of system entities + # as they are being discovered by querying or other operations (typically getters). + # + # @todo The use of a hash as a parameter needs a better exaplanation; why is this done? What is the intent? + # @param resource [Puppet::Resource, Hash] optional resource or hash + # def initialize(resource = nil) if resource.is_a?(Hash) # We don't use a duplicate here, because some providers (ParsedFile, at least) @@ -362,6 +551,10 @@ def initialize(resource = nil) end end + # Returns the name of the resource this provider is operating on. + # @return [String] the name of the resource instance (e.g. the file path of a File). + # @raise [Puppet::DevError] if no resource is set, or no name defined. + # def name if n = @property_hash[:name] return n @@ -372,24 +565,53 @@ def name end end - # Set passed params as the current values. + # Sets the given parameters values as the current values for those parameters. + # Other parameters are unchanged. + # @param [Array(other) # We can only have ordering against other providers. return nil unless other.is_a? Puppet::Provider # Otherwise, order by the providers class name. return self.class.name <=> other.class.name end + + # @comment Document prefetch here as it does not exist anywhere else (called from transaction if implemented) + # @!method self.prefetch(resource_hash) + # @abstract A subclass may implement this - it is not implemented in the Provider class + # This method may be implemented by a provider in order to pre-fetch resource properties. + # If implemented it should set the provider instance of the managed resources to a provider with the + # fetched state (i.e. what is returned from the {instances} method). + # @param resources_hash [Hash<{String => Puppet::Resource}>] map from name to resource of resources to prefetch + # @return [void] + + # @comment Document flush here as it does not exist anywhere (called from transaction if implemented) + # @!method flush() + # @abstract A subclass may implement this - it is not implemented in the Provider class + # This method may be implemented by a provider in order to flush properties that has not been individually + # applied to the managed entity's current state. + # @return [void] + end diff --git a/lib/puppet/provider/confiner.rb b/lib/puppet/provider/confiner.rb index 6e1fb23ab17..63499e78cfa 100644 --- a/lib/puppet/provider/confiner.rb +++ b/lib/puppet/provider/confiner.rb @@ -1,15 +1,44 @@ require 'puppet/provider/confine_collection' +# The Confiner module contains methods for managing a Provider's confinement (suitability under given +# conditions). The intent is to include this module in an object where confinement management is wanted. +# It lazily adds an instance variable `@confine_collection` to the object where it is included. +# module Puppet::Provider::Confiner + # Confines a provider to be suitable only under the given conditions. + # The hash describes a confine using mapping from symbols to values or predicate code. + # + # * _fact_name_ => value of fact (or array of facts) + # * `:exists` => the path to an existing file + # * `:true` => a predicate code block returning true + # * `:false` => a predicate code block returning false + # * `:feature` => name of system feature that must be present + # + # @example + # confine :operatingsystem => [:redhat, :fedora] + # confine :true { ... } + # + # @param hash [Hash<{Symbol => Object}>] hash of confines + # @return [void] + # @api public + # def confine(hash) confine_collection.confine(hash) end + # @return [Puppet::Provider::ConfineCollection] the collection of confines + # @api private + # def confine_collection @confine_collection ||= Puppet::Provider::ConfineCollection.new(self.to_s) end - # Check whether this implementation is suitable for our platform. + # Checks whether this implementation is suitable for the current platform (or returns a summary + # of all confines if short == false). + # @return [Boolean. Hash] Returns whether the confines are all valid (if short == true), or a hash of all confines + # if short == false. + # @api public + # def suitable?(short = true) return(short ? confine_collection.valid? : confine_collection.summary) end diff --git a/lib/puppet/reports.rb b/lib/puppet/reports.rb index 69a3196e81e..bf89ad5106c 100755 --- a/lib/puppet/reports.rb +++ b/lib/puppet/reports.rb @@ -1,6 +1,32 @@ require 'puppet/util/instance_loader' -# A simple mechanism for loading and returning reports. +# This class is an implementation of a simple mechanism for loading and returning reports. +# The intent is that a user registers a report by calling {register_report} and providing a code +# block that performs setup, and defines a method called `process`. The setup, and the `process` method +# are called in the context where `self` is an instance of {Puppet::Transaction::Report} which provides the actual +# data for the report via its methods. +# +# @example Minimal scaffolding for a report... +# Puppet::Reports::.register_report(:myreport) do +# # do setup here +# def process +# if self.status == 'failed' +# msg = "failed puppet run for #{self.host} #{self.status}" +# . . . +# else +# . . . +# end +# end +# end +# +# Required configuration: +# -- +# * A .rb file that defines a new report should be placed in the master's directory `lib/puppet/reports` +# * The `puppet.conf` file must have `report = true` in the `[agent]` section +# +# @see Puppet::Transaction::Report +# @api public +# class Puppet::Reports extend Puppet::Util::ClassGen extend Puppet::Util::InstanceLoader @@ -9,10 +35,22 @@ class Puppet::Reports instance_load :report, 'puppet/reports' class << self + # @api private attr_reader :hooks end - # Add a new report type. + # Adds a new report type. + # The block should contain setup, and define a method with the name `process`. The `process` method is + # called when the report is executed; the `process` method has access to report data via methods available + # in its context where `self` is an instance of {Puppet::Transaction::Report}. + # + # For an example, see the overview of this class. + # + # @param name [Symbol] the name of the report (do not use whitespace in the name). + # @param options [Hash] a hash of options + # @option options [Boolean] :useyaml whether yaml should be used or not + # @todo Uncertain what the options :useyaml really does; "whether yaml should be used or not", used where/how? + # def self.register_report(name, options = {}, &block) name = name.intern @@ -25,7 +63,8 @@ def self.register_report(name, options = {}, &block) end end - # Collect the docs for all of our reports. + # Collects the docs for all reports. + # @api private def self.reportdocs docs = "" @@ -41,7 +80,8 @@ def self.reportdocs docs end - # List each of the reports. + # Lists each of the reports. + # @api private def self.reports instance_loader(:report).loadall loaded_instances(:report) diff --git a/lib/puppet/transaction/report.rb b/lib/puppet/transaction/report.rb index 4fe5062daa8..260a068b21c 100644 --- a/lib/puppet/transaction/report.rb +++ b/lib/puppet/transaction/report.rb @@ -1,34 +1,119 @@ require 'puppet' require 'puppet/indirector' -# A class for reporting what happens on each client. Reports consist of -# two types of data: Logs and Metrics. Logs are the output that each -# change produces, and Metrics are all of the numerical data involved -# in the transaction. +# This class is used to report what happens on a client. +# There are two types of data in a report; _Logs_ and _Metrics_. +# +# * **Logs** - are the output that each change produces. +# * **Metrics** - are all of the numerical data involved in the transaction. +# +# Use {Puppet::Reports} class to create a new custom report type. This class is indirectly used +# as a source of data to report in such a registered report. +# +# ##Metrics +# There are three types of metrics in each report, and each type of metric has one or more values. +# +# * Time: Keeps track of how long things took. +# * Total: Total time for the configuration run +# * File: +# * Exec: +# * User: +# * Group: +# * Config Retrieval: How long the configuration took to retrieve +# * Service: +# * Package: +# * Resources: Keeps track of the following stats: +# * Total: The total number of resources being managed +# * Skipped: How many resources were skipped, because of either tagging or scheduling restrictions +# * Scheduled: How many resources met any scheduling restrictions +# * Out of Sync: How many resources were out of sync +# * Applied: How many resources were attempted to be fixed +# * Failed: How many resources were not successfully fixed +# * Restarted: How many resources were restarted because their dependencies changed +# * Failed Restarts: How many resources could not be restarted +# * Changes: The total number of changes in the transaction. +# +# @api public class Puppet::Transaction::Report extend Puppet::Indirector indirects :report, :terminus_class => :processor - attr_accessor :configuration_version, :host, :environment - attr_reader :resource_statuses, :logs, :metrics, :time, :kind, :status, - :puppet_version, :report_format - - # This is necessary since Marshall doesn't know how to - # dump hash with default proc (see below @records) + # The version of the configuration + # @todo Uncertain what this is? + # @return [???] the configuration version + attr_accessor :configuration_version + + # The host name for which the report is generated + # @return [String] the host name + attr_accessor :host + + # The name of the environment the host is in + # @return [String] the environment name + attr_accessor :environment + + # A hash with a map from resource to status + # @return [Hash<{String => String}>] Resource name to status string. + # @todo Uncertain if the types in the hash are correct... + attr_reader :resource_statuses + + # A list of log messages. + # @return [Array] logged messages + attr_reader :logs + + # A hash of metric name to metric value. + # @return [Hash<{String => Object}>] A map of metric name to value. + # @todo Uncertain if all values are numbers - now marked as Object. + # + attr_reader :metrics + + # The time when the report data was generated. + # @return [Time] A time object indicating when the report data was generated + # + attr_reader :time + + # The 'kind' of report is the name of operation that triggered the report to be produced. + # Typically "apply". + # @return [String] the kind of operation that triggered the generation of the report. + # + attr_reader :kind + + # The status of the client run is an enumeration: 'failed', 'changed' or 'unchanged' + # @return [String] the status of the run - one of the values 'failed', 'changed', or 'unchanged' + # + attr_reader :status + + # @return [String] The Puppet version in String form. + # @see Puppet::version() + # + attr_reader :puppet_version + + # @return [Integer] (3) a report format version number + # @todo Unclear what this is - a version? + # + attr_reader :report_format + + # This is necessary since Marshal doesn't know how to + # dump hash with default proc (see below "@records") ? + # @todo there is no "@records" to see below, uncertain what this is for. + # @api private + # def self.default_format :yaml end + # @api private def <<(msg) @logs << msg self end + # @api private def add_times(name, value) @external_times[name] = value end + # @api private def add_metric(name, hash) metric = Puppet::Util::Metric.new(name) @@ -40,10 +125,12 @@ def add_metric(name, hash) metric end + # @api private def add_resource_status(status) @resource_statuses[status.resource] = status end + # @api private def compute_status(resource_metrics, change_metric) if (resource_metrics["failed"] || 0) > 0 'failed' @@ -54,10 +141,12 @@ def compute_status(resource_metrics, change_metric) end end + # @api private def prune_internal_data resource_statuses.delete_if {|name,res| res.resource_type == 'Whit'} end + # @api private def finalize_report prune_internal_data @@ -69,6 +158,7 @@ def finalize_report @status = compute_status(resource_metrics, change_metric) end + # @api private def initialize(kind, configuration_version=nil, environment=nil) @metrics = {} @logs = [] @@ -84,11 +174,18 @@ def initialize(kind, configuration_version=nil, environment=nil) @status = 'failed' # assume failed until the report is finalized end + # @return [String] the host name + # @api public + # def name host end # Provide a human readable textual summary of this report. + # @note This is intended for debugging purposes + # @return [String] A string with a textual summary of this report. + # @api public + # def summary report = raw_summary @@ -115,7 +212,10 @@ def summary ret end - # Provide a raw hash summary of this report. + # Provides a raw hash summary of this report. + # @return [Hash<{String => Object}>] A hash with metrics key to value map + # @api public + # def raw_summary report = { "version" => { "config" => configuration_version, "puppet" => Puppet.version } } @@ -131,9 +231,16 @@ def raw_summary report end - # Based on the contents of this report's metrics, compute a single number - # that represents the report. The resulting number is a bitmask where + # Computes a single number that represents the report's status. + # The computation is based on the contents of this report's metrics. + # The resulting number is a bitmask where # individual bits represent the presence of different metrics. + # + # * 0x2 set if there are changes + # * 0x4 set if there are failures + # @return [Integer] A bitmask where 0x2 is set if there are changes, and 0x4 is set of there are failures. + # @api public + # def exit_status status = 0 status |= 2 if @metrics["changes"]["total"] > 0 @@ -141,6 +248,8 @@ def exit_status status end + # @api private + # def to_yaml_properties instance_variables - [:@external_times] end diff --git a/lib/puppet/type.rb b/lib/puppet/type.rb index e537ddbb588..ccab27cb717 100644 --- a/lib/puppet/type.rb +++ b/lib/puppet/type.rb @@ -15,8 +15,65 @@ module Puppet -# The base class for all Puppet types +# The base class for all Puppet types. +# +# A type describes: +#-- +# * **Attributes** - properties, parameters, and meta-parameters are different types of attributes of a type. +# * **Properties** - these are the properties of the managed resource (attributes of the entity being managed; like +# a file's owner, group and mode). A property describes two states; the 'is' (current state) and the 'should' (wanted +# state). +# * **Ensurable** - a set of traits that control the lifecycle (create, remove, etc.) of a managed entity. +# There is a default set of operations associated with being _ensurable_, but this can be changed. +# * **Name/Identity** - one property is the name/identity of a resource, the _namevar_ that uniquely identifies +# one instance of a type from all others. +# * **Parameters** - additional attributes of the type (that does not directly related to an instance of the managed +# resource; if an operation is recursive or not, where to look for things, etc.). A Parameter (in contrast to Property) +# has one current value where a Property has two (current-state and wanted-state). +# * **Meta-Parameters** - parameters that are available across all types. A meta-parameter typically has +# additional semantics; like the `require` meta-parameter. A new type typically does not add new meta-parameters, +# but you need to be aware of their existence so you do not inadvertently shadow an existing meta-parameters. +# * **Parent** - a type can have a super type (that it inherits from). +# * **Validation** - If not just a basic data type, or an enumeration of symbolic values, it is possible to provide +# validation logic for a type, properties and parameters. +# * **Munging** - munging/unmunging is the process of turning a value in external representation (as used +# by a provider) into an internal representation and vice versa. A Type supports adding custom logic for these. +# * **Auto Requirements** - a type can specify automatic relationships to resources to ensure that if they are being +# managed, they will be processed before this type. +# * **Providers** - a provider is an implementation of a type's behavior - the management of a resource in the +# system being managed. A provider is often platform specific and is selected at runtime based on +# criteria/predicates specified in the configured providers. See {Puppet::Provider} for details. +# * **Device Support** - A type has some support for being applied to a device; i.e. something that is managed +# by running logic external to the device itself. There are several methods that deals with type +# applicability for these special cases such as {apply_to_device}. +# +# Additional Concepts: +# -- +# * **Resource-type** - A _resource type_ is a term used to denote the type of a resource; internally a resource +# is really an instance of a Ruby class i.e. {Puppet::Resource} which defines its behavior as "resource data". +# Conceptually however, a resource is an instance of a subclass of Type (e.g. File), where such a class describes +# its interface (what can be said/what is known about a resource of this type), +# * **Managed Entity** - This is not a term in general use, but is used here when there is a need to make +# a distinction between a resource (a description of what/how something should be managed), and what it is +# managing (a file in the file system). The term _managed entity_ is a reference to the "file in the file system" +# * **Isomorphism** - the quality of being _isomorphic_ means that two resource instances with the same name +# refers to the same managed entity. Or put differently; _an isomorphic name is the identity of a resource_. +# As an example, `exec` resources (that executes some command) have the command (i.e. the command line string) as +# their name, and these resources are said to be non-isomorphic. +# +# @note The Type class deals with multiple concerns; some methods provide an internal DSL for convenient definition +# of types, other methods deal with various aspects while running; wiring up a resource (expressed in Puppet DSL +# or Ruby DSL) with its _resource type_ (i.e. an instance of Type) to enable validation, transformation of values +# (munge/unmunge), etc. Lastly, Type is also responsible for dealing with Providers; the concrete implementations +# of the behavior that constitutes how a particular Type behaves on a particular type of system (e.g. how +# commands are executed on a flavor of Linux, on Windows, etc.). This means that as you are reading through the +# documentation of this class, you will be switching between these concepts, as well as switching between +# the conceptual level "a resource is an instance of a resource-type" and the actual implementation classes +# (Type, Resource, Provider, and various utility and helper classes). +# # @api public +# +# class Type include Puppet::Util include Puppet::Util::Errors @@ -24,9 +81,15 @@ class Type include Puppet::Util::Logging include Puppet::Util::Tagging - ############################### # Comparing type instances. include Comparable + + # Compares this type against the given _other_ (type) and returns -1, 0, or +1 depending on the order. + # @param other [Object] the object to compare against (produces nil, if not kind of Type} + # @return [-1, 0, +1, nil] produces -1 if this type is before the given _other_ type, 0 if equals, and 1 if after. + # Returns nil, if the given _other_ is not a kind of Type. + # @see Comparable + # def <=>(other) # We only order against other types, not arbitrary objects. return nil unless other.is_a? Puppet::Type @@ -35,22 +98,30 @@ def <=>(other) self.ref <=> other.ref end - ############################### # Code related to resource type attributes. class << self include Puppet::Util::ClassGen include Puppet::Util::Warnings + + # @return [Array] The list of declared properties for the resource type. + # The returned lists contains instances if Puppet::Property or its subclasses. attr_reader :properties end - # All parameters, in the appropriate order. The key_attributes come first, then - # the provider, then the properties, and finally the params and metaparams - # in the order they were specified in the files. + # Returns all the attribute names of the type in the appropriate order. + # The {key_attributes} come first, then the {provider}, then the {properties}, and finally + # the {parameters} and {metaparams}, + # all in the order they were specified in the respective files. + # @return [Array] all type attribute names in a defined order. + # def self.allattrs key_attributes | (parameters & [:provider]) | properties.collect { |property| property.name } | parameters | metaparams end - # Find the class associated with any given attribute. + # Returns the class associated with the given attribute name. + # @param name [String] the name of the attribute to obtain the class for + # @return [Class, nil] the class for the given attribute, or nil if the name does not refer to an existing attribute + # def self.attrclass(name) @attrclasses ||= {} @@ -66,8 +137,11 @@ def self.attrclass(name) @attrclasses[name] end - # What type of parameter are we dealing with? Cache the results, because - # this method gets called so many times. + # Returns the attribute type (`:property`, `;param`, `:meta`). + # @comment What type of parameter are we dealing with? Cache the results, because + # this method gets called so many times. + # @return [Symbol] a symbol describing the type of attribute (`:property`, `;param`, `:meta`) + # def self.attrtype(attr) @attrtypes ||= {} unless @attrtypes.include?(attr) @@ -80,13 +154,35 @@ def self.attrtype(attr) @attrtypes[attr] end - + + # Provides iteration over meta-parameters. + # @yieldparam p [Puppet::Parameter] each meta parameter + # @return [void] + # def self.eachmetaparam @@metaparams.each { |p| yield p.name } end - # Create the 'ensure' class. This is a separate method so other types - # can easily call it and create their own 'ensure' values. + # Creates a new `ensure` property with configured default values or with configuration by an optional block. + # This method is a convenience method for creating a property `ensure` with default accepted values. + # If no block is specified, the new `ensure` property will accept the default symbolic + # values `:present`, and `:absent` - see {Puppet::Property::Ensure}. + # If something else is wanted, pass a block and make calls to {Puppet::Property.newvalue} from this block + # to define each possible value. If a block is passed, the defaults are not automatically added to the set of + # valid values. + # + # @note This method will be automatically called without a block if the type implements the methods + # specified by {ensurable?}. It is recommended to always call this method and not rely on this automatic + # specification to clearly state that the type is ensurable. + # + # @overload ensurable() + # @overload ensurable({|| ... }) + # @yield [ ] A block evaluated in scope of the new Parameter + # @yieldreturn [void] + # @return [void] + # @dsl type + # @api public + # def self.ensurable(&block) if block_given? self.newproperty(:ensure, :parent => Puppet::Property::Ensure, &block) @@ -97,7 +193,12 @@ def self.ensurable(&block) end end - # Should we add the 'ensure' property to this class? + # Returns true if the type implements the default behavior expected by being _ensurable_ "by default". + # A type is _ensurable_ by default if it responds to `:exists`, `:create`, and `:destroy`. + # If a type implements these methods and have not already specified that it is _ensurable_, it will be + # made so with the defaults specified in {ensurable}. + # @return [Boolean] whether the type is _ensurable_ or not. + # def self.ensurable? # If the class has all three of these methods defined, then it's # ensurable. @@ -106,34 +207,60 @@ def self.ensurable? } end - # These `apply_to` methods are horrible. They should really be implemented - # as part of the usual system of constraints that apply to a type and - # provider pair, but were implemented as a separate shadow system. + # @comment These `apply_to` methods are horrible. They should really be implemented + # as part of the usual system of constraints that apply to a type and + # provider pair, but were implemented as a separate shadow system. + # + # @comment We should rip them out in favour of a real constraint pattern around the + # target device - whatever that looks like - and not have this additional + # magic here. --daniel 2012-03-08 + # + # Makes this type applicable to `:device`. + # @return [Symbol] Returns `:device` + # @api private # - # We should rip them out in favour of a real constraint pattern around the - # target device - whatever that looks like - and not have this additional - # magic here. --daniel 2012-03-08 def self.apply_to_device @apply_to = :device end + # Makes this type applicable to `:host`. + # @return [Symbol] Returns `:host` + # @api private + # def self.apply_to_host @apply_to = :host end + # Makes this type applicable to `:both` (i.e. `:host` and `:device`). + # @return [Symbol] Returns `:both` + # @api private + # def self.apply_to_all @apply_to = :both end + # Makes this type apply to `:host` if not already applied to something else. + # @return [Symbol] a `:device`, `:host`, or `:both` enumeration + # @api private def self.apply_to @apply_to ||= :host end + # Returns true if this type is applicable to the given target. + # @param target [Symbol] should be :device, :host or :target, if anything else, :host is enforced + # @return [Boolean] true + # @api private + # def self.can_apply_to(target) [ target == :device ? :device : :host, :both ].include?(apply_to) end - # Deal with any options passed into parameters. + # Processes the options for a named parameter. + # @param name [String] the name of a parameter + # @param options [Hash] a hash of options + # @option options [Boolean] :boolean if option set to true, an access method on the form _name_? is added for the param + # @return [void] + # def self.handle_param_options(name, options) # If it's a boolean parameter, create a method to test the value easily if options[:boolean] @@ -146,28 +273,59 @@ def self.handle_param_options(name, options) end end - # Is the parameter in question a meta-parameter? + # Is the given parameter a meta-parameter? + # @return [Boolean] true if the given parameter is a meta-parameter. + # def self.metaparam?(param) @@metaparamhash.include?(param.intern) end - # Find the metaparameter class associated with a given metaparameter name. - # Must accept a `nil` name, and return nil. + # Returns the meta-parameter class associated with the given meta-parameter name. + # Accepts a `nil` name, and return nil. + # @param name [String, nil] the name of a meta-parameter + # @return [Class,nil] the class for the given meta-parameter, or `nil` if no such meta-parameter exists, (or if + # the given meta-parameter name is `nil`. + # def self.metaparamclass(name) return nil if name.nil? @@metaparamhash[name.intern] end + # Returns all meta-parameter names. + # @return [Array] all meta-parameter names + # def self.metaparams @@metaparams.collect { |param| param.name } end + # Returns the documentation for a given meta-parameter of this type. + # @todo the type for the param metaparam + # @param metaparam [??? Puppet::Parameter] the meta-parameter to get documentation for. + # @return [String] the documentation associated with the given meta-parameter, or nil of not such documentation + # exists. + # @raises [?] if the given metaparam is not a meta-parameter in this type + # def self.metaparamdoc(metaparam) @@metaparamhash[metaparam].doc end - # Create a new metaparam. Requires a block and a name, stores it in the - # @parameters array, and does some basic checking on it. + # Creates a new meta-parameter. + # This creates a new meta-parameter that is added to all types. + # @param name [Symbol] the name of the parameter + # @param options [Hash] a hash with options. + # @option options [Class] :parent (Puppet::Parameter) the super class of this parameter + # @option options [Hash{String => Object}] :attributes a hash that is applied to the generated class + # by calling setter methods corresponding to this hash's keys/value pairs. This is done before the given + # block is evaluated. + # @option options [Boolean] :boolean (false) specifies if this is a boolean parameter + # @option options [Boolean] :namevar (false) specifies if this parameter is the namevar + # @option options [Symbol, Array] :required_features specifies required provider features by name + # @return [Class] the created parameter + # @yield [ ] a required block that is evaluated in the scope of the new meta-parameter + # @api public + # @dsl type + # @todo Verify that this description is ok + # def self.newmetaparam(name, options = {}, &block) @@metaparams ||= [] @@metaparamhash ||= {} @@ -193,6 +351,11 @@ def self.newmetaparam(name, options = {}, &block) param end + # Returns parameters that act as a key. + # All parameters that return true from #isnamevar? or is named `:name` are included in the returned result. + # @todo would like a better explanation + # @return Array + # def self.key_attribute_parameters @key_attribute_parameters ||= ( params = @parameters.find_all { |param| @@ -201,11 +364,43 @@ def self.key_attribute_parameters ) end + # Returns cached {key_attribute_parameters} names + # @todo what is a 'key_attribute' ? + # @return [Array] cached key_attribute names + # def self.key_attributes # This is a cache miss around 0.05 percent of the time. --daniel 2012-07-17 @key_attributes_cache ||= key_attribute_parameters.collect { |p| p.name } end + # Returns a mapping from the title string to setting of attribute value(s). + # This default implementation provides a mapping of title to the one and only _namevar_ present + # in the type's definition. + # @note Advanced: some logic requires this mapping to be done differently, using a different + # validation/pattern, breaking up the title + # into several parts assigning each to an individual attribute, or even use a composite identity where + # all namevars are seen as part of the unique identity (such computation is done by the {#uniqueness} method. + # These advanced options are rarely used (only one of the built in puppet types use this, and then only + # a small part of the available functionality), and the support for these advanced mappings is not + # implemented in a straight forward way. For these reasons, this method has been marked as private). + # + # @raise [Puppet::DevError] if there is no title pattern and there are two or more key attributes + # @return [Array>>>, nil] a structure with a regexp and the first key_attribute ??? + # @comment This wonderful piece of logic creates a structure used by Resource.parse_title which + # has the capability to assign parts of the title to one or more attributes; It looks like an implementation + # of a composite identity key (all parts of the key_attributes array are in the key). This can also + # be seen in the method uniqueness_key. + # The implementation in this method simply assigns the title to the one and only namevar (which is name + # or a variable marked as namevar). + # If there are multiple namevars (any in addition to :name?) then this method MUST be implemented + # as it raises an exception if there is more than 1. Note that in puppet, it is only File that uses this + # to create a different pattern for assigning to the :path attribute + # This requires further digging. + # The entire construct is somewhat strange, since resource checks if the method "title_patterns" is + # implemented (it seems it always is) - why take this more expensive regexp mathching route for all + # other types? + # @api private + # def self.title_patterns case key_attributes.length when 0; [] @@ -216,12 +411,29 @@ def self.title_patterns end end + # Produces a _uniqueness_key_ + # @todo Explain what a uniqueness_key is + # @return [Object] an object that is a _uniqueness_key_ for this object + # def uniqueness_key self.class.key_attributes.sort_by { |attribute_name| attribute_name.to_s }.map{ |attribute_name| self[attribute_name] } end - # Create a new parameter. Requires a block and a name, stores it in the - # @parameters array, and does some basic checking on it. + # Creates a new parameter. + # @param name [Symbol] the name of the parameter + # @param options [Hash] a hash with options. + # @option options [Class] :parent (Puppet::Parameter) the super class of this parameter + # @option options [Hash{String => Object}] :attributes a hash that is applied to the generated class + # by calling setter methods corresponding to this hash's keys/value pairs. This is done before the given + # block is evaluated. + # @option options [Boolean] :boolean (false) specifies if this is a boolean parameter + # @option options [Boolean] :namevar (false) specifies if this parameter is the namevar + # @option options [Symbol, Array] :required_features specifies required provider features by name + # @return [Class] the created parameter + # @yield [ ] a required block that is evaluated in the scope of the new parameter + # @api public + # @dsl type + # def self.newparam(name, options = {}, &block) options[:attributes] ||= {} @@ -245,12 +457,24 @@ def self.newparam(name, options = {}, &block) param end - # Create a new property. The first parameter must be the name of the property; - # this is how users will refer to the property when creating new instances. - # The second parameter is a hash of options; the options are: - # * :parent: The parent class for the property. Defaults to Puppet::Property. - # * :retrieve: The method to call on the provider or @parent object (if - # the provider is not set) to retrieve the current value. + # Creates a new property. + # @param name [Symbol] the name of the property + # @param options [Hash] a hash with options. + # @option options [Symbol] :array_matching (:first) specifies how the current state is matched against + # the wanted state. Use `:first` if the property is single valued, and (`:all`) otherwise. + # @option options [Class] :parent (Puppet::Property) the super class of this property + # @option options [Hash{String => Object}] :attributes a hash that is applied to the generated class + # by calling setter methods corresponding to this hash's keys/value pairs. This is done before the given + # block is evaluated. + # @option options [Boolean] :boolean (false) specifies if this is a boolean parameter + # @option options [Symbol] :retrieve the method to call on the provider (or `parent` if `provider` is not set) + # to retrieve the current value of this property. + # @option options [Symbol, Array] :required_features specifies required provider features by name + # @return [Class] the created property + # @yield [ ] a required block that is evaluated in the scope of the new property + # @api public + # @dsl type + # def self.newproperty(name, options = {}, &block) name = name.intern @@ -298,22 +522,25 @@ def self.paramdoc(param) @paramhash[param].doc end - # Return the parameter names + # @return [Array] Returns the parameter names def self.parameters return [] unless defined?(@parameters) @parameters.collect { |klass| klass.name } end - # Find the parameter class associated with a given parameter name. + # @return [Puppet::Parameter] Returns the parameter class associated with the given parameter name. def self.paramclass(name) @paramhash[name] end - # Return the property class associated with a name + # @return [Puppet::Property] Returns the property class ??? associated with the given property name def self.propertybyname(name) @validproperties[name] end + # Returns whether or not the given name is the name of a property, parameter or meta-parameter + # @return [Boolean] true if the given attribute name is the name of an existing property, parameter or meta-parameter + # def self.validattr?(name) name = name.intern return true if name == :name @@ -326,37 +553,43 @@ def self.validattr?(name) @validattrs[name] end - # does the name reflect a valid property? + # @return [Boolean] Returns true if the given name is the name of an existing property def self.validproperty?(name) name = name.intern @validproperties.include?(name) && @validproperties[name] end - # Return the list of validproperties + # @return [Array, {}] Returns a list of valid property names, or an empty hash if there are none. + # @todo An empty hash is returned if there are no defined parameters (not an empty array). This looks like + # a bug. + # def self.validproperties return {} unless defined?(@parameters) @validproperties.keys end - # does the name reflect a valid parameter? + # @return [Boolean] Returns true if the given name is the name of an existing parameter def self.validparameter?(name) raise Puppet::DevError, "Class #{self} has not defined parameters" unless defined?(@parameters) !!(@paramhash.include?(name) or @@metaparamhash.include?(name)) end - # This is a forward-compatibility method - it's the validity interface we'll use in Puppet::Resource. + # (see validattr?) + # @note see comment in code - how should this be documented? Are some of the other query methods deprecated? + # (or should be). + # @comment This is a forward-compatibility method - it's the validity interface we'll use in Puppet::Resource. def self.valid_parameter?(name) validattr?(name) end - # Are we deleting this resource? + # @return [Boolean] Returns true if the wanted state of the resoure is that it should be absent (i.e. to be deleted). def deleting? obj = @parameters[:ensure] and obj.should == :absent end - # Create a new property if it is valid but doesn't exist - # Returns: true if a new parameter was added, false otherwise + # Creates a new property value holder for the resource if it is valid and does not already exist + # @return [Boolean] true if a new parameter was added, false otherwise def add_property_parameter(prop_name) if self.class.validproperty?(prop_name) && !@parameters[prop_name] self.newattr(prop_name) @@ -365,20 +598,24 @@ def add_property_parameter(prop_name) false end - # - # The name_var is the key_attribute in the case that there is only one. - # + # @return [Symbol, Boolean] Returns the name of the namevar if there is only one or false otherwise. + # @comment This is really convoluted and part of the support for multiple namevars (?). + # If there is only one namevar, the produced value is naturally this namevar, but if there are several? + # The logic caches the name of the namevar if it is a single name, but otherwise always + # calls key_attributes, and then caches the first if there was only one, otherwise it returns + # false and caches this (which is then subsequently returned as a cache hit). + # def name_var return @name_var_cache unless @name_var_cache.nil? key_attributes = self.class.key_attributes @name_var_cache = (key_attributes.length == 1) && key_attributes.first end - # abstract accessing parameters and properties, and normalize access to - # always be symbols, not strings This returns a value, not an object. - # It returns the 'is' value, but you can also specifically return 'is' and - # 'should' values using 'object.is(:property)' or - # 'object.should(:property)'. + # Gets the 'is' (current state) value of a parameter or property by name. + # To explicitly get the 'is' value use `o.is(:name)`, and to get the 'should' value + # use `o.should(:name)` + # @param name [String] the name of the attribute to obtain the 'is' value for. + # @return [Object] current value of the given attribute def [](name) name = name.intern fail("Invalid parameter #{name}(#{name.inspect})") unless self.class.validattr?(name) @@ -396,9 +633,9 @@ def [](name) end end - # Abstract setting parameters and properties, and normalize - # access to always be symbols, not strings. This sets the 'should' - # value on properties, and otherwise just sets the appropriate parameter. + # Sets the 'should' (wanted state) value of a property, or the value of a parameter. + # @return + # @raise [Puppet::Error] if the setting of the value fails, or if the given name is nil. def []=(name,value) name = name.intern @@ -425,8 +662,16 @@ def []=(name,value) nil end - # remove a property from the object; useful in testing or in cleanup + # Removes a property from the object; useful in testing or in cleanup # when an error has been encountered + # @todo Incomprehensible - the comment says "Remove a property", the code refers to @parameters, and + # the method parameter is called "attr" - What is it, property, parameter, both (i.e an attribute) or what? + # @todo Don't know what the attr is (name or Property/Parameter?). Guessing it is a String name... + # @todo Is it possible to delete a meta-parameter? + # @todo What does delete mean? Is it deleted from the type or is its value state 'is'/'should' deleted? + # @param attr [String] the attribute to delete from this object. WHAT IS THE TYPE? + # @raise [Puppet::DecError] when an attempt is made to delete an attribute that does not exists. + # def delete(attr) attr = attr.intern if @parameters.has_key?(attr) @@ -436,7 +681,11 @@ def delete(attr) end end - # iterate across the existing properties + # Iterates over the existing properties. + # @todo what does this mean? As opposed to iterating over the "non existing properties" ??? Is it an + # iteration over those properties that have state? CONFUSING. + # @yieldparam property [Puppet::Property] each property + # @return [void] def eachproperty # properties is a private method properties.each { |property| @@ -444,21 +693,38 @@ def eachproperty } end - # Create a transaction event. Called by Transaction or by - # a property. + # Creates a transaction event. + # Called by Transaction or by a property. + # Merges the given options with the options `:resource`, `:file`, `:line`, and `:tags`, initialized from + # values in this object. For possible options to pass (if any ????) see {Puppet::Transaction::Event}. + # @todo Needs a better explanation "Why should I care who is calling this method?", What do I need to know + # about events and how they work? Where can I read about them? + # @param options [Hash] options merged with a fixed set of options defined by this method, passed on to {Puppet::Transaction::Event}. + # @return [Puppet::Transaction::Event] the created event def event(options = {}) Puppet::Transaction::Event.new({:resource => self, :file => file, :line => line, :tags => tags}.merge(options)) end - # retrieve the 'should' value for a specified property + # @return [Object, nil] Returns the 'should' (wanted state) value for a specified property, or nil if the + # given attribute name is not a property (i.e. if it is a parameter, meta-parameter, or does not exist). def should(name) name = name.intern (prop = @parameters[name] and prop.is_a?(Puppet::Property)) ? prop.should : nil end - # Create the actual attribute instance. Requires either the attribute - # name or class as the first argument, then an optional hash of + # Creates an instance to represent/manage the given attribute. + # Requires either the attribute name or class as the first argument, then an optional hash of # attributes to set during initialization. + # @todo The original comment is just wrong - the method does not accept a hash of options + # @todo Detective work required; this method interacts with provider to ask if it supports a parameter of + # the given class. it then returns the parameter if it exists, otherwise creates a parameter + # with its :resource => self. + # @overload newattr(name) + # @param name [String] Unclear what name is (probably a symbol) - Needs investigation. + # @overload newattr(klass) + # @param klass [Class] a class supported as an attribute class - Needs clarification what that means. + # @return [???] Probably returns a new instance of the class - Needs investigation. + # def newattr(name) if name.is_a?(Class) klass = name @@ -480,31 +746,46 @@ def newattr(name) @parameters[name] = klass.new(:resource => self) end - # return the value of a parameter + # Returns the value of this object's parameter given by name + # @param name [String] the name of the parameter + # @return [Object] the value def parameter(name) @parameters[name.to_sym] end + # Returns a shallow copy of this object's hash of parameters. + # @todo Add that this is not only "parameters", but also "properties" and "meta-parameters" ? + # Changes to the contained parameters will have an effect on the parameters of this type, but changes to + # the returned hash does not. + # @return [Hash{String => Puppet:???Parameter}] a new hash being a shallow copy of the parameters map name to parameter def parameters @parameters.dup end - # Is the named property defined? + # @return [Boolean] Returns whether the property given by name is defined or not. + # @todo what does it mean to be defined? def propertydefined?(name) name = name.intern unless name.is_a? Symbol @parameters.include?(name) end - # Return an actual property instance by name; to return the value, use 'resource[param]' - # LAK:NOTE(20081028) Since the 'parameter' method is now a superset of this method, - # this one should probably go away at some point. + # Returns a {Puppet::Property} instance by name. + # To return the value, use 'resource[param]' + # @todo LAK:NOTE(20081028) Since the 'parameter' method is now a superset of this method, + # this one should probably go away at some point. - Does this mean it should be deprecated ? + # @return [Puppet::Property] the property with the given name, or nil if not a property or does not exist. def property(name) (obj = @parameters[name.intern] and obj.is_a?(Puppet::Property)) ? obj : nil end - # For any parameters or properties that have defaults and have not yet been - # set, set them now. This method can be handed a list of attributes, - # and if so it will only set defaults for those attributes. + # @todo comment says "For any parameters or properties that have defaults and have not yet been + # set, set them now. This method can be handed a list of attributes, + # and if so it will only set defaults for those attributes." + # @todo Needs a better explanation, and investigation about the claim an array can be passed (it is passed + # to self.class.attrclass to produce a class on which a check is made if it has a method class :default (does + # not seem to support an array... + # @return [void] + # def set_default(attr) return unless klass = self.class.attrclass(attr) return unless klass.method_defined?(:default) @@ -519,7 +800,14 @@ def set_default(attr) end end - # Convert our object to a hash. This just includes properties. + # @todo the comment says: "Convert our object to a hash. This just includes properties." + # @todo this is confused, again it is the @parameters instance variable that is consulted, and + # each value is copied - does it contain "properties" and "parameters" or both? Does it contain + # meta-parameters? + # + # @return [Hash{ ??? => ??? }] a hash of WHAT?. The hash is a shallow copy, any changes to the + # objects returned in this hash will be reflected in the original resource having these attributes. + # def to_hash rethash = {} @@ -530,31 +818,47 @@ def to_hash rethash end + # @return [String] the name of this object's class + # @todo Would that be "file" for the "File" resource type? of "File" or something else? + # def type self.class.name end - # Return a specific value for an attribute. + # @todo Comment says "Return a specific value for an attribute.", as opposed to what "An upspecific value"??? + # @todo is this the 'is' or the 'should' value? + # @todo why is the return restricted to things that respond to :value? (Only non structural basic data types + # supported? + # + # @return [Object, nil] the value of the attribute having the given name, or nil if the given name is not + # an attribute, or the referenced attribute does not respond to `:value`. def value(name) name = name.intern (obj = @parameters[name] and obj.respond_to?(:value)) ? obj.value : nil end + # @todo What is this used for? Needs a better explanation. + # @return [???] the version of the catalog or 0 if there is no catalog. def version return 0 unless catalog catalog.version end - # Return all of the property objects, in the order specified in the - # class. + # @return [Array] Returns all of the property objects, in the order specified in the + # class. + # @todo "what does the 'order specified in the class' mean? The order the properties where added in the + # ruby file adding a new type with new properties? + # def properties self.class.properties.collect { |prop| @parameters[prop.name] }.compact end - # Is this type's name isomorphic with the object? That is, if the - # name conflicts, does it necessarily mean that the objects conflict? + # Returns true if the type's notion of name is the identity of a resource. + # See the overview of this class for a longer explanation of the concept _isomorphism_. # Defaults to true. + # + # @return [Boolan] true, if this type's name is isomorphic with the object def self.isomorphic? if defined?(@isomorphic) return @isomorphic @@ -563,14 +867,18 @@ def self.isomorphic? end end + # @todo check that this gets documentation (it is at the class level as well as instance). + # (see isomorphic?) def isomorphic? self.class.isomorphic? end - # is the instance a managed instance? A 'yes' here means that - # the instance was created from the language, vs. being created - # in order resolve other questions, such as finding a package - # in a list + # Returns true if the instance is a managed instance. + # A 'yes' here means that the instance was created from the language, vs. being created + # in order resolve other questions, such as finding a package in a list. + # @note An object that is managed always stays managed, but an object that is not managed + # may become managed later in its lifecycle. + # @return [Boolean] true if the object is managed def managed? # Once an object is managed, it always stays managed; but an object # that is listed as unmanaged might become managed later in the process, @@ -593,12 +901,25 @@ def managed? ############################### # Code related to the container behaviour. + + # Returns true if the search should be done in depth-first order. + # This implementation always returns false. + # @todo What is this used for? + # + # @return [Boolean] true if the search should be done in depth first order. + # def depthfirst? false end - # Remove an object. The argument determines whether the object's - # subscriptions get eliminated, too. + # Removes this object (FROM WHERE?) + # @todo removes if from where? + # @overload remove(rmdeps) + # @deprecated Use remove() + # @param rmdeps [Boolean] intended to indicate that all subscriptions should also be removed, ignored. + # @overload remove() + # @return [void] + # def remove(rmdeps = true) # This is hackish (mmm, cut and paste), but it works for now, and it's # better than warnings. @@ -619,19 +940,32 @@ def remove(rmdeps = true) ############################### # Code related to evaluating the resources. + + # Returns the ancestors - WHAT? + # This implementation always returns an empty list. + # @todo WHAT IS THIS ? + # @return [Array] returns a list of ancestors. def ancestors [] end - # Flush the provider, if it supports it. This is called by the - # transaction. + # Flushes the provider if supported by the provider, else no action. + # This is called by the transaction. + # @todo What does Flushing the provider mean? Why is it interesting to know that this is + # called by the transaction? (It is not explained anywhere what a transaction is). + # + # @return [???, nil] WHAT DOES IT RETURN? GUESS IS VOID def flush self.provider.flush if self.provider and self.provider.respond_to?(:flush) end - # if all contained objects are in sync, then we're in sync - # FIXME I don't think this is used on the type instances any more, - # it's really only used for testing + # Returns true if all contained objects are in sync. + # @todo "contained in what?" in the given "in" parameter? + # + # @todo deal with the comment _"FIXME I don't think this is used on the type instances any more, + # it's really only used for testing"_ + # @return [Boolean] true if in sync, false otherwise. + # def insync?(is) insync = true @@ -665,7 +999,12 @@ def insync?(is) insync end - # retrieve the current value of all contained properties + # Retrieves the current value of all contained properties. + # Parameters and meta-parameters are not included in the result. + # @todo As oposed to all non contained properties? How is this different than any of the other + # methods that also "gets" properties/parameters/etc. ? + # @return [Array] array of all property values (mix of types) + # @raise [fail???] if there is a provider and it is not suitable for the host this is evaluated for. def retrieve fail "Provider #{provider.class.name} is not functional on this host" if self.provider.is_a?(Puppet::Provider) and ! provider.class.suitable? @@ -692,15 +1031,22 @@ def retrieve result end + # ??? + # @todo what does this do? It seems to create a new Resource based on the result of calling #retrieve + # and if that is a Hash, else this method produces nil. + # @return [Puppet::Resource, nil] a new Resource, or nil, if this object did not produce a Hash as the + # result from #retrieve + # def retrieve_resource resource = retrieve resource = Resource.new(type, title, :parameters => resource) if resource.is_a? Hash resource end - # Get a hash of the current properties. Returns a hash with - # the actual property instance as the key and the current value - # as the, um, value. + # Returns a hash of the current properties and their values. + # If a resource is absent, it's value is the symbol `:absent` + # @return [Hash{Puppet::Property => Object}] mapping of property instance to its value + # def currentpropvalues # It's important to use the 'properties' method here, as it follows the order # in which they're defined in the class. It also guarantees that 'ensure' @@ -722,7 +1068,8 @@ def currentpropvalues end end - # Are we running in noop mode? + # Returns the `noop` run mode status of this. + # @return [Boolean] true if running in noop mode. def noop? # If we're not a host_config, we're almost certainly part of # Settings, and we want to ignore 'noop' @@ -735,11 +1082,15 @@ def noop? end end + # (see #noop?) def noop noop? end - # Retrieve all known instances. Either requires providers or must be overridden. + # Retrieves all known instances. + # @todo Retrieves them from where? Known to whom? + # Either requires providers or must be overridden. + # @raise [Puppet::DevError] when there are no providers and the implementation has not overridded this method. def self.instances raise Puppet::DevError, "#{self.name} has no providers and has not overridden 'instances'" if provider_hash.empty? @@ -769,7 +1120,10 @@ def self.instances end.flatten.compact end - # Return a list of one suitable provider per source, with the default provider first. + # Returns a list of one suitable provider per source, with the default provider first. + # @todo Needs better explanation; what does "source" mean in this context? + # @return [Array] list of providers + # def self.providers_by_source # Put the default provider first (can be nil), then the rest of the suitable providers. sources = [] @@ -782,7 +1136,11 @@ def self.providers_by_source end.compact end - # Convert a simple hash into a Resource instance. + # Converts a simple hash into a Resource instance. + # @todo as opposed to a complex hash? Other raised exceptions? + # @param [Hash{Symbol, String => Object}] resource attribute to value map to initialize the created resource from + # @return [Puppet::Resource] the resource created from the hash + # @raise [Puppet::Error] if a title is missing in the given hash def self.hash2resource(hash) hash = hash.inject({}) { |result, ary| result[ary[0].to_sym] = ary[1]; result } @@ -807,7 +1165,10 @@ def self.hash2resource(hash) resource end - # Create the path for logging and such. + # Creates the path for logging and such. + # @todo "and such?", what? + # @api private + # def pathbuilder if p = parent [p.pathbuilder, self.ref].flatten @@ -817,7 +1178,7 @@ def pathbuilder end ############################### - # Add all of the meta parameters. + # Add all of the meta-parameters. newmetaparam(:noop) do desc "Boolean flag indicating whether work should actually be done." @@ -1012,6 +1373,10 @@ def properties_to_audit(list) end end + # RelationshipMetaparam is an implementation supporting the meta-parameters `:require`, `:subscribe`, + # `:notify`, and `:before`. + # + # class RelationshipMetaparam < Puppet::Parameter class << self attr_accessor :direction, :events, :callback, :subclasses @@ -1023,6 +1388,7 @@ def self.inherited(sub) @subclasses << sub end + # @return [Array] turns attribute value(s) into list of resources def munge(references) references = [references] unless references.is_a?(Array) references.collect do |ref| @@ -1034,6 +1400,10 @@ def munge(references) end end + # Checks each reference to assert that what it references exists in the catalog. + # + # @raise [???fail] if the referenced resource can not be found + # @return [void] def validate_relationship @value.each do |ref| unless @resource.catalog.resource(ref.to_s) @@ -1043,14 +1413,19 @@ def validate_relationship end end - # Create edges from each of our relationships. :in - # relationships are specified by the event-receivers, and :out - # relationships are specified by the event generator. This - # way 'source' and 'target' are consistent terms in both edges - # and events -- that is, an event targets edges whose source matches - # the event's source. The direction of the relationship determines + # Creates edges for all relationships. + # The `:in` relationships are specified by the event-receivers, and `:out` + # relationships are specified by the event generator. + # @todo references to "event-receivers" and "event generator" means in this context - are those just + # the resources at the two ends of the relationship? + # This way 'source' and 'target' are consistent terms in both edges + # and events, i.e. an event targets edges whose source matches + # the event's source. The direction of the relationship determines # which resource is applied first and which resource is considered # to be the event generator. + # @return [Array] + # @raise [???fail] when a reference can not be resolved + # def to_edges @value.collect do |reference| reference.catalog = resource.catalog @@ -1089,6 +1464,8 @@ def to_edges end end + # @todo document this, have no clue what this does... it retuns "RelationshipMetaparam.subclasses" + # def self.relationship_params RelationshipMetaparam.subclasses end @@ -1239,15 +1616,33 @@ class { 'foo': stage => 'pre' } # Add the feature handling module. extend Puppet::Util::ProviderFeatures + # The provider that has been selected for the instance of the resource type. + # @return [Puppet::Provider,nil] the selected provider or nil, if none has been selected + # attr_reader :provider # the Type class attribute accessors class << self + # The loader of providers to use when loading providers from disk. + # Although it looks like this attribute provides a way to operate with different loaders of + # providers that is not the case; the attribute is written when a new type is created, + # and should not be changed thereafter. + # @api private + # attr_accessor :providerloader + + # @todo Don't know if this is a name, or a reference to a Provider instance (now marked up as an instance + # of Provider. + # @return [Puppet::Provider, nil] The default provider for this type, or nil if non is defines + # attr_writer :defaultprovider end - # Find the default provider. + # The default provider, or the most suitable provider if no default provider was set. + # @note a warning will be issued if no default provider has been configured and a search for the most + # suitable provider returns more than one equally suitable provider. + # @return [Puppet::Provider, nil] the default or most suitable provider, or nil if no provider was found + # def self.defaultprovider return @defaultprovider if @defaultprovider @@ -1270,16 +1665,26 @@ def self.defaultprovider @defaultprovider = defaults.shift unless defaults.empty? end + # @return [Hash{??? => Puppet::Provider}] Returns a hash of WHAT EXACTLY for the given type + # @todo what goes into this hash? def self.provider_hash_by_type(type) @provider_hashes ||= {} @provider_hashes[type] ||= {} end + # @return [Hash{ ??? => Puppet::Provider}] Returns a hash of WHAT EXACTLY for this type. + # @see provider_hash_by_type method to get the same for some other type def self.provider_hash Puppet::Type.provider_hash_by_type(self.name) end - # Retrieve a provider by name. + # Returns the provider having the given name. + # This will load a provider if it is not already loaded. The returned provider is the first found provider + # having the given name, where "first found" semantics is defined by the {providerloader} in use. + # + # @param name [String] the name of the provider to get + # @return [Puppet::Provider, nil] the found provider, or nil if no provider of the given name was found + # def self.provider(name) name = name.intern @@ -1288,19 +1693,41 @@ def self.provider(name) provider_hash[name] end - # Just list all of the providers. + # Returns a list of loaded providers by name. + # This method will not load/search for available providers. + # @return [Array] list of loaded provider names + # def self.providers provider_hash.keys end + # Returns true if the given name is a reference to a provider and if this is a suitable provider for + # this type. + # @todo How does the provider know if it is suitable for the type? Is it just suitable for the platform/ + # environment where this method is executing? + # @param name [String] the name of the provider for which validity is checked + # @return [Boolean] true if the given name references a provider that is suitable + # def self.validprovider?(name) name = name.intern (provider_hash.has_key?(name) && provider_hash[name].suitable?) end - # Create a new provider of a type. This method must be called - # directly on the type that it's implementing. + # Creates a new provider of a type. + # This method must be called directly on the type that it's implementing. + # @todo Fix Confusing Explanations! + # Is this a new provider of a Type (metatype), or a provider of an instance of Type (a resource), or + # a Provider (the implementation of a Type's behavior). CONFUSED. It calls magically named methods like + # "providify" ... + # @param name [String, Symbol] the name of the WHAT? provider? type? + # @param options [Hash{Symbol => Object}] a hash of options, used by this method, and passed on to {#genclass}, (see + # it for additional options to pass). + # @option options [Puppet::Provider] :parent the parent provider (what is this?) + # @option options [Puppet::Type] :resource_type the resource type, defaults to this type if unspecified + # @return [Puppet::Provider] a provider ??? + # @raise [Puppet::DevError] when the parent provider could not be found. + # def self.provide(name, options = {}, &block) name = name.intern @@ -1342,8 +1769,9 @@ def self.provide(name, options = {}, &block) provider end - # Make sure we have a :provider parameter defined. Only gets called if there - # are providers. + # Ensures there is a `:provider` parameter defined. + # Should only be called if there are providers. + # @return [void] def self.providify return if @paramhash.has_key? :provider @@ -1361,10 +1789,14 @@ def self.providify # This is so we can refer back to the type to get a list of # providers for documentation. class << self + # The reference to a parent type for the parameter `:provider` used to get a list of + # providers for documentation purposes. + # attr_accessor :parenttype end - # We need to add documentation for each provider. + # Provides the ability to add documentation to a provider. + # def self.doc # Since we're mixing @doc with text from other sources, we must normalize # its indentation with scrub. But we don't need to manually scrub the @@ -1376,6 +1808,8 @@ def self.doc }.join end + # @todo this does what? where and how? + # @returns [String] the name of the provider defaultto { prov = @resource.class.defaultprovider prov.name if prov @@ -1404,6 +1838,9 @@ def self.doc end.parenttype = self end + # @todo this needs a better explanation + # Removes the implementation class of a given provider. + # @return [Object] returns what {Puppet::Util::ClassGen#rmclass} returns def self.unprovide(name) if @defaultprovider and @defaultprovider.name == name @defaultprovider = nil @@ -1412,7 +1849,12 @@ def self.unprovide(name) rmclass(name, :hash => provider_hash, :prefix => "Provider") end - # Return an array of all of the suitable providers. + # Returns a list of suitable providers for the given type. + # A call to this method will load all providers if not already loaded and ask each if it is + # suitable - those that are are included in the result. + # @note This method also does some special processing which rejects a provider named `:fake` (for testing purposes). + # @return [Array] Returns an array of all suitable providers. + # def self.suitableprovider providerloader.loadall if provider_hash.empty? provider_hash.find_all { |name, provider| @@ -1422,6 +1864,9 @@ def self.suitableprovider }.reject { |p| p.name == :fake } # For testing end + # @return [Boolean] Returns true if this is something else than a `:provider`, or if it + # is a provider and it is suitable, or if there is a default provider. Otherwise, false is returned. + # def suitable? # If we don't use providers, then we consider it suitable. return true unless self.class.paramclass(:provider) @@ -1440,6 +1885,16 @@ def suitable? false end + # Sets the provider to the given provider/name. + # @overload provider=(name) + # Sets the provider to the result of resolving the name to an instance of Provider. + # @param name [String] the name of the provider + # @overload provider=(provider) + # Sets the provider to the given instances of Provider. + # @param provider [Puppet::Provider] the provider to set + # @return [Puppet::Provider] the provider set + # @raise [ArgumentError] if the provider could not be found/resolved. + # def provider=(name) if name.is_a?(Puppet::Provider) @provider = name @@ -1454,15 +1909,30 @@ def provider=(name) ############################### # All of the relationship code. - # Specify a block for generating a list of objects to autorequire. This - # makes it so that you don't have to manually specify things that you clearly - # require. + + # Adds a block producing a single name (or list of names) of the given resource type name to autorequire. + # @example Autorequire the files File['foo', 'bar'] + # autorequire( 'file', {|| ['foo', 'bar'] }) + # + # @todo original = _"Specify a block for generating a list of objects to autorequire. + # This makes it so that you don't have to manually specify things that you clearly require."_ + # @param name [String] the name of a type of which one or several resources should be autorequired e.g. "file" + # @yield [ ] a block returning list of names of given type to auto require + # @yieldreturn [String, Array] one or several resource names for the named type + # @return [void] + # @dsl type + # @api public + # def self.autorequire(name, &block) @autorequires ||= {} @autorequires[name] = block end - # Yield each of those autorequires in turn, yo. + # Provides iteration over added auto-requirements (see {autorequire}). + # @yieldparam type [String] the name of the type to autoriquire an instance of + # @yieldparam block [Proc] a block producing one or several dependencies to auto require (see {autorequire}). + # @yieldreturn [void] + # @return [void] def self.eachautorequire @autorequires ||= {} @autorequires.each { |type, block| @@ -1470,8 +1940,13 @@ def self.eachautorequire } end - # Figure out of there are any objects we can automatically add as - # dependencies. + # Adds dependencies to the catalog from added autorequirements. + # See {autorequire} for how to add an auto-requirement. + # @todo needs details - see the param rel_catalog, and type of this param + # @param rel_catalog [Puppet::Catalog, nil] the catalog to add dependencies to. Defaults to the + # catalog (TODO: what is the type of the catalog). + # @raise [Puppet::DevError] if there is no catalog + # def autorequire(rel_catalog = nil) rel_catalog ||= catalog raise(Puppet::DevError, "You cannot add relationships without a catalog") unless rel_catalog @@ -1502,7 +1977,12 @@ def autorequire(rel_catalog = nil) reqs end - # Build the dependencies associated with an individual object. + # Builds the dependencies associated with an individual object. + # @todo Which object is the "individual object", as opposed to "object as a group?" or should it simply + # be "this object" as in "this resource" ? + # @todo Does this method "build dependencies" or "build what it depends on" ... CONFUSING + # + # @return [Array] list of WHAT? resources? edges? def builddepends # Handle the requires self.class.relationship_params.collect do |klass| @@ -1512,21 +1992,40 @@ def builddepends end.flatten.reject { |r| r.nil? } end - # Define the initial list of tags. + # Sets the initial list of tags... + # @todo The initial list of tags, that ... that what? + # @return [void] ??? def tags=(list) tag(self.class.name) tag(*list) end - # Types (which map to resources in the languages) are entirely composed of - # attribute value pairs. Generally, Puppet calls any of these things an - # 'attribute', but these attributes always take one of three specific - # forms: parameters, metaparams, or properties. - - # In naming methods, I have tried to consistently name the method so - # that it is clear whether it operates on all attributes (thus has 'attr' in - # the method name, or whether it operates on a specific type of attributes. + # @comment - these two comments were floating around here, and turned up as documentation + # for the attribute "title", much to my surprise and amusement. Clearly these comments + # are orphaned ... I think they can just be removed as what they say should be covered + # by the now added yardoc. (Yo! to quote some of the other actual awsome specific comments applicable + # to objects called from elsewhere, or not. ;-) + # + # @comment Types (which map to resources in the languages) are entirely composed of + # attribute value pairs. Generally, Puppet calls any of these things an + # 'attribute', but these attributes always take one of three specific + # forms: parameters, metaparams, or properties. + + # @comment In naming methods, I have tried to consistently name the method so + # that it is clear whether it operates on all attributes (thus has 'attr' in + # the method name, or whether it operates on a specific type of attributes. + + + # The title attribute of WHAT ??? + # @todo Figure out what this is the title attribute of (it appears on line 1926 currently). + # @return [String] the title attr_writer :title + + # The noop attribute of WHAT ??? does WHAT??? + # @todo Figure out what this is the noop attribute of (it appears on line 1931 currently). + # @return [???] the noop WHAT ??? (mode? if so of what, or noop for an instance of the type, or for all + # instances of a type, or for what??? + # attr_writer :noop include Enumerable @@ -1535,9 +2034,14 @@ def tags=(list) public - # the Type class attribute accessors + # The Type class attribute accessors class << self + # @return [String] the name of the resource type; e.g., "File" + # attr_reader :name + + # @return [Boolean] true if the type should send itself a refresh event on change. + # attr_accessor :self_refresh include Enumerable, Puppet::Util::ClassGen include Puppet::MetaType::Manager @@ -1546,7 +2050,9 @@ class << self include Puppet::Util::Logging end - # all of the variables that must be initialized for each subclass + # Initializes all of the variables that must be initialized for each subclass. + # @todo Does the explanation make sense? + # @return [void] def self.initvars # all of the instances of this class @objects = Hash.new @@ -1574,6 +2080,11 @@ def self.initvars end + # Returns the name of this type (if specified) or the parent type #to_s. + # The returned name is on the form "Puppet::Type::", where the first letter of name is + # capitalized. + # @return [String] the fully qualified name Puppet::Type:: where the first letter of name is captialized + # def self.to_s if defined?(@name) "Puppet::Type::#{@name.to_s.capitalize}" @@ -1582,26 +2093,42 @@ def self.to_s end end - # Create a block to validate that our object is set up entirely. This will - # be run before the object is operated on. + # Creates a `validate` method that is used to validate a resource before it is operated on. + # The validation should raise exceptions if the validation finds errors. (It is not recommended to + # issue warnings as this typically just ends up in a logfile - you should fail if a validation fails). + # The easiest way to raise an appropriate exception is to call the method {Puppet::Util::Errors.fail} with + # the message as an argument. + # + # @yield [ ] a required block called with self set to the instance of a Type class representing a resource. + # @return [void] + # @dsl type + # @api public + # def self.validate(&block) define_method(:validate, &block) #@validate = block end - # Origin information. - attr_accessor :file, :line + # @return [String] The file from which this type originates from + attr_accessor :file + + # @return [Integer] The line in {#file} from which this type originates from + attr_accessor :line - # The catalog that this resource is stored in. + # @todo what does this mean "this resource" (sounds like this if for an instance of the type, not the meta Type), + # but not sure if this is about the catalog where the meta Type is included) + # @return [??? TODO] The catalog that this resource is stored in. attr_accessor :catalog - # is the resource exported + # @return [Boolean] Flag indicating if this type is exported attr_accessor :exported - # is the resource virtual (it should not :-)) + # @return [Boolean] Flag indicating if the type is virtual (it should not be). attr_accessor :virtual - # create a log at specified level + # Creates a log entry with the given message at the log level specified by the parameter `loglevel` + # @return [void] + # def log(msg) Puppet::Util::Log.create( @@ -1619,9 +2146,24 @@ def log(msg) public + # @return [Hash] hash of parameters originally defined + # @api private attr_reader :original_parameters - # initialize the type instance + # Creates an instance of Type from a hash or a {Puppet::Resource}. + # @todo Unclear if this is a new Type or a new instance of a given type (the initialization ends + # with calling validate - which seems like validation of an instance of a given type, not a new + # meta type. + # + # @todo Explain what the Hash and Resource are. There seems to be two different types of + # resources; one that causes the title to be set to resource.title, and one that + # causes the title to be resource.ref ("for components") - what is a component? + # + # @overaload initialize(hsh) + # @param hsh [Hash] + # @overload initialize(resource) + # @param resource [Puppet:Resource] + # def initialize(resource) resource = self.class.hash2resource(resource) unless resource.is_a?(Puppet::Resource) @@ -1658,12 +2200,32 @@ def initialize(resource) private - # Set our resource's name. + # Sets the name of the resource from a hash containing a mapping of `name_var` to value. + # Sets the value of the property/parameter appointed by the `name_var` (if it is defined). The value set is + # given by the corresponding entry in the given hash - e.g. if name_var appoints the name `:path` the value + # of `:path` is set to the value at the key `:path` in the given hash. As a side effect this key/value is then + # removed from the given hash. + # + # @note This method mutates the given hash by removing the entry with a key equal to the value + # returned from name_var! + # @param hash [Hash] a hash of what + # @return [void] def set_name(hash) self[name_var] = hash.delete(name_var) if name_var end - # Set all of the parameters from a hash, in the appropriate order. + # Sets parameters from the given hash. + # Values are set in _attribute order_ i.e. higher priority attributes before others, otherwise in + # the order they were specified (as opposed to just setting them in the order they happen to appear in + # when iterating over the given hash). + # + # Attributes that are not included in the given hash are set to their default value. + # + # @todo Is this description accurate? Is "ensure" an example of such a higher priority attribute? + # @return [void] + # @raise [Puppet::DevError] when impossible to set the value due to some problem + # @raise [ArgumentError, TypeError, Puppet::Error] when faulty arguments have been passed + # def set_parameters(hash) # Use the order provided by allattrs, but add in any # extra attributes from the resource so we get failures @@ -1694,7 +2256,14 @@ def set_parameters(hash) public - # Set up all of our autorequires. + # Finishes any outstanding processing. + # This method should be called as a final step in setup, + # to allow the parameters that have associated auto-require needs to be processed. + # + # @todo what is the expected sequence here - who is responsible for calling this? When? + # Is the returned type correct? + # @return [Array] the validated list/set of attributes + # def finish # Make sure all of our relationships are valid. Again, must be done # when the entire catalog is instantiated. @@ -1705,13 +2274,22 @@ def finish end.flatten.reject { |r| r.nil? } end - # For now, leave the 'name' method functioning like it used to. Once 'title' - # works everywhere, I'll switch it. + # @comment For now, leave the 'name' method functioning like it used to. Once 'title' + # works everywhere, I'll switch it. + # Returns the resource's name + # @todo There is a comment in source that this is not quite the same as ':title' and that a switch should + # be made... + # @return [String] the name of a resource def name self[:name] end - # Look up our parent in the catalog, if we have one. + # Returns the parent of this in the catalog. + # In case of an erroneous catalog where multiple parents have been produced, the first found (non deterministic) + # parent is returned. + # @return [???, nil] WHAT (which types can be the parent of a resource in a catalog?), or nil if there + # is no catalog. + # def parent return nil unless catalog @@ -1727,24 +2305,36 @@ def parent @parent end - # Return the "type[name]" style reference. + # Returns a reference to this as a string in "Type[name]" format. + # @return [String] a reference to this object on the form 'Type[name]' + # def ref # memoizing this is worthwhile ~ 3 percent of calls are the "first time # around" in an average run of Puppet. --daniel 2012-07-17 @ref ||= "#{self.class.name.to_s.capitalize}[#{self.title}]" end + # (see self_refresh) + # @todo check that meaningful yardoc is produced - this method delegates to "self.class.self_refresh" + # @return [Boolean] - ??? returns true when ... what? + # def self_refresh? self.class.self_refresh end - # Mark that we're purging. + # Marks the object as "being purged". + # This method is used by transactions to forbid deletion when there are dependencies. + # @todo what does this mean; "mark that we are purging" (purging what from where). How to use/when? + # Is this internal API in transactions? + # @see purging? def purging @purging = true end - # Is this resource being purged? Used by transactions to forbid - # deletion when there are dependencies. + # Returns whether this resource is being purged or not. + # This method is used by transactions to forbid deletion when there are dependencies. + # @return [Boolean] the current "purging" state + # def purging? if defined?(@purging) @purging @@ -1753,8 +2343,15 @@ def purging? end end - # Retrieve the title of an object. If no title was set separately, - # then use the object's name. + # Returns the title of this object, or it's name if title was not explicetly set. + # If the title is not already set, it will be computed by looking up the {#name_var} and using + # that value as the title. + # @todo it is somewhat confusing that if the name_var is a valid parameter, it is assumed to + # be the name_var called :name, but if it is a property, it uses the name_var. + # It is further confusing as Type in some respects supports multiple namevars. + # + # @return [String] Returns the title of this object, or it's name if title was not explicetly set. + # @raise [??? devfail] if title is not set, and name_var can not be found. def title unless @title if self.class.validparameter?(name_var) @@ -1769,11 +2366,16 @@ def title @title end - # convert to a string + # Produces a reference to this in reference format. + # @see #ref + # def to_s self.ref end + # @todo What to resource? Which one of the resource forms is prroduced? returned here? + # @return [??? Resource] a resource that WHAT??? + # def to_resource resource = self.retrieve_resource resource.tag(*self.tags) @@ -1790,13 +2392,21 @@ def to_resource resource end + # @return [Boolean] Returns whether the resource is virtual or not def virtual?; !!@virtual; end + # @return [Boolean] Returns whether the resource is exported or not def exported?; !!@exported; end + # @return [Boolean] Returns whether the resource is applicable to `:device` + # @todo Explain what this means + # @api private def appliable_to_device? self.class.can_apply_to(:device) end + # @return [Boolean] Returns whether the resource is applicable to `:host` + # @todo Explain what this means + # @api private def appliable_to_host? self.class.can_apply_to(:host) end diff --git a/lib/puppet/util/classgen.rb b/lib/puppet/util/classgen.rb index 7993e695beb..e03bf2ab707 100644 --- a/lib/puppet/util/classgen.rb +++ b/lib/puppet/util/classgen.rb @@ -3,51 +3,66 @@ class ConstantAlreadyDefined < Error; end class SubclassAlreadyDefined < Error; end end +# This is a utility module for generating classes. +# @api public +# module Puppet::Util::ClassGen include Puppet::Util::MethodHelper include Puppet::Util - # Create a new subclass. Valid options are: - # * :array: An array of existing classes. If specified, the new - # class is added to this array. - # * :attributes: A hash of attributes to set before the block is - # evaluated. - # * :block: The block to evaluate in the context of the class. - # You can also just pass the block normally, but it will still be evaluated - # with class_eval. - # * :constant: What to set the constant as. Defaults to the - # capitalized name. - # * :hash: A hash of existing classes. If specified, the new - # class is added to this hash, and it is also used for overwrite tests. - # * :overwrite: Whether to overwrite an existing class. - # * :parent: The parent class for the generated class. Defaults to - # self. - # * :prefix: The constant prefix. Default to nothing; if specified, - # the capitalized name is appended and the result is set as the constant. + # Create a new class. + # @param name [String] the name of the generated class + # @param options [Hash] a hash of options + # @option options [Array] :array if specified, the generated class is appended to this array + # @option options [Hash<{String => Object}>] :attributes a hash that is applied to the generated class + # by calling setter methods corresponding to this hash's keys/value pairs. This is done before the given + # block is evaluated. + # @option options [Proc] :block a block to evaluate in the context of the class (this block can be provided + # this way, or as a normal yield block). + # @option options [String] :constant (name with first letter capitalized) what to set the constant that references + # the generated class to. + # @option options [Hash] :hash a hash of existing classes that this class is appended to (name => class). + # This hash must be specified if the `:overwrite` option is set to `true`. + # @option options [Boolean] :overwrite whether an overwrite of an existing class should be allowed (requires also + # defining the `:hash` with existing classes as the test is based on the content of this hash). + # @option options [Class] :parent (self) the parent class of the generated class. + # @option options [String] ('') :prefix the constant prefix to prepend to the constant name referencing the + # generated class. + # @return [Class] the generated class + # def genclass(name, options = {}, &block) genthing(name, Class, options, block) end - # Create a new module. Valid options are: - # * :array: An array of existing classes. If specified, the new - # class is added to this array. - # * :attributes: A hash of attributes to set before the block is - # evaluated. - # * :block: The block to evaluate in the context of the class. - # You can also just pass the block normally, but it will still be evaluated - # with class_eval. - # * :constant: What to set the constant as. Defaults to the - # capitalized name. - # * :hash: A hash of existing classes. If specified, the new - # class is added to this hash, and it is also used for overwrite tests. - # * :overwrite: Whether to overwrite an existing class. - # * :prefix: The constant prefix. Default to nothing; if specified, + # Creates a new module. + # @param name [String] the name of the generated module + # @param optinos [Hash] hash with options + # @option options [Array] :array if specified, the generated class is appended to this array + # @option options [Hash<{String => Object}>] :attributes a hash that is applied to the generated class + # by calling setter methods corresponding to this hash's keys/value pairs. This is done before the given + # block is evaluated. + # @option options [Proc] :block a block to evaluate in the context of the class (this block can be provided + # this way, or as a normal yield block). + # @option options [String] :constant (name with first letter capitalized) what to set the constant that references + # the generated class to. + # @option options [Hash] :hash a hash of existing classes that this class is appended to (name => class). + # This hash must be specified if the `:overwrite` option is set to `true`. + # @option options [Boolean] :overwrite whether an overwrite of an existing class should be allowed (requires also + # defining the `:hash` with existing classes as the test is based on the content of this hash). # the capitalized name is appended and the result is set as the constant. + # @option options [String] ('') :prefix the constant prefix to prepend to the constant name referencing the + # generated class. + # @return [Module] the generated Module def genmodule(name, options = {}, &block) genthing(name, Module, options, block) end - # Remove an existing class + # Removes an existing class. + # @param name [String] the name of the class to remove + # @param options [Hash] options + # @option options [Hash] :hash a hash of existing classes from which the class to be removed is also removed + # @return [Boolean] whether the class was removed or not + # def rmclass(name, options) options = symbolize_options(options) const = genconst_string(name, options) @@ -68,7 +83,8 @@ def rmclass(name, options) private - # Generate the constant to create or remove. + # Generates the constant to create or remove. + # @api private def genconst_string(name, options) unless const = options[:constant] prefix = options[:prefix] || "" @@ -80,6 +96,7 @@ def genconst_string(name, options) # This does the actual work of creating our class or module. It's just a # slightly abstract version of genclass. + # @api private def genthing(name, type, options, block) options = symbolize_options(options) @@ -128,6 +145,8 @@ class << self # of which class hierarchy it polls for nested namespaces # # See http://redmine.ruby-lang.org/issues/show/1915 + # @api private + # def is_constant_defined?(const) if ::RUBY_VERSION =~ /1.9/ const_defined?(const, false) @@ -137,6 +156,8 @@ def is_constant_defined?(const) end # Handle the setting and/or removing of the associated constant. + # @api private + # def handleclassconst(klass, name, options) const = genconst_string(name, options) @@ -155,6 +176,8 @@ def handleclassconst(klass, name, options) end # Perform the initializations on the class. + # @api private + # def initclass(klass, options) klass.initvars if klass.respond_to? :initvars @@ -178,11 +201,13 @@ def initclass(klass, options) end # Convert our name to a constant. + # @api private def name2const(name) name.to_s.capitalize end # Store the class in the appropriate places. + # @api private def storeclass(klass, klassname, options) if hash = options[:hash] if hash.include? klassname and ! options[:overwrite] diff --git a/lib/puppet/util/execution.rb b/lib/puppet/util/execution.rb index c0baec0f349..5f008930319 100644 --- a/lib/puppet/util/execution.rb +++ b/lib/puppet/util/execution.rb @@ -6,16 +6,31 @@ module Puppet class ExecutionFailure < Puppet::Error end +# This module defines methods for execution of system commands. It is intented for inclusion +# in classes that needs to execute system commands. +# @api public module Util::Execution - # Execute the provided command with STDIN connected to a pipe, yielding the - # pipe object. That allows data to be fed to that subprocess. + # Executes the provided command with STDIN connected to a pipe, yielding the + # pipe object. + # This allows data to be fed to the subprocess. # # The command can be a simple string, which is executed as-is, or an Array, - # which is treated as a set of command arguments to pass through.# + # which is treated as a set of command arguments to pass through. # # In all cases this is passed directly to the shell, and STDOUT and STDERR # are connected together during execution. + # @param command [String, Array] the command to execute as one string, or as parts in an array. + # the parts of the array are joined with one separating space between each entry when converting to + # the command line string to execute. + # @param failonfail [Boolean] (true) if the execution should fail with Exception on failure or not. + # @yield [pipe] to a block executing a subprocess + # @yieldparam pipe [IO] the opened pipe + # @yieldreturn [String] the output to return + # @raise [ExecutionFailure] if the executed chiled process did not exit with status == 0 and `failonfail` is + # `true`. + # @return [String] a string with the output from the subprocess executed by the given block + # def self.execpipe(command, failonfail = true) if respond_to? :debug debug "Executing '#{command}'" @@ -44,6 +59,9 @@ def self.execpipe(command, failonfail = true) output end + # Wraps execution of {execute} with mapping of exception to given exception (and output as argument). + # @raise [exception] under same conditions as {execute}, but raises the given `exception` with the output as argument + # @return (see execute) def self.execfail(command, exception) output = execute(command) return output @@ -51,35 +69,37 @@ def self.execfail(command, exception) raise exception, output end + # Default empty options for {execute} + NoOptionsSpecified = {} - # Execute the desired command, and return the status and output. + # Executes the desired command, and return the status and output. # def execute(command, options) - # [command] an Array or String representing the command to execute. If it is + # @param command [Array, String] the command to execute. If it is # an Array the first element should be the executable and the rest of the # elements should be the individual arguments to that executable. - # [options] a Hash optionally containing any of the following keys: - # :failonfail (see below) -- if this value is set to true, then this method will raise an error if the - # command is not executed successfully. - # :uid (default nil) -- the user id of the user that the process should be run as - # :gid (default nil) -- the group id of the group that the process should be run as - # :combine (see below) -- sets whether or not to combine stdout/stderr in the output - # :stdinfile (default nil) -- sets a file that can be used for stdin. Passing a string for stdin is not currently - # supported. - # :squelch (default false) -- if true, ignore stdout / stderr completely - # :override_locale (default true) -- by default (and if this option is set to true), we will temporarily override - # the user/system locale to "C" (via environment variables LANG and LC_*) while we are executing the command. - # This ensures that the output of the command will be formatted consistently, making it predictable for parsing. - # Passing in a value of false for this option will allow the command to be executed using the user/system locale. - # :custom_environment (default {}) -- a hash of key/value pairs to set as environment variables for the duration - # of the command + # @param options [Hash] a Hash of options + # @option options [Boolean] :failonfail if this value is set to true, then this method will raise an error if the + # command is not executed successfully. + # @option options [?] :uid (nil) the user id of the user that the process should be run as + # @option options [?] :gid (nil) the group id of the group that the process should be run as + # @option options [Boolean] :combine sets whether or not to combine stdout/stderr in the output + # @option options [String] :stdinfile (nil) sets a file that can be used for stdin. Passing a string for stdin is not currently + # supported. + # @option options [Boolean] :squelch (true) if true, ignore stdout / stderr completely. + # @option options [Boolean] :override_locale (true) by default (and if this option is set to true), we will temporarily override + # the user/system locale to "C" (via environment variables LANG and LC_*) while we are executing the command. + # This ensures that the output of the command will be formatted consistently, making it predictable for parsing. + # Passing in a value of false for this option will allow the command to be executed using the user/system locale. + # @option options [Hash<{String => String}>] :custom_environment ({}) a hash of key/value pairs to set as environment variables for the duration + # of the command. + # @return [String] output as specified by options + # @note Unfortunately, the default behavior for failonfail and combine (since + # 0.22.4 and 0.24.7, respectively) depend on whether options are specified + # or not. If specified, then failonfail and combine default to false (even + # when the options specified are neither failonfail nor combine). If no + # options are specified, then failonfail and combine default to true. + # @comment See commits efe9a833c and d32d7f30 # - # Unfortunately, the default behavior for failonfail and combine (since - # 0.22.4 and 0.24.7, respectively) depend on whether options are specified - # or not. If specified, then failonfail and combine default to false (even - # when the options specified are neither failonfail nor combine). If no - # options are specified, then failonfail and combine default to true. See - # commits efe9a833c and d32d7f30 - NoOptionsSpecified = {} def self.execute(command, options = NoOptionsSpecified) # specifying these here rather than in the method signature to allow callers to pass in a partial # set of overrides without affecting the default values for options that they don't pass in @@ -147,9 +167,10 @@ def self.execute(command, options = NoOptionsSpecified) output end - # get the path to the ruby executable (available via Config object, even if - # it's not in the PATH... so this is slightly safer than just using - # Puppet::Util.which) + # Returns the path to the ruby executable (available via Config object, even if + # it's not in the PATH... so this is slightly safer than just using Puppet::Util.which) + # @return [String] the path to the Ruby executable + # def self.ruby_path() File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name'] + RbConfig::CONFIG['EXEEXT']). @@ -162,7 +183,10 @@ class << self end - # this is private method, see call to private_class_method after method definition + # This is private method. + # @comment see call to private_class_method after method definition + # @api private + # def self.execute_posix(command, options, stdin, stdout, stderr) child_pid = Puppet::Util.safe_posix_fork(stdin, stdout, stderr) do @@ -208,7 +232,10 @@ def self.execute_posix(command, options, stdin, stdout, stderr) private_class_method :execute_posix - # this is private method, see call to private_class_method after method definition + # This is private method. + # @comment see call to private_class_method after method definition + # @api private + # def self.execute_windows(command, options, stdin, stdout, stderr) command = command.map do |part| part.include?(' ') ? %Q["#{part.gsub(/"/, '\"')}"] : part @@ -222,7 +249,10 @@ def self.execute_windows(command, options, stdin, stdout, stderr) private_class_method :execute_windows - # this is private method, see call to private_class_method after method definition + # This is private method. + # @comment see call to private_class_method after method definition + # @api private + # def self.wait_for_output(stdout) # Make sure the file's actually been written. This is basically a race # condition, and is probably a horrible way to handle it, but, well, oh diff --git a/lib/puppet/util/provider_features.rb b/lib/puppet/util/provider_features.rb index 7d61ae55cc9..f2f7a10285e 100644 --- a/lib/puppet/util/provider_features.rb +++ b/lib/puppet/util/provider_features.rb @@ -2,10 +2,15 @@ require 'puppet/util/methodhelper' require 'puppet/util/docs' require 'puppet/util' +# This module models provider features and handles checking whether the features +# are present. +# @todo Unclear what is api and what is private in this module. +# module Puppet::Util::ProviderFeatures include Puppet::Util::Docs - # The class that models the features and handles checking whether the features + # This class models provider features and handles checking whether the features # are present. + # @todo Unclear what is api and what is private in this class class ProviderFeature include Puppet::Util include Puppet::Util::MethodHelper @@ -13,6 +18,9 @@ class ProviderFeature attr_accessor :name, :docs, :methods # Are all of the requirements met? + # Requirements are checked by checking if feature predicate methods have been generated - see {#methods_available?}. + # @param obj [Object, Class] the object or class to check if requirements are met + # @return [Boolean] whether all requirements for this feature are met or not. def available?(obj) if self.methods return !!methods_available?(obj) @@ -33,7 +41,9 @@ def initialize(name, docs, hash) private - # Are all of the required methods available? + # Checks whether all feature predicate methods are available. + # @param obj [Object, Class] the object or class to check if feature predicates are available or not. + # @return [Boolean] Returns whether all of the required methods are available or not in the given object. def methods_available?(obj) methods.each do |m| if obj.is_a?(Class) @@ -46,9 +56,11 @@ def methods_available?(obj) end end - # Define one or more features. At a minimum, features require a name + # Defines one feature. + # At a minimum, a feature requires a name # and docs, and at this point they should also specify a list of methods # required to determine if the feature is present. + # @todo How methods that determine if the feature is present are specified. def feature(name, docs, hash = {}) @features ||= {} raise(Puppet::DevError, "Feature #{name} is already defined") if @features.include?(name) @@ -64,7 +76,7 @@ def feature(name, docs, hash = {}) end end - # Return a hash of all feature documentation. + # @return [String] Returns a string with documentation covering all features. def featuredocs str = "" @features ||= {} @@ -94,14 +106,14 @@ def featuredocs str end - # Return a list of features. + # @return [Array] Returns a list of features. def features @features ||= {} @features.keys end - # Generate a module that sets up the boolean methods to test for given - # features. + # Generates a module that sets up the boolean predicate methods to test for given features. + # def feature_module unless defined?(@feature_module) @features ||= {} @@ -158,7 +170,11 @@ def feature_module @feature_module end - # Return the actual provider feature instance. Really only used for testing. + # @return [ProviderFeature] Returns a provider feature instance by name. + # @param name [String] the name of the feature to return + # @note Should only be used for testing. + # @api private + # def provider_feature(name) return nil unless defined?(@features) diff --git a/yardoc/templates/default/method_details/html/method_signature.erb b/yardoc/templates/default/method_details/html/method_signature.erb new file mode 100644 index 00000000000..5416782d6c9 --- /dev/null +++ b/yardoc/templates/default/method_details/html/method_signature.erb @@ -0,0 +1,25 @@ +

+ <% if object.tags(:overload).size == 1 %> + <%= signature(object.tag(:overload), false) + format_method_detail_extras(object) %> + <% elsif object.tags(:overload).size > 1 %> + <% object.tags(:overload).each do |overload| %> + <%= signature(overload, false) + format_method_detail_extras(object) %> + <% end %> + <% else %> + <%= signature(object, false) + format_method_detail_extras(object)%> + <% end %> + + <% if object.aliases.size > 0 %> + Also known as: + <%= object.aliases.map {|o| + "" + h(o.name.to_s) + "" }.join(", ") %> + + <% end %> + + <% if owner != object.namespace %> + + Originally defined in <%= object.namespace.type %> + <%= linkify object, owner.relative_path(object.namespace) %> + + <% end %> +

\ No newline at end of file diff --git a/yardoc/templates/default/method_details/html/setup.rb b/yardoc/templates/default/method_details/html/setup.rb new file mode 100644 index 00000000000..3de2542b91a --- /dev/null +++ b/yardoc/templates/default/method_details/html/setup.rb @@ -0,0 +1,37 @@ +def init + super +end + +def format_method_detail_extras(object) + result = "" + if object + if object.respond_to?(:visibility) + if object.visibility != :public + result << "#{object.visibility}" + end + end + if object.has_tag?(:abstract) + result << 'abstract' + end + if object.has_tag?(:deprecated) + result << 'deprecated' + end + if object.respond_to?(:visibility) + if object.has_tag?(:api) && object.tag(:api).text == 'private' && object.visibility != :private + result << 'private' + end + else + if object.has_tag?(:api) && object.tag(:api).text == 'private' + result << 'private' + end + end + if object.has_tag?(:dsl) + result << 'DSL' + end + end + # separate the extras with one space + if result != "" + result = " " + result + end + result +end diff --git a/yardoc/templates/default/module/html/item_summary.erb b/yardoc/templates/default/module/html/item_summary.erb new file mode 100644 index 00000000000..a42c45f4bea --- /dev/null +++ b/yardoc/templates/default/module/html/item_summary.erb @@ -0,0 +1,47 @@ +<% +# This is a copy of the original erb file with the same name. It adds "DSL" marker. +# It also removes the double output of "private" when being both api private and private +%> +
  • + + <% if @item.tags(:overload).size == 1 %> + <%= signature(@item.tag(:overload), true, !@item.attr_info) %> + <% else %> + <%= signature(@item, true, false, !@item.attr_info) %> + <% end %> + + <% if @item.aliases.size > 0 %> + (also: <%= @item.aliases.map {|o| h(o.name(true)) }.join(", ") %>) + <% end %> + + <% if object != @item.namespace %> + + <%= @item.namespace.type == :class ? 'inherited' : (@item.scope == :class ? 'extended' : 'included') %> + from <%= linkify @item, object.relative_path(@item.namespace) %> + + <% end %> + <% if @item.constructor? %> + constructor + <% end %> + <% if @item.has_tag?(:dsl) %> + DSL + <% end %> + <% if rw = @item.attr_info %> + <% if !run_verifier([rw[:read]].compact).empty? && run_verifier([rw[:write]].compact).empty? %> + readonly + <% end %> + <% if !run_verifier([rw[:write]].compact).empty? && run_verifier([rw[:read]].compact).empty? %> + writeonly + <% end %> + <% end %> + <% if @item.visibility != :public %><%= @item.visibility %><% end %> + <% if @item.has_tag?(:abstract) %>abstract<% end %> + <% if @item.has_tag?(:deprecated) %>deprecated<% end %> + <% if @item.has_tag?(:api) && @item.tag(:api).text == 'private'&& @item.visibility != :private %>private<% end %> + + <% if @item.has_tag?(:deprecated) %> + Deprecated. <%= htmlify_line @item.tag(:deprecated).text %> + <% else %> + <%= htmlify_line docstring_summary(@item) %> + <% end %> +