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