class CmdParse::Command

Base class for commands

This class implements all needed methods so that it can be used by the CommandParser class.

Commands can either be created by sub-classing or on the fly when using the add_command method. The latter allows for a more terse specification of a command while the sub-class approach allows to customize all aspects of a command by overriding methods.

Basic example for sub-classing:

class TestCommand < CmdParse::Command
  def initialize
    super('test', takes_commands: false)
    options.on('-m', '--my-opt', 'My option') { 'Do something' }
  end
end

parser = CmdParse::CommandParser.new
parser.add_command(TestCommand.new)
parser.parse

Basic example for on the fly creation:

parser = CmdParse::CommandParser.new
parser.add_command('test') do |cmd|
  takes_commands(false)
  options.on('-m', '--my-opt', 'My option') { 'Do something' }
end
parser.parse

Basic Properties

The only thing that is mandatory to set for a Command is its name. If the command does not take any sub-commands, then additionally an action block needs to be specified or the execute method overridden.

However, there are several other methods that can be used to configure the behavior of a command:

takes_commands

For specifying whether sub-commands are allowed.

options

For specifying command specific options.

add_command

For specifying sub-commands if the command takes them.

Help Related Methods

Many of this class' methods are related to providing useful help output. While the most common methods can directly be invoked to set or retrieve information, many other methods compute the needed information dynamically and therefore need to be overridden to customize their return value.

short_desc

For a short description of the command (getter/setter).

long_desc

For a detailed description of the command (getter/setter).

argument_desc

For describing command arguments (setter).

help, help_banner, help_short_desc, help_long_desc, help_commands, help_arguments, help_options

For outputting the general command help or individual sections of the command help (getter).

usage, usage_options, usage_arguments, usage_commands

For outputting the usage line or individual parts of it (getter).

Built-in Commands

cmdparse ships with two built-in commands:

Attributes

commands[R]

Returns the mapping of command name to command for all sub-commands of this command.

data[RW]

A data store (initially an empty Hash) that can be used for storing anything. For example, it can be used to store option values. cmdparse itself doesn't do anything with it.

default_command[R]

Returns the name of the default sub-command or nil if there isn't any.

name[R]

The name of the command.

super_command[RW]

Sets or returns the super-command of this command. The super-command is either a Command instance for normal commands or a CommandParser instance for the main command (ie. CommandParser#main_command).

Public Class Methods

new(name, takes_commands: true) click to toggle source

Initializes the command called name.

Options:

takes_commands

Specifies whether this command can take sub-commands.

    # File lib/cmdparse.rb
257 def initialize(name, takes_commands: true)
258   @name = name.freeze
259   @options = OptionParser.new
260   @commands = CommandHash.new
261   @default_command = nil
262   @action = nil
263   @argument_desc ||= {}
264   @data = {}
265   takes_commands(takes_commands)
266 end

Public Instance Methods

<=>(other) click to toggle source

For sorting commands by name.

    # File lib/cmdparse.rb
567 def <=>(other)
568   name <=> other.name
569 end
action(&block) click to toggle source

Sets the given block as the action block that is used on when executing this command.

If a sub-class is created for specifying a command, then the execute method should be overridden instead of setting an action block.

See also: execute

    # File lib/cmdparse.rb
351 def action(&block)
352   @action = block
353 end
add_command(other_command, default: false) {|cmd| ... } → command click to toggle source
add_command('other', default: false) {|cmd| ...} → command

Adds a command to the command list.

The argument command can either be a Command object or a String in which case a new Command object is created. In both cases the Command object is yielded.

If the optional argument default is true, then the command is used when no other sub-command is specified on the command line.

If this command takes no other commands, an error is raised.

    # File lib/cmdparse.rb
309 def add_command(command, default: false) # :yields: command_object
310   raise TakesNoCommandError.new(name) unless takes_commands?
311 
312   command = Command.new(command) if command.kind_of?(String)
313   command.super_command = self
314   @commands[command.name] = command
315   @default_command = command.name if default
316   command.fire_hook_after_add
317   yield(command) if block_given?
318 
319   self
320 end
argument_desc(name → desc, ...) click to toggle source

Sets the descriptions for one or more arguments using name-description pairs.

The used names should correspond to the names used in usage_arguments.

    # File lib/cmdparse.rb
393 def argument_desc(hash)
394   @argument_desc.update(hash)
395 end
arity() click to toggle source

Returns the number of arguments required for the execution of the command, i.e. the number of arguments the action block or the execute method takes.

If the returned number is negative, it means that the minimum number of arguments is -n-1.

See: Method#arity, Proc#arity

    # File lib/cmdparse.rb
403 def arity
404   (@action || method(:execute)).arity
405 end
command_chain → [top_level_command, super_command, ..., command] click to toggle source

Returns the command chain, i.e. a list containing this command and all of its super-commands, starting at the top level command.

    # File lib/cmdparse.rb
327 def command_chain
328   cmds = []
329   cmd = self
330   while !cmd.nil? && !cmd.super_command.kind_of?(CommandParser)
331     cmds.unshift(cmd)
332     cmd = cmd.super_command
333   end
334   cmds
335 end
command_parser() click to toggle source

Returns the associated CommandParser instance for this command or nil if no command parser is associated.

    # File lib/cmdparse.rb
339 def command_parser
340   cmd = super_command
341   cmd = cmd.super_command while !cmd.nil? && !cmd.kind_of?(CommandParser)
342   cmd
343 end
execute(*args) click to toggle source

Invokes the action block with the parsed arguments.

This method is called by the CommandParser instance if this command was specified on the command line to be executed.

Sub-classes can either specify an action block or directly override this method (the latter is preferred).

    # File lib/cmdparse.rb
362 def execute(*args)
363   @action.call(*args)
364 end
help() click to toggle source

Returns a string containing the help message for the command.

    # File lib/cmdparse.rb
413 def help
414   output = ''
415   output << help_banner
416   output << help_short_desc
417   output << help_long_desc
418   output << help_commands
419   output << help_arguments
420   output << help_options('Options (take precedence over global options)', options)
421   output << help_options('Global Options', command_parser.global_options)
422 end
help_arguments() click to toggle source

Returns the formatted arguments of this command.

For the output format see cond_format_help_section

    # File lib/cmdparse.rb
539 def help_arguments
540   desc = @argument_desc.map {|k, v| k.to_s.ljust(command_parser.help_desc_indent) << v.to_s}
541   cond_format_help_section('Arguments', desc, condition: !@argument_desc.empty?)
542 end
help_banner() click to toggle source

Returns the banner (including the usage line) of the command.

The usage line is command specific but the rest is the same for all commands and can be set via command_parser.main_options.banner.

    # File lib/cmdparse.rb
428 def help_banner
429   output = ''
430   if command_parser.main_options.banner?
431     output << format(command_parser.main_options.banner, indent: 0) << "\n\n"
432   end
433   output << format(usage, indent: 7) << "\n\n"
434 end
help_commands() click to toggle source

Returns the formatted sub-commands of this command.

For the output format see cond_format_help_section

    # File lib/cmdparse.rb
522 def help_commands
523   describe_commands = lambda do |command, level = 0|
524     command.commands.sort.collect do |name, cmd|
525       str = "  " * level << name << (name == command.default_command ? " (*)" : '')
526       str = str.ljust(command_parser.help_desc_indent) << cmd.short_desc.to_s
527       str = format(str, width: command_parser.help_line_width - command_parser.help_indent,
528                    indent: command_parser.help_desc_indent)
529       str << "\n" << (cmd.takes_commands? ? describe_commands.call(cmd, level + 1) : "")
530     end.join('')
531   end
532   cond_format_help_section("Available commands", describe_commands.call(self),
533                            condition: takes_commands?, preformatted: true)
534 end
help_long_desc() click to toggle source

Returns the formatted detailed description.

For the output format see cond_format_help_section

    # File lib/cmdparse.rb
514 def help_long_desc
515   cond_format_help_section("Description", [long_desc].flatten,
516                            condition: long_desc && !long_desc.empty?)
517 end
help_options(title, options) click to toggle source

Returns the formatted option descriptions for the given OptionParser instance.

The section title needs to be specified with the title argument.

For the output format see cond_format_help_section

    # File lib/cmdparse.rb
549 def help_options(title, options)
550   summary = ''
551   summary_width = command_parser.main_options.summary_width
552   options.summarize([], summary_width, summary_width - 1, '') do |line|
553     summary << format(line, width: command_parser.help_line_width - command_parser.help_indent,
554                       indent: summary_width + 1, indent_first_line: false) << "\n"
555   end
556   cond_format_help_section(title, summary, condition: !summary.empty?, preformatted: true)
557 end
help_short_desc() click to toggle source

Returns the formatted short description.

For the output format see cond_format_help_section

    # File lib/cmdparse.rb
506 def help_short_desc
507   cond_format_help_section("Summary", "#{name} - #{short_desc}",
508                            condition: short_desc && !short_desc.empty?)
509 end
long_desc(*val) click to toggle source

Sets the detailed description of the command if an argument is given. Always returns the detailed description.

This may be a single string or an array of strings for multiline description. Each string is ideally shorter than 76 characters.

    # File lib/cmdparse.rb
381 def long_desc(*val)
382   @long_desc = val.flatten unless val.empty?
383   @long_desc
384 end
Also aliased as: long_desc=
long_desc=(*val)
Alias for: long_desc
on_after_add() click to toggle source

This hook method is called when the command (or one of its super-commands) is added to another Command instance that has an associated command parser (see command_parser).

It can be used, for example, to add global options.

    # File lib/cmdparse.rb
563 def on_after_add
564 end
options {|opts| ...} → opts click to toggle source
options → opts

Yields the OptionParser instance that is used for parsing the options of this command (if a block is given) and returns it.

    # File lib/cmdparse.rb
291 def options #:yields: options
292   yield(@options) if block_given?
293   @options
294 end
short_desc(*val) click to toggle source

Sets the short description of the command if an argument is given. Always returns the short description.

The short description is ideally shorter than 60 characters.

    # File lib/cmdparse.rb
370 def short_desc(*val)
371   @short_desc = val[0] unless val.empty?
372   @short_desc
373 end
Also aliased as: short_desc=
short_desc=(*val)
Alias for: short_desc
takes_arguments?() click to toggle source

Returns true if the command can take one or more arguments.

    # File lib/cmdparse.rb
408 def takes_arguments?
409   arity.abs > 0
410 end
takes_commands(val) click to toggle source

Sets whether this command can take sub-command.

The argument val needs to be true or false.

    # File lib/cmdparse.rb
271 def takes_commands(val)
272   if !val && !commands.empty?
273     raise Error, "Can't change takes_commands to false because there are already sub-commands"
274   else
275     @takes_commands = val
276   end
277 end
Also aliased as: takes_commands=
takes_commands=(val)
Alias for: takes_commands
takes_commands?() click to toggle source

Return true if this command can take sub-commands.

    # File lib/cmdparse.rb
281 def takes_commands?
282   @takes_commands
283 end
usage() click to toggle source

Returns the usage line for the command.

The usage line is automatically generated from the available information. If this is not suitable, override this method to provide a command specific usage line.

Typical usage lines looks like the following:

Usage: program [options] command [options] {sub_command1 | sub_command2}
Usage: program [options] command [options] ARG1 [ARG2] [REST...]

See: usage_options, usage_arguments, usage_commands

    # File lib/cmdparse.rb
447 def usage
448   tmp = "Usage: #{command_parser.main_options.program_name}"
449   tmp << command_parser.main_command.usage_options
450   tmp << command_chain.map {|cmd| " #{cmd.name}#{cmd.usage_options}"}.join('')
451   if takes_commands?
452     tmp << " #{usage_commands}"
453   elsif takes_arguments?
454     tmp << " #{usage_arguments}"
455   end
456   tmp
457 end
usage_arguments() click to toggle source

Returns a string describing the arguments for the command for use in the usage line.

By default the names of the action block or execute method arguments are used (done via Ruby's reflection API). If this is not wanted, override this method.

A typical return value would look like the following:

ARG1 [ARG2] [REST...]

See: usage, argument_desc

    # File lib/cmdparse.rb
482 def usage_arguments
483   (@action || method(:execute)).parameters.map do |type, name|
484     case type
485     when :req then name.to_s
486     when :opt then "[#{name}]"
487     when :rest then "[#{name}...]"
488     end
489   end.join(" ").upcase
490 end
usage_commands() click to toggle source

Returns a string describing the sub-commands of the commands for use in the usage line.

Override this method for providing a command specific specialization.

A typical return value would look like the following:

{command | other_command | another_command }
    # File lib/cmdparse.rb
499 def usage_commands
500   (commands.empty? ? '' : "{#{commands.keys.sort.join(" | ")}}")
501 end
usage_options() click to toggle source

Returns a string describing the options of the command for use in the usage line.

If there are any options, the resulting string also includes a leading space!

A typical return value would look like the following:

[options]

See: usage

    # File lib/cmdparse.rb
468 def usage_options
469   (options.options_defined? ? ' [options]' : '')
470 end

Protected Instance Methods

cond_format_help_section(title, *lines, condition: true, indent: true, preformatted: false) click to toggle source

Conditionally formats a help section.

Returns either the formatted help section if the condition is true or an empty string otherwise.

The help section starts with a title and the given lines are indented to easily distinguish different sections.

A typical help section would look like the following:

Summary:
    help - Provide help for individual commands

Options:

condition

The formatted help section is only returned if the condition is true.

indent

Whether the lines should be indented with CommandParser#help_indent spaces.

preformatted

Assume that the given lines are already correctly formatted and don't try to reformat them.

    # File lib/cmdparse.rb
594 def cond_format_help_section(title, *lines, condition: true, indent: true, preformatted: false)
595   if condition
596     out = "#{title}:\n"
597     lines = lines.flatten.join("\n").split(/\n/)
598     if preformatted
599       lines.map! {|l| ' ' * command_parser.help_indent << l} if indent
600       out << lines.join("\n")
601     else
602       out << format(lines.join("\n"), indent: (indent ? command_parser.help_indent : 0), indent_first_line: true)
603     end
604     out << "\n\n"
605   else
606     ''
607   end
608 end
format(content, width: command_parser.help_line_width, indent: command_parser.help_indent, indent_first_line: false) click to toggle source

Returns the text in content formatted so that no line is longer than width characters.

Options:

width

The maximum width of a line. If not specified, the CommandParser#help_line_width value is used.

indent

This option specifies the amount of spaces prepended to each line. If not specified, the CommandParser#help_indent value is used.

indent_first_line

If this option is true, then the first line is also indented.

    # File lib/cmdparse.rb
621 def format(content, width: command_parser.help_line_width,
622            indent: command_parser.help_indent, indent_first_line: false)
623   content = (content || '').dup
624   line_length = width - indent
625   first_line_pattern = other_lines_pattern = /\A.{1,#{line_length}}\z|\A.{1,#{line_length}}[ \n]/m
626   (first_line_pattern = /\A.{1,#{width}}\z|\A.{1,#{width}}[ \n]/m) unless indent_first_line
627   pattern = first_line_pattern
628 
629   content.split(/\n\n/).map do |paragraph|
630     lines = []
631     until paragraph.empty?
632       unless (str = paragraph.slice!(pattern)) and (str = str.sub(/[ \n]\z/, ''))
633         str = paragraph.slice!(0, line_length)
634       end
635       lines << (lines.empty? && !indent_first_line ? '' : ' ' * indent) + str.tr("\n", ' ')
636       pattern = other_lines_pattern
637     end
638     lines.join("\n")
639   end.join("\n\n")
640 end