# class Puppet::Property

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

### Attributes

name[R]

@return [Symbol] The name of the property as given when the property was created.

unmanaged[RW]

@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

noop[W]

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` mode as well as the noop mode in the _associated resource_

shadow[R]

@todo What is this? What is this used for?

shouldorig[R]

Returns the original wanted value(s) _(should)_ unprocessed by munging/unmunging. The original values are set by {#value=} or {#should=}. @return (see should)

### Public Class Methods

method_added(sym) click to toggle source

Protects against override of the {#safe_insync?} method. @raise [RuntimeError] if the added method is `:safe_insync?` @api private

    # File lib/puppet/property.rb, line 312
    def self.method_added(sym)
      raise "Puppet::Property#safe_insync? shouldn't be overridden; please override insync? instead" if sym == :safe_insync?
    end

new(hash = {}) click to toggle source

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

Calls superclass method Puppet::Parameter.new

    # File lib/puppet/property.rb, line 284
    def initialize(hash = {})
      super

      if ! self.metaparam? and klass = Puppet::Type.metaparamclass(self.class.name)
        setup_shadow(klass)
      end
    end

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

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.

@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

    # File lib/puppet/property.rb, line 156
    def self.newvalue(name, options = {}, &block)
      value = value_collection.newvalue(name, options, &block)

      define_method(value.method, &value.block) if value.method and value.block
      value
    end

value_name(name) click to toggle source

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

    # File lib/puppet/property.rb, line 111
    def self.value_name(name)
      if value = value_collection.match?(name)
        value.name
      end
    end

value_option(name, option) click to toggle source

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

    # File lib/puppet/property.rb, line 125
    def self.value_option(name, option)
      if value = value_collection.value(name)
        value.send(option)
      end
    end

### Public Instance Methods

array_matching() click to toggle source

@!attribute [rw] #array_matching @comment note that $#46; is a period - char code require to not terminate sentence. The `is` vs&#46; `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

    # File lib/puppet/property.rb, line 93
    def array_matching
      @array_matching ||= :first
    end

array_matching=(value) click to toggle source

@comment This is documented as an attribute - see the {array_matching} method.

    # File lib/puppet/property.rb, line 99
    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)
      @array_matching = value
    end

call_provider(value) click to toggle source

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

    # File lib/puppet/property.rb, line 169
    def call_provider(value)
        method = self.class.name.to_s + "="
        unless provider.respond_to? method
          self.fail "The #{provider.class.name} provider can not handle attribute #{self.class.name}"
        end
        provider.send(method, value)
    end

call_valuemethod(name, value) click to toggle source

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

    # File lib/puppet/property.rb, line 190
    def call_valuemethod(name, value)
      if method = self.class.value_option(name, :method) and self.respond_to?(method)
        begin
          event = self.send(method)
        rescue Puppet::Error
          raise
        rescue => detail
          error = Puppet::ResourceError.new("Could not set '#{value}' on #{self.class.name}: #{detail}", @resource.line, @resource.file, detail)
          error.set_backtrace detail.backtrace
          Puppet.log_exception(detail, error.message)
          raise error
        end
      elsif block = self.class.value_option(name, :block)
        # FIXME It'd be better here to define a method, so that
        # the blocks could return values.
        self.instance_eval(&block)
      else
        devfail "Could not find method for value '#{name}'"
      end
    end

change_to_s(current_value, newvalue) click to toggle source

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

    # File lib/puppet/property.rb, line 216
    def change_to_s(current_value, newvalue)
      begin
        if current_value == :absent
          return "defined '#{name}' as #{self.class.format_value_for_display should_to_s(newvalue)}"
        elsif newvalue == :absent or newvalue == [:absent]
          return "undefined '#{name}' from #{self.class.format_value_for_display is_to_s(current_value)}"
        else
          return "#{name} changed #{self.class.format_value_for_display is_to_s(current_value)} to #{self.class.format_value_for_display should_to_s(newvalue)}"
        end
      rescue Puppet::Error, Puppet::DevError
        raise
      rescue => detail
        message = "Could not convert change '#{name}' to string: #{detail}"
        Puppet.log_exception(detail, message)
        raise Puppet::DevError, message
      end
    end

event() click to toggle source

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

    # File lib/puppet/property.rb, line 267
    def event
      resource.event :name => event_name, :desired_value => should, :property => self, :source_description => path
    end

event_name() click to toggle source

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

    # File lib/puppet/property.rb, line 240
    def event_name
      value = self.should

      event_name = self.class.value_option(value, :event) and return event_name

      name == :ensure or return (name.to_s + "_changed").to_sym

      return (resource.type.to_s + case value
      when :present; "_created"
      when :absent; "_removed"
      else
        "_changed"
      end).to_sym
    end

insync?(is) click to toggle source

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

    # File lib/puppet/property.rb, line 336
    def insync?(is)
      self.devfail "#{self.class.name}'s should is not array" unless @should.is_a?(Array)

      # an empty array is analogous to no should values
      return true if @should.empty?

      # Look for a matching value, either for all the @should values, or any of
      # them, depending on the configuration of this property.
      if match_all? then
        # Emulate Array#== using our own comparison function.
        # A non-array was not equal to an array, which @should always is.
        return false unless is.is_a? Array

        # If they were different lengths, they are not equal.
        return false unless is.length == @should.length

        # Finally, are all the elements equal?  In order to preserve the
        # behaviour of previous 2.7.x releases, we need to impose some fun rules
        # on "equality" here.
        #
        # Specifically, we need to implement *this* comparison: the two arrays
        # are identical if the is values are == the should values, or if the is
        # values are == the should values, stringified.
        #
        # This does mean that property equality is not commutative, and will not
        # work unless the `is` value is carefully arranged to match the should.
        return (is == @should or is == @should.map(&:to_s))

        # When we stop being idiots about this, and actually have meaningful
        # semantics, this version is the thing we actually want to do.
        #
        # return is.zip(@should).all? {|a, b| property_matches?(a, b) }
      else
        return @should.any? {|want| property_matches?(is, want) }
      end
    end

is_to_s(currentvalue) click to toggle source

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

    # File lib/puppet/property.rb, line 395
    def is_to_s(currentvalue)
      currentvalue
    end

log(msg) click to toggle source

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]

    # File lib/puppet/property.rb, line 404
    def log(msg)
      Puppet::Util::Log.create(
        :level   => resource[:loglevel],
        :message => msg,
        :source  => self
      )
    end

match_all?() click to toggle source

@return [Boolean] whether the {array_matching} mode is set to `:all` or not

    # File lib/puppet/property.rb, line 413
    def match_all?
      self.class.array_matching == :all
    end

munge(value) click to toggle source

(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.

Calls superclass method Puppet::Parameter#munge

    # File lib/puppet/property.rb, line 421
    def munge(value)
      self.shadow.munge(value) if self.shadow

      super
    end

name() click to toggle source

@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.

    # File lib/puppet/property.rb, line 430
    def name
      self.class.name
    end

noop() click to toggle source

@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. @todo This logic is different than Puppet::Parameter#noop in that the resource noop mode overrides

    the property's mode - in parameter it is the other way around. Bug or feature?

    # File lib/puppet/property.rb, line 442
    def noop
      # This is only here to make testing easier.
      if @resource.respond_to?(:noop?)
        @resource.noop?
      else
        if defined?(@noop)
          @noop
        else
          Puppet[:noop]
        end
      end
    end

property_matches?(current, desired) click to toggle source

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?}.

    # File lib/puppet/property.rb, line 383
    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
      # desirable, but at least it is not a breaking change. --daniel 2011-11-11
      current == desired or current == desired.to_s
    end

retrieve() click to toggle source

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

    # File lib/puppet/property.rb, line 461
    def retrieve
      provider.send(self.class.name)
    end

safe_insync?(is) click to toggle source

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.

    # File lib/puppet/property.rb, line 300
    def safe_insync?(is)
      # If there is no @should value, consider the property to be in sync.
      return true unless @should

      # Otherwise delegate to the (possibly derived) insync? method.
      insync?(is)
    end

set(value) click to toggle source

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

    # File lib/puppet/property.rb, line 482
    def set(value)
      # Set a name for looking up associated options like the event.
      name = self.class.value_name(value)

      call = self.class.value_option(name, :call) || :none

      if call == :instead
        call_valuemethod(name, value)
      elsif call == :none
        # They haven't provided a block, and our parent does not have
        # a provider, so we have no idea how to handle this.
        self.fail "#{self.class.name} cannot handle values of type #{value.inspect}" unless @resource.provider
        call_provider(value)
      else
        # LAK:NOTE 20081031 This is a change in behaviour -- you could
        # previously specify :call => [;before|:after], which would call
        # the setter *in addition to* the block.  I'm convinced this
        # was never used, and it makes things unecessarily complicated.
        # If you want to specify a block and still call the setter, then
        # do so in the block.
        devfail "Cannot use obsolete :call value '#{call}' for property '#{self.class.name}'"
      end
    end

setup_shadow(klass) click to toggle source

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<inherits Puppet::Parameter>] the class of the shadowed meta-parameter @return [Puppet::Parameter] an instance of the given class (a parameter or property)

    # File lib/puppet/property.rb, line 512
    def setup_shadow(klass)
      @shadow = klass.new(:resource => self.resource)
    end

should() click to toggle source

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>, 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

    # File lib/puppet/property.rb, line 529
    def should
      return nil unless defined?(@should)

      self.devfail "should for #{self.class.name} on #{resource.name} is not an array" unless @should.is_a?(Array)

      if match_all?
        return @should.collect { |val| self.unmunge(val) }
      else
        return self.unmunge(@should[0])
      end
    end

should=(values) click to toggle source

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>, Object] the value(s) to set as the wanted value(s) @raise [StandardError] when validation of a value fails (see {#validate}). @api public

    # File lib/puppet/property.rb, line 549
    def should=(values)
      values = [values] unless values.is_a?(Array)

      @shouldorig = values

      values.each { |val| validate(val) }
      @should = values.collect { |val| self.munge(val) }
    end

should_to_s(newvalue) click to toggle source

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

    # File lib/puppet/property.rb, line 562
    def should_to_s(newvalue)
      [newvalue].flatten.join(" ")
    end

sync() click to toggle source

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.

    # File lib/puppet/property.rb, line 570
    def sync
      devfail "Got a nil value for should" unless should
      set(should)
    end

unsafe_validate(value) click to toggle source

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

Calls superclass method Puppet::Parameter#unsafe_validate

    # File lib/puppet/property.rb, line 581
    def unsafe_validate(value)
      super
      validate_features_per_value(value)
    end

validate_features_per_value(value) click to toggle source

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

    # File lib/puppet/property.rb, line 591
    def validate_features_per_value(value)
      if features = self.class.value_option(self.class.value_name(value), :required_features)
        features = Array(features)
        needed_features = features.collect { |f| f.to_s }.join(", ")
        raise ArgumentError, "Provider must have features '#{needed_features}' to set '#{self.class.name}' to '#{value}'" unless provider.satisfies?(features)
      end
    end

value() click to toggle source

@return [Object, nil] Returns the wanted _(should)_ value of this property.

    # File lib/puppet/property.rb, line 600
    def value
      self.should
    end

value=(value) click to toggle source

(see should=)

    # File lib/puppet/property.rb, line 605
    def value=(value)
      self.should = value
    end