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.rb by avoiding tying in with the IO class in favor of creating a new object entirely to allow for more granular 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: Adopt, Child, Errstr, Interface, Lambda Classes: Interpreter, ProcessError

Constant Summary collapse

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

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

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

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

cmd_args - This may be either a Hash containing arguments (described below)

or a String or File Object referencing the application to launch
(assuming Child interface).  This argument, if not a Hash, will
be changed into the Hash { cmd: cmd_args }.  This argument will
be  merged with the args Hash, overriding any arguments
specified there.
This argument is kept around for the sake of backward
compatibility with extant Expectr scripts and may be deprecated
in the future.  (default: {})

args - A Hash used to specify options for the instance. (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 characters.
                (default: false)
:interface    - Interface Object to use when instantiating the
                new Expectr object. (default: Child)


82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/expectr.rb', line 82

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

  cmd_args = { cmd: cmd_args } unless cmd_args.is_a?(Hash)
  args.merge!(cmd_args)

  unless [:lambda, :adopt, :child].include?(args[:interface])
    args[:interface] = :child
  end

  self.extend self.class.const_get(args[:interface].capitalize)
  init_interface(args)

  Thread.new { output_loop }
end

Instance Attribute Details

#bufferObject (readonly)

Public: Returns the active buffer to match against



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

def buffer
  @buffer
end

#buffer_sizeObject

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



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

def buffer_size
  @buffer_size
end

#constrainObject

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



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

def constrain
  @constrain
end

#discardObject (readonly)

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



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

def discard
  @discard
end

#flush_bufferObject

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



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

def flush_buffer
  @flush_buffer
end

#pidObject (readonly)

Public: Returns the PID of the running process



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

def pid
  @pid
end

#timeoutObject

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



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

def timeout
  @timeout
end

Instance Method Details

#clear_buffer!Object

Public: Clear output buffer.

Returns nothing.



227
228
229
230
231
# File 'lib/expectr.rb', line 227

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, optionally taking further action based upon which, if any, match was found.

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



177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/expectr.rb', line 177

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.



207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/expectr.rb', line 207

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: Allow direct control of the running process from 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, calling #join on it if :blocking is true.



110
111
112
113
114
115
116
117
# File 'lib/expectr.rb', line 110

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 a boolean.

Returns:

  • (Boolean)


122
123
124
# File 'lib/expectr.rb', line 122

def interact?
  @interact
end

#leave!Object

Public: Cause the current Expectr object to leave interact mode.

Returns nothing.



129
130
131
# File 'lib/expectr.rb', line 129

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.



138
139
140
# File 'lib/expectr.rb', line 138

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