Class: RubyExpect::Expect

Inherits:
Object
  • Object
show all
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

Class Method Summary collapse

Instance Method Summary collapse

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)


72
73
74
75
76
77
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
# File 'lib/ruby_expect/expect.rb', line 72

def initialize *args, &block
  options = {}
  if (args.last.is_a?(Hash))
    options = 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 = options[:child_pid]
  @debug = options[:debug] || false
  @buffer = ''
  @before = ''
  @match = ''
  @timeout = 0

  unless (block.nil?)
    procedure(&block)
  end
end

Instance Attribute Details

#beforeObject (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



32
33
34
# File 'lib/ruby_expect/expect.rb', line 32

def before
  @before
end

#bufferObject (readonly)

The accumulator buffer populated by read_loop. Only access this if you really know what you are doing!



42
43
44
# File 'lib/ruby_expect/expect.rb', line 42

def buffer
  @buffer
end

#last_matchObject (readonly)

The MatchData object from the last expect call or nil upon a timeout



38
39
40
# File 'lib/ruby_expect/expect.rb', line 38

def last_match
  @last_match
end

#matchObject (readonly)

The exact string that matched in the last expect call



35
36
37
# File 'lib/ruby_expect/expect.rb', line 35

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



130
131
132
133
134
135
136
137
138
139
# File 'lib/ruby_expect/expect.rb', line 130

def self.connect socket, options = {}, &block
  require 'socket'
  client = nil
  if (socket.is_a?(UNIXSocket))
   client = socket
  else
   client = UNIXSocket.new(socket)
  end
  return RubyExpect::Expect.new(client, options, &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



114
115
116
117
118
# File 'lib/ruby_expect/expect.rb', line 114

def self.spawn command, options = {}, &block
  shell_in, shell_out, pid = PTY.spawn(command)
  options[:child_pid] = pid
  return RubyExpect::Expect.new(shell_out, shell_in, options, &block)
end

Instance Method Details

#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


226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
# File 'lib/ruby_expect/expect.rb', line 226

def expect *patterns, &block
  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)
    return nil if (@read_fh.closed?)
    break unless (read_proc)
    @last_match = nil
    patterns.each_index do |i|
      if (match = patterns[i].match(@buffer))
        @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
  return nil
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


162
163
164
# File 'lib/ruby_expect/expect.rb', line 162

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



199
200
201
# File 'lib/ruby_expect/expect.rb', line 199

def send command
  @write_fh.write("#{command}\n")
end

#soft_closeObject



258
259
260
261
262
263
264
265
266
267
# File 'lib/ruby_expect/expect.rb', line 258

def soft_close
  while (! @read_fh.eof?)
    read_proc
  end
  @read_fh.close unless (@read_fh.closed?)
  @write_fh.close unless (@write_fh.closed?)
  if (@child_pid)
    Process.wait(@child_pid)
  end
end

#timeoutObject

Get the current timeout value



188
189
190
# File 'lib/ruby_expect/expect.rb', line 188

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



173
174
175
176
177
178
179
180
181
182
183
# File 'lib/ruby_expect/expect.rb', line 173

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