class Toys::Utils::Exec::Controller
An object that controls a subprocess. This object is returned from an execution running in the background, or is yielded to a control block for an execution running in the foreground. You may use this object to interact with the subcommand's streams, send signals to the process, and get its result.
Attributes
The subcommand's standard error stream (which can be read from).
@return [IO] if the command was configured with `err: :controller` @return [nil] if the command was not configured with
`err: :controller`
The exception raised when the process failed to start.
Exactly one of {#exception} and {#pid} will be non-nil.
@return [Exception] if the process failed to start. @return [nil] if the process start was successful.
The subcommand's standard input stream (which can be written to).
@return [IO] if the command was configured with `in: :controller` @return [nil] if the command was not configured with
`in: :controller`
The subcommand's name. @return [Object]
The subcommand's standard output stream (which can be read from).
@return [IO] if the command was configured with `out: :controller` @return [nil] if the command was not configured with
`out: :controller`
The process ID.
Exactly one of {#exception} and {#pid} will be non-nil.
@return [Integer] if the process start was successful @return [nil] if the process could not be started.
Public Class Methods
@private
# File lib/toys/utils/exec.rb, line 494 def initialize(name, controller_streams, captures, pid, join_threads, result_callback, mutex) @name = name @in = controller_streams[:in] @out = controller_streams[:out] @err = controller_streams[:err] @captures = captures @pid = @exception = @wait_thread = nil case pid when ::Integer @pid = pid @wait_thread = ::Process.detach(pid) when ::Exception @exception = pid end @join_threads = join_threads @result_callback = result_callback @mutex = mutex @result = nil end
Public Instance Methods
Captures the remaining data in the given stream. After calling this, do not read directly from the stream.
@param which [:out,:err] Which stream to capture @return [self]
# File lib/toys/utils/exec.rb, line 575 def capture(which) stream = stream_for(which) @join_threads << ::Thread.new do begin data = stream.read @mutex.synchronize do @captures[which] = data end ensure stream.close end end self end
Captures the remaining data in the standard error stream. After calling this, do not read directly from the stream.
@return [self]
# File lib/toys/utils/exec.rb, line 606 def capture_err capture(:err) end
Captures the remaining data in the standard output stream. After calling this, do not read directly from the stream.
@return [self]
# File lib/toys/utils/exec.rb, line 596 def capture_out capture(:out) end
Close the controller's streams. @private
# File lib/toys/utils/exec.rb, line 753 def close_streams(which) @in.close if which != :out && @in && !@in.closed? @out.close if which != :in && @out && !@out.closed? @err.close if which != :in && @err && !@err.closed? self end
Determine whether the subcommand is still executing
@return [Boolean]
# File lib/toys/utils/exec.rb, line 722 def executing? @wait_thread&.status ? true : false end
Send the given signal to the process. The signal may be specified by name or number.
@param sig [Integer,String] The signal to send. @return [self]
# File lib/toys/utils/exec.rb, line 711 def kill(sig) ::Process.kill(sig, pid) if pid self end
Redirects the remainder of the given stream.
You may specify the stream as an IO or IO-like object, or as a file specified by its path. If specifying a file, you may optionally provide the mode and permissions for the call to `File#open`. You can also specify the value `:null` to indicate the null file.
After calling this, do not interact directly with the stream.
@param which [:in,:out,:err] Which stream to redirect @param io [IO,StringIO,String,:null] Where to redirect the stream @param io_args [Object…] The mode and permissions for opening the
file, if redirecting to/from a file.
@return [self]
# File lib/toys/utils/exec.rb, line 626 def redirect(which, io, *io_args) io = ::File::NULL if io == :null if io.is_a?(::String) io_args = which == :in ? ["r"] : ["w"] if io_args.empty? io = ::File.open(io, *io_args) end stream = stream_for(which, allow_in: true) @join_threads << ::Thread.new do begin if which == :in ::IO.copy_stream(io, stream) else ::IO.copy_stream(stream, io) end ensure stream.close io.close end end self end
Redirects the remainder of the standard error stream.
You may specify the stream as an IO or IO-like object, or as a file specified by its path. If specifying a file, you may optionally provide the mode and permissions for the call to `File#open`.
After calling this, do not interact directly with the stream.
@param io [IO,StringIO,String] Where to redirect the stream @param io_args [Object…] The mode and permissions for opening the
file, if redirecting to a file.
@return [self]
# File lib/toys/utils/exec.rb, line 700 def redirect_err(io, *io_args) redirect(:err, io, *io_args) end
Redirects the remainder of the standard input stream.
You may specify the stream as an IO or IO-like object, or as a file specified by its path. If specifying a file, you may optionally provide the mode and permissions for the call to `File#open`. You can also specify the value `:null` to indicate the null file.
After calling this, do not interact directly with the stream.
@param io [IO,StringIO,String,:null] Where to redirect the stream @param io_args [Object…] The mode and permissions for opening the
file, if redirecting from a file.
@return [self]
# File lib/toys/utils/exec.rb, line 663 def redirect_in(io, *io_args) redirect(:in, io, *io_args) end
Redirects the remainder of the standard output stream.
You may specify the stream as an IO or IO-like object, or as a file specified by its path. If specifying a file, you may optionally provide the mode and permissions for the call to `File#open`. You can also specify the value `:null` to indicate the null file.
After calling this, do not interact directly with the stream.
@param io [IO,StringIO,String,:null] Where to redirect the stream @param io_args [Object…] The mode and permissions for opening the
file, if redirecting to a file.
@return [self]
# File lib/toys/utils/exec.rb, line 682 def redirect_out(io, *io_args) redirect(:out, io, *io_args) end
Wait for the subcommand to complete, and return a result object.
Closes the control streams if present. The stdin stream is always closed, even if the call times out. The stdout and stderr streams are closed only after the command terminates.
@param timeout [Numeric,nil] The timeout in seconds, or `nil` to
wait indefinitely.
@return [Toys::Utils::Exec::Result] The result object @return [nil] if a timeout occurred.
# File lib/toys/utils/exec.rb, line 738 def result(timeout: nil) close_streams(:in) return nil if @wait_thread && !@wait_thread.join(timeout) @result ||= begin close_streams(:out) @join_threads.each(&:join) Result.new(name, @captures[:out], @captures[:err], @wait_thread&.value, @exception) .tap { |result| @result_callback&.call(result) } end end
Private Instance Methods
# File lib/toys/utils/exec.rb, line 762 def stream_for(which, allow_in: false) stream = nil case which when :out stream = @out @out = nil when :err stream = @err @err = nil when :in if allow_in stream = @in @in = nil end else raise ::ArgumentError, "Unknown stream #{which}" end raise ::ArgumentError, "Stream #{which} not available" unless stream stream end