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



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

#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



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

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



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

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)



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

def block
  @block
end

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

Returns This command’s subcommands

Returns:



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

def commands
  @commands
end

#default_subcommand_nameSymbol

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

#descriptionString

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

#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



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

def hidden
  @hidden
end

#nameString

Returns The name

Returns:

  • (String)

    The name



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

def name
  @name
end

#option_definitionsArray<Cri::OptionDefinition>

Returns The list of option definitions

Returns:



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

def option_definitions
  @option_definitions
end

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

#summaryString

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

#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



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

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.



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:



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:



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

Returns a new command that implements showing help.

Returns:



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



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:

Returns:

  • (-1, 0, 1)

    The result of the comparison between names

See Also:

  • Object<=>


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

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

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



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:



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



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



369
370
371
# File 'lib/cri/command.rb', line 369

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:



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:



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

    # 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