# 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)
Alias for: display_global_options
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