module Puppet class Parameter < Puppet::Element class << self attr_reader :validater, :munger, :name, :default attr_accessor :ismetaparameter, :element # 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. def defaultto(value = nil, &block) if block define_method(:default, &block) else define_method(:default) do value end end end def nodefault undef_method :default #if defined_method? :default #end end # Store documentation for this parameter. def desc(str) @doc = str end # This is how we munge the value. Basically, this is our # opportunity to convert the value from one form into another. 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 # class's context, not the instance's, thus the two methods, # instead of just one. define_method(:unsafe_munge, &block) define_method(:munge) do |*args| begin unsafe_munge(*args) rescue Puppet::Error => detail Puppet.debug "Reraising %s" % detail raise rescue => detail raise Puppet::DevError, "Munging failed for class %s: %s" % [self.name, detail] end end #@munger = block end #def inspect # "Parameter(#{self.name})" #end # Mark whether we're the namevar. def isnamevar @isnamevar = true @required = true end # Is this parameter the namevar? Defaults to false. def isnamevar? if defined? @isnamevar return @isnamevar else return false end end # This parameter is required. def isrequired @required = true end # Is this parameter required? Defaults to false. def required? if defined? @required return @required else return false end end def to_s if self.ismetaparameter "Puppet::Type::" + @name.to_s.capitalize else self.element.to_s + @name.to_s.capitalize end end # Verify that we got a good value def validate(&block) #@validater = block define_method(:unsafe_validate, &block) define_method(:validate) do |*args| begin unsafe_validate(*args) rescue ArgumentError, Puppet::Error, TypeError raise rescue => detail raise Puppet::DevError, "Validate method failed for class %s: %s" % [self.name, detail] end end end # Define a new value for our parameter. def newvalues(*names) @parametervalues ||= [] names.each { |name| if @parametervalues.include?(name) Puppet.warning "%s already has a value for %s" % [name, name] end @parametervalues << name } end def aliasvalue(name, other) @parametervalues ||= [] unless @parametervalues.include?(other) raise Puppet::DevError, "Cannot alias nonexistent value %s" % other end @aliasvalues ||= {} @aliasvalues[name] = other end def alias(name) @aliasvalues[name] end # Return the list of valid values. def values @parametervalues ||= [] @aliasvalues ||= {} #[@aliasvalues.keys, @parametervalues.keys].flatten @parametervalues.dup end end # Just a simple method to proxy instance methods to class methods def self.proxymethods(*values) values.each { |val| eval "def #{val}; self.class.#{val}; end" } end # And then define one of these proxies for each method in our # ParamHandler class. proxymethods("required?", "isnamevar?") attr_accessor :parent # This doesn't work, because the instance_eval doesn't bind the inner block # only the outer one. # def munge(value) # if munger = self.class.munger # return @parent.instance_eval { # munger.call(value) # } # else # return value # end # end # # def validate(value) # if validater = self.class.validater # return @parent.instance_eval { # validater.call(value) # } # end # end #def default # default = self.class.default # if default.is_a?(Proc) # val = self.instance_eval(&default) # return val # else # return default # end #end def devfail(msg) self.fail(Puppet::DevError, msg) end def fail(*args) type = nil if args[0].is_a?(Class) type = args.shift else type = Puppet::Error end error = type.new(args.join(" ")) if defined? @parent and @parent and @parent.line error.line = @parent.line end if defined? @parent and @parent and @parent.file error.file = @parent.file end raise error end # Log a message using the parent's log level. def log(msg) unless @parent[:loglevel] p @parent self.devfail "Parent %s has no loglevel" % @parent.name end Puppet::Log.create( :level => @parent[:loglevel], :message => msg, :source => self ) 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 def name return self.class.name end # for testing whether we should actually do anything def noop unless defined? @noop @noop = false end tmp = @noop || self.parent.noop || Puppet[:noop] || false #debug "noop is %s" % tmp return tmp end # return the full path to us, for logging and rollback; not currently # used def path return [@parent.path, self.name].join("/") end # If the specified value is allowed, then munge appropriately. munge do |value| if self.class.values.empty? # This parameter isn't using defined values to do its work. return value end intern = value.to_s.intern # If it's a valid value, always return it as a symbol. if self.class.values.include?(intern) retval = intern elsif other = self.class.alias(intern) self.info "returning alias %s for %s" % [other, intern] retval = other else retval = value end retval end # Verify that the passed value is valid. validate do |value| if self.class.values.empty? # This parameter isn't using defined values to do its work. return end unless value.is_a?(Symbol) value = value.to_s.intern end unless self.class.values.include?(value) or self.class.alias(value) self.fail "Invalid '%s' value '%s'. Valid values are '%s'" % [self.class.name, value, self.class.values.join(", ")] end end # This should only be called for parameters, but go ahead and make # it possible to call for states, too. def value if self.is_a?(Puppet::State) return self.should else if defined? @value return @value else return nil end end 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). def value=(value) # If we're a parameter, just hand the processing off to the should # method. if self.is_a?(Puppet::State) return self.should = value end if respond_to?(:validate) validate(value) end if respond_to?(:munge) value = munge(value) end @value = value end def inspect s = "Parameter(%s = %s" % [self.name, self.value || "nil"] if defined? @parent s += ", @parent = %s)" % @parent else s += ")" end end def to_s s = "Parameter(%s)" % self.name end end end