tty-process-ctl 0.1.1 → 0.2.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.
- data/README.rdoc +35 -6
- data/VERSION +1 -1
- data/examples/chain.rb +7 -0
- data/lib/tty-process-ctl.rb +35 -15
- data/spec/tty-process-ctl_spec.rb +112 -0
- data/tty-process-ctl.gemspec +3 -2
- metadata +4 -3
data/README.rdoc
CHANGED
@@ -1,13 +1,23 @@
|
|
1
1
|
= tty-process-ctl
|
2
2
|
|
3
3
|
This gem was created to enable control of interactive terminal applications.
|
4
|
-
It is using pseudo
|
4
|
+
It is using pseudo TTY to communicate with the process via simple API.
|
5
5
|
|
6
|
-
|
6
|
+
For more advanced gem see also: https://github.com/avdi/greenletters
|
7
|
+
|
8
|
+
== Usage
|
9
|
+
|
10
|
+
You can install the gem with:
|
11
|
+
|
12
|
+
gem install tty-process-ctl
|
13
|
+
|
14
|
+
In your code require the gem with:
|
15
|
+
|
16
|
+
require 'tty-process-ctl'
|
7
17
|
|
8
18
|
=== Reading program output
|
9
19
|
|
10
|
-
Here we run
|
20
|
+
Here we run *ls* command and iterate its output:
|
11
21
|
|
12
22
|
TTYProcessCtl.new('ls').each do |line|
|
13
23
|
puts line
|
@@ -18,10 +28,10 @@ Result:
|
|
18
28
|
Gemfile LICENSE.txt Rakefile lib
|
19
29
|
Gemfile.lock README.rdoc examples spec
|
20
30
|
|
21
|
-
===
|
31
|
+
=== Sending commands
|
22
32
|
|
23
|
-
This example show how to send command to irb process.
|
24
|
-
Output can be skipped with wait_until and iterated until pattern matches
|
33
|
+
This example show how to send command to *irb* process.
|
34
|
+
Output can be skipped with *wait_until* and iterated until pattern matches with *each_until*:
|
25
35
|
|
26
36
|
irb = TTYProcessCtl.new('irb')
|
27
37
|
|
@@ -46,6 +56,25 @@ Result:
|
|
46
56
|
|
47
57
|
=> 4
|
48
58
|
|
59
|
+
=== Timeout support
|
60
|
+
|
61
|
+
Normally *each* and *wait* methods will wait infinitely for event to happen.
|
62
|
+
They accept options hash as additional argument where *:timeout* key value will be respected as number of seconds after which the method will rise *TTYProcessCtl*::*Timeout* exception if awaited event did not happen:
|
63
|
+
|
64
|
+
TTYProcessCtl.new('sleep 10').wait_until(/Done/, timeout: 1) => TTYProcessCtl::Timeout
|
65
|
+
|
66
|
+
=== Chaining
|
67
|
+
|
68
|
+
All *each*, *wait* and *flush* methods can be chained:
|
69
|
+
|
70
|
+
TTYProcessCtl.new('echo "abc\ndef\nghi"').wait_until(/def/).each do |line|
|
71
|
+
puts line
|
72
|
+
end
|
73
|
+
|
74
|
+
Result:
|
75
|
+
|
76
|
+
ghi
|
77
|
+
|
49
78
|
== Contributing to tty-process-ctl
|
50
79
|
|
51
80
|
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/examples/chain.rb
ADDED
data/lib/tty-process-ctl.rb
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
require 'thread'
|
2
|
+
require 'timeout'
|
2
3
|
require 'pty'
|
3
4
|
|
4
5
|
class TTYProcessCtl
|
6
|
+
class Timeout < Timeout::Error
|
7
|
+
end
|
8
|
+
|
5
9
|
include Enumerable
|
6
10
|
|
7
11
|
def initialize(command, options = {})
|
@@ -45,47 +49,63 @@ class TTYProcessCtl
|
|
45
49
|
@messages
|
46
50
|
end
|
47
51
|
|
48
|
-
def each
|
49
|
-
return enum_for(:each) unless block_given?
|
50
|
-
|
51
|
-
|
52
|
+
def each(options = {})
|
53
|
+
return enum_for(:each, options) unless block_given?
|
54
|
+
timeout(options[:timeout]) do
|
55
|
+
while !@out_queue.empty? or alive? do
|
56
|
+
yield (dequeue or break)
|
57
|
+
end
|
52
58
|
end
|
59
|
+
self
|
53
60
|
end
|
54
61
|
|
55
|
-
def each_until(pattern)
|
56
|
-
return enum_for(:each_until, pattern) unless block_given?
|
57
|
-
each do |message|
|
62
|
+
def each_until(pattern, options = {})
|
63
|
+
return enum_for(:each_until, pattern, options) unless block_given?
|
64
|
+
each(options) do |message|
|
58
65
|
yield message
|
59
66
|
break if message =~ pattern
|
60
67
|
end
|
68
|
+
self
|
61
69
|
end
|
62
70
|
|
63
|
-
def each_until_exclude(pattern)
|
64
|
-
return enum_for(:each_until_exclude, pattern) unless block_given?
|
65
|
-
each do |message|
|
71
|
+
def each_until_exclude(pattern, options = {})
|
72
|
+
return enum_for(:each_until_exclude, pattern, options) unless block_given?
|
73
|
+
each(options) do |message|
|
66
74
|
break if message =~ pattern
|
67
75
|
yield message
|
68
76
|
end
|
77
|
+
self
|
69
78
|
end
|
70
79
|
|
71
|
-
def
|
72
|
-
|
73
|
-
@thread.join
|
80
|
+
def wait_until(pattern, options = {})
|
81
|
+
each_until(pattern, options){}
|
74
82
|
end
|
75
83
|
|
76
|
-
def
|
77
|
-
|
84
|
+
def wait_exit(options = {})
|
85
|
+
each(options){}
|
86
|
+
@thread.join
|
87
|
+
self
|
78
88
|
end
|
79
89
|
|
80
90
|
def flush
|
81
91
|
loop do
|
82
92
|
dequeue(true)
|
83
93
|
end
|
94
|
+
self
|
84
95
|
rescue ThreadError
|
96
|
+
self
|
85
97
|
end
|
86
98
|
|
87
99
|
private
|
88
100
|
|
101
|
+
def timeout(t)
|
102
|
+
yield unless t
|
103
|
+
|
104
|
+
::Timeout::timeout(t, Timeout) do
|
105
|
+
yield
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
89
109
|
def dequeue(block = false)
|
90
110
|
message = @out_queue.pop(block)
|
91
111
|
return nil unless message
|
@@ -155,5 +155,117 @@ describe TTYProcessCtl do
|
|
155
155
|
subject.messages.length.should == 2
|
156
156
|
end
|
157
157
|
end
|
158
|
+
|
159
|
+
describe 'timeout' do
|
160
|
+
after :each do
|
161
|
+
subject.send_command 'stop' if subject.alive?
|
162
|
+
subject.wait_exit
|
163
|
+
end
|
164
|
+
|
165
|
+
describe 'each calls with block' do
|
166
|
+
it 'should raise TTYProcessCtl::Timeout on timieout' do
|
167
|
+
expect {
|
168
|
+
subject.each(timeout: 0.1) {}
|
169
|
+
}.to raise_error TTYProcessCtl::Timeout
|
170
|
+
|
171
|
+
expect {
|
172
|
+
subject.each_until(/bogous/, timeout: 0.1) {}
|
173
|
+
}.to raise_error TTYProcessCtl::Timeout
|
174
|
+
|
175
|
+
expect {
|
176
|
+
subject.each_until_exclude(/bogous/, timeout: 0.1) {}
|
177
|
+
}.to raise_error TTYProcessCtl::Timeout
|
178
|
+
end
|
179
|
+
|
180
|
+
it 'should not raise error if they return before timeout' do
|
181
|
+
expect {
|
182
|
+
subject.each_until(/recipes/, timeout: 1) {}
|
183
|
+
}.to_not raise_error TTYProcessCtl::Timeout
|
184
|
+
|
185
|
+
expect {
|
186
|
+
subject.each_until_exclude(/achievements/, timeout: 1) {}
|
187
|
+
}.to_not raise_error TTYProcessCtl::Timeout
|
188
|
+
|
189
|
+
expect {
|
190
|
+
subject.each(timeout: 1) { break }
|
191
|
+
}.to_not raise_error TTYProcessCtl::Timeout
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
describe 'each calls with use of Enumerator object' do
|
196
|
+
it 'should raise TTYProcessCtl::Timeout on timieout' do
|
197
|
+
expect {
|
198
|
+
subject.each(timeout: 0.1).to_a
|
199
|
+
}.to raise_error TTYProcessCtl::Timeout
|
200
|
+
|
201
|
+
expect {
|
202
|
+
subject.each_until(/bogous/, timeout: 0.1).to_a
|
203
|
+
}.to raise_error TTYProcessCtl::Timeout
|
204
|
+
|
205
|
+
expect {
|
206
|
+
subject.each_until_exclude(/bogous/, timeout: 0.1).to_a
|
207
|
+
}.to raise_error TTYProcessCtl::Timeout
|
208
|
+
end
|
209
|
+
|
210
|
+
it 'should not raise error if they return before timeout' do
|
211
|
+
expect {
|
212
|
+
subject.each_until(/recipes/, timeout: 1).to_a
|
213
|
+
}.to_not raise_error TTYProcessCtl::Timeout
|
214
|
+
|
215
|
+
expect {
|
216
|
+
subject.each_until_exclude(/achievements/, timeout: 1).to_a
|
217
|
+
}.to_not raise_error TTYProcessCtl::Timeout
|
218
|
+
|
219
|
+
expect {
|
220
|
+
subject.each(timeout: 1).first
|
221
|
+
}.to_not raise_error TTYProcessCtl::Timeout
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
describe 'wait calls' do
|
226
|
+
it 'should raise TTYProcessCtl::Timeout on timieout' do
|
227
|
+
expect {
|
228
|
+
subject.wait_until(/bogous/, timeout: 0.1)
|
229
|
+
}.to raise_error TTYProcessCtl::Timeout
|
230
|
+
|
231
|
+
expect {
|
232
|
+
subject.wait_exit(timeout: 0.1)
|
233
|
+
}.to raise_error TTYProcessCtl::Timeout
|
234
|
+
end
|
235
|
+
|
236
|
+
it 'should not raise error if they return before timeout' do
|
237
|
+
expect {
|
238
|
+
subject.wait_until(/Done/, timeout: 1)
|
239
|
+
}.to_not raise_error TTYProcessCtl::Timeout
|
240
|
+
|
241
|
+
subject.send_command 'stop'
|
242
|
+
|
243
|
+
expect {
|
244
|
+
subject.wait_exit(timeout: 1)
|
245
|
+
}.to_not raise_error TTYProcessCtl::Timeout
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
describe 'chaining' do
|
251
|
+
subject do
|
252
|
+
TTYProcessCtl.new('spec/stub --exit')
|
253
|
+
end
|
254
|
+
|
255
|
+
it 'should work with each methods' do
|
256
|
+
subject.each_until(/recipes/){}.should == subject
|
257
|
+
subject.each_until_exclude(/achievements/){}.should == subject
|
258
|
+
subject.each{}.should == subject
|
259
|
+
end
|
260
|
+
|
261
|
+
it 'should work with wait methods' do
|
262
|
+
subject.wait_until(/Done/){}.should == subject
|
263
|
+
subject.wait_exit{}.should == subject
|
264
|
+
end
|
265
|
+
|
266
|
+
it 'should work with flush method' do
|
267
|
+
subject.flush.should == subject
|
268
|
+
end
|
269
|
+
end
|
158
270
|
end
|
159
271
|
|
data/tty-process-ctl.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "tty-process-ctl"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.2.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Jakub Pastuszek"]
|
12
|
-
s.date = "2012-09-
|
12
|
+
s.date = "2012-09-03"
|
13
13
|
s.description = "This gem was created to enable control of interactive terminal applications. It is using pseudo tty to communicate with the process via simple API."
|
14
14
|
s.email = "jpastuszek@gmail.com"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -25,6 +25,7 @@ Gem::Specification.new do |s|
|
|
25
25
|
"README.rdoc",
|
26
26
|
"Rakefile",
|
27
27
|
"VERSION",
|
28
|
+
"examples/chain.rb",
|
28
29
|
"examples/irb.rb",
|
29
30
|
"examples/ls.rb",
|
30
31
|
"lib/tty-process-ctl.rb",
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tty-process-ctl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-09-
|
12
|
+
date: 2012-09-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -124,6 +124,7 @@ files:
|
|
124
124
|
- README.rdoc
|
125
125
|
- Rakefile
|
126
126
|
- VERSION
|
127
|
+
- examples/chain.rb
|
127
128
|
- examples/irb.rb
|
128
129
|
- examples/ls.rb
|
129
130
|
- lib/tty-process-ctl.rb
|
@@ -146,7 +147,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
146
147
|
version: '0'
|
147
148
|
segments:
|
148
149
|
- 0
|
149
|
-
hash:
|
150
|
+
hash: 1001191325266459894
|
150
151
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
151
152
|
none: false
|
152
153
|
requirements:
|