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, OptionParserPartitioningDelegate

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeCommand

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

#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


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

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


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

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)


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

def block
  @block
end

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

Returns This command’s subcommands.

Returns:


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

def commands
  @commands
end

#default_subcommand_nameSymbol

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

#descriptionString

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

#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


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

def hidden
  @hidden
end

#nameString

Returns The name.

Returns:


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

def name
  @name
end

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

#summaryString

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

#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


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

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.


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:


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

Returns a new command that implements showing help.

Returns:


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


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:

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:


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:


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_definitionsHash

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:


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:


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