Class: Toys::Utils::Exec::Controller

Inherits:
Object
  • Object
show all
Defined in:
lib/toys/utils/exec.rb

Overview

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 can use this object to interact with the subcommand's streams, send signals to the process, and get its result.

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#errIO? (readonly)

The subcommand's standard error stream (which can be read from).

Returns:

  • (IO)

    if the command was configured with err: :controller

  • (nil)

    if the command was not configured with err: :controller



495
496
497
# File 'lib/toys/utils/exec.rb', line 495

def err
  @err
end

#exceptionException? (readonly)

The exception raised when the process failed to start.

Exactly one of #exception and #pid will be non-nil.

Returns:

  • (Exception)

    if the process failed to start.

  • (nil)

    if the process start was successful.



515
516
517
# File 'lib/toys/utils/exec.rb', line 515

def exception
  @exception
end

#inIO? (readonly)

The subcommand's standard input stream (which can be written to).

Returns:

  • (IO)

    if the command was configured with in: :controller

  • (nil)

    if the command was not configured with in: :controller



477
478
479
# File 'lib/toys/utils/exec.rb', line 477

def in
  @in
end

#nameObject (readonly)

The subcommand's name.

Returns:

  • (Object)


468
469
470
# File 'lib/toys/utils/exec.rb', line 468

def name
  @name
end

#outIO? (readonly)

The subcommand's standard output stream (which can be read from).

Returns:

  • (IO)

    if the command was configured with out: :controller

  • (nil)

    if the command was not configured with out: :controller



486
487
488
# File 'lib/toys/utils/exec.rb', line 486

def out
  @out
end

#pidInteger? (readonly)

The process ID.

Exactly one of #exception and #pid will be non-nil.

Returns:

  • (Integer)

    if the process start was successful

  • (nil)

    if the process could not be started.



505
506
507
# File 'lib/toys/utils/exec.rb', line 505

def pid
  @pid
end

Instance Method Details

#capture(which) ⇒ self

Captures the remaining data in the given stream. After calling this, do not read directly from the stream.

Parameters:

  • which (:out, :err)

    Which stream to capture

Returns:

  • (self)


524
525
526
527
528
529
530
531
532
533
534
535
# File 'lib/toys/utils/exec.rb', line 524

def capture(which)
  stream = stream_for(which)
  @join_threads << ::Thread.new do
    data = stream.read
    @captures_mutex.synchronize do
      @captures[which] = data
    end
  ensure
    stream.close
  end
  self
end

#capture_errself

Captures the remaining data in the standard error stream. After calling this, do not read directly from the stream.

Returns:

  • (self)


553
554
555
# File 'lib/toys/utils/exec.rb', line 553

def capture_err
  capture(:err)
end

#capture_outself

Captures the remaining data in the standard output stream. After calling this, do not read directly from the stream.

Returns:

  • (self)


543
544
545
# File 'lib/toys/utils/exec.rb', line 543

def capture_out
  capture(:out)
end

#executing?Boolean

Determine whether the subcommand is still executing

Returns:

  • (Boolean)


673
674
675
# File 'lib/toys/utils/exec.rb', line 673

def executing?
  @wait_thread&.status ? true : false
end

#kill(sig) ⇒ self Also known as: signal

Send the given signal to the process. The signal can be specified by name or number.

Parameters:

  • sig (Integer, String)

    The signal to send.

Returns:

  • (self)


662
663
664
665
# File 'lib/toys/utils/exec.rb', line 662

def kill(sig)
  ::Process.kill(sig, pid) if pid
  self
end

#redirect(which, io, *io_args) ⇒ self

Redirects the remainder of the given stream.

You can specify the stream as an IO or IO-like object, or as a file specified by its path. If specifying a file, you can optionally provide the mode and permissions for the call to File#open. You can also specify the value :null to indicate the null file.

If the stream is redirected to an IO-like object, it is not closed when the process is completed. (If it is redirected to a file specified by path, the file is closed on completion.)

After calling this, do not interact directly with the stream.

Parameters:

  • which (:in, :out, :err)

    Which stream to redirect

  • io (IO, StringIO, String, :null)

    Where to redirect the stream

  • io_args (Object...)

    The mode and permissions for opening the file, if redirecting to/from a file.

Returns:

  • (self)


577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
# File 'lib/toys/utils/exec.rb', line 577

def redirect(which, io, *io_args)
  io = ::File::NULL if io == :null
  close_afterward = false
  if io.is_a?(::String)
    io_args = which == :in ? ["r"] : ["w"] if io_args.empty?
    io = ::File.open(io, *io_args)
    close_afterward = true
  end
  stream = stream_for(which, allow_in: true)
  @join_threads << ::Thread.new do
    if which == :in
      ::IO.copy_stream(io, stream)
    else
      ::IO.copy_stream(stream, io)
    end
  ensure
    stream.close
    io.close if close_afterward
  end
  self
end

#redirect_err(io, *io_args) ⇒ self

Redirects the remainder of the standard error stream.

You can specify the stream as an IO or IO-like object, or as a file specified by its path. If specifying a file, you can optionally provide the mode and permissions for the call to File#open.

After calling this, do not interact directly with the stream.

Parameters:

  • io (IO, StringIO, String)

    Where to redirect the stream

  • io_args (Object...)

    The mode and permissions for opening the file, if redirecting to a file.

Returns:

  • (self)


651
652
653
# File 'lib/toys/utils/exec.rb', line 651

def redirect_err(io, *io_args)
  redirect(:err, io, *io_args)
end

#redirect_in(io, *io_args) ⇒ self

Redirects the remainder of the standard input stream.

You can specify the stream as an IO or IO-like object, or as a file specified by its path. If specifying a file, you can 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.

Parameters:

  • io (IO, StringIO, String, :null)

    Where to redirect the stream

  • io_args (Object...)

    The mode and permissions for opening the file, if redirecting from a file.

Returns:

  • (self)


614
615
616
# File 'lib/toys/utils/exec.rb', line 614

def redirect_in(io, *io_args)
  redirect(:in, io, *io_args)
end

#redirect_out(io, *io_args) ⇒ self

Redirects the remainder of the standard output stream.

You can specify the stream as an IO or IO-like object, or as a file specified by its path. If specifying a file, you can 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.

Parameters:

  • io (IO, StringIO, String, :null)

    Where to redirect the stream

  • io_args (Object...)

    The mode and permissions for opening the file, if redirecting to a file.

Returns:

  • (self)


633
634
635
# File 'lib/toys/utils/exec.rb', line 633

def redirect_out(io, *io_args)
  redirect(:out, io, *io_args)
end

#result(timeout: nil) ⇒ Toys::Utils::Exec::Result?

Wait for the subcommand to complete, and return a result object.

Parameters:

  • timeout (Numeric, nil) (defaults to: nil)

    The timeout in seconds, or nil to wait indefinitely.

Returns:



685
686
687
688
689
690
691
692
693
694
695
696
697
698
# File 'lib/toys/utils/exec.rb', line 685

def result(timeout: nil)
  return nil if @wait_thread && !@wait_thread.join(timeout)
  should_run_callback = false
  @result_mutex.synchronize do
    @result ||= begin
      should_run_callback = true
      close_streams(:both)
      @join_threads.each(&:join)
      Result.new(name, @captures[:out], @captures[:err], @wait_thread&.value, @exception)
    end
  end
  @result_callback&.call(@result) if should_run_callback
  @result
end