class Puppet::Interface::Action

This represents an action that is attached to a face. Actions should be constructed by calling {Puppet::Interface::ActionManager#action}, which is available on {Puppet::Interface}, and then calling methods of {Puppet::Interface::ActionBuilder} in the supplied block. @api private

Attributes

default[RW]

Whether this is the default action for the face @return [Boolean] @api private

face[R]

The face this action is attached to @return [Puppet::Interface]

name[R]

The name of this action @return [Symbol]

positional_arg_count[R]

The arity of the action @return [Integer]

render_as[RW]

@api private @return [Symbol]

when_invoked[RW]

The block that is executed when the action is invoked @return [block]

Public Class Methods

new(face, name, attrs = {}) click to toggle source

@api private

# File lib/puppet/interface/action.rb, line 17
def initialize(face, name, attrs = {})
  raise "#{name.inspect} is an invalid action name" unless name.to_s =~ /^[a-z]\w*$/
  @face    = face
  @name    = name.to_sym

  # The few bits of documentation we actually demand.  The default license
  # is a favour to our end users; if you happen to get that in a core face
  # report it as a bug, please. --daniel 2011-04-26
  @authors = []
  @license  = 'All Rights Reserved'

  set_options(attrs)

  # @options collects the added options in the order they're declared.
  # @options_hash collects the options keyed by alias for quick lookups.
  @options        = []
  @display_global_options = []
  @options_hash   = {}
  @when_rendering = {}
end

Public Instance Methods

__dup_and_rebind_to(to) click to toggle source

@return [void] @api private

# File lib/puppet/interface/action.rb, line 44
def __dup_and_rebind_to(to)
  bound_version = self.dup
  bound_version.instance_variable_set(:@face, to)
  return bound_version
end
add_display_global_options(*args) click to toggle source
# File lib/puppet/interface/action.rb, line 287
def add_display_global_options(*args)
  @display_global_options ||= []
  [args].flatten.each do |refopt|
    raise ArgumentError, "Global option #{refopt} does not exist in Puppet.settings" unless Puppet.settings.include? refopt
    @display_global_options << refopt
  end
  @display_global_options.uniq!
  @display_global_options
end
add_option(option) click to toggle source
# File lib/puppet/interface/action.rb, line 261
def add_option(option)
  option.aliases.each do |name|
    if conflict = get_option(name) then
      raise ArgumentError, "Option #{option} conflicts with existing option #{conflict}"
    elsif conflict = @face.get_option(name) then
      raise ArgumentError, "Option #{option} conflicts with existing option #{conflict} on #{@face}"
    end
  end

  @options << option.name

  option.aliases.each do |name|
    @options_hash[name] = option
  end

  option
end
default?() click to toggle source
# File lib/puppet/interface/action.rb, line 64
def default?
  !!@default
end
display_global_option(*args)
display_global_options(*args) click to toggle source
# File lib/puppet/interface/action.rb, line 297
def display_global_options(*args)
  args ? add_display_global_options(args) : @display_global_options + @face.display_global_options 
end
Also aliased as: display_global_option
get_option(name, with_inherited_options = true) click to toggle source
# File lib/puppet/interface/action.rb, line 302
def get_option(name, with_inherited_options = true)
  option = @options_hash[name.to_sym]
  if option.nil? and with_inherited_options
    option = @face.get_option(name)
  end
  option
end
option?(name) click to toggle source
# File lib/puppet/interface/action.rb, line 279
def option?(name)
  @options_hash.include? name.to_sym
end
options() click to toggle source
# File lib/puppet/interface/action.rb, line 283
def options
  @face.options + @options
end
render_as=(value) click to toggle source
# File lib/puppet/interface/action.rb, line 140
def render_as=(value)
  @render_as = value.to_sym
end
set_rendering_method_for(type, proc) click to toggle source

@api private

# File lib/puppet/interface/action.rb, line 97
def set_rendering_method_for(type, proc)
  unless proc.is_a? Proc
    msg = "The second argument to set_rendering_method_for must be a Proc"
    msg += ", not #{proc.class.name}" unless proc.nil?
    raise ArgumentError, msg
  end

  if proc.arity != 1 and proc.arity != (@positional_arg_count + 1)
    msg =  "the when_rendering method for the #{@face.name} face #{name} action "
    msg += "takes either just one argument, the result of when_invoked, "
    msg += "or the result plus the #{@positional_arg_count} arguments passed "
    msg += "to the when_invoked block, not "
    if proc.arity < 0 then
      msg += "a variable number"
    else
      msg += proc.arity.to_s
    end
    raise ArgumentError, msg
  end
  unless type.is_a? Symbol
    raise ArgumentError, "The rendering format must be a symbol, not #{type.class.name}"
  end
  if @when_rendering.has_key? type then
    raise ArgumentError, "You can't define a rendering method for #{type} twice"
  end
  # Now, the ugly bit.  We add the method to our interface object, and
  # retrieve it, to rotate through the dance of getting a suitable method
  # object out of the whole process. --daniel 2011-04-18
  @when_rendering[type] =
    @face.__send__( :__add_method, __render_method_name_for(type), proc)
end
synopsis() click to toggle source
# File lib/puppet/interface/action.rb, line 72
def synopsis
  build_synopsis(@face.name, default? ? nil : name, arguments)
end
to_s() click to toggle source
# File lib/puppet/interface/action.rb, line 50
def to_s() "#{@face}##{@name}" end
validate_and_clean(original) click to toggle source
# File lib/puppet/interface/action.rb, line 310
def validate_and_clean(original)
  # The final set of arguments; effectively a hand-rolled shallow copy of
  # the original, which protects the caller from the surprises they might
  # get if they passed us a hash and we mutated it...
  result = {}

  # Check for multiple aliases for the same option, and canonicalize the
  # name of the argument while we are about it.
  overlap = Hash.new do |h, k| h[k] = [] end
  unknown = []
  original.keys.each do |name|
    if option = get_option(name) then
      canonical = option.name
      if result.has_key? canonical
        overlap[canonical] << name
      else
        result[canonical] = original[name]
      end
    elsif Puppet.settings.include? name
      result[name] = original[name]
    else
      unknown << name
    end
  end

  unless overlap.empty?
    msg = overlap.map {|k, v| "(#{k}, #{v.sort.join(', ')})" }.join(", ")
    raise ArgumentError, "Multiple aliases for the same option passed: #{msg}"
  end

  unless unknown.empty?
    msg = unknown.sort.join(", ")
    raise ArgumentError, "Unknown options passed: #{msg}"
  end

  # Inject default arguments and check for missing mandating options.
  missing = []
  options.map {|x| get_option(x) }.each do |option|
    name = option.name
    next if result.has_key? name

    if option.has_default?
      result[name] = option.default
    elsif option.required?
      missing << name
    end
  end

  unless missing.empty?
    msg = missing.sort.join(', ')
    raise ArgumentError, "The following options are required: #{msg}"
  end

  # All done.
  return result
end
when_invoked=(block) click to toggle source
# File lib/puppet/interface/action.rb, line 208
  def when_invoked=(block)

    internal_name = "#{@name} implementation, required on Ruby 1.8".to_sym

    arity = @positional_arg_count = block.arity
    if arity == 0 then
      # This will never fire on 1.8.7, which treats no arguments as "*args",
      # but will on 1.9.2, which treats it as "no arguments".  Which bites,
      # because this just begs for us to wind up in the horrible situation
      # where a 1.8 vs 1.9 error bites our end users. --daniel 2011-04-19
      raise ArgumentError, "when_invoked requires at least one argument (options) for action #{@name}"
    elsif arity > 0 then
      range = Range.new(1, arity - 1)
      decl = range.map { |x| "arg#{x}" } << "options = {}"
      optn = ""
      args = "[" + (range.map { |x| "arg#{x}" } << "options").join(", ") + "]"
    else
      range = Range.new(1, arity.abs - 1)
      decl = range.map { |x| "arg#{x}" } << "*rest"
      optn = "rest << {} unless rest.last.is_a?(Hash)"
      if arity == -1 then
        args = "rest"
      else
        args = "[" + range.map { |x| "arg#{x}" }.join(", ") + "] + rest"
      end
    end

    file    = __FILE__ + "+eval[wrapper]"
    line    = __LINE__ + 2 # <== points to the same line as 'def' in the wrapper.
    wrapper = <<WRAPPER
def #{@name}(#{decl.join(", ")})
  #{optn}
  args    = #{args}
  action  = get_action(#{name.inspect})
  args   << action.validate_and_clean(args.pop)
  __invoke_decorations(:before, action, args, args.last)
  rval = self.__send__(#{internal_name.inspect}, *args)
  __invoke_decorations(:after, action, args, args.last)
  return rval
end
WRAPPER

    if @face.is_a?(Class)
      @face.class_eval do eval wrapper, nil, file, line end
      @face.send(:define_method, internal_name, &block)
      @when_invoked = @face.instance_method(name)
    else
      @face.instance_eval do eval wrapper, nil, file, line end
      @face.meta_def(internal_name, &block)
      @when_invoked = @face.method(name).unbind
    end
  end
when_rendering(type) click to toggle source

@api private

# File lib/puppet/interface/action.rb, line 81
def when_rendering(type)
  unless type.is_a? Symbol
    raise ArgumentError, "The rendering format must be a symbol, not #{type.class.name}"
  end
  # Do we have a rendering hook for this name?
  return @when_rendering[type].bind(@face) if @when_rendering.has_key? type

  # How about by another name?
  alt = type.to_s.sub(/^to_/, '').to_sym
  return @when_rendering[alt].bind(@face) if @when_rendering.has_key? alt

  # Guess not, nothing to run.
  return nil
end