lib/expectr/child.rb in expectr-2.0.0 vs lib/expectr/child.rb in expectr-2.0.1

- old
+ new

@@ -1,165 +1,146 @@ require 'pty' +require 'expectr' require 'expectr/interface' class Expectr - # Internal: The Expectr::Child class contains the interface to interacting + # Public: The Expectr::Child Module defines the interface for interacting # with child processes. - # - # All methods with the prefix 'interface_' in their name will return a Proc - # designed to be defined as an instance method in the primary Expectr object. - # These methods will all be documented as if they are the Proc in question. - class Child + module Child include Expectr::Interface - attr_reader :stdin - attr_reader :stdout - attr_reader :pid - # Public: Initialize a new Expectr::Child object. - # Spawns a sub-process and attaches to STDIN and STDOUT for the new - # process. + # Public: Initialize the Expectr interface, spawning a sub-process + # attaching to STDIN and STDOUT of the new process. # - # cmd - A String or File referencing the application to launch. + # args - A Hash containing arguments to Expectr. Only :cmd is presently + # used for this function. + # :cmd - A String or File referencing the application to launch. # - # Raises TypeError if argument is anything other than String or File. - def initialize(cmd) + # Returns nothing. + # Raises TypeError if args[:cmd] is anything other than String or File. + def init_interface(args) + cmd = args[:cmd] cmd = cmd.path if cmd.kind_of?(File) + unless cmd.kind_of?(String) raise(TypeError, Errstr::STRING_FILE_EXPECTED) end @stdout,@stdin,@pid = PTY.spawn(cmd) @stdout.winsize = $stdout.winsize if $stdout.tty? end # Public: Send a signal to the running child process. # - # signal - Symbol, String or FixNum corresponding to the symbol to be sent - # to the running process. (default: :TERM) + # signal - Symbol, String or FixNum indicating the symbol to be sent to the + # running process. (default: :TERM) # # Returns a boolean indicating whether the process was successfully sent # the signal. # Raises ProcessError if the process is not running (@pid = 0). - def interface_kill! - ->(signal = :TERM) { - unless @pid > 0 - raise(ProcessError, Errstr::PROCESS_NOT_RUNNING) - end - Process::kill(signal.to_sym, @pid) == 1 - } + def kill!(signal = :TERM) + unless @pid > 0 + raise(ProcessError, Errstr::PROCESS_NOT_RUNNING) + end + Process::kill(signal.to_sym, @pid) == 1 end # Public: Send input to the active child process. # # str - String to be sent. # # Returns nothing. - # Raises Expectr::ProcessError if the process is not running (@pid = 0) - def interface_send - ->(str) { - begin - @stdin.syswrite str - rescue Errno::EIO #Application is not running - @pid = 0 - end - unless @pid > 0 - raise(Expectr::ProcessError, Errstr::PROCESS_GONE) - end - } + # Raises Expectr::ProcessError if the process is not running (@pid = 0). + def send(str) + begin + @stdin.syswrite str + rescue Errno::EIO #Application is not running + @pid = 0 + end + unless @pid > 0 + raise(Expectr::ProcessError, Errstr::PROCESS_GONE) + end end - # Public: Read the child process's output, force UTF-8 encoding, then + # Public: Read the output of the child process, force UTF-8 encoding, then # append to the internal buffer and print to $stdout if appropriate. # # Returns nothing. - def interface_output_loop - -> { - while @pid > 0 - unless select([@stdout], nil, nil, @timeout).nil? - buf = '' + def output_loop + while @pid > 0 + unless select([@stdout], nil, nil, @timeout).nil? + buf = '' - begin - @stdout.sysread(@buffer_size, buf) - rescue Errno::EIO #Application is not running - @pid = 0 - return - end - process_output(buf) + begin + @stdout.sysread(@buffer_size, buf) + rescue Errno::EIO #Application is not running + @pid = 0 + return end + process_output(buf) end - } + end end - def interface_prepare_interact_environment - -> { - env = {sig: {}} + # Public: Return the PTY's window size. + # + # Returns a two-element Array (same as IO#winsize) + def winsize + @stdout.winsize + end - # Save old tty settings and set up the new environment - env[:tty] = `stty -g` - `stty -icanon min 1 time 0 -echo` + private - # SIGINT should be sent to the child as \C-c - env[:sig]['INT'] = trap 'INT' do - send "\C-c" - end + # Internal: Set up the execution environment to prepare for entering + # interact mode. + # Presently assumes a Linux system. + # + # Returns nothing. + def prepare_interact_environment + env = {sig: {}} - # SIGTSTP should be sent to the process as \C-z - env[:sig]['TSTP'] = trap 'TSTP' do - send "\C-z" - end + # Save old tty settings and set up the new environment + env[:tty] = `stty -g` + `stty -icanon min 1 time 0 -echo` - # SIGWINCH should trigger an update to the child processes window size - env[:sig]['WINCH'] = trap 'WINCH' do - @stdout.winsize = $stdout.winsize - end + # SIGINT should be sent to the child as \C-c + env[:sig]['INT'] = trap 'INT' do + send "\C-c" + end - env - } + # SIGTSTP should be sent to the process as \C-z + env[:sig]['TSTP'] = trap 'TSTP' do + send "\C-z" + end + + # SIGWINCH should trigger an update to the child processes window size + env[:sig]['WINCH'] = trap 'WINCH' do + @stdout.winsize = $stdout.winsize + end + + env end - # Public: Create a Thread containing the loop which is responsible for - # handling input from the user in interact mode. + # Internal: Create a Thread containing the loop which is responsible for + # handling input from the user while in interact mode. # # Returns a Thread containing the running loop. - def interface_interact_thread - -> { - @interact = true - env = prepare_interact_environment - Thread.new do - begin - input = '' + def interact_thread + @interact = true + env = prepare_interact_environment + Thread.new do + begin + input = '' - while @pid > 0 && @interact - if select([$stdin], nil, nil, 1) - c = $stdin.getc.chr - send c unless c.nil? - end + while @pid > 0 && @interact + if select([$stdin], nil, nil, 1) + c = $stdin.getc.chr + send c unless c.nil? end - ensure - restore_environment(env) end + ensure + restore_environment(env) end - } - end - - # Public: Return the PTY's window size. - # - # Returns a two-element Array (same as IO#winsize) - def interface_winsize - -> { - @stdout.winsize - } - end - - # Public: Present a streamlined interface to create a new Expectr instance. - # - # cmd - A String or File referencing the application to launch. - # args - A Hash used to specify options for the new object, per - # Expectr#initialize. - # - # Returns a new Expectr object - def self.spawn(cmd, args = {}) - args[:interface] = :child - Expectr.new(cmd, args) + end end end end