tty-process-ctl 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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 tty to communicate with the process via simple API.
4
+ It is using pseudo TTY to communicate with the process via simple API.
5
5
 
6
- == Example
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 'ls' command and iterate its output:
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
- === irb example
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 wit each_until.
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.1
1
+ 0.2.0
data/examples/chain.rb ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative '../lib/tty-process-ctl'
3
+
4
+ TTYProcessCtl.new('echo "abc\ndef\nghi"').wait_until(/def/).each do |line|
5
+ puts line
6
+ end
7
+
@@ -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
- while !@out_queue.empty? or alive? do
51
- yield (dequeue or break)
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 wait_exit
72
- each{}
73
- @thread.join
80
+ def wait_until(pattern, options = {})
81
+ each_until(pattern, options){}
74
82
  end
75
83
 
76
- def wait_until(pattern)
77
- each_until(pattern){}
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
 
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "tty-process-ctl"
8
- s.version = "0.1.1"
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-01"
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.1.1
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-01 00:00:00.000000000 Z
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: -464255591999730974
150
+ hash: 1001191325266459894
150
151
  required_rubygems_version: !ruby/object:Gem::Requirement
151
152
  none: false
152
153
  requirements: