class ProcessExecuter::Runner
@api public
This class is used internally by {ProcessExecuter.run}.
- Raise detailed exceptions for common command failures, such as timeouts or subprocess errors.
- Process command results, including logging and error handling.
- Run commands (‘call`) with options for capturing output, handling timeouts, and merging stdout/stderr.
It does the following:
The `Runner` class executes subprocess commands and captures their status and output.
def call(command, options)
-
(ProcessExecuter::Result)- The result of the completed subprocess
Parameters:
-
options(ProcessExecuter::Options::RunOptions) -- Options for running the command -
command(Array) -- The command to run
def call(command, options) spawn(command, options).tap { |result| process_result(result) } end
def log_result(result)
- Api: - private
Returns:
-
(void)-
Parameters:
-
result(ProcessExecuter::Result) -- the result of the command including
def log_result(result) result.options.logger.info { "#{result.command} exited with status #{result}" } result.options.logger.debug { "stdout:\n#{result.stdout.inspect}\nstderr:\n#{result.stderr.inspect}" } end
def process_result(result)
- Api: - private
Raises:
-
(ProcessExecuter::Error)- if the command could not be executed or failed
Returns:
-
(Void)-
Parameters:
-
result(ProcessExecuter::Result) -- The result of the command
def process_result(result) log_result(result) raise_errors(result) if result.options.raise_errors end
def raise_errors(result)
- Api: - private
Raises:
-
(ProcessExecuter::TimeoutError)- If the command times out -
(ProcessExecuter::SignaledError)- If the command was signaled -
(ProcessExecuter::FailedError)- If the command failed
Returns:
-
(void)-
def raise_errors(result) raise TimeoutError, result if result.timed_out? raise SignaledError, result if result.signaled? raise FailedError, result unless result.success? end
def raise_pipe_error(command, option_key, pipe)
- Api: - private
Returns:
-
(void)- This method always raises an error
Raises:
-
(ProcessExecuter::ProcessIOError)-
Parameters:
-
pipe(ProcessExecuter::MonitoredPipe) -- The pipe that raised the exception -
option_key(Symbol) -- The name of the pipe that raised the exception -
command(Array) -- The command that was executed
def raise_pipe_error(command, option_key, pipe) return unless pipe.exception error = ProcessExecuter::ProcessIOError.new("Pipe Exception for #{command}: #{option_key.inspect}") raise(error, cause: pipe.exception) end
def should_wrap?(options, key, value)
- Api: - private
Returns:
-
(Boolean)- Whether the option should be wrapped
Parameters:
-
value(Object) -- The option value -
key(Object) -- The option key
def should_wrap?(options, key, value) (options.stdout_redirection?(key) || options.stderr_redirection?(key)) && ProcessExecuter::Destinations.compatible_with_monitored_pipe?(value) end
def spawn(command, options)
- Api: - private
Returns:
-
(ProcessExecuter::Result)- The result of the completed subprocess
Raises:
-
(ProcessExecuter::Error)- if the command could not be executed or failed
Parameters:
-
options(ProcessExecuter::Options::RunOptions) -- Options for running the command -
command(Array) -- The command to execute
def spawn(command, options) opened_pipes = wrap_stdout_stderr(options) ProcessExecuter.spawn_and_wait_with_options(command, options) ensure opened_pipes.each_value(&:close) opened_pipes.each { |option_key, pipe| raise_pipe_error(command, option_key, pipe) } end
def wrap_stdout_stderr(options)
- Api: - private
Returns:
-
(Hash- The opened pipes (the Object is the option key)
Parameters:
-
options(ProcessExecuter::Options::RunOptions) -- Options for running the command
def wrap_stdout_stderr(options) options.each_with_object({}) do |key_value, opened_pipes| key, value = key_value next unless should_wrap?(options, key, value) wrapped_destination = ProcessExecuter::MonitoredPipe.new(value) opened_pipes[key] = wrapped_destination options.merge!(key => wrapped_destination) end end