Class: RubyExpect::Expect
- Inherits:
-
Object
- Object
- RubyExpect::Expect
- Defined in:
- lib/ruby_expect/expect.rb
Overview
This is the main class used to interact with IO objects An Expect object can be used to send and receive data on any read/write IO object.
Instance Attribute Summary collapse
-
#before ⇒ Object
readonly
Any data that was in the accumulator buffer before match in the last expect call if the last call to expect resulted in a timeout, then before is an empty string.
-
#buffer ⇒ Object
readonly
The accumulator buffer populated by read_loop.
-
#last_match ⇒ Object
readonly
The MatchData object from the last expect call or nil upon a timeout.
-
#logger ⇒ Object
Any supplied logger will be used both for errors and warnings as well as debug information (I/O when sending and expecting).
-
#match ⇒ Object
readonly
The exact string that matched in the last expect call.
Class Method Summary collapse
-
.connect(socket, options = {}, &block) ⇒ Object
Connect to a socket.
-
.spawn(command, options = {}, &block) ⇒ Object
Spawn a command and interact with it.
Instance Method Summary collapse
- #debug ⇒ Object
-
#debug=(debug) ⇒ Object
Set debug in order to see the output being read from the spawned process.
- #debug? ⇒ Boolean
-
#expect(*patterns, &block) ⇒ Object
Wait until either the timeout occurs or one of the given patterns is seen in the input.
-
#initialize(*args, &block) ⇒ Expect
constructor
Create a new Expect object for the given IO object.
-
#interact ⇒ Object
Provides the ability to hand control back to the user and allow them to interact with the spawned process.
-
#procedure(&block) ⇒ Object
Perform a series of ‘expects’ using the DSL defined in Procedure.
-
#send(command) ⇒ Object
Convenience method that will send a string followed by a newline to the write handle of the IO object.
-
#soft_close ⇒ Object
Wait for the process to complete or the read handle to be closed and then clean everything up.
-
#timeout ⇒ Object
Get the current timeout value.
-
#timeout=(timeout) ⇒ Object
Set the time to wait for an expected pattern.
Constructor Details
#initialize(*args, &block) ⇒ Expect
Create a new Expect object for the given IO object
There are two ways to create a new Expect object. The first is to supply a single IO object with a read/write mode. The second method is to supply a read file handle as the first argument and a write file handle as the second argument.
args
-
at most 3 arguments, 1 or 2 IO objects (read/write or read + write and an optional options hash. The only currently supported option is :debug (default false) which, if enabled, will send data received on the input filehandle to STDOUT
block
-
An optional block called upon initialization. See procedure
Examples
# expect with a read/write filehandle
exp = Expect.new(rwfh)
# expect with separate read and write filehandles
exp = Expect.new(rfh, wfh)
# turning on debugging
exp = Expect.new(rfh, wfh, :debug => true)
78 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 |
# File 'lib/ruby_expect/expect.rb', line 78 def initialize *args, &block = {} if (args.last.is_a?(Hash)) = args.pop end raise ArgumentError("First argument must be an IO object") unless (args[0].is_a?(IO)) if (args.size == 1) @write_fh = args.shift @read_fh = @write_fh elsif (args.size == 2) raise ArgumentError("Second argument must be an IO object") unless (args[1].is_a?(IO)) @write_fh = args.shift @read_fh = args.shift else raise ArgumentError.new("either specify a read/write IO object, or a read IO object and a write IO object") end raise "Input file handle is not readable!" unless (@read_fh.stat.readable?) raise "Output file handle is not writable!" unless (@write_fh.stat.writable?) @child_pid = [:child_pid] @debug = [:debug] || false @logger = [:logger] if @logger.nil? @logger = Logger.new(STDERR) @logger.level = Logger::FATAL end @buffer = '' @log_buffer = '' @before = '' @match = '' @timeout = 0 unless (block.nil?) procedure(&block) end end |
Instance Attribute Details
#before ⇒ Object (readonly)
Any data that was in the accumulator buffer before match in the last expect call if the last call to expect resulted in a timeout, then before is an empty string
34 35 36 |
# File 'lib/ruby_expect/expect.rb', line 34 def before @before end |
#buffer ⇒ Object (readonly)
The accumulator buffer populated by read_loop. Only access this if you really know what you are doing!
44 45 46 |
# File 'lib/ruby_expect/expect.rb', line 44 def buffer @buffer end |
#last_match ⇒ Object (readonly)
The MatchData object from the last expect call or nil upon a timeout
40 41 42 |
# File 'lib/ruby_expect/expect.rb', line 40 def last_match @last_match end |
#logger ⇒ Object
Any supplied logger will be used both for errors and warnings as well as debug information (I/O when sending and expecting)
48 49 50 |
# File 'lib/ruby_expect/expect.rb', line 48 def logger @logger end |
#match ⇒ Object (readonly)
The exact string that matched in the last expect call
37 38 39 |
# File 'lib/ruby_expect/expect.rb', line 37 def match @match end |
Class Method Details
.connect(socket, options = {}, &block) ⇒ Object
Connect to a socket
command
-
The socket or file to connect to
block
-
Optional block to call and run a procedure in
143 144 145 146 147 148 149 150 151 152 |
# File 'lib/ruby_expect/expect.rb', line 143 def self.connect socket, = {}, &block require 'socket' client = nil if (socket.is_a?(UNIXSocket)) client = socket else client = UNIXSocket.new(socket) end return RubyExpect::Expect.new(client, , &block) end |
.spawn(command, options = {}, &block) ⇒ Object
Spawn a command and interact with it
command
-
The command to execute
block
-
Optional block to call and run a procedure in
127 128 129 130 131 |
# File 'lib/ruby_expect/expect.rb', line 127 def self.spawn command, = {}, &block shell_in, shell_out, pid = PTY.spawn(command) [:child_pid] = pid return RubyExpect::Expect.new(shell_out, shell_in, , &block) end |
Instance Method Details
#debug ⇒ Object
165 166 167 168 |
# File 'lib/ruby_expect/expect.rb', line 165 def debug warn "`debug` is deprecated. Use a logger instead" @logger.debug? end |
#debug=(debug) ⇒ Object
Set debug in order to see the output being read from the spawned process
156 157 158 159 160 161 162 163 |
# File 'lib/ruby_expect/expect.rb', line 156 def debug= debug warn "`debug` is deprecated. Use a logger instead" if debug @logger.level = Logger::DEBUG else @logger.level = -1 end end |
#debug? ⇒ Boolean
170 171 172 173 |
# File 'lib/ruby_expect/expect.rb', line 170 def debug? warn "`debug` is deprecated. Use a logger instead" @logger.debug? end |
#expect(*patterns, &block) ⇒ Object
Wait until either the timeout occurs or one of the given patterns is seen in the input. Upon a match, the property before is assigned all input in the accumulator before the match, the matched string itself is assigned to the match property and an optional block is called
The method will return the index of the matched pattern or nil if no match has occurred during the timeout period
patterns
-
list of patterns to look for. These can be either literal strings or Regexp objects
block
-
An optional block to be called if one of the patterns matches
Example
exp = Expect.new(io)
exp.expect('Password:') do
send("12345")
end
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 |
# File 'lib/ruby_expect/expect.rb', line 260 def expect *patterns, &block @logger.debug("Expecting: #{patterns.inspect}") if @logger.debug? patterns = pattern_escape(*patterns) @end_time = 0 if (@timeout != 0) @end_time = Time.now + @timeout end @before = '' matched_index = nil while (@end_time == 0 || Time.now < @end_time) raise ClosedError.new("Read filehandle is closed") if (@read_fh.closed?) break unless (read_proc) @last_match = nil patterns.each_index do |i| if (match = patterns[i].match(@buffer)) log_buffer(true) @logger.debug(" Matched: #{match}") if @logger.debug? @last_match = match @before = @buffer.slice!(0...match.begin(0)) @match = @buffer.slice!(0...match.to_s.length) matched_index = i break end end unless (@last_match.nil?) unless (block.nil?) instance_eval(&block) end return matched_index end end @logger.debug("Timeout") return nil end |
#interact ⇒ Object
Provides the ability to hand control back to the user and allow them to interact with the spawned process.
317 318 319 320 321 322 323 324 325 |
# File 'lib/ruby_expect/expect.rb', line 317 def interact if ($stdin.tty?) $stdin.raw do |stdin| interact_loop(stdin) end else interact_loop($stdin) end end |
#procedure(&block) ⇒ Object
Perform a series of ‘expects’ using the DSL defined in Procedure
block
-
The block will be called in the context of a new Procedure object
Example
exp = Expect.new(io)
exp.procedure do
each do
expect /first expected line/ do
send "some text to send"
end
expect /second expected line/ do
send "some more text to send"
end
end
end
196 197 198 |
# File 'lib/ruby_expect/expect.rb', line 196 def procedure &block RubyExpect::Procedure.new(self, &block) end |
#send(command) ⇒ Object
Convenience method that will send a string followed by a newline to the write handle of the IO object
command
-
String to send down the pipe
233 234 235 |
# File 'lib/ruby_expect/expect.rb', line 233 def send command @write_fh.write("#{command}\n") end |
#soft_close ⇒ Object
Wait for the process to complete or the read handle to be closed and then clean everything up. This method call will block until the spawned process or connected filehandle/socket is closed
301 302 303 304 305 306 307 308 309 310 311 312 |
# File 'lib/ruby_expect/expect.rb', line 301 def soft_close while (! @read_fh.closed?) read_proc end @read_fh.close unless (@read_fh.closed?) @write_fh.close unless (@write_fh.closed?) if (@child_pid) Process.wait(@child_pid) return $? end return true end |
#timeout ⇒ Object
Get the current timeout value
222 223 224 |
# File 'lib/ruby_expect/expect.rb', line 222 def timeout @timeout end |
#timeout=(timeout) ⇒ Object
Set the time to wait for an expected pattern
timeout
-
number of seconds to wait before giving up. A value of zero means wait forever
207 208 209 210 211 212 213 214 215 216 217 |
# File 'lib/ruby_expect/expect.rb', line 207 def timeout= timeout unless (timeout.is_a?(Integer)) raise "Timeout must be an integer" end unless (timeout >= 0) raise "Timeout must be greater than or equal to zero" end @timeout = timeout @end_time = 0 end |