Class: CmdParse::Command

Inherits:
Object
  • Object
show all
Defined in:
lib/cmdparse.rb

Overview

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.

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).

Direct Known Subclasses

HelpCommand, VersionCommand

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, takes_commands: true) ⇒ Command

Initializes the command called name.

Options:

takes_commands

Specifies whether this command can take sub-commands.



253
254
255
256
257
258
259
260
261
262
# File 'lib/cmdparse.rb', line 253

def initialize(name, takes_commands: true)
  @name = name.freeze
  @options = OptionParser.new
  @commands = CommandHash.new
  @default_command = nil
  @action = nil
  @argument_desc ||= {}
  @data = {}
  takes_commands(takes_commands)
end

Instance Attribute Details

#commandsObject (readonly)

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



242
243
244
# File 'lib/cmdparse.rb', line 242

def commands
  @commands
end

#dataObject

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.



246
247
248
# File 'lib/cmdparse.rb', line 246

def data
  @data
end

#default_commandObject (readonly)

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



234
235
236
# File 'lib/cmdparse.rb', line 234

def default_command
  @default_command
end

#nameObject (readonly)

The name of the command.



231
232
233
# File 'lib/cmdparse.rb', line 231

def name
  @name
end

#super_commandObject

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).



239
240
241
# File 'lib/cmdparse.rb', line 239

def super_command
  @super_command
end

Instance Method Details

#<=>(other) ⇒ Object

For sorting commands by name.



563
564
565
# File 'lib/cmdparse.rb', line 563

def <=>(other)
  name <=> other.name
end

#action(&block) ⇒ Object

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



347
348
349
# File 'lib/cmdparse.rb', line 347

def action(&block)
  @action = block
end

#add_command(command, default: false) {|command| ... } ⇒ Object

:call-seq:

command.add_command(other_command, default: false) {|cmd| ... }     -> command
command.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.

Yields:

  • (command)

Raises:



305
306
307
308
309
310
311
312
313
314
315
316
# File 'lib/cmdparse.rb', line 305

def add_command(command, default: false) # :yields: command_object
  raise TakesNoCommandError.new(name) unless takes_commands?

  command = Command.new(command) if command.kind_of?(String)
  command.super_command = self
  @commands[command.name] = command
  @default_command = command.name if default
  command.fire_hook_after_add
  yield(command) if block_given?

  self
end

#argument_desc(hash) ⇒ Object

:call-seq:

cmd.argument_desc(name => desc, ...)

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

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



389
390
391
# File 'lib/cmdparse.rb', line 389

def argument_desc(hash)
  @argument_desc.update(hash)
end

#arityObject

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



399
400
401
# File 'lib/cmdparse.rb', line 399

def arity
  (@action || method(:execute)).arity
end

#command_chainObject

:call-seq:

command.command_chain   -> [top_level_command, super_command, ..., command]

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



323
324
325
326
327
328
329
330
331
# File 'lib/cmdparse.rb', line 323

def command_chain
  cmds = []
  cmd = self
  while !cmd.nil? && !cmd.super_command.kind_of?(CommandParser)
    cmds.unshift(cmd)
    cmd = cmd.super_command
  end
  cmds
end

#command_parserObject

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



335
336
337
338
339
# File 'lib/cmdparse.rb', line 335

def command_parser
  cmd = super_command
  cmd = cmd.super_command while !cmd.nil? && !cmd.kind_of?(CommandParser)
  cmd
end

#execute(*args) ⇒ Object

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).



358
359
360
# File 'lib/cmdparse.rb', line 358

def execute(*args)
  @action.call(*args)
end

#helpObject

Returns a string containing the help message for the command.



409
410
411
412
413
414
415
416
417
418
# File 'lib/cmdparse.rb', line 409

def help
  output = ''
  output << help_banner
  output << help_short_desc
  output << help_long_desc
  output << help_commands
  output << help_arguments
  output << help_options('Options (take precedence over global options)', options)
  output << help_options('Global Options', command_parser.global_options)
end

#help_argumentsObject

Returns the formatted arguments of this command.

For the output format see #cond_format_help_section



535
536
537
538
# File 'lib/cmdparse.rb', line 535

def help_arguments
  desc = @argument_desc.map {|k, v| k.to_s.ljust(command_parser.help_desc_indent) << v.to_s}
  cond_format_help_section('Arguments', desc, condition: !@argument_desc.empty?)
end

#help_bannerObject

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.



424
425
426
427
428
429
430
# File 'lib/cmdparse.rb', line 424

def help_banner
  output = ''
  if command_parser.main_options.banner?
    output << format(command_parser.main_options.banner, indent: 0) << "\n\n"
  end
  output << format(usage, indent: 7) << "\n\n"
end

#help_commandsObject

Returns the formatted sub-commands of this command.

For the output format see #cond_format_help_section



518
519
520
521
522
523
524
525
526
527
528
529
530
# File 'lib/cmdparse.rb', line 518

def help_commands
  describe_commands = lambda do |command, level = 0|
    command.commands.sort.collect do |name, cmd|
      str = "  " * level << name << (name == command.default_command ? " (*)" : '')
      str = str.ljust(command_parser.help_desc_indent) << cmd.short_desc.to_s
      str = format(str, width: command_parser.help_line_width - command_parser.help_indent,
                   indent: command_parser.help_desc_indent)
      str << "\n" << (cmd.takes_commands? ? describe_commands.call(cmd, level + 1) : "")
    end.join('')
  end
  cond_format_help_section("Available commands", describe_commands.call(self),
                           condition: takes_commands?, preformatted: true)
end

#help_long_descObject

Returns the formatted detailed description.

For the output format see #cond_format_help_section



510
511
512
513
# File 'lib/cmdparse.rb', line 510

def help_long_desc
  cond_format_help_section("Description", [long_desc].flatten,
                           condition: long_desc && !long_desc.empty?)
end

#help_options(title, options) ⇒ Object

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



545
546
547
548
549
550
551
552
553
# File 'lib/cmdparse.rb', line 545

def help_options(title, options)
  summary = ''
  summary_width = command_parser.main_options.summary_width
  options.summarize([], summary_width, summary_width - 1, '') do |line|
    summary << format(line, width: command_parser.help_line_width - command_parser.help_indent,
                      indent: summary_width + 1, indent_first_line: false) << "\n"
  end
  cond_format_help_section(title, summary, condition: !summary.empty?, preformatted: true)
end

#help_short_descObject

Returns the formatted short description.

For the output format see #cond_format_help_section



502
503
504
505
# File 'lib/cmdparse.rb', line 502

def help_short_desc
  cond_format_help_section("Summary", "#{name} - #{short_desc}",
                           condition: short_desc && !short_desc.empty?)
end

#long_desc(*val) ⇒ Object Also known as: long_desc=

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.



377
378
379
380
# File 'lib/cmdparse.rb', line 377

def long_desc(*val)
  @long_desc = val.flatten unless val.empty?
  @long_desc
end

#on_after_addObject

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.



559
560
# File 'lib/cmdparse.rb', line 559

def on_after_add
end

#options {|@options| ... } ⇒ Object

:call-seq:

command.options {|opts| ...}   -> opts
command.options                -> opts

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

Yields:



287
288
289
290
# File 'lib/cmdparse.rb', line 287

def options #:yields: options
  yield(@options) if block_given?
  @options
end

#short_desc(*val) ⇒ Object Also known as: short_desc=

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.



366
367
368
369
# File 'lib/cmdparse.rb', line 366

def short_desc(*val)
  @short_desc = val[0] unless val.empty?
  @short_desc
end

#takes_arguments?Boolean

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

Returns:

  • (Boolean)


404
405
406
# File 'lib/cmdparse.rb', line 404

def takes_arguments?
  arity.abs > 0
end

#takes_commands(val) ⇒ Object Also known as: takes_commands=

Sets whether this command can take sub-command.

The argument val needs to be true or false.



267
268
269
270
271
272
273
# File 'lib/cmdparse.rb', line 267

def takes_commands(val)
  if !val && !commands.empty?
    raise Error, "Can't change takes_commands to false because there are already sub-commands"
  else
    @takes_commands = val
  end
end

#takes_commands?Boolean

Return true if this command can take sub-commands.

Returns:

  • (Boolean)


277
278
279
# File 'lib/cmdparse.rb', line 277

def takes_commands?
  @takes_commands
end

#usageObject

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



443
444
445
446
447
448
449
450
451
452
453
# File 'lib/cmdparse.rb', line 443

def usage
  tmp = "Usage: #{command_parser.main_options.program_name}"
  tmp << command_parser.main_command.usage_options
  tmp << command_chain.map {|cmd| " #{cmd.name}#{cmd.usage_options}"}.join('')
  if takes_commands?
    tmp << " #{usage_commands}"
  elsif takes_arguments?
    tmp << " #{usage_arguments}"
  end
  tmp
end

#usage_argumentsObject

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



478
479
480
481
482
483
484
485
486
# File 'lib/cmdparse.rb', line 478

def usage_arguments
  (@action || method(:execute)).parameters.map do |type, name|
    case type
    when :req then name.to_s
    when :opt then "[#{name}]"
    when :rest then "[#{name}...]"
    end
  end.join(" ").upcase
end

#usage_commandsObject

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 }


495
496
497
# File 'lib/cmdparse.rb', line 495

def usage_commands
  (commands.empty? ? '' : "{#{commands.keys.sort.join(" | ")}}")
end

#usage_optionsObject

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



464
465
466
# File 'lib/cmdparse.rb', line 464

def usage_options
  (options.options_defined? ? ' [options]' : '')
end