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:
-
HelpCommand
(for showing help messages) and -
VersionCommand
(for showing version information).
Attributes
Returns the mapping of command name to command for all sub-commands of this command.
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.
Returns the name of the default sub-command or nil
if there isn't any.
The name of the command.
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
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
For sorting commands by name.
# File lib/cmdparse.rb 567 def <=>(other) 568 name <=> other.name 569 end
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
Return true
if this command can take sub-commands.
# File lib/cmdparse.rb 281 def takes_commands? 282 @takes_commands 283 end
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
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
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
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
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
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