module Expectr::Lambda

Public: The Expectr::Lambda Module defines the interface for interacting with Proc objects in a manner which is similar to interacting with processes.

Public Class Methods

spawn(reader, writer, args = {}) click to toggle source

Public: Present a streamlined interface to create a new Expectr instance.

reader - Lambda which is meant to be interacted with as if it were

analogous to STDIN for a child process.

writer - Lambda which is meant to be interacted with as if it were

analogous to STDOUT for a child process.

args - A Hash used to specify options for the new object, per

Expectr#initialize.

Returns a new Expectr object

# File lib/expectr/lambda.rb, line 40
def self.spawn(reader, writer, args = {})
  args[:interface] = :lambda
  args[:reader] = reader
  args[:writer] = writer
  Expectr.new(args)
end

Public Instance Methods

init_interface(args) click to toggle source

Public: Initialize the Expectr Lambda interface.

args - Hash containing Proc objects to act as reader and writer:

reader - Lambda which is meant to be interacted with as if it were
         analogous to STDIN for a child process.
writer - Lambda which is meant to be interacted with as if it were
         analogous to STDOUT for a child process.

Raises TypeError if arguments aren’t of type Proc.

# File lib/expectr/lambda.rb, line 20
def init_interface(args)
  unless args[:reader].kind_of?(Proc) && args[:writer].kind_of?(Proc)
    raise(TypeError, Errstr::PROC_EXPECTED)
  end

  @pid = -1
  @reader = args[:reader]
  @writer = args[:writer]
end
interact_thread() click to toggle source

Public: Create a Thread containing the loop which is responsible for handling input from the user in interact mode.

Returns a Thread containing the running loop.

# File lib/expectr/lambda.rb, line 85
def interact_thread
  Thread.new do
    env = prepare_interact_environment
    input = ''

    while @interact
      if select([$stdin], nil, nil, 1)
        c = $stdin.getc.chr
        send c unless c.nil?
      end
    end

    restore_environment(env)
  end
end
prepare_interact_environment() click to toggle source

Public: Prepare the operating environment for interact mode, set the interact flag to true.

Returns a Hash containing old signal handlers and tty parameters.

# File lib/expectr/lambda.rb, line 60
def prepare_interact_environment
  env = {sig: {}}

  # Save old tty settings and set up the new environment
  env[:tty] = `stty -g`
  `stty -icanon min 1 time 0 -echo`

  # SIGINT should be sent to the child as \C-c
  env[:sig]['INT'] = trap 'INT' do
    send "\C-c"
  end

  # SIGTSTP should be sent to the process as \C-z
  env[:sig]['TSTP'] = trap 'TSTP' do
    send "\C-z"
  end

  @interact = true
  env
end
send(args) click to toggle source

Public: Send input to the reader Proc.

args - Arguments to pass to the reader interface.

Returns nothing.

# File lib/expectr/lambda.rb, line 52
def send(args)
  @reader.call(*args)
end

Private Instance Methods

output_loop() click to toggle source

Internal: Call the writer lambda, reading the output produced, forcing UTF-8, appending to the internal buffer and printing to $stdout if appropriate.

Returns nothing.

# File lib/expectr/lambda.rb, line 108
def output_loop
  buf = ''
  loop do
    buf.clear

    begin
      buf << @writer.call.to_s
    rescue Errno::EIO # Lambda is signaling that execution should end.
      return
    end
    process_output(buf)
  end
end