Class: Brut::CLI::Command

Inherits:
Object
  • Object
show all
Includes:
ExecutionResults, Framework::Errors, I18n::ForCLI
Defined in:
lib/brut/cli/command.rb

Overview

Base class for subcommands of a App. You must implement #execute to perform whatever action this command must perform.

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Framework::Errors

#abstract_method!, #bug!

Methods included from I18n::ForCLI

#capture, #html_escape, #safe

Methods included from I18n::BaseMethods

#l, #t, #t_direct, #this_field_value

Methods included from ExecutionResults

#abort_execution, #as_execution_result, #cli_usage_error, #continue_execution, #show_cli_usage, #stop_execution

Constructor Details

#initialize(command_options:, global_options:, args:, out:, err:, executor:) ⇒ Command

Creates the command before executing it. Generally you would not call this directly.

Parameters:

  • command_options (Brut::CLI::Options)

    the command options parsed from the command line.

  • global_options (Brut::CLI::Options)

    the global options parsed from the command line.

  • args (Array)

    Any unparsed arguments

  • out (Brut::CLI::Output)

    an IO used to send messages to the standard output

  • err (Brut::CLI::Output)

    an IO used to send messages to the standard error

  • executor (Brut::CLI::Executor)

    used to execute child processes instead of e.g. system



139
140
141
142
143
144
145
146
147
148
149
# File 'lib/brut/cli/command.rb', line 139

def initialize(command_options:,global_options:, args:,out:,err:,executor:)
  @command_options = command_options
  @global_options  = global_options
  @args            = args
  @out             = out
  @err             = err
  @executor        = executor
  if self.class.default_env
    @command_options.set_default(:env,self.class.default_env)
  end
end

Class Method Details

.args(new_args = nil) ⇒ String the current value (if called with no parameters)

Set a description of the command line args this command accepts. Args are any part of the command line that is not a switch or flag. The string you give will be used only for documentation. Typically, you would format it in one a few ways:

  • args "some_arg" indicates that exactly one arg called some_arg is required
  • args "[some_arg]" indicates that exactly one arg called some_arg is optional
  • args "args..." indicates that
  • args "[args...]" indicates that zero or more args called args are accepted

Parameters:

  • new_args (String) (defaults to: nil)

    documentation for this command's args. If omitted, returns the current value.

Returns:

  • (String the current value (if called with no parameters))

    String the current value (if called with no parameters)



46
47
48
49
50
51
52
# File 'lib/brut/cli/command.rb', line 46

def self.args(new_args=nil)
  if new_args.nil?
    return @args.to_s
  else
    @args = new_args
  end
end

.command_nameObject

Returns the name of this command, for use on the command line. By default, returns the "underscorized" name of this class (excluding any module namespaces). For exaple, if your command's class is MyApp::CLI::Commands::ClearFiles::DryRun, the command name would be "dry_run". You can override this if you want something different. It is recommended that it not include spaces or other characters meaningful to the shell, but if you like quotes, cool.



74
# File 'lib/brut/cli/command.rb', line 74

def self.command_name = RichString.new(self.name.split(/::/).last).underscorized

.default_envObject

Returns the default project env, based on the logic described in requires_project_env



127
128
# File 'lib/brut/cli/command.rb', line 127

def self.default_env           = @default_env
# Returns true if this app requires a project env

.description(new_description = nil) ⇒ String

Call this to set the one-line description of this command

Parameters:

  • new_description (String) (defaults to: nil)

    When present, sets the description of this command. When omitted, returns the current description.

Returns:

  • (String)

    the current description (if called with no parameters)



12
13
14
15
16
17
18
# File 'lib/brut/cli/command.rb', line 12

def self.description(new_description=nil)
  if new_description.nil?
    return @description.to_s
  else
    @description = new_description
  end
end

.detailed_description(new_description = nil) ⇒ String

Call this to set a an additional detailed description of this command. Currently, this should not be formatted and will be shown all on one line. This is shown after the description, so this text can follow from that, without having to restate it.

Parameters:

  • new_description (String) (defaults to: nil)

    When present, sets the detailed description of this command. When omitted, returns the current detailed description.

Returns:

  • (String)

    the current detailed description (if called with no parameters)



25
26
27
28
29
30
31
32
33
34
# File 'lib/brut/cli/command.rb', line 25

def self.detailed_description(new_description=nil)
  if new_description.nil?
    if @detailed_description.nil?
      return @detailed_description
    end
    return @detailed_description.to_s
  else
    @detailed_description = new_description
  end
end

.env_var(var_name, purpose:) ⇒ Object

Call this for each environment variable this command responds to. These would be variables that affect only this command. For app-wide environment variables, see App.env_var.

Parameters:

  • var_name (String)

    Declares that this command recognizes this environment variable.

  • purpose (String)

    An explanation for how this environment variable affects the command. Used in documentation.



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

def self.env_var(var_name,purpose:)
  env_vars[var_name] = purpose
end

.name_matches?(string) ⇒ true|false

Checks if a given string matches this command name. This exists to allow the use of underscores or dashes as delimiters. Ruby likes underscores, but the shell vibe is often dashes.

Parameters:

  • string (String)

    the command given on the command line

Returns:

  • (true|false)

    true if string is considered an exact match for this command's name.



81
82
83
# File 'lib/brut/cli/command.rb', line 81

def self.name_matches?(string)
  self.command_name == string || self.command_name.to_s.gsub(/_/,"-") == string
end

.option_parserOptionParser

Returns the configured OptionParser used to parse the command portion of the command line. If you don't want to call opts, you can create and return a fully-formed OptionParser by overriding this method. By default, it will create one with a conventional banner.

Returns:

  • (OptionParser)


100
101
102
103
104
# File 'lib/brut/cli/command.rb', line 100

def self.option_parser
  @option_parser ||= OptionParser.new do |opts|
    opts.banner = "%{app} %{global_options} #{command_name} %{command_options} %{args}"
  end
end

.optsOptionParser

Provides access to an OptionParser you can use to declare flags and switches that should be accepted only by this command. The way to use this is to call .on and provide a description for an option as you would to OptionParser. The only difference is that you should not pass a block to this. When the command line is parsed, the results will be placed into a Options instance made available to your command.

Returns:

  • (OptionParser)


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

def self.opts
  self.option_parser
end

.requires_project_env(default: "development") ⇒ Object

Call this if this command requires a project environment as context for what it does. When called, this will do a few things:

  • Your command (not app) will recognize --env=ENVIRONMENT as a global option
  • Your command will document that it recognizes the RACK_ENV environment variable.

Parameters:

  • default (String) (defaults to: "development")

    name of the environment to use if none was specified. nil should be used to require the environment to be specified explicitly.

See Also:



114
115
116
117
118
119
120
121
122
123
124
# File 'lib/brut/cli/command.rb', line 114

def self.requires_project_env(default: "development")
  default_message = if default.nil?
                      ""
                    else
                      " (default '#{default}')"
                    end
  opts.on("--env=ENVIRONMENT","Project environment#{default_message}")
  @default_env = default
  @requires_project_env = true
  self.env_var("RACK_ENV",purpose: "default project environment when --env is omitted")
end

.requires_project_env?Boolean

Returns true if this app requires a project env

Returns:

  • (Boolean)


129
# File 'lib/brut/cli/command.rb', line 129

def self.requires_project_env? = @requires_project_env

Instance Method Details

#after_bootstrap(app:) ⇒ Object

Called after all setup has been executed. Brut will have been started/loaded. This will not be called if anything caused execution to be aborted.

Parameters:



205
206
# File 'lib/brut/cli/command.rb', line 205

def after_bootstrap(app:)
end

#argsArray<String>

Returns the arguments parsed from the command line.

Returns:

  • (Array<String>)

    the arguments parsed from the command line



239
240
241
# File 'lib/brut/cli/command.rb', line 239

def args           = @args
# @!visibility public
# @return [Brut::CLI::Output] IO to use for sending messages to the standard output

#before_executeObject

Called before any execution or bootstrapping happens but after App#before_execute is called. This will have access to everything #execute can access.

Raises:

  • (Brurt::CLI::Error)

    if thrown, this will be caught and handled by AppRunner and execution will be aborted.

  • (StandardError)

    if thrown, this will bubble up and show your user a very sad stack trace that will make them cry. Don't.



198
199
# File 'lib/brut/cli/command.rb', line 198

def before_execute
end

#delegate_to_commands(*command_klasses) ⇒ Brut::CLI::ExecutionResults::Result

Use this inside #execute to createa compound command that executes other commands that are a part of your CLI app. Note that each command will be given the same global and commmand options and the same arguments, so these commands must be able to complete as desired in that way.

Note that since commands are just classes, you can certianly create them however you like and call execute yourself.

result of the last class executed.

Parameters:

  • command_klasses (Enumerable<Class>)

    one or more classes that subclass Brut::CLI::Command to delegate to.

Returns:



165
166
167
168
169
170
171
172
173
174
175
# File 'lib/brut/cli/command.rb', line 165

def delegate_to_commands(*command_klasses)
  result = nil
  command_klasses.each do |command_klass|
    result = delegate_to_command(command_klass)
    if !result.ok?
      err.puts "#{command_klass.command_name} failed"
      return result
    end
  end
  result
end

#errBrut::CLI::Output

Returns IO to use for sending messages to the standard error.

Returns:



245
# File 'lib/brut/cli/command.rb', line 245

def err            = @err

#executeBrut::CLI::ExecutionResults::Result

You must implement this to perform whatever action your command needs to perform. In order to do this, you will have access to:

  • #options - the command options passed on the command line
  • #global_options - the global options passed on the command line
  • #args - the args passed on the command line
  • #out - an IO you should use to print messages to the standard out.
  • #err - on IO you should use to print messages to the standard error.
  • #system! - the method you should use to spawn child processes.

something instead of raising an exception.

Returns:

Raises:

  • (Brurt::CLI::Error)

    if thrown, this will be caught and handled by AppRunner.

  • (StandardError)

    if thrown, this will bubble up and show your user a very sad stack trace that will make them cry. Don't.



190
191
192
# File 'lib/brut/cli/command.rb', line 190

def execute
  abstract_method!
end

#global_optionsBrut::CLI::Options

Returns the global options parsed on the command line.

Returns:



236
237
238
# File 'lib/brut/cli/command.rb', line 236

def global_options = @global_options
# @!visibility public
# @return [Array<String>] the arguments parsed from the command line

#handle_bootstrap_exception(ex) ⇒ void

This method returns an undefined value.

Called if there is an exception during bootstrapping. By default, it re-raises the exception, which cases the command to abort. The reason you may want to overrid this is that your command line app may exist to handle a bootstrapping exception. For example, the built-in Apps::DB app will catch various database connection errors and then create or migrate the database.

Yes, I realize this means we are using exceptions for control flow. It's fine.

Parameters:

  • ex (StandardError)

    whichever exception was caught during bootstrapping

Raises:

  • (Brurt::CLI::Error)

    if thrown, this will be caught and handled by AppRunner and execution will be aborted.

  • (StandardError)

    if thrown, this will bubble up and show your user a very sad stack trace that will make them cry. Don't.



225
226
227
# File 'lib/brut/cli/command.rb', line 225

def handle_bootstrap_exception(ex)
  raise ex
end

#optionsBrut::CLI::Options

Returns the command options parsed on the command line.

Returns:



233
234
235
# File 'lib/brut/cli/command.rb', line 233

def options        = @command_options
# @!visibility public
# @return [Brut::CLI::Options] the global options parsed on the command line

#outBrut::CLI::Output

Returns IO to use for sending messages to the standard output.

Returns:



242
243
244
# File 'lib/brut/cli/command.rb', line 242

def out            = @out
# @!visibility public
# @return [Brut::CLI::Output] IO to use for sending messages to the standard error

#system!(*args) ⇒ int

Convienince method to call Executor#system! on the executor given in the constructor.

Parameters:

  • args (String|Array)

    Whatever you would give to Kernel#system or Open3.popen3.

Returns:

  • (int)

    Always returns 0



154
# File 'lib/brut/cli/command.rb', line 154

def system!(*args) = @executor.system!(*args)