expectr 2.0.0 → 2.0.1

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 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