Module: Toys::StandardMixins::Exec

Includes:
Mixin
Defined in:
lib/toys/standard_mixins/exec.rb

Overview

A set of helper methods for invoking subcommands. Provides shortcuts for common cases such as invoking Ruby in a subprocess or capturing output in a string. Also provides an interface for controlling a spawned process's streams.

You may make these methods available to your tool by including the following directive in your tool configuration:

include :exec

This is a frontend for Utils::Exec. More information is available in that class's documentation.

Configuration Options

Subprocesses may be configured using the options in the Utils::Exec class. These include a variety of options supported by Process#spawn, and some options supported by Utils::Exec itself.

You can set default configuration by passing options to the include directive. For example, to log commands at the debug level for all subprocesses spawned by this tool:

include :exec, log_level: Logger::DEBUG

Two special options are also recognized by the mixin.

  • A :result_callback proc may take a second argument. If it does, the context object is passed as the second argument. This is useful if a :result_callback is applied to the entire tool by passing it to the include directive. In that case, self is not set to the context object as it normally would be in a tool's run method, so you cannot access it otherwise. For example, here is how to log the exit code for every subcommand:

    tool "mytool" do
      callback = proc do |result, context|
        context.logger.info "Exit code: #{result.exit_code}"
      end
      include :exec, result_callback: callback  # ...
    
    end
    

    You may also pass a symbol as the :result_callback. The method with that name is then called as the callback. The method must take one argument, the result object.

  • If :exit_on_nonzero_status is set to true, a nonzero exit code returned by the subprocess will also cause the tool to exit immediately with that same code.

    This is particularly useful as an option to the include directive, where it causes any subprocess failure to abort the tool, similar to setting set -e in a bash script.

    include :exec, exit_on_nonzero_status: true
    

    :e can be used as a shortcut for :exit_on_nonzero_status

    include :exec, e: true
    

Constant Summary collapse

KEY =

Context key for the executor object.

Returns:

  • (Object)
::Object.new.freeze

Instance Method Summary collapse

Methods included from Mixin

create

Instance Method Details

#capture(cmd, **opts) {|controller| ... } ⇒ String

Execute a command. The command may be given as a single string to pass to a shell, or an array of strings indicating a posix command.

Captures standard out and returns it as a string. Cannot be run in the background.

If a block is provided, a Utils::Exec::Controller will be yielded to it.

Parameters:

  • cmd (String, Array<String>)

    The command to execute.

  • opts (keywords)

    The command options. All options listed in the Utils::Exec documentation are supported, plus the exit_on_nonzero_status option.

Yield Parameters:

Returns:

  • (String)

    What was written to standard out.



269
270
271
272
# File 'lib/toys/standard_mixins/exec.rb', line 269

def capture(cmd, **opts, &block)
  opts = Exec._setup_exec_opts(opts, self)
  self[KEY].capture(cmd, **opts, &block)
end

#capture_proc(func, **opts) {|controller| ... } ⇒ String

Execute a proc in a forked subprocess.

Captures standard out and returns it as a string. Cannot be run in the background.

If a block is provided, a Utils::Exec::Controller will be yielded to it.

Beware that some Ruby environments (e.g. JRuby, and Ruby on Windows) do not support this method because they do not support fork.

Parameters:

  • func (Proc)

    The proc to call.

  • opts (keywords)

    The command options. Most options listed in the Utils::Exec documentation are supported, plus the exit_on_nonzero_status option.

Yield Parameters:

Returns:

  • (String)

    What was written to standard out.



318
319
320
321
# File 'lib/toys/standard_mixins/exec.rb', line 318

def capture_proc(func, **opts, &block)
  opts = Exec._setup_exec_opts(opts, self)
  self[KEY].capture_proc(func, **opts, &block)
end

#capture_ruby(args, **opts) {|controller| ... } ⇒ String

Spawn a ruby process and pass the given arguments to it.

Captures standard out and returns it as a string. Cannot be run in the background.

If a block is provided, a Utils::Exec::Controller will be yielded to it.

Parameters:

  • args (String, Array<String>)

    The arguments to ruby.

  • opts (keywords)

    The command options. All options listed in the Utils::Exec documentation are supported, plus the exit_on_nonzero_status option.

Yield Parameters:

Returns:

  • (String)

    What was written to standard out.



292
293
294
295
# File 'lib/toys/standard_mixins/exec.rb', line 292

def capture_ruby(args, **opts, &block)
  opts = Exec._setup_exec_opts(opts, self)
  self[KEY].capture_ruby(args, **opts, &block)
end

#capture_separate_tool(cmd, **opts) {|controller| ... } ⇒ String

Execute a tool in a separately spawned process.

Captures standard out and returns it as a string. Cannot be run in the background.

The command may be given as a single string or an array of strings, representing the tool to run and the arguments to pass.

If a block is provided, a Utils::Exec::Controller will be yielded to it.

An entirely separate spawned process is run for this tool, using the setting of Toys.executable_path. Thus, this method can be run only if that setting is present. The normal Toys gem does set it, but if you are writing your own executable using Toys-Core, you will need to set it explicitly for this method to work. Furthermore, Bundler, if present, is reset to its "unbundled" environment. Thus, the tool found, the behavior of the CLI, and the gem environment, might not be the same as those of the calling tool.

This method is often used if you are already in a bundle and need to run a tool that uses a different bundle. It may also be necessary on environments without "fork" (such as JRuby or Ruby on Windows).

Parameters:

  • cmd (String, Array<String>)

    The tool to execute.

  • opts (keywords)

    The command options. Most options listed in the Utils::Exec documentation are supported, plus the exit_on_nonzero_status option.

Yield Parameters:

Returns:

  • (String)

    What was written to standard out.



387
388
389
390
391
# File 'lib/toys/standard_mixins/exec.rb', line 387

def capture_separate_tool(cmd, **opts, &block)
  Exec._setup_clean_process(cmd) do |clean_cmd|
    capture(clean_cmd, **opts, &block)
  end
end

#capture_tool(cmd, **opts) {|controller| ... } ⇒ String

Execute a tool in the current CLI in a forked process.

Captures standard out and returns it as a string. Cannot be run in the background.

The command may be given as a single string or an array of strings, representing the tool to run and the arguments to pass.

If a block is provided, a Utils::Exec::Controller will be yielded to it.

Beware that some Ruby environments (e.g. JRuby, and Ruby on Windows) do not support this method because they do not support fork.

Parameters:

  • cmd (String, Array<String>)

    The tool to execute.

  • opts (keywords)

    The command options. Most options listed in the Utils::Exec documentation are supported, plus the exit_on_nonzero_status option.

Yield Parameters:

Returns:

  • (String)

    What was written to standard out.



347
348
349
350
351
# File 'lib/toys/standard_mixins/exec.rb', line 347

def capture_tool(cmd, **opts, &block)
  func = Exec._make_tool_caller(cmd)
  opts = Exec._setup_exec_opts(opts, self)
  self[KEY].capture_proc(func, **opts, &block)
end

#configure_exec(**opts) ⇒ self

Set default configuration keys.

All options listed in the Utils::Exec documentation are supported, plus the exit_on_nonzero_status option.

Parameters:

  • opts (keywords)

    The default options.

Returns:

  • (self)


100
101
102
103
104
# File 'lib/toys/standard_mixins/exec.rb', line 100

def configure_exec(**opts)
  opts = Exec._setup_exec_opts(opts, self)
  self[KEY].configure_defaults(**opts)
  self
end

#exec(cmd, **opts) {|controller| ... } ⇒ Toys::Utils::Exec::Controller, Toys::Utils::Exec::Result

Execute a command. The command may be given as a single string to pass to a shell, or an array of strings indicating a posix command.

If the process is not set to run in the background, and a block is provided, a Utils::Exec::Controller will be yielded to it.

Parameters:

  • cmd (String, Array<String>)

    The command to execute.

  • opts (keywords)

    The command options. All options listed in the Utils::Exec documentation are supported, plus the exit_on_nonzero_status option.

Yield Parameters:

Returns:



125
126
127
128
# File 'lib/toys/standard_mixins/exec.rb', line 125

def exec(cmd, **opts, &block)
  opts = Exec._setup_exec_opts(opts, self)
  self[KEY].exec(cmd, **opts, &block)
end

#exec_proc(func, **opts) {|controller| ... } ⇒ Toys::Utils::Exec::Controller, Toys::Utils::Exec::Result

Execute a proc in a forked subprocess.

If the process is not set to run in the background, and a block is provided, a Utils::Exec::Controller will be yielded to it.

Beware that some Ruby environments (e.g. JRuby, and Ruby on Windows) do not support this method because they do not support fork.

Parameters:

  • func (Proc)

    The proc to call.

  • opts (keywords)

    The command options. Most options listed in the Utils::Exec documentation are supported, plus the exit_on_nonzero_status option.

Yield Parameters:

Returns:



175
176
177
178
# File 'lib/toys/standard_mixins/exec.rb', line 175

def exec_proc(func, **opts, &block)
  opts = Exec._setup_exec_opts(opts, self)
  self[KEY].exec_proc(func, **opts, &block)
end

#exec_ruby(args, **opts) {|controller| ... } ⇒ Toys::Utils::Exec::Controller, Toys::Utils::Exec::Result Also known as: ruby

Spawn a ruby process and pass the given arguments to it.

If the process is not set to run in the background, and a block is provided, a Utils::Exec::Controller will be yielded to it.

Parameters:

  • args (String, Array<String>)

    The arguments to ruby.

  • opts (keywords)

    The command options. All options listed in the Utils::Exec documentation are supported, plus the exit_on_nonzero_status option.

Yield Parameters:

Returns:



148
149
150
151
# File 'lib/toys/standard_mixins/exec.rb', line 148

def exec_ruby(args, **opts, &block)
  opts = Exec._setup_exec_opts(opts, self)
  self[KEY].exec_ruby(args, **opts, &block)
end

#exec_separate_tool(cmd, **opts) {|controller| ... } ⇒ Toys::Utils::Exec::Controller, Toys::Utils::Exec::Result

Execute a tool in a separately spawned process.

The command may be given as a single string or an array of strings, representing the tool to run and the arguments to pass.

If the process is not set to run in the background, and a block is provided, a Utils::Exec::Controller will be yielded to it.

An entirely separate spawned process is run for this tool, using the setting of Toys.executable_path. Thus, this method can be run only if that setting is present. The normal Toys gem does set it, but if you are writing your own executable using Toys-Core, you will need to set it explicitly for this method to work. Furthermore, Bundler, if present, is reset to its "unbundled" environment. Thus, the tool found, the behavior of the CLI, and the gem environment, might not be the same as those of the calling tool.

This method is often used if you are already in a bundle and need to run a tool that uses a different bundle. It may also be necessary on environments without "fork" (such as JRuby or Ruby on Windows).

Parameters:

  • cmd (String, Array<String>)

    The tool to execute.

  • opts (keywords)

    The command options. Most options listed in the Utils::Exec documentation are supported, plus the exit_on_nonzero_status option.

Yield Parameters:

Returns:



244
245
246
247
248
# File 'lib/toys/standard_mixins/exec.rb', line 244

def exec_separate_tool(cmd, **opts, &block)
  Exec._setup_clean_process(cmd) do |clean_cmd|
    exec(clean_cmd, **opts, &block)
  end
end

#exec_tool(cmd, **opts) {|controller| ... } ⇒ Toys::Utils::Exec::Controller, Toys::Utils::Exec::Result

Execute a tool in the current CLI in a forked process.

The command may be given as a single string or an array of strings, representing the tool to run and the arguments to pass.

If the process is not set to run in the background, and a block is provided, a Utils::Exec::Controller will be yielded to it.

Beware that some Ruby environments (e.g. JRuby, and Ruby on Windows) do not support this method because they do not support fork.

Parameters:

  • cmd (String, Array<String>)

    The tool to execute.

  • opts (keywords)

    The command options. Most options listed in the Utils::Exec documentation are supported, plus the exit_on_nonzero_status option.

Yield Parameters:

Returns:



204
205
206
207
208
# File 'lib/toys/standard_mixins/exec.rb', line 204

def exec_tool(cmd, **opts, &block)
  func = Exec._make_tool_caller(cmd)
  opts = Exec._setup_exec_opts(opts, self)
  self[KEY].exec_proc(func, **opts, &block)
end

#exit_on_nonzero_status(status) ⇒ Integer

Exit if the given status code is nonzero. Otherwise, returns 0.

Parameters:

Returns:

  • (Integer)


420
421
422
423
424
425
# File 'lib/toys/standard_mixins/exec.rb', line 420

def exit_on_nonzero_status(status)
  status = status.exit_code if status.respond_to?(:exit_code)
  status = status.exitstatus if status.respond_to?(:exitstatus)
  Context.exit(status) unless status.zero?
  0
end

#sh(cmd, **opts) {|controller| ... } ⇒ Integer

Execute the given string in a shell. Returns the exit code. Cannot be run in the background.

If a block is provided, a Utils::Exec::Controller will be yielded to it.

Parameters:

  • cmd (String)

    The shell command to execute.

  • opts (keywords)

    The command options. All options listed in the Utils::Exec documentation are supported, plus the exit_on_nonzero_status option.

Yield Parameters:

Returns:

  • (Integer)

    The exit code



409
410
411
412
# File 'lib/toys/standard_mixins/exec.rb', line 409

def sh(cmd, **opts, &block)
  opts = Exec._setup_exec_opts(opts, self)
  self[KEY].sh(cmd, **opts, &block)
end