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.

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

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.



257
258
259
260
261
262
263
264
265
266
# File 'lib/cmdparse.rb', line 257

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.



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

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.



250
251
252
# File 'lib/cmdparse.rb', line 250

def data
  @data
end

#default_commandObject (readonly)

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



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

def default_command
  @default_command
end

#nameObject (readonly)

The name of the command.



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

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



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

def super_command
  @super_command
end

Instance Method Details

#<=>(other) ⇒ Object

For sorting commands by name.



567
568
569
# File 'lib/cmdparse.rb', line 567

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



351
352
353
# File 'lib/cmdparse.rb', line 351

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:



309
310
311
312
313
314
315
316
317
318
319
320
# File 'lib/cmdparse.rb', line 309

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.



393
394
395
# File 'lib/cmdparse.rb', line 393

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



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

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.



327
328
329
330
331
332
333
334
335
# File 'lib/cmdparse.rb', line 327

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.



339
340
341
342
343
# File 'lib/cmdparse.rb', line 339

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



362
363
364
# File 'lib/cmdparse.rb', line 362

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

#helpObject

Returns a string containing the help message for the command.



413
414
415
416
417
418
419
420
421
422
# File 'lib/cmdparse.rb', line 413

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



539
540
541
542
# File 'lib/cmdparse.rb', line 539

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.



428
429
430
431
432
433
434
# File 'lib/cmdparse.rb', line 428

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



522
523
524
525
526
527
528
529
530
531
532
533
534
# File 'lib/cmdparse.rb', line 522

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



514
515
516
517
# File 'lib/cmdparse.rb', line 514

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



549
550
551
552
553
554
555
556
557
# File 'lib/cmdparse.rb', line 549

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



506
507
508
509
# File 'lib/cmdparse.rb', line 506

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.



381
382
383
384
# File 'lib/cmdparse.rb', line 381

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.



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

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:



291
292
293
294
# File 'lib/cmdparse.rb', line 291

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.



370
371
372
373
# File 'lib/cmdparse.rb', line 370

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)


408
409
410
# File 'lib/cmdparse.rb', line 408

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.



271
272
273
274
275
276
277
# File 'lib/cmdparse.rb', line 271

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)


281
282
283
# File 'lib/cmdparse.rb', line 281

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



447
448
449
450
451
452
453
454
455
456
457
# File 'lib/cmdparse.rb', line 447

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



482
483
484
485
486
487
488
489
490
# File 'lib/cmdparse.rb', line 482

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 }


499
500
501
# File 'lib/cmdparse.rb', line 499

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



468
469
470
# File 'lib/cmdparse.rb', line 468

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