Module: Nuggets::IO::InteractMixin
- Included in:
- IO
- Defined in:
- lib/nuggets/io/interact_mixin.rb
Instance Method Summary collapse
-
#interact(input, output, timeout = nil, maxlen = 2 ** 16) ⇒ Object
call-seq: IO.interact(input, output[, timeout[, maxlen]]) -> anArray | nil.
Instance Method Details
#interact(input, output, timeout = nil, maxlen = 2 ** 16) ⇒ Object
call-seq:
IO.interact(input, output[, timeout[, maxlen]]) -> anArray | nil
Interact with both ends of a pipe in a non-blocking manner.
input represents the sending end and is a mapping from the actual input IO (to send into the pipe) to the pipe’s input handle (stdin). The input IO must support read_nonblock. If it’s a StringIO it will be extended appropriately. If it’s a String it will be converted to a StringIO.
output represents the receiving end and is a mapping from the pipe’s output handle (stdout) to the designated output IO (to receive data from the pipe), and, optionally, from the pipe’s error handle (stderr) to the designated error IO. The output and error IO must support << with a string argument. If either of them is a Proc it will be extended such that << delegates to call.
timeout, if given, will be passed to IO::select and nil is returned if the select call times out; in all other cases an empty array is returned.
maxlen is the chunk size for read_nonblock.
Examples:
require 'open3'
# simply prints 'input string' on STDOUT, ignores +stderr+
Open3.popen3('cat') { |stdin, stdout, stderr|
IO.interact({ "input string\n" => stdin }, { stdout => STDOUT })
}
# prints lines you type in reverse order to a string
str = ''
Open3.popen3('tac') { |stdin, stdout, stderr|
IO.interact({ STDIN => stdin }, { stdout => str })
}
puts str
# prints the IP adresses from /etc/hosts on STDOUT and their lengths
# on STDERR
cmd = %q{ruby -ne 'i = $_.split.first or next; warn i.length; puts i'}
Open3.popen3(cmd) { |stdin, stdout, stderr|
File.open('/etc/hosts') { |f|
IO.interact({ f => stdin }, { stdout => STDOUT, stderr => STDERR })
}
}
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/nuggets/io/interact_mixin.rb', line 79 def interact(input, output, timeout = nil, maxlen = 2 ** 16) readers, writers = {}, {} output.each { |key, val| if val.is_a?(::Proc) && !val.respond_to?(:<<) class << val; alias_method :<<, :call; end end readers[key] = val } input.each { |key, val| if key.is_a?(::String) require 'stringio' key = ::StringIO.new(key) end unless key.respond_to?(:read_nonblock) def key.read_nonblock(*args) read(*args) or raise ::EOFError, 'end of string reached' end end writers[val] = [key, ''] } close = lambda { |*args| container, item, read = args container.delete(item) read ? item.close_read : item.close_write } read = lambda { |*args| reader, buffer, writer = args container = writer ? writers : readers buffer ||= container[reader] begin buffer << reader.read_nonblock(maxlen) rescue ::Errno::EAGAIN rescue ::EOFError buffer.force_encoding( reader.internal_encoding || reader.external_encoding || Encoding.default_internal || Encoding.default_external ) if buffer.respond_to?(:force_encoding) close[container, writer || reader, !writer] end } until readers.empty? && writers.empty? fhs = select(readers.keys, writers.keys, nil, timeout) or return fhs[0].each { |reader| read[reader] } fhs[1].each { |writer| reader, buffer = writers[writer] read[reader, buffer, writer] or next if buffer.empty? begin bytes = writer.write_nonblock(buffer) rescue ::Errno::EPIPE close[writers, writer] end if bytes buffer.force_encoding('BINARY') if buffer.respond_to?(:force_encoding) buffer.slice!(0, bytes) end } end [] end |