Class: Cri::Command

Inherits:
Object
  • Object
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

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeCommand

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

#aliasesArray<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_argsBoolean 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

#blockProc

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

#commandsSet<Cri::Command> Also known as: subcommands

Returns This command’s subcommands.

Returns:



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

def commands
  @commands
end

#default_subcommand_nameSymbol

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

#descriptionString

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

#hiddenBoolean 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

#nameString

Returns The name.

Returns:

  • (String)

    The name



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

def name
  @name
end

#option_definitionsArray<Cri::OptionDefinition>

Returns The list of option definitions.

Returns:



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

def option_definitions
  @option_definitions
end

#parameter_definitionsArray<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

#summaryString

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

#supercommandCri::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

#usageString

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:



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:



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_helpCri::Command

Returns a new command that implements showing help.

Returns:



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_rootCri::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:



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:

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_defnsObject



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:



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:



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_definitionsEnumerable<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:



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:



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