# 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

    169
    170
    171
    172
    173
    174
    175
    176

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

    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

    73
    74
    75

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

    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

    106
    107
    108

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

    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)

    102
    103
    104

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

    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

    62
    63
    64

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

    def commands
      @commands
    end

### #default_subcommand_name ⇒ `Symbol`

Returns The name of the default subcommand

Returns:

- (`Symbol`) —

  The name of the default subcommand

    66
    67
    68

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

    def default_subcommand_name
      @default_subcommand_name
    end

### #description ⇒ `String`

Returns The long description (“description”)

Returns:

- (`String`) —

  The long description (“description”)

    79
    80
    81

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

    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

    97
    98
    99

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

    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

    87
    88
    89

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

    def hidden
      @hidden
    end

### #name ⇒ `String`

Returns The name

Returns:

- (`String`) —

  The name

    69
    70
    71

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

    def name
      @name
    end

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

Returns The list of option definitions

Returns:

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

  The list of option definitions

    91
    92
    93

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

    def option_definitions
      @option_definitions
    end

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

Returns The list of parameter definitions

Returns:

- (`Array<Hash>`) —

  The list of parameter definitions

    94
    95
    96

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

    def parameter_definitions
      @parameter_definitions
    end

### #summary ⇒ `String`

Returns The short description (“summary”)

Returns:

- (`String`) —

  The short description (“summary”)

    76
    77
    78

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

    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

    59
    60
    61

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

    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.

    83
    84
    85

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

    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

    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133

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

    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

    141
    142
    143
    144
    145
    146
    147
    148
    149

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

    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

    164
    165
    166
    167

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

    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

    156
    157
    158
    159

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

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

    389
    390
    391

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

    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

    209
    210
    211
    212

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

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

### #all_opt_defns ⇒ `Object`

    363
    364
    365
    366
    367
    368
    369

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

    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

    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279

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

    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

    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253

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

    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

    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234

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

    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

    197
    198
    199
    200
    201
    202

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

    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

    378
    379
    380

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

    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

    185
    186
    187
    188
    189
    190
    191
    192
    193

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

    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

    290
    291
    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

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

    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

    333
    334
    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

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

    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