expectr 2.0.0 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: eb60ae1f64e5710fe3e5cfdff6e49bf938a9aaa3
4
- data.tar.gz: 98fd24b1cadf73f5c88a313dd48cd1edb0e6f64d
3
+ metadata.gz: fdb63c98692074edfb85dfd5652de4bb858b98bc
4
+ data.tar.gz: c0cf8fcfbc6d926bf779884265db4fb1a484839f
5
5
  SHA512:
6
- metadata.gz: 1748339aa16a902c05c7b19a17e6469a94eaf3a2e21d3143ac92a2291d932cd52a3ecad280607efaa409ec7fb1cd017e5945c09d5a9bafda4a2906800a86a64c
7
- data.tar.gz: 7d21a1bc05f4067f823d0f0292f1242dcee4bb4952d7df7ca520f8287e5aa4280de8e5b281f4f0b60c9321ae53bf45a76171abc35c7b48ea139067a2e00bc0b2
6
+ metadata.gz: fb3dac4b059cc9d1453637b020ffa0fa0c094ca04ff5a62acba1dc95d23de265ac4781daf3ce59bd174cc0110a839d4826127a01298e06a4f2650ca5f1047b55
7
+ data.tar.gz: 47d9d136860390bcb8deb5296b6f3b2eac9406db8ef561770ae6a610a5600eaf07f8ec3acd0c260710278b72585c877106e61d63012d626f58f16684909b4ce6
data/lib/expectr/adopt.rb CHANGED
@@ -3,33 +3,43 @@ require 'expectr/interface'
3
3
  require 'expectr/child'
4
4
 
5
5
  class Expectr
6
- # Internal: The Expectr::Adopt Class contains the interface to interacting
7
- # with child processes not spawned by Expectr.
8
- #
9
- # All methods with the prefix 'interface_' in their name will return a Proc
10
- # designed to be defined as an instance method in the primary Expectr object.
11
- # These methods will all be documented as if they are the Proc in question.
12
- class Adopt < Expectr::Child
13
- # Public: Initialize a new Expectr::Adopt object.
6
+ # Public: The Expectr::Adopt Module defines the interface for interacting
7
+ # with child processes without spawning them through Expectr.
8
+ module Adopt
9
+ include Expectr::Child
10
+
11
+ # Public: Initialize the Expectr interface, adopting IO objects to act on
12
+ # them as if they were produced by spawning a child process.
14
13
  # IO Objects are named in such a way as to maintain interoperability with
15
- # the methods from the Expectr::Child class.
14
+ # the methods from the Expectr::Child module.
16
15
  #
17
- # stdin - IO object open for writing.
18
- # stdout - IO object open for reading.
19
- # pid - FixNum corresponding to the PID of the process being adopted
20
- # (default: 1)
16
+ # args - Hash containing IO Objects and optionally a PID to watch.
17
+ # stdin - IO object open for writing.
18
+ # stdout - IO object open for reading.
19
+ # pid - FixNum corresponding to the PID of the process being
20
+ # adopted. (default: 1)
21
21
  #
22
- # Raises TypeError if arguments are of type other than IO.
23
- def initialize(stdin, stdout)
24
- unless stdin.kind_of?(IO) && stdout.kind_of?(IO)
22
+ # Returns nothing.
23
+ # Raises TypeError if args[:stdin] or args[:stdout] aren't of type IO.
24
+ def init_interface(args)
25
+ unless args[:stdin].kind_of?(IO) && args[:stdout].kind_of?(IO)
25
26
  raise(TypeError, Errstr::IO_EXPECTED)
26
27
  end
27
- @stdin = stdin
28
- @stdout = stdout
28
+ @stdin = args[:stdin]
29
+ @stdout = args[:stdout]
29
30
  @stdout.winsize = $stdout.winsize if $stdout.tty?
31
+ @pid = args[:pid] || 0
32
+
33
+ if @pid > 0
34
+ Thread.new do
35
+ Process.wait @pid
36
+ @pid = 0
37
+ end
38
+ end
30
39
  end
31
40
 
32
- # Public: Present a streamlined interface to create a new Expectr instance.
41
+ # Public: Present a streamlined interface to create a new Expectr instance
42
+ # using the Adopt interface.
33
43
  #
34
44
  # stdout - IO object open for reading.
35
45
  # stdin - IO object open for writing.
@@ -44,7 +54,7 @@ class Expectr
44
54
  args[:stdin] = stdin
45
55
  args[:stdout] = stdout
46
56
  args[:pid] = pid
47
- Expectr.new('', args)
57
+ Expectr.new(args)
48
58
  end
49
59
  end
50
60
  end
data/lib/expectr/child.rb CHANGED
@@ -1,28 +1,26 @@
1
1
  require 'pty'
2
+ require 'expectr'
2
3
  require 'expectr/interface'
3
4
 
4
5
  class Expectr
5
- # Internal: The Expectr::Child class contains the interface to interacting
6
+ # Public: The Expectr::Child Module defines the interface for interacting
6
7
  # with child processes.
7
- #
8
- # All methods with the prefix 'interface_' in their name will return a Proc
9
- # designed to be defined as an instance method in the primary Expectr object.
10
- # These methods will all be documented as if they are the Proc in question.
11
- class Child
8
+ module Child
12
9
  include Expectr::Interface
13
- attr_reader :stdin
14
- attr_reader :stdout
15
- attr_reader :pid
16
10
 
17
- # Public: Initialize a new Expectr::Child object.
18
- # Spawns a sub-process and attaches to STDIN and STDOUT for the new
19
- # process.
11
+ # Public: Initialize the Expectr interface, spawning a sub-process
12
+ # attaching to STDIN and STDOUT of the new process.
20
13
  #
21
- # cmd - A String or File referencing the application to launch.
14
+ # args - A Hash containing arguments to Expectr. Only :cmd is presently
15
+ # used for this function.
16
+ # :cmd - A String or File referencing the application to launch.
22
17
  #
23
- # Raises TypeError if argument is anything other than String or File.
24
- def initialize(cmd)
18
+ # Returns nothing.
19
+ # Raises TypeError if args[:cmd] is anything other than String or File.
20
+ def init_interface(args)
21
+ cmd = args[:cmd]
25
22
  cmd = cmd.path if cmd.kind_of?(File)
23
+
26
24
  unless cmd.kind_of?(String)
27
25
  raise(TypeError, Errstr::STRING_FILE_EXPECTED)
28
26
  end
@@ -33,19 +31,17 @@ class Expectr
33
31
 
34
32
  # Public: Send a signal to the running child process.
35
33
  #
36
- # signal - Symbol, String or FixNum corresponding to the symbol to be sent
37
- # to the running process. (default: :TERM)
34
+ # signal - Symbol, String or FixNum indicating the symbol to be sent to the
35
+ # running process. (default: :TERM)
38
36
  #
39
37
  # Returns a boolean indicating whether the process was successfully sent
40
38
  # the signal.
41
39
  # Raises ProcessError if the process is not running (@pid = 0).
42
- def interface_kill!
43
- ->(signal = :TERM) {
44
- unless @pid > 0
45
- raise(ProcessError, Errstr::PROCESS_NOT_RUNNING)
46
- end
47
- Process::kill(signal.to_sym, @pid) == 1
48
- }
40
+ def kill!(signal = :TERM)
41
+ unless @pid > 0
42
+ raise(ProcessError, Errstr::PROCESS_NOT_RUNNING)
43
+ end
44
+ Process::kill(signal.to_sym, @pid) == 1
49
45
  end
50
46
 
51
47
  # Public: Send input to the active child process.
@@ -53,113 +49,98 @@ class Expectr
53
49
  # str - String to be sent.
54
50
  #
55
51
  # Returns nothing.
56
- # Raises Expectr::ProcessError if the process is not running (@pid = 0)
57
- def interface_send
58
- ->(str) {
59
- begin
60
- @stdin.syswrite str
61
- rescue Errno::EIO #Application is not running
62
- @pid = 0
63
- end
64
- unless @pid > 0
65
- raise(Expectr::ProcessError, Errstr::PROCESS_GONE)
66
- end
67
- }
52
+ # Raises Expectr::ProcessError if the process is not running (@pid = 0).
53
+ def send(str)
54
+ begin
55
+ @stdin.syswrite str
56
+ rescue Errno::EIO #Application is not running
57
+ @pid = 0
58
+ end
59
+ unless @pid > 0
60
+ raise(Expectr::ProcessError, Errstr::PROCESS_GONE)
61
+ end
68
62
  end
69
63
 
70
- # Public: Read the child process's output, force UTF-8 encoding, then
64
+ # Public: Read the output of the child process, force UTF-8 encoding, then
71
65
  # append to the internal buffer and print to $stdout if appropriate.
72
66
  #
73
67
  # Returns nothing.
74
- def interface_output_loop
75
- -> {
76
- while @pid > 0
77
- unless select([@stdout], nil, nil, @timeout).nil?
78
- buf = ''
79
-
80
- begin
81
- @stdout.sysread(@buffer_size, buf)
82
- rescue Errno::EIO #Application is not running
83
- @pid = 0
84
- return
85
- end
86
- process_output(buf)
68
+ def output_loop
69
+ while @pid > 0
70
+ unless select([@stdout], nil, nil, @timeout).nil?
71
+ buf = ''
72
+
73
+ begin
74
+ @stdout.sysread(@buffer_size, buf)
75
+ rescue Errno::EIO #Application is not running
76
+ @pid = 0
77
+ return
87
78
  end
79
+ process_output(buf)
88
80
  end
89
- }
81
+ end
90
82
  end
91
83
 
92
- def interface_prepare_interact_environment
93
- -> {
94
- env = {sig: {}}
84
+ # Public: Return the PTY's window size.
85
+ #
86
+ # Returns a two-element Array (same as IO#winsize)
87
+ def winsize
88
+ @stdout.winsize
89
+ end
95
90
 
96
- # Save old tty settings and set up the new environment
97
- env[:tty] = `stty -g`
98
- `stty -icanon min 1 time 0 -echo`
91
+ private
99
92
 
100
- # SIGINT should be sent to the child as \C-c
101
- env[:sig]['INT'] = trap 'INT' do
102
- send "\C-c"
103
- end
93
+ # Internal: Set up the execution environment to prepare for entering
94
+ # interact mode.
95
+ # Presently assumes a Linux system.
96
+ #
97
+ # Returns nothing.
98
+ def prepare_interact_environment
99
+ env = {sig: {}}
104
100
 
105
- # SIGTSTP should be sent to the process as \C-z
106
- env[:sig]['TSTP'] = trap 'TSTP' do
107
- send "\C-z"
108
- end
101
+ # Save old tty settings and set up the new environment
102
+ env[:tty] = `stty -g`
103
+ `stty -icanon min 1 time 0 -echo`
109
104
 
110
- # SIGWINCH should trigger an update to the child processes window size
111
- env[:sig]['WINCH'] = trap 'WINCH' do
112
- @stdout.winsize = $stdout.winsize
113
- end
105
+ # SIGINT should be sent to the child as \C-c
106
+ env[:sig]['INT'] = trap 'INT' do
107
+ send "\C-c"
108
+ end
109
+
110
+ # SIGTSTP should be sent to the process as \C-z
111
+ env[:sig]['TSTP'] = trap 'TSTP' do
112
+ send "\C-z"
113
+ end
114
114
 
115
- env
116
- }
115
+ # SIGWINCH should trigger an update to the child processes window size
116
+ env[:sig]['WINCH'] = trap 'WINCH' do
117
+ @stdout.winsize = $stdout.winsize
118
+ end
119
+
120
+ env
117
121
  end
118
122
 
119
- # Public: Create a Thread containing the loop which is responsible for
120
- # handling input from the user in interact mode.
123
+ # Internal: Create a Thread containing the loop which is responsible for
124
+ # handling input from the user while in interact mode.
121
125
  #
122
126
  # Returns a Thread containing the running loop.
123
- def interface_interact_thread
124
- -> {
125
- @interact = true
126
- env = prepare_interact_environment
127
- Thread.new do
128
- begin
129
- input = ''
127
+ def interact_thread
128
+ @interact = true
129
+ env = prepare_interact_environment
130
+ Thread.new do
131
+ begin
132
+ input = ''
130
133
 
131
- while @pid > 0 && @interact
132
- if select([$stdin], nil, nil, 1)
133
- c = $stdin.getc.chr
134
- send c unless c.nil?
135
- end
134
+ while @pid > 0 && @interact
135
+ if select([$stdin], nil, nil, 1)
136
+ c = $stdin.getc.chr
137
+ send c unless c.nil?
136
138
  end
137
- ensure
138
- restore_environment(env)
139
139
  end
140
+ ensure
141
+ restore_environment(env)
140
142
  end
141
- }
142
- end
143
-
144
- # Public: Return the PTY's window size.
145
- #
146
- # Returns a two-element Array (same as IO#winsize)
147
- def interface_winsize
148
- -> {
149
- @stdout.winsize
150
- }
151
- end
152
-
153
- # Public: Present a streamlined interface to create a new Expectr instance.
154
- #
155
- # cmd - A String or File referencing the application to launch.
156
- # args - A Hash used to specify options for the new object, per
157
- # Expectr#initialize.
158
- #
159
- # Returns a new Expectr object
160
- def self.spawn(cmd, args = {})
161
- args[:interface] = :child
162
- Expectr.new(cmd, args)
143
+ end
163
144
  end
164
145
  end
165
146
  end
@@ -4,7 +4,7 @@ class Expectr
4
4
  ALREADY_INTERACT = "Already in interact mode"
5
5
  end
6
6
 
7
- class Child
7
+ module Child
8
8
  module Errstr
9
9
  STRING_FILE_EXPECTED = "Command should be of type String or File"
10
10
  PROCESS_NOT_RUNNING = "No process is running"
@@ -12,7 +12,7 @@ class Expectr
12
12
  end
13
13
  end
14
14
 
15
- class Adopt < Child
15
+ module Adopt
16
16
  module Errstr
17
17
  IO_EXPECTED = "Arguments of type IO expected"
18
18
  end
@@ -25,7 +25,7 @@ class Expectr
25
25
  end
26
26
  end
27
27
 
28
- class Lambda
28
+ module Lambda
29
29
  module Errstr
30
30
  PROC_EXPECTED = "Proc Objects expected for reader and writer"
31
31
  end
@@ -1,53 +1,35 @@
1
1
  class Expectr
2
2
  module Interface
3
- # Public: Enumerate and expose all interface functions for a given
4
- # Interface.
5
- #
6
- # Returns a two-dimensional Array containing a name for the new method and
7
- # a reference to the extant method.
8
- def init_instance
9
- methods = []
10
- public_methods.select { |m| m =~ /^interface_/ }.each do |name|
11
- method = public_method(name)
12
- name = name.to_s.gsub(/^interface_/, '').to_sym
13
- methods << [name, method]
14
- end
15
- methods
16
- end
17
-
18
3
  # Public: Return a Thread which does nothing, representing an interface
19
4
  # with no functional interact environment available.
20
5
  #
21
6
  # Returns a Thread.
22
- def interface_interact_thread
23
- -> {
24
- Thread.new { }
25
- }
7
+ def interact_thread
8
+ Thread.new { }
26
9
  end
27
10
 
28
11
  # Public: Return an empty Hash representing a case where no action needed
29
12
  # to be taken in order to prepare the environment for interact mode.
30
13
  #
31
14
  # Returns an empty Hash.
32
- def interface_prepare_interact_interface
33
- -> {
34
- {}
35
- }
15
+ def prepare_interact_interface
16
+ {}
36
17
  end
37
18
 
38
- # Internal: Restore environment after interact mode has been left.
19
+ private
20
+
21
+ # Internal: Restore environment (TTY parameters, signal handlers) after
22
+ # leaving interact mode.
39
23
  #
40
24
  # Returns nothing.
41
- def interface_restore_environment
42
- ->(env) {
43
- env[:sig].each do |signal, handler|
44
- trap signal, handler
45
- end
46
- unless env[:tty].nil?
47
- `stty #{env[:tty]}`
48
- end
49
- @interact = false
50
- }
25
+ def restore_environment(env)
26
+ env[:sig].each do |signal, handler|
27
+ trap signal, handler
28
+ end
29
+ unless env[:tty].nil?
30
+ `stty #{env[:tty]}`
31
+ end
32
+ @interact = false
51
33
  end
52
34
  end
53
35
  end
@@ -9,7 +9,7 @@ class Expectr
9
9
  # Public: Filename of currently executing script.
10
10
  attr_accessor :filename
11
11
 
12
- # Public: Initialize a new Expectr interface.
12
+ # Public: Initialize a new Expectr Interpreter interface.
13
13
  #
14
14
  # source - String containing the source to be executed.
15
15
  def initialize(source)
@@ -18,8 +18,7 @@ class Expectr
18
18
  @expect = nil
19
19
  end
20
20
 
21
- # Public: Run the source associated with the Interface object, bound to the
22
- # Object.
21
+ # Public: Run the source associated with the Interface object.
23
22
  #
24
23
  # Returns nothing.
25
24
  def run
@@ -45,7 +44,7 @@ class Expectr
45
44
  # true, and 0 or less is false.
46
45
  #
47
46
  # Returns nothing.
48
- # Raises Argument
47
+ # Raises TypeError if enable is not a boolean.
49
48
  def log_user(enable)
50
49
  if enable.is_a?(Numeric)
51
50
  enable = (enable > 0)
@@ -2,33 +2,29 @@ require 'expectr'
2
2
  require 'expectr/interface'
3
3
 
4
4
  class Expectr
5
- # Internal: The Expectr::Lambda Class contains the interface to interacting
6
- # with ruby objects transparently via lambdas in a manner consistent with
7
- # other Expectr interfaces.
8
- #
9
- # All methods with the prefix 'interface_' in their name will return a Proc
10
- # designed to be defined as an instance method in the primary Expectr object.
11
- # These methods will all be documented as if they are the Proc in question.
12
- class Lambda
5
+ # Public: The Expectr::Lambda Module defines the interface for interacting
6
+ # with Proc objects in a manner which is similar to interacting with
7
+ # processes.
8
+ module Lambda
13
9
  include Expectr::Interface
14
- attr_reader :reader
15
- attr_reader :writer
16
10
 
17
- # Public: Initialize a new Expectr::Lambda object.
11
+ # Public: Initialize the Expectr Lambda interface.
18
12
  #
19
- # reader - Lambda which is meant to be interacted with as if it were
20
- # analogous to STDIN for a child process.
21
- # writer - Lambda which is meant to be interacted with as if it were
22
- # analogous to STDOUT for a child process.
13
+ # args - Hash containing Proc objects to act as reader and writer:
14
+ # reader - Lambda which is meant to be interacted with as if it were
15
+ # analogous to STDIN for a child process.
16
+ # writer - Lambda which is meant to be interacted with as if it were
17
+ # analogous to STDOUT for a child process.
23
18
  #
24
19
  # Raises TypeError if arguments aren't of type Proc.
25
- def initialize(reader, writer)
26
- unless reader.kind_of?(Proc) && writer.kind_of?(Proc)
20
+ def init_interface(args)
21
+ unless args[:reader].kind_of?(Proc) && args[:writer].kind_of?(Proc)
27
22
  raise(TypeError, Errstr::PROC_EXPECTED)
28
23
  end
29
24
 
30
- @reader = reader
31
- @writer = writer
25
+ @pid = -1
26
+ @reader = args[:reader]
27
+ @writer = args[:writer]
32
28
  end
33
29
 
34
30
  # Public: Present a streamlined interface to create a new Expectr instance.
@@ -45,87 +41,82 @@ class Expectr
45
41
  args[:interface] = :lambda
46
42
  args[:reader] = reader
47
43
  args[:writer] = writer
48
- Expectr.new('', args)
44
+ Expectr.new(args)
49
45
  end
50
46
 
51
- # Public: Send input to the active child process.
47
+ # Public: Send input to the reader Proc.
52
48
  #
53
49
  # args - Arguments to pass to the reader interface.
54
50
  #
55
51
  # Returns nothing.
56
- def interface_send
57
- ->(args) {
58
- @reader.call(*args)
59
- }
52
+ def send(args)
53
+ @reader.call(*args)
60
54
  end
61
55
 
62
56
  # Public: Prepare the operating environment for interact mode, set the
63
57
  # interact flag to true.
64
58
  #
65
59
  # Returns a Hash containing old signal handlers and tty parameters.
66
- def interface_prepare_interact_environment
67
- -> {
68
- env = {sig: {}}
60
+ def prepare_interact_environment
61
+ env = {sig: {}}
69
62
 
70
- # Save old tty settings and set up the new environment
71
- env[:tty] = `stty -g`
72
- `stty -icanon min 1 time 0 -echo`
63
+ # Save old tty settings and set up the new environment
64
+ env[:tty] = `stty -g`
65
+ `stty -icanon min 1 time 0 -echo`
73
66
 
74
- # SIGINT should be sent to the child as \C-c
75
- env[:sig]['INT'] = trap 'INT' do
76
- send "\C-c"
77
- end
67
+ # SIGINT should be sent to the child as \C-c
68
+ env[:sig]['INT'] = trap 'INT' do
69
+ send "\C-c"
70
+ end
78
71
 
79
- # SIGTSTP should be sent to the process as \C-z
80
- env[:sig]['TSTP'] = trap 'TSTP' do
81
- send "\C-z"
82
- end
72
+ # SIGTSTP should be sent to the process as \C-z
73
+ env[:sig]['TSTP'] = trap 'TSTP' do
74
+ send "\C-z"
75
+ end
83
76
 
84
- @interact = true
85
- env
86
- }
77
+ @interact = true
78
+ env
87
79
  end
88
80
 
89
81
  # Public: Create a Thread containing the loop which is responsible for
90
82
  # handling input from the user in interact mode.
91
83
  #
92
84
  # Returns a Thread containing the running loop.
93
- def interface_interact_thread
94
- -> {
95
- Thread.new do
96
- env = prepare_interact_environment
97
- input = ''
98
-
99
- while @interact
100
- if select([$stdin], nil, nil, 1)
101
- c = $stdin.getc.chr
102
- send c unless c.nil?
103
- end
104
- end
85
+ def interact_thread
86
+ Thread.new do
87
+ env = prepare_interact_environment
88
+ input = ''
105
89
 
106
- restore_environment(env)
90
+ while @interact
91
+ if select([$stdin], nil, nil, 1)
92
+ c = $stdin.getc.chr
93
+ send c unless c.nil?
94
+ end
107
95
  end
108
- }
96
+
97
+ restore_environment(env)
98
+ end
109
99
  end
110
100
 
111
- # Internal: Call the writer lambda, reading the output, forcing UTF-8 and
112
- # appending to the internal buffer and printing to $stdout if appropriate.
101
+ private
102
+
103
+ # Internal: Call the writer lambda, reading the output produced, forcing
104
+ # UTF-8, appending to the internal buffer and printing to $stdout if
105
+ # appropriate.
113
106
  #
114
107
  # Returns nothing.
115
- def interface_output_loop
116
- -> {
117
- buf = ''
118
- loop do
119
- buf.clear
120
-
121
- begin
122
- buf << @writer.call.to_s
123
- rescue Errno::EIO # Lambda is signaling that execution should end.
124
- return
125
- end
126
- process_output(buf)
108
+ def output_loop
109
+ buf = ''
110
+ loop do
111
+ buf.clear
112
+
113
+ begin
114
+ buf << @writer.call.to_s
115
+ rescue Errno::EIO # Lambda is signaling that execution should end.
116
+ return
127
117
  end
128
- }
118
+ process_output(buf)
119
+ end
129
120
  end
130
121
  end
131
122
  end
@@ -1,3 +1,3 @@
1
1
  class Expectr
2
- VERSION = '2.0.0'
2
+ VERSION = '2.0.1'
3
3
  end
data/lib/expectr.rb CHANGED
@@ -13,10 +13,9 @@ require 'expectr/lambda'
13
13
  # Public: Expectr is an API to the functionality of Expect (see
14
14
  # http://expect.nist.gov) implemented in ruby.
15
15
  #
16
- # Expectr contrasts with Ruby's built-in Expect class by avoiding tying in
17
- # with the IO class, instead creating a new object entirely to allow for more
18
- # grainular control over the execution and display of the program being
19
- # run.
16
+ # Expectr contrasts with Ruby's built-in expect.rb by avoiding tying in with
17
+ # the IO class in favor of creating a new object entirely to allow for more
18
+ # granular control over the execution and display of the program being run.
20
19
  #
21
20
  # Examples
22
21
  #
@@ -57,51 +56,57 @@ class Expectr
57
56
  # Public: Initialize a new Expectr object.
58
57
  # Spawns a sub-process and attaches to STDIN and STDOUT for the new process.
59
58
  #
60
- # cmd - A String or File referencing the application to launch (default: '')
61
- # args - A Hash used to specify options for the new object (default: {}):
62
- # :timeout - Number of seconds that a call to Expectr#expect has
63
- # to complete (default: 30)
64
- # :flush_buffer - Whether to flush output of the process to the
65
- # console (default: true)
66
- # :buffer_size - Number of bytes to attempt to read from sub-process
67
- # at a time. If :constrain is true, this will be the
68
- # maximum size of the internal buffer as well.
69
- # (default: 8192)
70
- # :constrain - Whether to constrain the internal buffer from the
71
- # sub-process to :buffer_size (default: false)
72
- # :interface - Interface Object to use when instantiating the new
73
- # Expectr object. (default: Child)
74
- def initialize(cmd = '', args = {})
59
+ # cmd_args - This may be either a Hash containing arguments (described below)
60
+ # or a String or File Object referencing the application to launch
61
+ # (assuming Child interface). This argument, if not a Hash, will
62
+ # be changed into the Hash { cmd: cmd_args }. This argument will
63
+ # be merged with the args Hash, overriding any arguments
64
+ # specified there.
65
+ # This argument is kept around for the sake of backward
66
+ # compatibility with extant Expectr scripts and may be deprecated
67
+ # in the future. (default: {})
68
+ # args - A Hash used to specify options for the instance. (default: {}):
69
+ # :timeout - Number of seconds that a call to Expectr#expect
70
+ # has to complete (default: 30)
71
+ # :flush_buffer - Whether to flush output of the process to the
72
+ # console (default: true)
73
+ # :buffer_size - Number of bytes to attempt to read from
74
+ # sub-process at a time. If :constrain is true,
75
+ # this will be the maximum size of the internal
76
+ # buffer as well. (default: 8192)
77
+ # :constrain - Whether to constrain the internal buffer from
78
+ # the sub-process to :buffer_size characters.
79
+ # (default: false)
80
+ # :interface - Interface Object to use when instantiating the
81
+ # new Expectr object. (default: Child)
82
+ def initialize(cmd_args = '', args = {})
75
83
  setup_instance
76
84
  parse_options(args)
77
85
 
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)
85
- end
86
+ cmd_args = { cmd: cmd_args } unless cmd_args.is_a?(Hash)
87
+ args.merge!(cmd_args)
86
88
 
87
- interface.init_instance.each do |spec|
88
- ->(name, func) { define_singleton_method(name, func.call) }.call(*spec)
89
+ unless [:lambda, :adopt, :child].include?(args[:interface])
90
+ args[:interface] = :child
89
91
  end
90
92
 
93
+ self.extend self.class.const_get(args[:interface].capitalize)
94
+ init_interface(args)
95
+
91
96
  Thread.new { output_loop }
92
97
  end
93
98
 
94
- # Public: Relinquish control of the running process to the controlling
99
+ # Public: Allow direct control of the running process from the controlling
95
100
  # terminal, acting as a pass-through for the life of the process (or until
96
101
  # the leave! method is called).
97
102
  #
98
- # args - A Hash used to specify options to be used for interaction (default:
99
- # {}):
103
+ # args - A Hash used to specify options to be used for interaction.
104
+ # (default: {}):
100
105
  # :flush_buffer - explicitly set @flush_buffer to the value specified
101
106
  # :blocking - Whether to block on this call or allow code
102
107
  # execution to continue (default: false)
103
108
  #
104
- # Returns the interaction Thread
109
+ # Returns the interaction Thread, calling #join on it if :blocking is true.
105
110
  def interact!(args = {})
106
111
  if @interact
107
112
  raise(ProcessError, Errstr::ALREADY_INTERACT)
@@ -111,23 +116,23 @@ class Expectr
111
116
  args[:blocking] ? interact_thread.join : interact_thread
112
117
  end
113
118
 
114
- # Public: Report whether or not current Expectr object is in interact mode
119
+ # Public: Report whether or not current Expectr object is in interact mode.
115
120
  #
116
- # Returns true or false
121
+ # Returns a boolean.
117
122
  def interact?
118
123
  @interact
119
124
  end
120
125
 
121
- # Public: Cause the current Expectr object to drop out of interact mode
126
+ # Public: Cause the current Expectr object to leave interact mode.
122
127
  #
123
128
  # Returns nothing.
124
129
  def leave!
125
130
  @interact=false
126
131
  end
127
132
 
128
- # Public: Wraps Expectr#send, appending a newline to the end of the string
133
+ # Public: Wraps Expectr#send, appending a newline to the end of the string.
129
134
  #
130
- # str - String to be sent to the active process (default: '')
135
+ # str - String to be sent to the active process. (default: '')
131
136
  #
132
137
  # Returns nothing.
133
138
  def puts(str = '')
@@ -135,7 +140,8 @@ class Expectr
135
140
  end
136
141
 
137
142
  # Public: Begin a countdown and search for a given String or Regexp in the
138
- # output buffer.
143
+ # output buffer, optionally taking further action based upon which, if any,
144
+ # match was found.
139
145
  #
140
146
  # pattern - Object String or Regexp representing pattern for which to
141
147
  # search, or a Hash containing pattern -> Proc mappings to be
@@ -163,7 +169,7 @@ class Expectr
163
169
  # "Second possibility" => -> { puts "option b" },
164
170
  # default: => -> { puts "called on timeout" } }
165
171
  # exp.expect(hash)
166
- #
172
+ #
167
173
  # Returns a MatchData object once a match is found if no block is given
168
174
  # Yields the MatchData object representing the match
169
175
  # Raises TypeError if something other than a String or Regexp is given
@@ -196,7 +202,7 @@ class Expectr
196
202
  # /option 2/ => -> { puts "action 2" },
197
203
  # :default => -> { puts "default" }
198
204
  # })
199
- #
205
+ #
200
206
  # Calls the procedure associated with the pattern provided.
201
207
  def expect_procmap(pattern_map)
202
208
  pattern_map, pattern, recoverable = process_procmap(pattern_map)
@@ -215,7 +221,7 @@ class Expectr
215
221
  nil
216
222
  end
217
223
 
218
- # Public: Clear output buffer
224
+ # Public: Clear output buffer.
219
225
  #
220
226
  # Returns nothing.
221
227
  def clear_buffer!
@@ -226,9 +232,10 @@ class Expectr
226
232
 
227
233
  private
228
234
 
229
- # Internal: Print buffer to $stdout if @flush_buffer is true
235
+ # Internal: Print buffer to $stdout if program output is expected to be
236
+ # echoed.
230
237
  #
231
- # buf - String to be printed to $stdout
238
+ # buf - String to be printed to $stdout.
232
239
  #
233
240
  # Returns nothing.
234
241
  def print_buffer(buf)
@@ -236,19 +243,18 @@ class Expectr
236
243
  $stdout.flush unless $stdout.sync
237
244
  end
238
245
 
239
- # Internal: Encode a String twice to force UTF-8 encoding, dropping
240
- # problematic characters in the process.
241
- #
242
- # buf - String to be encoded.
243
- #
244
- # Returns the encoded String.
245
- def force_utf8(buf)
246
+ # Internal: Encode a String twice to force UTF-8 encoding, dropping
247
+ # problematic characters in the process.
248
+ #
249
+ # buf - String to be encoded.
250
+ #
251
+ # Returns the encoded String.
252
+ def force_utf8(buf)
246
253
  return buf if buf.valid_encoding?
247
- buf.force_encoding('ISO-8859-1').encode('UTF-8', 'UTF-8', replace: nil)
248
- end
254
+ buf.force_encoding('ISO-8859-1').encode('UTF-8', 'UTF-8', replace: nil)
255
+ end
249
256
 
250
- # Internal: Determine values of instance options and set instance variables
251
- # appropriately, allowing for default values where nothing is passed.
257
+ # Internal: Initialize instance variables based upon arguments provided.
252
258
  #
253
259
  # args - A Hash used to specify options for the new object (default: {}):
254
260
  # :timeout - Number of seconds that a call to Expectr#expect has
@@ -283,7 +289,7 @@ class Expectr
283
289
 
284
290
  # Internal: Handle data from the interface, forcing UTF-8 encoding, appending
285
291
  # it to the internal buffer, and printing it to $stdout if appropriate.
286
- #
292
+ #
287
293
  # Returns nothing.
288
294
  def process_output(buf)
289
295
  force_utf8(buf)
@@ -302,8 +308,7 @@ class Expectr
302
308
  # This method should be wrapped in a Timeout block or otherwise have some
303
309
  # mechanism to break out of the loop.
304
310
  #
305
- # pattern - String or Regexp object containing the pattern for which to
306
- # watch.
311
+ # pattern - String or Regexp containing the pattern for which to watch.
307
312
  #
308
313
  # Returns a MatchData object containing the match found.
309
314
  def check_match(pattern)
@@ -320,62 +325,8 @@ class Expectr
320
325
  @thread = nil
321
326
  end
322
327
 
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
333
-
334
- Thread.new do
335
- Process.wait @pid
336
- @pid = 0
337
- end
338
-
339
- interface
340
- end
341
-
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
372
- end
373
-
374
- interface
375
- end
376
-
377
328
  # Internal: Watch for a match within the timeout period.
378
- #
329
+ #
379
330
  # pattern - String or Regexp object containing the pattern for which to
380
331
  # watch.
381
332
  # recoverable - Boolean denoting whether a failure to find a match should be
@@ -404,23 +355,31 @@ class Expectr
404
355
  # Internal: Process a pattern to procedure mapping, producing a sanitized
405
356
  # Hash, a unified Regexp and a boolean denoting whether an Exception should
406
357
  # be raised upon timeout.
407
- #
358
+ #
408
359
  # pattern_map - A Hash containing mappings between patterns designated by
409
360
  # either strings or Regexp objects, to procedures. Optionally,
410
361
  # either :default or :timeout may be mapped to a procedure in
411
- # order to designate an action to take upon timeout.
362
+ # order to designate an action to take upon failure to match
363
+ # any other pattern.
412
364
  #
413
365
  # Returns a Hash, Regexp and boolean object.
414
366
  def process_procmap(pattern_map)
367
+ # Normalize Hash keys, allowing only Regexps and Symbols for keys.
415
368
  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])
369
+ unless e[0].is_a?(Symbol) || e[0].is_a?(Regexp)
370
+ e[0] = Regexp.new(Regexp.escape(e[0].to_s))
371
+ end
372
+ c.merge(e[0] => e[1])
417
373
  end
418
- pattern = pattern_map.keys.reduce("") do |c,e|
419
- e.is_a?(Regexp) ? c + "(#{e.source})|" : c
374
+
375
+ # Separate out non-Symbol keys and build a unified Regexp.
376
+ regex_keys = pattern_map.keys.select { |e| e.is_a?(Regexp) }
377
+ pattern = regex_keys.reduce("") do |c,e|
378
+ c += "|" unless c.empty?
379
+ c + "(#{e.source})"
420
380
  end
421
- pattern = Regexp.new(pattern.gsub(/\|$/, ''))
422
- recoverable = pattern_map.keys.include?(:default)
423
- recoverable ||= pattern_map.keys.include?(:timeout)
381
+
382
+ recoverable = regex_keys.include?(:default) || regex_keys.include?(:timeout)
424
383
 
425
384
  return pattern_map, pattern, recoverable
426
385
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: expectr
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Wuest
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-06-25 00:00:00.000000000 Z
11
+ date: 2013-07-29 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Expectr is an interface to the functionality of Expect in Ruby
14
14
  email: chris@chriswuest.com
@@ -47,7 +47,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
47
47
  version: '0'
48
48
  requirements: []
49
49
  rubyforge_project:
50
- rubygems_version: 2.0.3
50
+ rubygems_version: 2.0.5
51
51
  signing_key:
52
52
  specification_version: 4
53
53
  summary: Expect for Ruby