# 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, ParserPartitioningDelegate

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

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

  Whether or not this command has parameters.

- #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<Cri::OptionDefinition>

  The list of option definitions.

- #parameter_definitions ⇒ Array<Hash>

  The list of parameter 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.

- .load_file(filename, infer_name: false) ⇒ Cri::Command

  Creates a new command using a DSL, from code defined in the given filename.

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

- #all_opt_defns ⇒ Object
- #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 ⇒ Enumerable<Cri::OptionDefinition>

  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.

    171
    172
    173
    174
    175
    176
    177
    178

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

    def initialize
      @aliases            = Set.new
      @commands           = Set.new
      @option_definitions = Set.new
      @parameter_definitions = []
      @explicitly_no_params = false
      @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

    75
    76
    77

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

    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

    108
    109
    110

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

    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)

    104
    105
    106

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

    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

    64
    65
    66

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

    def commands
      @commands
    end

### #default_subcommand_name ⇒ `Symbol`

Returns The name of the default subcommand.

Returns:

- (`Symbol`) —

  The name of the default subcommand

    68
    69
    70

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

    def default_subcommand_name
      @default_subcommand_name
    end

### #description ⇒ `String`

Returns The long description (“description”).

Returns:

- (`String`) —

  The long description (“description”)

    81
    82
    83

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

    def description
      @description
    end

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

Returns Whether or not this command has parameters.

Returns:

- (`Boolean`) —

  Whether or not this command has parameters

    99
    100
    101

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

    def explicitly_no_params
      @explicitly_no_params
    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

    89
    90
    91

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

    def hidden
      @hidden
    end

### #name ⇒ `String`

Returns The name.

Returns:

- (`String`) —

  The name

    71
    72
    73

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

    def name
      @name
    end

### #option_definitions ⇒ `Array<Cri::OptionDefinition>`

Returns The list of option definitions.

Returns:

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

  The list of option definitions

    93
    94
    95

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

    def option_definitions
      @option_definitions
    end

### #parameter_definitions ⇒ `Array<Hash>`

Returns The list of parameter definitions.

Returns:

- (`Array<Hash>`) —

  The list of parameter definitions

    96
    97
    98

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

    def parameter_definitions
      @parameter_definitions
    end

### #summary ⇒ `String`

Returns The short description (“summary”).

Returns:

- (`String`) —

  The short description (“summary”)

    78
    79
    80

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

    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

    61
    62
    63

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

    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.

    85
    86
    87

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

    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

    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135

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

    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

### .load_file(filename, infer_name: false) ⇒ `Cri::Command`

Creates a new command using a DSL, from code defined in the given filename.

Parameters:

- filename (`String`) —

  The filename that contains the command definition as a string

Returns:

- (`Cri::Command`) —

  The newly defined command

    143
    144
    145
    146
    147
    148
    149
    150
    151

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

    def self.load_file(filename, infer_name: false)
      code = File.read(filename, encoding: 'UTF-8')
      define(code, filename).tap do |cmd|
        if infer_name
          command_name = File.basename(filename, '.rb')
          cmd.modify { name command_name }
        end
      end
    end

### .new_basic_help ⇒ `Cri::Command`

Returns a new command that implements showing help.

Returns:

- (`Cri::Command`) —

  A basic help command

    166
    167
    168
    169

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

    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

    158
    159
    160
    161

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

    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<=>

    391
    392
    393

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

    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

    211
    212
    213
    214

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

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

### #all_opt_defns ⇒ `Object`

    365
    366
    367
    368
    369
    370
    371

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

    def all_opt_defns
      if supercommand
        supercommand.all_opt_defns | option_definitions
      else
        option_definitions
      end
    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

    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281

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

    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

    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255

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

    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

    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236

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

    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 ⇒ `Enumerable<Cri::OptionDefinition>`

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

Returns:

- (`Enumerable<Cri::OptionDefinition>`) —

  The option definitions for the command itself and all its ancestors

    199
    200
    201
    202
    203
    204

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

    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`) —

  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

    380
    381
    382

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

    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

    187
    188
    189
    190
    191
    192
    193
    194
    195

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

    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

    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320

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

    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, parent_opts.merge(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

    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363

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

    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::Parser.new(
          opts_and_args,
          global_option_definitions,
          parameter_definitions,
          explicitly_no_params?,
        )
        handle_errors_while { parser.run }
        local_opts = parser.options
        global_opts = parent_opts.merge(parser.options)
        global_opts = add_defaults(global_opts)

        # Handle options
        handle_options(local_opts)
        args = handle_errors_while { parser.gen_argument_list }
      end

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