class Puppet::Application::FaceBase

Attributes

action[RW]
arguments[RW]
face[RW]
render_as[RW]
type[RW]

Public Instance Methods

find_application_argument(item) click to toggle source
# File lib/puppet/application/face_base.rb, line 145
def find_application_argument(item)
  self.class.option_parser_commands.each do |options, function|
    options.each do |option|
      next unless option =~ /^-/
      pattern = /^#{option.sub('[no-]', '').sub(/[ =].*$/, '')}(?:[ =].*)?$/
      next unless pattern.match(item)
      return {
        :argument => option =~ /[ =]/,
        :optional => option =~ /[ =]\[/
      }
    end
  end
  return nil                  # not found
end
find_global_settings_argument(item) click to toggle source
# File lib/puppet/application/face_base.rb, line 133
def find_global_settings_argument(item)
  Puppet.settings.each do |name, object|
    object.optparse_args.each do |arg|
      next unless arg =~ /^-/
      # sadly, we have to emulate some of optparse here...
      pattern = /^#{arg.sub('[no-]', '').sub(/[ =].*$/, '')}(?:[ =].*)?$/
      pattern.match item and return object
    end
  end
  return nil                  # nothing found.
end
main() click to toggle source
# File lib/puppet/application/face_base.rb, line 185
def main
  status = false

  # Call the method associated with the provided action (e.g., 'find').
  unless @action
    puts Puppet::Face[:help, :current].help(@face.name)
    raise "#{face} does not respond to action #{arguments.first}"
  end

  # We need to do arity checking here because this is generic code
  # calling generic methods – that have argument defaulting.  We need to
  # make sure we don't accidentally pass the options as the first
  # argument to a method that takes one argument.  eg:
  #
  #   puppet facts find
  #   => options => {}
  #      @arguments => [{}]
  #   => @face.send :bar, {}
  #
  #   def face.bar(argument, options = {})
  #   => bar({}, {})  # oops!  we thought the options were the
  #                   # positional argument!!
  #
  # We could also fix this by making it mandatory to pass the options on
  # every call, but that would make the Ruby API much more annoying to
  # work with; having the defaulting is a much nicer convention to have.
  #
  # We could also pass the arguments implicitly, by having a magic
  # 'options' method that was visible in the scope of the action, which
  # returned the right stuff.
  #
  # That sounds attractive, but adds complications to all sorts of
  # things, especially when you think about how to pass options when you
  # are writing Ruby code that calls multiple faces.  Especially if
  # faces are involved in that. ;)
  #
  # --daniel 2011-04-27
  if (arity = @action.positional_arg_count) > 0
    unless (count = arguments.length) == arity then
      s = arity == 2 ? '' : 's'
      raise ArgumentError, "puppet #{@face.name} #{@action.name} takes #{arity-1} argument#{s}, but you gave #{count-1}"
    end
  end

  result = @face.send(@action.name, *arguments)
  puts render(result, arguments) unless result.nil?
  status = true

# We need an easy way for the action to set a specific exit code, so we
# rescue SystemExit here; This allows each action to set the desired exit
# code by simply calling Kernel::exit.  eg:
#
#   exit(2)
#
# --kelsey 2012-02-14
rescue SystemExit => detail
  status = detail.status

rescue Exception => detail
  Puppet.log_exception(detail)
  Puppet.err "Try 'puppet help #{@face.name} #{@action.name}' for usage"

ensure
  exit status
end
parse_options() click to toggle source
Calls superclass method Puppet::Application#parse_options
# File lib/puppet/application/face_base.rb, line 54
def parse_options
  # We need to parse enough of the command line out early, to identify what
  # the action is, so that we can obtain the full set of options to parse.

  # REVISIT: These should be configurable versions, through a global
  # '--version' option, but we don't implement that yet... --daniel 2011-03-29
  @type = Puppet::Util::ConstantInflector.constant2file(self.class.name.to_s.sub(/.+:/, '')).to_sym
  @face = Puppet::Face[@type, :current]

  # Now, walk the command line and identify the action.  We skip over
  # arguments based on introspecting the action and all, and find the first
  # non-option word to use as the action.
  action_name = nil
  index       = -1
  until action_name or (index += 1) >= command_line.args.length do
    item = command_line.args[index]
    if item =~ /^-/ then
      option = @face.options.find do |name|
        item =~ /^-+#{name.to_s.gsub(/[-_]/, '[-_]')}(?:[ =].*)?$/
      end
      if option then
        option = @face.get_option(option)
        # If we have an inline argument, just carry on.  We don't need to
        # care about optional vs mandatory in that case because we do a real
        # parse later, and that will totally take care of raising the error
        # when we get there. --daniel 2011-04-04
        if option.takes_argument? and !item.index('=') then
          index += 1 unless
            (option.optional_argument? and command_line.args[index + 1] =~ /^-/)
        end
      elsif option = find_global_settings_argument(item) then
        unless Puppet.settings.boolean? option.name then
          # As far as I can tell, we treat non-bool options as always having
          # a mandatory argument. --daniel 2011-04-05
          # ... But, the mandatory argument will not be the next item if an = is
          # employed in the long form of the option. --jeffmccune 2012-09-18
          index += 1 unless item =~ /^--#{option.name}=/
        end
      elsif option = find_application_argument(item) then
        index += 1 if (option[:argument] and not option[:optional])
      else
        raise OptionParser::InvalidOption.new(item.sub(/=.*$/, ''))
      end
    else
      # Stash away the requested action name for later, and try to fetch the
      # action object it represents; if this is an invalid action name that
      # will be nil, and handled later.
      action_name = item.to_sym
      @action = Puppet::Face.find_action(@face.name, action_name)
      @face   = @action.face if @action
    end
  end

  if @action.nil?
    if @action = @face.get_default_action() then
      @is_default_action = true
    else
      face   = @face.name
      action = action_name.nil? ? 'default' : "'#{action_name}'"
      msg = "'#{face}' has no #{action} action.  See `puppet help #{face}`."

      Puppet.err(msg)
      Puppet::Util::Log.force_flushqueue()

      exit false
    end
  end

  # Now we can interact with the default option code to build behaviour
  # around the full set of options we now know we support.
  @action.options.each do |option|
    option = @action.get_option(option) # make it the object.
    self.class.option(*option.optparse) # ...and make the CLI parse it.
  end

  # ...and invoke our parent to parse all the command line options.
  super
end
preinit() click to toggle source
Calls superclass method Puppet::Application#preinit
# File lib/puppet/application/face_base.rb, line 46
def preinit
  super
  Signal.trap(:INT) do
    $stderr.puts "Cancelling Face"
    exit(0)
  end
end
render(result, args_and_options) click to toggle source
# File lib/puppet/application/face_base.rb, line 30
def render(result, args_and_options)
  hook = action.when_rendering(render_as.name)

  if hook
    # when defining when_rendering on your action you can optionally
    # include arguments and options
    if hook.arity > 1
      result = hook.call(result, *args_and_options)
    else
      result = hook.call(result)
    end
  end

  render_as.render(result)
end
render_as=(format) click to toggle source
# File lib/puppet/application/face_base.rb, line 21
def render_as=(format)
  if format == :json then
    @render_as = Puppet::Network::FormatHandler.format(:pson)
  else
    @render_as = Puppet::Network::FormatHandler.format(format)
  end
  @render_as or raise ArgumentError, "I don't know how to render '#{format}'"
end
setup() click to toggle source
# File lib/puppet/application/face_base.rb, line 160
def setup
  Puppet::Util::Log.newdestination :console

  @arguments = command_line.args

  # Note: because of our definition of where the action is set, we end up
  # with it *always* being the first word of the remaining set of command
  # line arguments.  So, strip that off when we construct the arguments to
  # pass down to the face action. --daniel 2011-04-04
  # Of course, now that we have default actions, we should leave the
  # "action" name on if we didn't actually consume it when we found our
  # action.
  @arguments.delete_at(0) unless @is_default_action

  # We copy all of the app options to the end of the call; This allows each
  # action to read in the options.  This replaces the older model where we
  # would invoke the action with options set as global state in the
  # interface object.  --daniel 2011-03-28
  @arguments << options

  # If we don't have a rendering format, set one early.
  self.render_as ||= (@action.render_as || :console)
end