# Class: Cri::Command

Inherits:
  Object
  - Object
  - Cri::Command
  show all

Defined in:
  lib/cri/command.rb

## Overview

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

## Defined Under Namespace

Classes: CriExitException, OptionParserPartitioningDelegate

## Instance Attribute Summary collapse

- #aliases ⇒ Array<String>

  A list of aliases for this command that can be used to invoke this command.

- #all_opts_as_args ⇒ Boolean (also: #all_opts_as_args?)

  treat all options as arguments.

- #block ⇒ Proc

  The block that should be executed when invoking this command (ignored for commands with subcommands).

- #commands ⇒ Set<Cri::Command> (also: #subcommands)

  This command’s subcommands.

- #default_subcommand_name ⇒ Symbol

  The name of the default subcommand.

- #description ⇒ String

  The long description (“description”).

- #hidden ⇒ Boolean (also: #hidden?)

  True if the command is hidden (e.g. because it is deprecated), false otherwise.

- #name ⇒ String

  The name.

- #option_definitions ⇒ Array<Hash>

  The list of option definitions.

- #summary ⇒ String

  The short description (“summary”).

- #supercommand ⇒ Cri::Command?

  This command’s supercommand, or nil if the command has no supercommand.

- #usage ⇒ String

  The usage, without the “usage:” prefix and without the supercommands’ names.

## Class Method Summary collapse

- .define(string = nil, filename = nil, &block) ⇒ Cri::Command

  Creates a new command using the DSL.

- .new_basic_help ⇒ Cri::Command

  Returns a new command that implements showing help.

- .new_basic_root ⇒ Cri::Command

  Returns a new command that has support for the `-h`/`--help` option and also has a `help` subcommand.

## Instance Method Summary collapse

- #<=>(other) ⇒ -1, ...

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

- #add_command(command) ⇒ void

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

- #command_named(name, hard_exit: true) ⇒ Cri::Command

  Returns the command with the given name.

- #commands_named(name) ⇒ Array<Cri::Command>

  Returns the commands that could be referred to with the given name.

- #define_command(name = nil, &block) ⇒ Cri::Command

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

- #global_option_definitions ⇒ Hash

  The option definitions for the command itself and all its ancestors.

- #help(params = {}) ⇒ String

  The help text for this command.

- #initialize ⇒ Command constructor

  A new instance of Command.

- #modify(&block) ⇒ Cri::Command

  Modifies the command using the DSL.

- #run(opts_and_args, parent_opts = {}, hard_exit: true) ⇒ void

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

- #run_this(opts_and_args, parent_opts = {}) ⇒ void

  Runs the actual command with the given command-line arguments, not invoking any subcommands.

## Constructor Details

### #initialize ⇒ `Command`

Returns a new instance of Command

    144
    145
    146
    147
    148
    149

    # File 'lib/cri/command.rb', line 144

    def initialize
      @aliases            = Set.new
      @commands           = Set.new
      @option_definitions = Set.new
      @default_subcommand_name = nil
    end

## Instance Attribute Details

### #aliases ⇒ `Array<String>`

Returns A list of aliases for this command that can be used to invoke this command

Returns:

- (`Array<String>`) —

  A list of aliases for this command that can be used to invoke this command

    71
    72
    73

    # File 'lib/cri/command.rb', line 71

    def aliases
      @aliases
    end

### #all_opts_as_args ⇒ `Boolean` Also known as: all_opts_as_args?

treat all options as arguments.

Returns:

- (`Boolean`) —

  true if the command should skip option parsing and

    97
    98
    99

    # File 'lib/cri/command.rb', line 97

    def all_opts_as_args
      @all_opts_as_args
    end

### #block ⇒ `Proc`

Returns The block that should be executed when invoking this command (ignored for commands with subcommands)

Returns:

- (`Proc`) —

  The block that should be executed when invoking this command (ignored for commands with subcommands)

    93
    94
    95

    # File 'lib/cri/command.rb', line 93

    def block
      @block
    end

### #commands ⇒ `Set<Cri::Command>` Also known as: subcommands

Returns This command’s subcommands

Returns:

- (`Set<Cri::Command>`) —

  This command’s subcommands

    60
    61
    62

    # File 'lib/cri/command.rb', line 60

    def commands
      @commands
    end

### #default_subcommand_name ⇒ `Symbol`

Returns The name of the default subcommand

Returns:

- (`Symbol`) —

  The name of the default subcommand

    64
    65
    66

    # File 'lib/cri/command.rb', line 64

    def default_subcommand_name
      @default_subcommand_name
    end

### #description ⇒ `String`

Returns The long description (“description”)

Returns:

- (`String`) —

  The long description (“description”)

    77
    78
    79

    # File 'lib/cri/command.rb', line 77

    def description
      @description
    end

### #hidden ⇒ `Boolean` Also known as: hidden?

Returns true if the command is hidden (e.g. because it is deprecated), false otherwise

Returns:

- (`Boolean`) —

  true if the command is hidden (e.g. because it is deprecated), false otherwise

    85
    86
    87

    # File 'lib/cri/command.rb', line 85

    def hidden
      @hidden
    end

### #name ⇒ `String`

Returns The name

Returns:

- (`String`) —

  The name

    67
    68
    69

    # File 'lib/cri/command.rb', line 67

    def name
      @name
    end

### #option_definitions ⇒ `Array<Hash>`

Returns The list of option definitions

Returns:

- (`Array<Hash>`) —

  The list of option definitions

    89
    90
    91

    # File 'lib/cri/command.rb', line 89

    def option_definitions
      @option_definitions
    end

### #summary ⇒ `String`

Returns The short description (“summary”)

Returns:

- (`String`) —

  The short description (“summary”)

    74
    75
    76

    # File 'lib/cri/command.rb', line 74

    def summary
      @summary
    end

### #supercommand ⇒ `Cri::Command`?

Returns This command’s supercommand, or nil if the command has no supercommand

Returns:

- (`Cri::Command`, `nil`) —

  This command’s supercommand, or nil if the command has no supercommand

    57
    58
    59

    # File 'lib/cri/command.rb', line 57

    def supercommand
      @supercommand
    end

### #usage ⇒ `String`

Returns The usage, without the “usage:” prefix and without the supercommands’ names.

Returns:

- (`String`) —

  The usage, without the “usage:” prefix and without the supercommands’ names.

    81
    82
    83

    # File 'lib/cri/command.rb', line 81

    def usage
      @usage
    end

## Class Method Details

### .define(string = nil, filename = nil, &block) ⇒ `Cri::Command`

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.

Parameters:

- string (`String`, `nil`) (defaults to: `nil`) —

  The command definition as a string

- filename (`String`, `nil`) (defaults to: `nil`) —

  The filename corresponding to the string parameter (only useful if a string is given)

Returns:

- (`Cri::Command`) —

  The newly defined command

    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124

    # File 'lib/cri/command.rb', line 113

    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_basic_help ⇒ `Cri::Command`

Returns a new command that implements showing help.

Returns:

- (`Cri::Command`) —

  A basic help command

    139
    140
    141
    142

    # File 'lib/cri/command.rb', line 139

    def self.new_basic_help
      filename = File.dirname(__FILE__) + '/commands/basic_help.rb'
      define(File.read(filename))
    end

### .new_basic_root ⇒ `Cri::Command`

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, …)

Returns:

- (`Cri::Command`) —

  A basic root command

    131
    132
    133
    134

    # File 'lib/cri/command.rb', line 131

    def self.new_basic_root
      filename = File.dirname(__FILE__) + '/commands/basic_root.rb'
      define(File.read(filename))
    end

## Instance Method Details

### #<=>(other) ⇒ `-1`, ...

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

Parameters:

- other (`Cri::Command`) —

  The command to compare with

Returns:

- (`-1`, `0`, `1`) —

  The result of the comparison between names

See Also:

- Object<=>

    350
    351
    352

    # File 'lib/cri/command.rb', line 350

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

### #add_command(command) ⇒ `void`

This method returns an undefined value.

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

Parameters:

- command (`Cri::Command`) —

  The command to add as a subcommand

    182
    183
    184
    185

    # File 'lib/cri/command.rb', line 182

    def add_command(command)
      @commands << command
      command.supercommand = self
    end

### #command_named(name, hard_exit: true) ⇒ `Cri::Command`

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

Parameters:

- name (`String`) —

  The full, partial or aliases name of the command

Returns:

- (`Cri::Command`) —

  The command with the given name

    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252

    # File 'lib/cri/command.rb', line 237

    def command_named(name, hard_exit: true)
      commands = commands_named(name)

      if commands.empty?
        warn "#{self.name}: unknown command '#{name}'\n"
        raise CriExitException.new(is_error: true)
      elsif commands.size > 1
        warn "#{self.name}: '#{name}' is ambiguous:"
        warn "  #{commands.map(&:name).sort.join(' ')}"
        raise CriExitException.new(is_error: true)
      else
        commands[0]
      end
    rescue CriExitException => e
      exit(e.error? ? 1 : 0) if hard_exit
    end

### #commands_named(name) ⇒ `Array<Cri::Command>`

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

Parameters:

- name (`String`) —

  The full, partial or aliases name of the command

Returns:

- (`Array<Cri::Command>`) —

  A list of commands matching the given name

    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226

    # File 'lib/cri/command.rb', line 215

    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) ⇒ `Cri::Command`

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

Parameters:

- name (`String`, `nil`) (defaults to: `nil`) —

  The name of the subcommand, or nil if no name should be set (yet)

Returns:

- (`Cri::Command`) —

  The subcommand

    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207

    # File 'lib/cri/command.rb', line 193

    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
        yield(dsl)
      end

      # Create command
      cmd = dsl.command
      add_command(cmd)
      cmd
    end

### #global_option_definitions ⇒ `Hash`

Returns The option definitions for the command itself and all its ancestors

Returns:

- (`Hash`) —

  The option definitions for the command itself and all its ancestors

    170
    171
    172
    173
    174
    175

    # File 'lib/cri/command.rb', line 170

    def global_option_definitions
      res = Set.new
      res.merge(option_definitions)
      res.merge(supercommand.global_option_definitions) if supercommand
      res
    end

### #help(params = {}) ⇒ `String`

Returns The help text for this command

Parameters:

- params (`Hash`) (defaults to: `{}`) —

  a customizable set of options

Options Hash (`params`):

- :verbose (`Boolean`) —

  true if the help output should be verbose, false otherwise.

- :io (`IO`) — default: `$stdout` —

  the IO the help text is intended for. This influences the decision to enable/disable colored output.

Returns:

- (`String`) —

  The help text for this command

    339
    340
    341

    # File 'lib/cri/command.rb', line 339

    def help(params = {})
      HelpRenderer.new(self, params).render
    end

### #modify(&block) ⇒ `Cri::Command`

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.

Returns:

- (`Cri::Command`) —

  The command itself

    158
    159
    160
    161
    162
    163
    164
    165
    166

    # File 'lib/cri/command.rb', line 158

    def modify(&block)
      dsl = Cri::CommandDSL.new(self)
      if [-1, 0].include? block.arity
        dsl.instance_eval(&block)
      else
        yield(dsl)
      end
      self
    end

### #run(opts_and_args, parent_opts = {}, hard_exit: true) ⇒ `void`

This method returns an undefined value.

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

Parameters:

- opts_and_args (`Array<String>`) —

  A list of unparsed arguments

- parent_opts (`Hash`) (defaults to: `{}`) —

  A hash of options already handled by the supercommand

    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291

    # File 'lib/cri/command.rb', line 263

    def run(opts_and_args, parent_opts = {}, hard_exit: true)
      # 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? && !block.nil?)
        run_this(opts_and_args, parent_opts)
      else
        # Handle options
        handle_options(opts_before_subcmd)

        # Get command
        if subcmd_name.nil?
          if default_subcommand_name
            subcmd_name = default_subcommand_name
          else
            warn "#{name}: no command given"
            raise CriExitException.new(is_error: true)
          end
        end
        subcommand = command_named(subcmd_name, hard_exit: hard_exit)
        return if subcommand.nil?

        # Run
        subcommand.run(opts_and_args_after_subcmd, opts_before_subcmd, hard_exit: hard_exit)
      end
    rescue CriExitException => e
      exit(e.error? ? 1 : 0) if hard_exit
    end

### #run_this(opts_and_args, parent_opts = {}) ⇒ `void`

This method returns an undefined value.

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

Parameters:

- opts_and_args (`Array<String>`) —

  A list of unparsed arguments

- parent_opts (`Hash`) (defaults to: `{}`) —

  A hash of options already handled by the supercommand

Raises:

- (`NotImplementedError`) —

  if the command does not have an execution block

    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330

    # File 'lib/cri/command.rb', line 306

    def run_this(opts_and_args, parent_opts = {})
      if all_opts_as_args?
        args = opts_and_args
        global_opts = parent_opts
      else
        # Parse
        parser = Cri::OptionParser.new(
          opts_and_args, global_option_definitions
        )
        handle_parser_errors_while { parser.run }
        local_opts  = parser.options
        global_opts = parent_opts.merge(parser.options)
        args = parser.arguments

        # Handle options
        handle_options(local_opts)
      end

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