expectr 1.1.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/bin/expectr +22 -0
- data/lib/expectr/adopt.rb +50 -0
- data/lib/expectr/child.rb +165 -0
- data/lib/expectr/error.rb +13 -2
- data/lib/expectr/errstr.rb +33 -0
- data/lib/expectr/interface.rb +53 -0
- data/lib/expectr/interpreter.rb +119 -0
- data/lib/expectr/lambda.rb +131 -0
- data/lib/expectr/version.rb +1 -1
- data/lib/expectr.rb +209 -175
- metadata +17 -25
- data/Gemfile +0 -2
- data/LICENSE +0 -20
- data/README.rdoc +0 -51
- data/Rakefile +0 -18
- data/expectr.gemspec +0 -18
- data/test/helper.rb +0 -4
- data/test/test_core.rb +0 -50
- data/test/test_initialization.rb +0 -17
- data/test/test_interaction.rb +0 -112
- data/test/test_signals.rb +0 -36
data/lib/expectr.rb
CHANGED
@@ -1,11 +1,15 @@
|
|
1
|
-
require 'pty'
|
2
1
|
require 'timeout'
|
3
2
|
require 'thread'
|
4
3
|
require 'io/console'
|
5
4
|
|
6
5
|
require 'expectr/error'
|
6
|
+
require 'expectr/errstr'
|
7
7
|
require 'expectr/version'
|
8
8
|
|
9
|
+
require 'expectr/child'
|
10
|
+
require 'expectr/adopt'
|
11
|
+
require 'expectr/lambda'
|
12
|
+
|
9
13
|
# Public: Expectr is an API to the functionality of Expect (see
|
10
14
|
# http://expect.nist.gov) implemented in ruby.
|
11
15
|
#
|
@@ -34,7 +38,6 @@ class Expectr
|
|
34
38
|
DEFAULT_FLUSH_BUFFER = true
|
35
39
|
DEFAULT_BUFFER_SIZE = 8192
|
36
40
|
DEFAULT_CONSTRAIN = false
|
37
|
-
DEFAULT_FORCE_MATCH = false
|
38
41
|
|
39
42
|
# Public: Gets/sets the number of seconds a call to Expectr#expect may last
|
40
43
|
attr_accessor :timeout
|
@@ -44,8 +47,6 @@ class Expectr
|
|
44
47
|
attr_accessor :buffer_size
|
45
48
|
# Public: Gets/sets whether to constrain the buffer to the buffer size
|
46
49
|
attr_accessor :constrain
|
47
|
-
# Public: Whether to always attempt to match once on calls to Expectr#expect.
|
48
|
-
attr_accessor :force_match
|
49
50
|
# Public: Returns the PID of the running process
|
50
51
|
attr_reader :pid
|
51
52
|
# Public: Returns the active buffer to match against
|
@@ -56,7 +57,7 @@ class Expectr
|
|
56
57
|
# Public: Initialize a new Expectr object.
|
57
58
|
# Spawns a sub-process and attaches to STDIN and STDOUT for the new process.
|
58
59
|
#
|
59
|
-
# cmd - A String or File referencing the application to launch
|
60
|
+
# cmd - A String or File referencing the application to launch (default: '')
|
60
61
|
# args - A Hash used to specify options for the new object (default: {}):
|
61
62
|
# :timeout - Number of seconds that a call to Expectr#expect has
|
62
63
|
# to complete (default: 30)
|
@@ -68,39 +69,31 @@ class Expectr
|
|
68
69
|
# (default: 8192)
|
69
70
|
# :constrain - Whether to constrain the internal buffer from the
|
70
71
|
# sub-process to :buffer_size (default: false)
|
71
|
-
# :
|
72
|
-
#
|
73
|
-
|
74
|
-
|
75
|
-
# set to false, preventing further matches until more
|
76
|
-
# output is generated otherwise. (default: false)
|
77
|
-
def initialize(cmd, args={})
|
78
|
-
cmd = cmd.path if cmd.kind_of?(File)
|
79
|
-
raise ArgumentError, "String or File expected" unless cmd.kind_of?(String)
|
80
|
-
|
72
|
+
# :interface - Interface Object to use when instantiating the new
|
73
|
+
# Expectr object. (default: Child)
|
74
|
+
def initialize(cmd = '', args = {})
|
75
|
+
setup_instance
|
81
76
|
parse_options(args)
|
82
|
-
@buffer = ''
|
83
|
-
@discard = ''
|
84
|
-
@out_mutex = Mutex.new
|
85
|
-
@out_update = false
|
86
|
-
@interact = false
|
87
|
-
|
88
|
-
@stdout,@stdin,@pid = PTY.spawn(cmd)
|
89
|
-
@stdout.winsize = $stdout.winsize if $stdout.tty?
|
90
77
|
|
91
|
-
|
92
|
-
|
78
|
+
case args[:interface]
|
79
|
+
when :lambda
|
80
|
+
interface = call_lambda_interface(args)
|
81
|
+
when :adopt
|
82
|
+
interface = call_adopt_interface(args)
|
83
|
+
else
|
84
|
+
interface = call_child_interface(cmd)
|
93
85
|
end
|
94
86
|
|
95
|
-
|
96
|
-
|
97
|
-
@pid = 0
|
87
|
+
interface.init_instance.each do |spec|
|
88
|
+
->(name, func) { define_singleton_method(name, func.call) }.call(*spec)
|
98
89
|
end
|
90
|
+
|
91
|
+
Thread.new { output_loop }
|
99
92
|
end
|
100
93
|
|
101
94
|
# Public: Relinquish control of the running process to the controlling
|
102
|
-
# terminal, acting as a pass-through for the life of the process
|
103
|
-
#
|
95
|
+
# terminal, acting as a pass-through for the life of the process (or until
|
96
|
+
# the leave! method is called).
|
104
97
|
#
|
105
98
|
# args - A Hash used to specify options to be used for interaction (default:
|
106
99
|
# {}):
|
@@ -110,26 +103,12 @@ class Expectr
|
|
110
103
|
#
|
111
104
|
# Returns the interaction Thread
|
112
105
|
def interact!(args = {})
|
113
|
-
|
114
|
-
|
115
|
-
blocking = args[:blocking] || false
|
116
|
-
@flush_buffer = args[:flush_buffer].nil? ? true : args[:flush_buffer]
|
117
|
-
|
118
|
-
interact = Thread.new do
|
119
|
-
env = prepare_interact_environment
|
120
|
-
input = ''
|
121
|
-
|
122
|
-
while @pid > 0 && @interact
|
123
|
-
if select([$stdin], nil, nil, 1)
|
124
|
-
c = $stdin.getc.chr
|
125
|
-
send c unless c.nil?
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
restore_environment(env)
|
106
|
+
if @interact
|
107
|
+
raise(ProcessError, Errstr::ALREADY_INTERACT)
|
130
108
|
end
|
131
109
|
|
132
|
-
|
110
|
+
@flush_buffer = args[:flush_buffer].nil? ? true : args[:flush_buffer]
|
111
|
+
args[:blocking] ? interact_thread.join : interact_thread
|
133
112
|
end
|
134
113
|
|
135
114
|
# Public: Report whether or not current Expectr object is in interact mode
|
@@ -146,32 +125,6 @@ class Expectr
|
|
146
125
|
@interact=false
|
147
126
|
end
|
148
127
|
|
149
|
-
# Public: Kill the running process, raise ProcessError if the pid isn't > 1
|
150
|
-
#
|
151
|
-
# signal - Symbol, String, or Fixnum representing the signal to send to the
|
152
|
-
# running process. (default: :TERM)
|
153
|
-
#
|
154
|
-
# Returns true if the process was successfully killed, false otherwise
|
155
|
-
def kill!(signal=:TERM)
|
156
|
-
raise ProcessError unless @pid > 0
|
157
|
-
(Process::kill(signal.to_sym, @pid) == 1)
|
158
|
-
end
|
159
|
-
|
160
|
-
# Public: Send input to the active process
|
161
|
-
#
|
162
|
-
# str - String to be sent to the active process
|
163
|
-
#
|
164
|
-
# Returns nothing.
|
165
|
-
# Raises Expectr::ProcessError if the process isn't running
|
166
|
-
def send(str)
|
167
|
-
begin
|
168
|
-
@stdin.syswrite str
|
169
|
-
rescue Errno::EIO #Application went away.
|
170
|
-
@pid = 0
|
171
|
-
end
|
172
|
-
raise Expectr::ProcessError unless @pid > 0
|
173
|
-
end
|
174
|
-
|
175
128
|
# Public: Wraps Expectr#send, appending a newline to the end of the string
|
176
129
|
#
|
177
130
|
# str - String to be sent to the active process (default: '')
|
@@ -184,7 +137,10 @@ class Expectr
|
|
184
137
|
# Public: Begin a countdown and search for a given String or Regexp in the
|
185
138
|
# output buffer.
|
186
139
|
#
|
187
|
-
# pattern - String or Regexp representing
|
140
|
+
# pattern - Object String or Regexp representing pattern for which to
|
141
|
+
# search, or a Hash containing pattern -> Proc mappings to be
|
142
|
+
# used in cases where multiple potential patterns should map
|
143
|
+
# to distinct actions.
|
188
144
|
# recoverable - Denotes whether failing to match the pattern should cause the
|
189
145
|
# method to raise an exception (default: false)
|
190
146
|
#
|
@@ -202,34 +158,61 @@ class Expectr
|
|
202
158
|
#
|
203
159
|
# exp.expect(/not there/, true)
|
204
160
|
# # => nil
|
161
|
+
#
|
162
|
+
# hash = { "First possibility" => -> { puts "option a" },
|
163
|
+
# "Second possibility" => -> { puts "option b" },
|
164
|
+
# default: => -> { puts "called on timeout" } }
|
165
|
+
# exp.expect(hash)
|
205
166
|
#
|
206
167
|
# Returns a MatchData object once a match is found if no block is given
|
207
168
|
# Yields the MatchData object representing the match
|
208
169
|
# Raises TypeError if something other than a String or Regexp is given
|
209
170
|
# Raises Timeout::Error if a match isn't found in time, unless recoverable
|
210
171
|
def expect(pattern, recoverable = false)
|
172
|
+
return expect_procmap(pattern) if pattern.is_a?(Hash)
|
173
|
+
|
211
174
|
match = nil
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
raise TypeError, "Pattern class should be String or Regexp"
|
175
|
+
pattern = Regexp.new(Regexp.quote(pattern)) if pattern.is_a?(String)
|
176
|
+
unless pattern.is_a?(Regexp)
|
177
|
+
raise(TypeError, Errstr::EXPECT_WRONG_TYPE)
|
216
178
|
end
|
217
179
|
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
end
|
180
|
+
match = watch_match(pattern, recoverable)
|
181
|
+
block_given? ? yield(match) : match
|
182
|
+
end
|
222
183
|
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
184
|
+
# Public: Begin a countdown and search for any of multiple possible patterns,
|
185
|
+
# performing designated actions upon success/failure.
|
186
|
+
#
|
187
|
+
# pattern_map - Hash containing mappings between Strings or Regexps and
|
188
|
+
# procedure objects. Additionally, an optional action,
|
189
|
+
# designated by :default or :timeout may be provided to specify
|
190
|
+
# an action to take upon failure.
|
191
|
+
#
|
192
|
+
# Examples
|
193
|
+
#
|
194
|
+
# exp.expect_procmap({
|
195
|
+
# "option 1" => -> { puts "action 1" },
|
196
|
+
# /option 2/ => -> { puts "action 2" },
|
197
|
+
# :default => -> { puts "default" }
|
198
|
+
# })
|
199
|
+
#
|
200
|
+
# Calls the procedure associated with the pattern provided.
|
201
|
+
def expect_procmap(pattern_map)
|
202
|
+
pattern_map, pattern, recoverable = process_procmap(pattern_map)
|
203
|
+
match = nil
|
204
|
+
|
205
|
+
match = watch_match(pattern, recoverable)
|
206
|
+
|
207
|
+
pattern_map.each do |s,p|
|
208
|
+
if s.is_a?(Regexp)
|
209
|
+
return p.call if s.match(match.to_s)
|
227
210
|
end
|
228
|
-
rescue Timeout::Error => details
|
229
|
-
raise details unless recoverable
|
230
211
|
end
|
231
212
|
|
232
|
-
|
213
|
+
pattern_map[:default].call unless pattern_map[:default].nil?
|
214
|
+
pattern_map[:timeout].call unless pattern_map[:timeout].nil?
|
215
|
+
nil
|
233
216
|
end
|
234
217
|
|
235
218
|
# Public: Clear output buffer
|
@@ -237,17 +220,11 @@ class Expectr
|
|
237
220
|
# Returns nothing.
|
238
221
|
def clear_buffer!
|
239
222
|
@out_mutex.synchronize do
|
240
|
-
@buffer
|
241
|
-
@out_update = false
|
223
|
+
@buffer.clear
|
242
224
|
end
|
243
225
|
end
|
244
226
|
|
245
|
-
|
246
|
-
#
|
247
|
-
# Returns a two-element array (same as IO#winsize).
|
248
|
-
def winsize
|
249
|
-
@stdout.winsize
|
250
|
-
end
|
227
|
+
private
|
251
228
|
|
252
229
|
# Internal: Print buffer to $stdout if @flush_buffer is true
|
253
230
|
#
|
@@ -255,7 +232,7 @@ class Expectr
|
|
255
232
|
#
|
256
233
|
# Returns nothing.
|
257
234
|
def print_buffer(buf)
|
258
|
-
print buf if @flush_buffer
|
235
|
+
$stdout.print buf if @flush_buffer
|
259
236
|
$stdout.flush unless $stdout.sync
|
260
237
|
end
|
261
238
|
|
@@ -270,8 +247,6 @@ class Expectr
|
|
270
247
|
buf.force_encoding('ISO-8859-1').encode('UTF-8', 'UTF-8', replace: nil)
|
271
248
|
end
|
272
249
|
|
273
|
-
private
|
274
|
-
|
275
250
|
# Internal: Determine values of instance options and set instance variables
|
276
251
|
# appropriately, allowing for default values where nothing is passed.
|
277
252
|
#
|
@@ -285,109 +260,168 @@ class Expectr
|
|
285
260
|
# maximum size of the internal buffer as well.
|
286
261
|
# :constrain - Whether to constrain the internal buffer from the
|
287
262
|
# sub-process to :buffer_size.
|
288
|
-
# :force_match - Whether to always attempt to match against the
|
289
|
-
# internal buffer on a call to Expectr#expect. This
|
290
|
-
# is relevant following a failed call to
|
291
|
-
# Expectr#expect, which will leave the update status
|
292
|
-
# set to false, preventing further matches until more
|
293
|
-
# output is generated otherwise.
|
294
263
|
#
|
295
264
|
# Returns nothing.
|
296
265
|
def parse_options(args)
|
297
266
|
@timeout = args[:timeout] || DEFAULT_TIMEOUT
|
298
267
|
@buffer_size = args[:buffer_size] || DEFAULT_BUFFER_SIZE
|
299
268
|
@constrain = args[:constrain] || DEFAULT_CONSTRAIN
|
300
|
-
@force_match = args[:force_match] || DEFAULT_FORCE_MATCH
|
301
269
|
@flush_buffer = args[:flush_buffer]
|
302
270
|
@flush_buffer = DEFAULT_FLUSH_BUFFER if @flush_buffer.nil?
|
303
271
|
end
|
304
272
|
|
305
|
-
# Internal:
|
306
|
-
#
|
273
|
+
# Internal: Initialize instance variables to their default values.
|
274
|
+
#
|
275
|
+
# Returns nothing.
|
276
|
+
def setup_instance
|
277
|
+
@buffer = ''
|
278
|
+
@discard = ''
|
279
|
+
@thread = nil
|
280
|
+
@out_mutex = Mutex.new
|
281
|
+
@interact = false
|
282
|
+
end
|
283
|
+
|
284
|
+
# Internal: Handle data from the interface, forcing UTF-8 encoding, appending
|
285
|
+
# it to the internal buffer, and printing it to $stdout if appropriate.
|
307
286
|
#
|
308
287
|
# Returns nothing.
|
309
|
-
def process_output
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
@pid = 0
|
318
|
-
return
|
319
|
-
end
|
320
|
-
|
321
|
-
print_buffer(force_utf8(buf))
|
322
|
-
|
323
|
-
@out_mutex.synchronize do
|
324
|
-
@buffer << buf
|
325
|
-
if @constrain && @buffer.length > @buffer_size
|
326
|
-
@buffer = @buffer[-@buffer_size..-1]
|
327
|
-
end
|
328
|
-
@out_update = true
|
329
|
-
end
|
288
|
+
def process_output(buf)
|
289
|
+
force_utf8(buf)
|
290
|
+
print_buffer(buf)
|
291
|
+
|
292
|
+
@out_mutex.synchronize do
|
293
|
+
@buffer << buf
|
294
|
+
if @constrain && @buffer.length > @buffer_size
|
295
|
+
@buffer = @buffer[-@buffer_size..-1]
|
330
296
|
end
|
297
|
+
@thread.wakeup if @thread
|
331
298
|
end
|
332
299
|
end
|
333
300
|
|
334
|
-
# Internal:
|
335
|
-
#
|
336
|
-
#
|
337
|
-
#
|
338
|
-
#
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
301
|
+
# Internal: Check for a match against a given pattern until a match is found.
|
302
|
+
# This method should be wrapped in a Timeout block or otherwise have some
|
303
|
+
# mechanism to break out of the loop.
|
304
|
+
#
|
305
|
+
# pattern - String or Regexp object containing the pattern for which to
|
306
|
+
# watch.
|
307
|
+
#
|
308
|
+
# Returns a MatchData object containing the match found.
|
309
|
+
def check_match(pattern)
|
310
|
+
match = nil
|
311
|
+
@thread = Thread.current
|
312
|
+
while match.nil?
|
313
|
+
@out_mutex.synchronize do
|
314
|
+
match = pattern.match(@buffer)
|
315
|
+
@out_mutex.sleep if match.nil?
|
316
|
+
end
|
348
317
|
end
|
318
|
+
match
|
319
|
+
ensure
|
320
|
+
@thread = nil
|
321
|
+
end
|
349
322
|
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
323
|
+
# Internal: Call the Child Interface to instantiate the Expectr object.
|
324
|
+
#
|
325
|
+
# cmd - String or File object referencing the command to be run.
|
326
|
+
#
|
327
|
+
# Returns the Interface object.
|
328
|
+
def call_child_interface(cmd)
|
329
|
+
interface = Expectr::Child.new(cmd)
|
330
|
+
@stdin = interface.stdin
|
331
|
+
@stdout = interface.stdout
|
332
|
+
@pid = interface.pid
|
354
333
|
|
355
|
-
|
356
|
-
|
357
|
-
@
|
334
|
+
Thread.new do
|
335
|
+
Process.wait @pid
|
336
|
+
@pid = 0
|
358
337
|
end
|
359
338
|
|
360
|
-
|
361
|
-
env
|
339
|
+
interface
|
362
340
|
end
|
363
341
|
|
364
|
-
# Internal:
|
365
|
-
#
|
366
|
-
#
|
367
|
-
|
368
|
-
|
369
|
-
|
342
|
+
# Internal: Call the Lambda Interface to instantiate the Expectr object.
|
343
|
+
#
|
344
|
+
# args - Arguments hash passed per #initialize.
|
345
|
+
#
|
346
|
+
# Returns the Interface object.
|
347
|
+
def call_lambda_interface(args)
|
348
|
+
interface = Expectr::Lambda.new(args[:reader], args[:writer])
|
349
|
+
@pid = -1
|
350
|
+
@reader = interface.reader
|
351
|
+
@writer = interface.writer
|
352
|
+
|
353
|
+
interface
|
354
|
+
end
|
355
|
+
|
356
|
+
# Internal: Call the Adopt Interface to instantiate the Expectr object.
|
357
|
+
#
|
358
|
+
# args - Arguments hash passed per #initialize.
|
359
|
+
#
|
360
|
+
# Returns the Interface object.
|
361
|
+
def call_adopt_interface(args)
|
362
|
+
interface = Expectr::Adopt.new(args[:stdin], args[:stdout])
|
363
|
+
@stdin = interface.stdin
|
364
|
+
@stdout = interface.stdout
|
365
|
+
@pid = args[:pid] || 1
|
366
|
+
|
367
|
+
if @pid > 0
|
368
|
+
Thread.new do
|
369
|
+
Process.wait @pid
|
370
|
+
@pid = 0
|
371
|
+
end
|
370
372
|
end
|
371
|
-
|
372
|
-
|
373
|
+
|
374
|
+
interface
|
373
375
|
end
|
374
376
|
|
375
|
-
# Internal:
|
376
|
-
#
|
377
|
-
#
|
377
|
+
# Internal: Watch for a match within the timeout period.
|
378
|
+
#
|
379
|
+
# pattern - String or Regexp object containing the pattern for which to
|
380
|
+
# watch.
|
381
|
+
# recoverable - Boolean denoting whether a failure to find a match should be
|
382
|
+
# considered fatal.
|
378
383
|
#
|
379
|
-
# Returns a MatchData object
|
380
|
-
|
384
|
+
# Returns a MatchData object if a match was found, or else nil.
|
385
|
+
# Raises Timeout::Error if no match is found and recoverable is false.
|
386
|
+
def watch_match(pattern, recoverable)
|
381
387
|
match = nil
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
match = pattern.match(@buffer)
|
386
|
-
@out_update = false
|
387
|
-
end
|
388
|
-
end
|
389
|
-
sleep 0.1
|
388
|
+
|
389
|
+
Timeout::timeout(@timeout) do
|
390
|
+
match = check_match(pattern)
|
390
391
|
end
|
392
|
+
|
393
|
+
@out_mutex.synchronize do
|
394
|
+
@discard = @buffer[0..match.begin(0)-1]
|
395
|
+
@buffer = @buffer[match.end(0)..-1]
|
396
|
+
end
|
397
|
+
|
391
398
|
match
|
399
|
+
rescue Timeout::Error => details
|
400
|
+
raise(Timeout::Error, details) unless recoverable
|
401
|
+
nil
|
402
|
+
end
|
403
|
+
|
404
|
+
# Internal: Process a pattern to procedure mapping, producing a sanitized
|
405
|
+
# Hash, a unified Regexp and a boolean denoting whether an Exception should
|
406
|
+
# be raised upon timeout.
|
407
|
+
#
|
408
|
+
# pattern_map - A Hash containing mappings between patterns designated by
|
409
|
+
# either strings or Regexp objects, to procedures. Optionally,
|
410
|
+
# either :default or :timeout may be mapped to a procedure in
|
411
|
+
# order to designate an action to take upon timeout.
|
412
|
+
#
|
413
|
+
# Returns a Hash, Regexp and boolean object.
|
414
|
+
def process_procmap(pattern_map)
|
415
|
+
pattern_map = pattern_map.reduce({}) do |c,e|
|
416
|
+
c.merge((e[0].is_a?(Symbol) ? e[0] : Regexp.new(e[0].to_s)) => e[1])
|
417
|
+
end
|
418
|
+
pattern = pattern_map.keys.reduce("") do |c,e|
|
419
|
+
e.is_a?(Regexp) ? c + "(#{e.source})|" : c
|
420
|
+
end
|
421
|
+
pattern = Regexp.new(pattern.gsub(/\|$/, ''))
|
422
|
+
recoverable = pattern_map.keys.include?(:default)
|
423
|
+
recoverable ||= pattern_map.keys.include?(:timeout)
|
424
|
+
|
425
|
+
return pattern_map, pattern, recoverable
|
392
426
|
end
|
393
427
|
end
|
metadata
CHANGED
@@ -1,62 +1,54 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: expectr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
5
|
-
prerelease:
|
4
|
+
version: 2.0.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Chris Wuest
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2013-
|
11
|
+
date: 2013-06-25 00:00:00.000000000 Z
|
13
12
|
dependencies: []
|
14
13
|
description: Expectr is an interface to the functionality of Expect in Ruby
|
15
14
|
email: chris@chriswuest.com
|
16
|
-
executables:
|
15
|
+
executables:
|
16
|
+
- expectr
|
17
17
|
extensions: []
|
18
18
|
extra_rdoc_files: []
|
19
19
|
files:
|
20
|
-
- Gemfile
|
21
|
-
- LICENSE
|
22
|
-
- README.rdoc
|
23
|
-
- Rakefile
|
24
|
-
- expectr.gemspec
|
25
20
|
- lib/expectr.rb
|
21
|
+
- lib/expectr/adopt.rb
|
22
|
+
- lib/expectr/child.rb
|
26
23
|
- lib/expectr/error.rb
|
24
|
+
- lib/expectr/errstr.rb
|
25
|
+
- lib/expectr/interface.rb
|
26
|
+
- lib/expectr/interpreter.rb
|
27
|
+
- lib/expectr/lambda.rb
|
27
28
|
- lib/expectr/version.rb
|
28
|
-
-
|
29
|
-
- test/test_core.rb
|
30
|
-
- test/test_initialization.rb
|
31
|
-
- test/test_interaction.rb
|
32
|
-
- test/test_signals.rb
|
29
|
+
- bin/expectr
|
33
30
|
homepage: http://github.com/cwuest/expectr
|
34
31
|
licenses:
|
35
32
|
- MIT
|
33
|
+
metadata: {}
|
36
34
|
post_install_message:
|
37
35
|
rdoc_options: []
|
38
36
|
require_paths:
|
39
37
|
- lib
|
40
38
|
required_ruby_version: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
39
|
requirements:
|
43
|
-
- -
|
40
|
+
- - '>='
|
44
41
|
- !ruby/object:Gem::Version
|
45
42
|
version: '0'
|
46
43
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
47
|
-
none: false
|
48
44
|
requirements:
|
49
|
-
- -
|
45
|
+
- - '>='
|
50
46
|
- !ruby/object:Gem::Version
|
51
47
|
version: '0'
|
52
48
|
requirements: []
|
53
49
|
rubyforge_project:
|
54
|
-
rubygems_version:
|
50
|
+
rubygems_version: 2.0.3
|
55
51
|
signing_key:
|
56
|
-
specification_version:
|
52
|
+
specification_version: 4
|
57
53
|
summary: Expect for Ruby
|
58
|
-
test_files:
|
59
|
-
- test/test_core.rb
|
60
|
-
- test/test_initialization.rb
|
61
|
-
- test/test_interaction.rb
|
62
|
-
- test/test_signals.rb
|
54
|
+
test_files: []
|
data/Gemfile
DELETED
data/LICENSE
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
Copyright (c) 2012 Chris Wuest
|
2
|
-
|
3
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
-
a copy of this software and associated documentation files (the
|
5
|
-
"Software"), to deal in the Software without restriction, including
|
6
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
-
permit persons to whom the Software is furnished to do so, subject to
|
9
|
-
the following conditions:
|
10
|
-
|
11
|
-
The above copyright notice and this permission notice shall be
|
12
|
-
included in all copies or substantial portions of the Software.
|
13
|
-
|
14
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
-
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
-
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
-
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
DELETED
@@ -1,51 +0,0 @@
|
|
1
|
-
= Expectr
|
2
|
-
|
3
|
-
Expectr is an API to the functionality of Expect (see http://expect.nist.gov/)
|
4
|
-
implemented in ruby.
|
5
|
-
|
6
|
-
== Using Expectr
|
7
|
-
|
8
|
-
Effort is taken to make implementation as simple as possible.
|
9
|
-
|
10
|
-
=== Automation
|
11
|
-
|
12
|
-
Implementation of task automation should be relatively straightforward. A
|
13
|
-
lightweight application might look similar to the following:
|
14
|
-
|
15
|
-
# myapp.rb
|
16
|
-
require 'expectr'
|
17
|
-
|
18
|
-
exp = Expectr.new( '/bin/bash', flush_buffer: false )
|
19
|
-
exp.send( "ls\n" )
|
20
|
-
match = exp.expect( %r{bash} ) # => MatchData
|
21
|
-
match = exp.expect( "this shouldn't match anything" ) # raises Timeout::Error
|
22
|
-
|
23
|
-
=== Interaction
|
24
|
-
|
25
|
-
It is possible to allow a user to interact directly with an Expectr object
|
26
|
-
as with the Expect functionality in TCL
|
27
|
-
|
28
|
-
# logmein.rb
|
29
|
-
require 'expectr'
|
30
|
-
|
31
|
-
ssh = { host: 'example.com', user: 'example', pass: 'password' }
|
32
|
-
exp = Expectr.new( "ssh #{ssh[:user]}@#{ssh[:host]}" )
|
33
|
-
exp.expect( %r{#{ssh[:user]}@#{ssh[:host]}'s password:} )
|
34
|
-
ssh.send( ssh[:pass] + "\n" )
|
35
|
-
|
36
|
-
ssh.interact!
|
37
|
-
|
38
|
-
== Contributions
|
39
|
-
|
40
|
-
Effort is made to keep external requirements to a minimum. As such, presently
|
41
|
-
there are no external requirements for development. If you want to
|
42
|
-
contribute, please:
|
43
|
-
|
44
|
-
* Fork the project.
|
45
|
-
* Make a branch for your contribution if it's more than a simple fix.
|
46
|
-
* Add tests for new functionality.
|
47
|
-
* Send me a pull request on Github
|
48
|
-
|
49
|
-
== Copyright
|
50
|
-
|
51
|
-
Copyright (c) 2012 Chris Wuest. See LICENSE for details.
|