Class: Expectr

Inherits:
Object
  • Object
show all
Defined in:
lib/expectr.rb,
lib/expectr/adopt.rb,
lib/expectr/child.rb,
lib/expectr/error.rb,
lib/expectr/errstr.rb,
lib/expectr/lambda.rb,
lib/expectr/version.rb,
lib/expectr/interface.rb,
lib/expectr/interpreter.rb

Overview

Public: Expectr is an API to the functionality of Expect (see expect.nist.gov) implemented in ruby.

Expectr contrasts with Ruby’s built-in Expect class by avoiding tying in with the IO class, instead creating a new object entirely to allow for more grainular control over the execution and display of the program being run.

Examples

# SSH Login to another machine
exp = Expectr.new('ssh [email protected]')
exp.expect("Password:")
exp.send('password')
exp.interact!(blocking: true)

# See if a web server is running on the local host, react accordingly
exp = Expectr.new('netstat -ntl|grep ":80 " && echo "WEB"', timeout: 1)
if exp.expeect("WEB")
  # Do stuff if we see 'WEB' in the output
else
  # Do other stuff
end

Defined Under Namespace

Modules: Errstr, Interface Classes: Adopt, Child, Interpreter, Lambda, ProcessError

Constant Summary collapse

DEFAULT_TIMEOUT =
30
DEFAULT_FLUSH_BUFFER =
true
DEFAULT_BUFFER_SIZE =
8192
DEFAULT_CONSTRAIN =
false
VERSION =
'2.0.0'

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(cmd = '', args = {}) ⇒ Expectr

Public: Initialize a new Expectr object. Spawns a sub-process and attaches to STDIN and STDOUT for the new process.

cmd - A String or File referencing the application to launch (default: ”) args - A Hash used to specify options for the new object (default: {}):

:timeout      - Number of seconds that a call to Expectr#expect has
                to complete (default: 30)
:flush_buffer - Whether to flush output of the process to the
                console (default: true)
:buffer_size  - Number of bytes to attempt to read from sub-process
                at a time.  If :constrain is true, this will be the
                maximum size of the internal buffer as well.
                (default: 8192)
:constrain    - Whether to constrain the internal buffer from the
                sub-process to :buffer_size (default: false)
:interface    - Interface Object to use when instantiating the new
                Expectr object. (default: Child)


74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/expectr.rb', line 74

def initialize(cmd = '', args = {})
  setup_instance
  parse_options(args)

  case args[:interface]
  when :lambda
    interface = call_lambda_interface(args)
  when :adopt
    interface = call_adopt_interface(args)
  else
    interface = call_child_interface(cmd)
  end

  interface.init_instance.each do |spec|
    ->(name, func) { define_singleton_method(name, func.call) }.call(*spec)
  end

  Thread.new { output_loop }
end

Instance Attribute Details

#bufferObject (readonly)

Public: Returns the active buffer to match against



53
54
55
# File 'lib/expectr.rb', line 53

def buffer
  @buffer
end

#buffer_sizeObject

Public: Gets/sets the number of bytes to use for the internal buffer



47
48
49
# File 'lib/expectr.rb', line 47

def buffer_size
  @buffer_size
end

#constrainObject

Public: Gets/sets whether to constrain the buffer to the buffer size



49
50
51
# File 'lib/expectr.rb', line 49

def constrain
  @constrain
end

#discardObject (readonly)

Public: Returns the buffer discarded by the latest call to Expectr#expect



55
56
57
# File 'lib/expectr.rb', line 55

def discard
  @discard
end

#flush_bufferObject

Public: Gets/sets whether to flush program output to $stdout



45
46
47
# File 'lib/expectr.rb', line 45

def flush_buffer
  @flush_buffer
end

#pidObject (readonly)

Public: Returns the PID of the running process



51
52
53
# File 'lib/expectr.rb', line 51

def pid
  @pid
end

#timeoutObject

Public: Gets/sets the number of seconds a call to Expectr#expect may last



43
44
45
# File 'lib/expectr.rb', line 43

def timeout
  @timeout
end

Instance Method Details

#clear_buffer!Object

Public: Clear output buffer

Returns nothing.



221
222
223
224
225
# File 'lib/expectr.rb', line 221

def clear_buffer!
  @out_mutex.synchronize do
    @buffer.clear
  end
end

#expect(pattern, recoverable = false) ⇒ Object

Public: Begin a countdown and search for a given String or Regexp in the output buffer.

pattern - Object String or Regexp representing pattern for which to

search, or a Hash containing pattern -> Proc mappings to be
used in cases where multiple potential patterns should map
to distinct actions.

recoverable - Denotes whether failing to match the pattern should cause the

method to raise an exception (default: false)

Examples

exp.expect("this should exist")
# => MatchData

exp.expect("this should exist") do
  # ...
end

exp.expect(/not there/)
# Raises Timeout::Error

exp.expect(/not there/, true)
# => nil

hash = { "First possibility"  => -> { puts "option a" },
         "Second possibility" => -> { puts "option b" },
         default:             => -> { puts "called on timeout" } }
exp.expect(hash)

Returns a MatchData object once a match is found if no block is given Yields the MatchData object representing the match Raises TypeError if something other than a String or Regexp is given Raises Timeout::Error if a match isn’t found in time, unless recoverable



171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/expectr.rb', line 171

def expect(pattern, recoverable = false)
  return expect_procmap(pattern) if pattern.is_a?(Hash)

  match = nil
  pattern = Regexp.new(Regexp.quote(pattern)) if pattern.is_a?(String)
  unless pattern.is_a?(Regexp)
    raise(TypeError, Errstr::EXPECT_WRONG_TYPE)
  end

  match = watch_match(pattern, recoverable)
  block_given? ? yield(match) : match
end

#expect_procmap(pattern_map) ⇒ Object

Public: Begin a countdown and search for any of multiple possible patterns, performing designated actions upon success/failure.

pattern_map - Hash containing mappings between Strings or Regexps and

procedure objects.  Additionally, an optional action,
designated by :default or :timeout may be provided to specify
an action to take upon failure.

Examples

exp.expect_procmap({
  "option 1" => -> { puts "action 1" },
  /option 2/ => -> { puts "action 2" },
  :default   => -> { puts "default" }
})

Calls the procedure associated with the pattern provided.



201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/expectr.rb', line 201

def expect_procmap(pattern_map)
  pattern_map, pattern, recoverable = process_procmap(pattern_map)
  match = nil

  match = watch_match(pattern, recoverable)

  pattern_map.each do |s,p|
    if s.is_a?(Regexp)
      return p.call if s.match(match.to_s)
    end
  end

  pattern_map[:default].call unless pattern_map[:default].nil?
  pattern_map[:timeout].call unless pattern_map[:timeout].nil?
  nil
end

#interact!(args = {}) ⇒ Object

Public: Relinquish control of the running process to the controlling terminal, acting as a pass-through for the life of the process (or until the leave! method is called).

args - A Hash used to specify options to be used for interaction (default:

{}):
:flush_buffer - explicitly set @flush_buffer to the value specified
:blocking     - Whether to block on this call or allow code
                execution to continue (default: false)

Returns the interaction Thread



105
106
107
108
109
110
111
112
# File 'lib/expectr.rb', line 105

def interact!(args = {})
  if @interact
    raise(ProcessError, Errstr::ALREADY_INTERACT)
  end

  @flush_buffer = args[:flush_buffer].nil? ? true : args[:flush_buffer]
  args[:blocking] ? interact_thread.join : interact_thread
end

#interact?Boolean

Public: Report whether or not current Expectr object is in interact mode

Returns true or false

Returns:

  • (Boolean)


117
118
119
# File 'lib/expectr.rb', line 117

def interact?
  @interact
end

#leave!Object

Public: Cause the current Expectr object to drop out of interact mode

Returns nothing.



124
125
126
# File 'lib/expectr.rb', line 124

def leave!
  @interact=false
end

#puts(str = '') ⇒ Object

Public: Wraps Expectr#send, appending a newline to the end of the string

str - String to be sent to the active process (default: ”)

Returns nothing.



133
134
135
# File 'lib/expectr.rb', line 133

def puts(str = '')
  send str + "\n"
end