Parent

Cri::Command

Cri::Command represents a command that can be executed on the commandline. It is also used for the commandline tool itself.

Attributes

aliases[RW]

@return [Array<String>] A list of aliases for this command that can be

used to invoke this command
block[RW]

@return [Proc] The block that should be executed when invoking this

command (ignored for commands with subcommands)
commands[RW]

@return [Set<Cri::Command>] This command’s subcommands

description[RW]

@return [String] The long description (“description”)

hidden[RW]

@return [Boolean] true if the command is hidden (e.g. because it is

deprecated), false otherwise
hidden?[RW]

@return [Boolean] true if the command is hidden (e.g. because it is

deprecated), false otherwise
name[RW]

@return [String] The name

option_definitions[RW]

@return [Array<Hash>] The list of option definitions

subcommands[RW]

@return [Set<Cri::Command>] This command’s subcommands

summary[RW]

@return [String] The short description (“summary”)

supercommand[RW]

@return [Cri::Command, nil] This command’s supercommand, or nil if the

command has no supercommand
usage[RW]

@return [String] The usage, without the “usage:” prefix and without the

supercommands’ names.

Public Class Methods

define(string=nil, filename=nil, &block) click to toggle source

Creates a new command using the DSL. If a string is given, the command will be defined using the string; if a block is given, the block will be used instead.

If the block has one parameter, the block will be executed in the same context with the command DSL as its parameter. If the block has no parameters, the block will be executed in the context of the DSL.

@param [String, nil] The string containing the command’s definition

@return [Cri::Command] The newly defined command

# File lib/cri/command.rb, line 95
def self.define(string=nil, filename=nil, &block)
  dsl = Cri::CommandDSL.new
  if string
    args = filename ? [ string, filename ] : [ string ]
    dsl.instance_eval(*args)
  elsif [ -1, 0 ].include? block.arity
    dsl.instance_eval(&block)
  else
    block.call(dsl)
  end
  dsl.command
end
new() click to toggle source
# File lib/cri/command.rb, line 126
def initialize
  @aliases            = Set.new
  @commands           = Set.new
  @option_definitions = Set.new
end
new_basic_help() click to toggle source

Returns a new command that implements showing help.

@return [Cri::Command] A basic help command

# File lib/cri/command.rb, line 121
def self.new_basic_help
  filename = File.dirname(__FILE__) + '/commands/basic_help.rb'
  self.define(File.read(filename))
end
new_basic_root() click to toggle source

Returns a new command that has support for the `-h`/`--help` option and also has a `help` subcommand. It is intended to be modified (adding name, summary, description, other subcommands, …)

@return [Cri::Command] A basic root command

# File lib/cri/command.rb, line 113
def self.new_basic_root
  filename = File.dirname(__FILE__) + '/commands/basic_root.rb'
  self.define(File.read(filename))
end

Public Instance Methods

<=>(other) click to toggle source

Compares this command's name to the other given command's name.

# File lib/cri/command.rb, line 394
def <=>(other)
  self.name <=> other.name
end
add_command(command) click to toggle source

Adds the given command as a subcommand to the current command.

@param [Cri::Command] command The command to add as a subcommand

@return [void]

# File lib/cri/command.rb, line 163
def add_command(command)
  @commands << command
  command.supercommand = self
end
command_named(name) click to toggle source

Returns the command with the given name. This method will display error messages and exit in case of an error (unknown or ambiguous command).

The name can be a full command name, a partial command name (e.g. “com” for “commit”) or an aliased command name (e.g. “ci” for “commit”).

@param [String] name The full, partial or aliases name of the command

@return [Cri::Command] The command with the given name

# File lib/cri/command.rb, line 218
def command_named(name)
  commands = commands_named(name)

  if commands.size < 1
    $stderr.puts "#{self.name}: unknown command '#{name}'\n"
    exit 1
  elsif commands.size > 1
    $stderr.puts "#{self.name}: '#{name}' is ambiguous:"
    $stderr.puts "  #{commands.map { |c| c.name }.sort.join(' ') }"
    exit 1
  else
    commands[0]
  end
end
commands_named(name) click to toggle source

Returns the commands that could be referred to with the given name. If the result contains more than one command, the name is ambiguous.

@param [String] name The full, partial or aliases name of the command

@return [Array<Cri::Command>] A list of commands matching the given name

# File lib/cri/command.rb, line 196
def commands_named(name)
  # Find by exact name or alias
  @commands.each do |cmd|
    found = cmd.name == name || cmd.aliases.include?(name)
    return [ cmd ] if found
  end

  # Find by approximation
  @commands.select do |cmd|
    cmd.name[0, name.length] == name
  end
end
define_command(name=nil, &block) click to toggle source

Defines a new subcommand for the current command using the DSL.

@param [String, nil] name The name of the subcommand, or nil if no name

should be set (yet)

@return [Cri::Command] The subcommand

# File lib/cri/command.rb, line 174
def define_command(name=nil, &block)
  # Execute DSL
  dsl = Cri::CommandDSL.new
  dsl.name name unless name.nil?
  if [ -1, 0 ].include? block.arity
    dsl.instance_eval(&block)
  else
    block.call(dsl)
  end

  # Create command
  cmd = dsl.command
  self.add_command(cmd)
  cmd
end
global_option_definitions() click to toggle source

@return [Hash] The option definitions for the command itself and all its

ancestors
# File lib/cri/command.rb, line 151
def global_option_definitions
  res = Set.new
  res.merge(option_definitions)
  res.merge(supercommand.global_option_definitions) if supercommand
  res
end
help(params={}) click to toggle source

@return [String] The help text for this command

# File lib/cri/command.rb, line 299
def help(params={})
  is_verbose = params.fetch(:verbose, false)

  text = ''

  # Append name and summary
  if summary
    text << "name".formatted_as_title << "\n"
    text << "    #{name.formatted_as_command} - #{summary}" << "\n"
    unless aliases.empty?
      text << "    aliases: " << aliases.map { |a| a.formatted_as_command }.join(' ') << "\n"
    end
  end

  # Append usage
  if usage
    path = [ self.supercommand ]
    path.unshift(path[0].supercommand) until path[0].nil?
    formatted_usage = usage.gsub(/^([^\s]+)/) { |m| m.formatted_as_command }
    full_usage = path[1..-1].map { |c| c.name.formatted_as_command + ' ' }.join + formatted_usage

    text << "\n"
    text << "usage".formatted_as_title << "\n"
    text << full_usage.wrap_and_indent(78, 4) << "\n"
  end

  # Append long description
  if description
    text << "\n"
    text << "description".formatted_as_title << "\n"
    text << description.wrap_and_indent(78, 4) + "\n"
  end

  # Append subcommands
  unless self.subcommands.empty?
    text << "\n"
    text << (self.supercommand ? 'subcommands' : 'commands').formatted_as_title
    text << "\n"

    visible_cmds, invisible_cmds = self.subcommands.partition { |c| !c.hidden? }

    commands_for_length = is_verbose ? self.subcommands : visible_cmds
    length = commands_for_length.map { |c| c.name.formatted_as_command.size }.max

    # Visible
    visible_cmds.sort_by { |cmd| cmd.name }.each do |cmd|
      text << sprintf("    %-#{length+4}s %s\n",
        cmd.name.formatted_as_command,
        cmd.summary)
    end

    # Invisible
    if is_verbose
      invisible_cmds.sort_by { |cmd| cmd.name }.each do |cmd|
        text << sprintf("    %-#{length+4}s %s\n",
          cmd.name.formatted_as_command,
          cmd.summary)
      end
    else
      case invisible_cmds.size
      when 0
      when 1
        text << "    (1 hidden command ommitted; show it with --verbose)\n"
      else
        text << "    (#{invisible_cmds.size} hidden commands ommitted; show them with --verbose)\n"
      end
    end
  end

  # Append options
  groups = { 'options' => self.option_definitions }
  if self.supercommand
    groups["options for #{self.supercommand.name}"] = self.supercommand.global_option_definitions
  end
  length = groups.values.inject(&:+).map { |o| o[:long].size }.max
  groups.each_pair do |name, defs|
    unless defs.empty?
      text << "\n"
      text << "#{name}".formatted_as_title
      text << "\n"
      defs.sort { |x,y| x[:long] <=> y[:long] }.each do |opt_def|
        text << sprintf(
          "    -%1s --%-#{length+4}s",
          opt_def[:short],
          opt_def[:long]).formatted_as_option

        text << opt_def[:desc] << "\n"
      end
    end
  end

  text
end
modify(&block) click to toggle source

Modifies the command using the DSL.

If the block has one parameter, the block will be executed in the same context with the command DSL as its parameter. If the block has no parameters, the block will be executed in the context of the DSL.

@return [Cri::Command] The command itself

# File lib/cri/command.rb, line 139
def modify(&block)
  dsl = Cri::CommandDSL.new(self)
  if [ -1, 0 ].include? block.arity
    dsl.instance_eval(&block)
  else
    block.call(dsl)
  end
  self
end
run(opts_and_args, parent_opts={}) click to toggle source

Runs the command with the given commandline arguments, possibly invoking subcommands and passing on the options and arguments.

@param [Array<String>] opts_and_args A list of unparsed arguments

@param [Hash] parent_opts A hash of options already handled by the

supercommand

@return [void]

# File lib/cri/command.rb, line 242
def run(opts_and_args, parent_opts={})
  # Parse up to command name
  stuff = partition(opts_and_args)
  opts_before_subcmd, subcmd_name, opts_and_args_after_subcmd = *stuff

  if subcommands.empty? || (subcmd_name.nil? && !self.block.nil?)
    run_this(opts_and_args, parent_opts)
  else
    # Handle options
    self.handle_options(opts_before_subcmd)

    # Get command
    if subcmd_name.nil?
      $stderr.puts "#{name}: no command given"
      exit 1
    end
    subcommand = self.command_named(subcmd_name)

    # Run
    subcommand.run(opts_and_args_after_subcmd, opts_before_subcmd)
  end
end
run_this(opts_and_args, parent_opts={}) click to toggle source

Runs the actual command with the given commandline arguments, not invoking any subcommands. If the command does not have an execution block, an error ir raised.

@param [Array<String>] opts_and_args A list of unparsed arguments

@param [Hash] parent_opts A hash of options already handled by the

supercommand

@raise [NotImplementedError] if the command does not have an execution

block

@return [void]

# File lib/cri/command.rb, line 278
def run_this(opts_and_args, parent_opts={})
  # Parse
  parser = Cri::OptionParser.new(
    opts_and_args, self.global_option_definitions)
  self.handle_parser_errors_while { parser.run }
  local_opts  = parser.options
  global_opts = parent_opts.merge(parser.options)
  args = parser.arguments

  # Handle options
  self.handle_options(local_opts)

  # Execute
  if self.block.nil?
    raise NotImplementedError,
      "No implementation available for '#{self.name}'"
  end
  self.block.call(global_opts, args, self)
end

Protected Instance Methods

handle_options(opts) click to toggle source
# File lib/cri/command.rb, line 400
def handle_options(opts)
  opts.each_pair do |key, value|
    opt_def = global_option_definitions.find { |o| o[:long] == key.to_s }
    block = opt_def[:block]
    block.call(value, self) if block
  end
end
handle_parser_errors_while(&block) click to toggle source
# File lib/cri/command.rb, line 424
def handle_parser_errors_while(&block)
  begin
    block.call
  rescue Cri::OptionParser::IllegalOptionError => e
    $stderr.puts "#{name}: illegal option -- #{e}"
    exit 1
  rescue Cri::OptionParser::OptionRequiresAnArgumentError => e
    $stderr.puts "#{name}: option requires an argument -- #{e}"
    exit 1
  end
end
partition(opts_and_args) click to toggle source
# File lib/cri/command.rb, line 408
def partition(opts_and_args)
  # Parse
  delegate = Cri::Command::OptionParserPartitioningDelegate.new
  parser = Cri::OptionParser.new(opts_and_args, global_option_definitions)
  parser.delegate = delegate
  self.handle_parser_errors_while { parser.run }
  parser

  # Extract
  [
    parser.options,
    delegate.last_argument,
    parser.unprocessed_arguments_and_options
  ]
end

[Validate]

Generated with the Darkfish Rdoc Generator 2.