crazy_ivan 0.3.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,365 @@
1
+ URIS
2
+
3
+ http://rubyforge.org/projects/codeforpeople/
4
+ http://www.codeforpeople.com/lib/ruby/
5
+
6
+ SYNOPSIS
7
+
8
+ open child process with handles on pid, stdin, stdout, and stderr: manage
9
+ child processes and their io handles easily.
10
+
11
+ INSTALL
12
+
13
+ ~> gem install open4
14
+
15
+ SAMPLES
16
+
17
+ ----------------------------------------------------------------------------
18
+ simple usage
19
+ ----------------------------------------------------------------------------
20
+
21
+ harp: > cat sample/simple.rb
22
+ require "open4"
23
+
24
+ pid, stdin, stdout, stderr = Open4::popen4 "sh"
25
+
26
+ stdin.puts "echo 42.out"
27
+ stdin.puts "echo 42.err 1>&2"
28
+ stdin.close
29
+
30
+ ignored, status = Process::waitpid2 pid
31
+
32
+ puts "pid : #{ pid }"
33
+ puts "stdout : #{ stdout.read.strip }"
34
+ puts "stderr : #{ stderr.read.strip }"
35
+ puts "status : #{ status.inspect }"
36
+ puts "exitstatus : #{ status.exitstatus }"
37
+
38
+
39
+ harp: > ruby sample/simple.rb
40
+ pid : 17273
41
+ stdout : 42.out
42
+ stderr : 42.err
43
+ status : #<Process::Status: pid=17273,exited(0)>
44
+ exitstatus : 0
45
+
46
+
47
+ ----------------------------------------------------------------------------
48
+ in block form - the child process is automatically waited for
49
+ ----------------------------------------------------------------------------
50
+
51
+ harp: > cat sample/block.rb
52
+ require 'open4'
53
+
54
+ status =
55
+ Open4::popen4("sh") do |pid, stdin, stdout, stderr|
56
+ stdin.puts "echo 42.out"
57
+ stdin.puts "echo 42.err 1>&2"
58
+ stdin.close
59
+
60
+ puts "pid : #{ pid }"
61
+ puts "stdout : #{ stdout.read.strip }"
62
+ puts "stderr : #{ stderr.read.strip }"
63
+ end
64
+
65
+ puts "status : #{ status.inspect }"
66
+ puts "exitstatus : #{ status.exitstatus }"
67
+
68
+
69
+ harp: > ruby sample/block.rb
70
+ pid : 17295
71
+ stdout : 42.out
72
+ stderr : 42.err
73
+ status : #<Process::Status: pid=17295,exited(0)>
74
+ exitstatus : 0
75
+
76
+ ----------------------------------------------------------------------------
77
+ exceptions are marshaled from child to parent if fork/exec fails
78
+ ----------------------------------------------------------------------------
79
+
80
+ harp: > cat sample/exception.rb
81
+ require "open4"
82
+ Open4::popen4 "noexist"
83
+
84
+
85
+ harp: > ruby sample/exception.rb
86
+ /dmsp/reference/ruby-1.8.1//lib/ruby/site_ruby/open4.rb:100:in `popen4': No such file or directory - noexist (Errno::ENOENT)
87
+ from sample/exception.rb:3
88
+
89
+ ----------------------------------------------------------------------------
90
+ the spawn method provides and even more convenient method of running a
91
+ process, allowing any object that supports 'each', 'read', or 'to_s' to be
92
+ given as stdin and any objects that support '<<' to be given as
93
+ stdout/stderr. an exception is thrown if the exec'd cmd fails (nonzero
94
+ exitstatus) unless the option 'raise'=>false is given
95
+ ----------------------------------------------------------------------------
96
+
97
+ harp: > cat sample/spawn.rb
98
+ require 'open4'
99
+ include Open4
100
+
101
+ cat = ' ruby -e" ARGF.each{|line| STDOUT << line} " '
102
+
103
+ stdout, stderr = '', ''
104
+ status = spawn cat, 'stdin' => '42', 'stdout' => stdout, 'stderr' => stderr
105
+ p status
106
+ p stdout
107
+ p stderr
108
+
109
+ stdout, stderr = '', ''
110
+ status = spawn cat, 0=>'42', 1=>stdout, 2=>stderr
111
+ p status
112
+ p stdout
113
+ p stderr
114
+
115
+
116
+ harp: > RUBYLIB=lib ruby sample/spawn.rb
117
+ 0
118
+ "42"
119
+ ""
120
+ 0
121
+ "42"
122
+ ""
123
+
124
+
125
+ ----------------------------------------------------------------------------
126
+ the bg/background method is similar to spawn, but the process is
127
+ automatically set running in a thread. the returned thread has several
128
+ methods added dynamically which return the pid and blocking calls to the
129
+ exitstatus.
130
+ ----------------------------------------------------------------------------
131
+
132
+ harp: > cat sample/bg.rb
133
+ require 'yaml'
134
+ require 'open4'
135
+ include Open4
136
+
137
+ stdin = '42'
138
+ stdout = ''
139
+ stderr = ''
140
+
141
+ t = bg 'ruby -e"sleep 4; puts ARGF.read"', 0=>stdin, 1=>stdout, 2=>stderr
142
+
143
+ waiter = Thread.new{ y t.pid => t.exitstatus } # t.exitstatus is a blocking call!
144
+
145
+ while((status = t.status))
146
+ y "status" => status
147
+ sleep 1
148
+ end
149
+
150
+ waiter.join
151
+
152
+ y "stdout" => stdout
153
+
154
+
155
+ harp: > ruby sample/bg.rb
156
+ ---
157
+ status: run
158
+ ---
159
+ status: sleep
160
+ ---
161
+ status: sleep
162
+ ---
163
+ status: sleep
164
+ ---
165
+ 21357: 0
166
+ ---
167
+ stdout: "42\n"
168
+
169
+ ----------------------------------------------------------------------------
170
+ the timeout methods can be used to ensure execution is preceding at the
171
+ desired interval. note also how to setup a 'pipeline'
172
+ ----------------------------------------------------------------------------
173
+
174
+ harp: > cat sample/stdin_timeout.rb
175
+ require 'open4'
176
+
177
+ producer = 'ruby -e" STDOUT.sync = true; loop{sleep(rand+rand) and puts 42} "'
178
+
179
+ consumer = 'ruby -e" STDOUT.sync = true; STDIN.each{|line| puts line} "'
180
+
181
+ open4(producer) do |pid, i, o, e|
182
+
183
+ open4.spawn consumer, :stdin=>o, :stdout=>STDOUT, :stdin_timeout => 1.4
184
+
185
+ end
186
+
187
+
188
+ harp: > ruby sample/stdin_timeout.rb
189
+ 42
190
+ 42
191
+ 42
192
+ 42
193
+ 42
194
+ /dmsp/reference/ruby-1.8.1//lib/ruby/1.8/timeout.rb:42:in `relay': execution expired (Timeout::Error)
195
+
196
+ HISTORY
197
+ 1.0.0
198
+ - added ability for spawn to take a proc (respond_to?(:call))
199
+
200
+ cmd = ' ruby -e" 42.times{ puts 0b101010 } " '
201
+ include Open4
202
+ spawn cmd, :stdout => lambda{|buf| puts buf}
203
+
204
+
205
+ 0.9.5:
206
+ - another patch from Corey Jewett, this time dealing with ruby's handling
207
+ of chdir and threads. basically the 'cwd' keyword to open4 cannot work
208
+ with multiple threads (aka background) because ruby cannot cause green
209
+ threads to have an actuall different working dir. the moral is that the
210
+ :cwd/'cwd' keyword to spawn will work with 0 or 1 threads in effect.
211
+
212
+ 0.9.4:
213
+ - patch to #background from Corey Jewett
214
+
215
+
216
+ 0.9.3:
217
+ - removed some debugging output accidentally left in 0.9.2. arggh!
218
+
219
+ 0.9.2:
220
+ - fixed a descriptor leak. thanks Andre Nathan.
221
+
222
+ 0.9.1:
223
+ - fixed warning with '-w' : @cid not initialized. thanks blaise tarr.
224
+
225
+ 0.9.0:
226
+ - added the ability for open4.spawn to take either an array of arguments
227
+ or multiple arguments in order to specify the argv for the command run.
228
+ for example
229
+
230
+ open4.spawn ['touch', 'difficult to "quote"'], :stdout=>STDOUT
231
+
232
+ same thing
233
+
234
+ open4.spawn 'touch', 'difficult to "quote"', :stdout=>STDOUT
235
+
236
+ thanks to jordan breeding for this suggestion
237
+
238
+
239
+ - added 'cwd'/:cwd keyword. usage is pretty obivous
240
+
241
+ open4.spawn 'pwd', 1=>STDOUT, :cwd=>'/tmp' #=> /tmp
242
+
243
+ this one also from jordan
244
+
245
+ 0.8.0:
246
+
247
+ - fixed a critical bug whereby a process producing tons of stdout, but for
248
+ which the stdout was not handled, would cause the child process to
249
+ become blocked/hung writing to the pipe. eg, this command would cause a
250
+ hang
251
+
252
+ include Open4
253
+
254
+ spawn 'ruby -e" puts Array.new(65536){ 42 } "'
255
+
256
+ whereas this one would not
257
+
258
+ include Open4
259
+
260
+ spawn 'ruby -e" puts Array.new(65536){ 42 } "', :stdout=>StringIO.new
261
+
262
+ this version handles the former by spawning a 'null' thread which reads,
263
+ but does not process stdout/stderr. that way commands which generate
264
+ tons of output will never become blocked.
265
+
266
+ 0.7.0:
267
+ - merged functionality of exitstatus/status keywords:
268
+
269
+ include Open4
270
+
271
+ spawn 'ruby -e "exit 42"' # raises
272
+ spawn 'ruby -e "exit 42"', :status=>true # ok, returns status
273
+ spawn 'ruby -e "exit 42"', :status=>42 # raises if status != 42
274
+ spawn 'ruby -e "exit 42"', :status=>0,42 # raises if status != 0||42
275
+
276
+ - the 0.6.0 was broken on rubyforge... this release fixes that (somehow!?)
277
+
278
+ 0.6.0:
279
+ - added feature for exitstatus to be list of acceptable exit statuses
280
+
281
+ Open4.spawn 'ruby -e "exit 42"' # raises
282
+ Open4.spawn 'ruby -e "exit 42"', :exitstatus=>[0,42] # ok
283
+
284
+ - added :status switch, which will always simply return the status (no
285
+ error thrown for failure)
286
+
287
+ Open4.spawn 'ruby -e "exit 42"' # raises
288
+ status = Open4.spawn 'ruby -e "exit 42"', :status=>true # ok
289
+
290
+ note, however, that any SpawnError does in fact contain the failed
291
+ status so, even when they are thrown, error status can be retrieved:
292
+
293
+ include Open4
294
+
295
+ status =
296
+ begin
297
+ spawn 'ruby -e "exit 42"'
298
+ rescue SpawnError => e
299
+ warn{ e }
300
+ e.status
301
+ end
302
+
303
+ 0.5.1:
304
+ - fixes a __critical__ but in ThreadEnsemble class that had a race
305
+ condition that could cause thread deadlock. sorry bout that folks.
306
+
307
+ 0.5.0:
308
+ - on the suggestion of tim pease (thanks tim!), i added timeout features
309
+ to open4. the command run may have an overall timeout and individual
310
+ timeouts set for each of the io handles. for example
311
+
312
+ cmd = 'command_that_produce_out_at_one_second_intervals'
313
+
314
+ open4.spawn cmd, :stdout_timeout => 2
315
+
316
+ or
317
+
318
+ cmd = 'command_that_should_complete_in_about_one_minute'
319
+
320
+ open4.spawn cmd, :timeout => 60
321
+
322
+ or
323
+
324
+ cmd = 'consumes_input_at_one_line_per_second_rate'
325
+
326
+ input = %w( 42 forty-two 42.0 )
327
+
328
+ open4.spawn cmd, :stdin=>input, :stdin_timeout=>1
329
+
330
+ - added 'open4' alias so one can write
331
+
332
+ open4.spawn vs Open4.spawn
333
+
334
+ or even
335
+
336
+ open4(cmd) do |pid,i,o,e|
337
+ end
338
+
339
+ - added signal info to SpawnError
340
+
341
+ 0.4.0:
342
+ - improved error handling contributed by jordan breeding.
343
+ - introduction of background/bg method
344
+
345
+ 0.3.0 :
346
+ - bug fix from jordan breeding. general clean up. added spawn method.
347
+
348
+ 0.2.0 :
349
+ - added exception marshaled from child -> parent when exec fails. thanks
350
+ to jordan breeding for a patch (yay!) and paul brannan for this most
351
+ excellent idea.
352
+
353
+ 0.1.0 :
354
+ - fixed docs to correctly show return value of popen4 (pid first not last).
355
+ thanks Stefanie Tellex <stefie10@alum.mit.edu> for catching this.
356
+ 0.0.0 :
357
+ - initial version
358
+
359
+ AUTHOR
360
+
361
+ ara.t.howard@gmail.com
362
+
363
+ LICENSE
364
+
365
+ ruby's
@@ -0,0 +1,225 @@
1
+
2
+ This.rubyforge_project = 'codeforpeople'
3
+ This.author = "Ara T. Howard"
4
+ This.email = "ara.t.howard@gmail.com"
5
+ This.homepage = "http://github.com/ahoward/#{ This.lib }/tree/master"
6
+
7
+
8
+ task :default do
9
+ puts(Rake::Task.tasks.map{|task| task.name} - ['default'])
10
+ end
11
+
12
+
13
+ task :gemspec do
14
+ ignore_extensions = 'git', 'svn', 'tmp', /sw./, 'bak', 'gem'
15
+ ignore_directories = 'pkg'
16
+
17
+ shiteless =
18
+ lambda do |list|
19
+ list.delete_if do |entry|
20
+ next unless test(?e, entry)
21
+ extension = File.basename(entry).split(%r/[.]/).last
22
+ ignore_extensions.any?{|ext| ext === extension}
23
+ end
24
+ list.delete_if do |entry|
25
+ next unless test(?d, entry)
26
+ dirname = File.expand_path(entry)
27
+ ignore_directories.any?{|dir| File.expand_path(dir) == dirname}
28
+ end
29
+ end
30
+
31
+ lib = This.lib
32
+ version = This.version
33
+ files = shiteless[Dir::glob("**/**")]
34
+ executables = shiteless[Dir::glob("bin/*")].map{|exe| File.basename(exe)}
35
+ has_rdoc = true #File.exist?('doc')
36
+ test_files = "test/#{ lib }.rb" if File.file?("test/#{ lib }.rb")
37
+
38
+ extensions = This.extensions
39
+ if extensions.nil?
40
+ %w( Makefile configure extconf.rb ).each do |ext|
41
+ extensions << ext if File.exists?(ext)
42
+ end
43
+ end
44
+ extensions = [extensions].flatten.compact
45
+
46
+ template =
47
+ if test(?e, 'gemspec.erb')
48
+ Template{ IO.read('gemspec.erb') }
49
+ else
50
+ Template {
51
+ <<-__
52
+ ## #{ lib }.gemspec
53
+ #
54
+
55
+ Gem::Specification::new do |spec|
56
+ spec.name = #{ lib.inspect }
57
+ spec.version = #{ version.inspect }
58
+ spec.platform = Gem::Platform::RUBY
59
+ spec.summary = #{ lib.inspect }
60
+
61
+ spec.description = "manage child processes and their io handles easily"
62
+
63
+ spec.files = #{ files.inspect }
64
+ spec.executables = #{ executables.inspect }
65
+
66
+ spec.require_path = "lib"
67
+
68
+ spec.has_rdoc = #{ has_rdoc.inspect }
69
+ spec.test_files = #{ test_files.inspect }
70
+ #spec.add_dependency 'lib', '>= version'
71
+ #spec.add_dependency 'fattr'
72
+
73
+ spec.extensions.push(*#{ extensions.inspect })
74
+
75
+ spec.rubyforge_project = #{ This.rubyforge_project.inspect }
76
+ spec.author = #{ This.author.inspect }
77
+ spec.email = #{ This.email.inspect }
78
+ spec.homepage = #{ This.homepage.inspect }
79
+ end
80
+ __
81
+ }
82
+ end
83
+
84
+ open("#{ lib }.gemspec", "w"){|fd| fd.puts template}
85
+ This.gemspec = "#{ lib }.gemspec"
86
+ end
87
+
88
+ task :gem => [:clean, :gemspec] do
89
+ Fu.mkdir_p This.pkgdir
90
+ before = Dir['*.gem']
91
+ cmd = "gem build #{ This.gemspec }"
92
+ `#{ cmd }`
93
+ after = Dir['*.gem']
94
+ gem = ((after - before).first || after.first) or abort('no gem!')
95
+ Fu.mv gem, This.pkgdir
96
+ This.gem = File.basename(gem)
97
+ end
98
+
99
+
100
+ task :readme do
101
+ samples = ''
102
+ prompt = '~ > '
103
+ lib = This.lib
104
+ version = This.version
105
+
106
+ Dir['sample*/*'].sort.each do |sample|
107
+ samples << "\n" << " <========< #{ sample } >========>" << "\n\n"
108
+
109
+ cmd = "cat #{ sample }"
110
+ samples << Util.indent(prompt + cmd, 2) << "\n\n"
111
+ samples << Util.indent(`#{ cmd }`, 4) << "\n"
112
+
113
+ cmd = "ruby #{ sample }"
114
+ samples << Util.indent(prompt + cmd, 2) << "\n\n"
115
+
116
+ cmd = "ruby -e'STDOUT.sync=true; exec %(ruby -Ilib #{ sample })'"
117
+ samples << Util.indent(`#{ cmd } 2>&1`, 4) << "\n"
118
+ end
119
+
120
+ template =
121
+ if test(?e, 'readme.erb')
122
+ Template{ IO.read('readme.erb') }
123
+ else
124
+ Template {
125
+ <<-__
126
+ NAME
127
+ #{ lib }
128
+
129
+ DESCRIPTION
130
+
131
+ INSTALL
132
+ gem install #{ lib }
133
+
134
+ SAMPLES
135
+ #{ samples }
136
+ __
137
+ }
138
+ end
139
+
140
+ open("README", "w"){|fd| fd.puts template}
141
+ end
142
+
143
+
144
+ task :clean do
145
+ Dir[File.join(This.pkgdir, '**/**')].each{|entry| Fu.rm_rf(entry)}
146
+ end
147
+
148
+
149
+ task :release => [:clean, :gemspec, :gem] do
150
+ gems = Dir[File.join(This.pkgdir, '*.gem')].flatten
151
+ raise "which one? : #{ gems.inspect }" if gems.size > 1
152
+ raise "no gems?" if gems.size < 1
153
+ cmd = "rubyforge login && rubyforge add_release #{ This.rubyforge_project } #{ This.lib } #{ This.version } #{ This.pkgdir }/#{ This.gem }"
154
+ puts cmd
155
+ system cmd
156
+ end
157
+
158
+
159
+
160
+
161
+
162
+ BEGIN {
163
+ $VERBOSE = nil
164
+
165
+ require 'ostruct'
166
+ require 'erb'
167
+ require 'fileutils'
168
+
169
+ Fu = FileUtils
170
+
171
+ This = OpenStruct.new
172
+
173
+ This.file = File.expand_path(__FILE__)
174
+ This.dir = File.dirname(This.file)
175
+ This.pkgdir = File.join(This.dir, 'pkg')
176
+
177
+ lib = ENV['LIB']
178
+ unless lib
179
+ lib = File.basename(Dir.pwd)
180
+ end
181
+ This.lib = lib
182
+
183
+ version = ENV['VERSION']
184
+ unless version
185
+ name = lib.capitalize
186
+ require "./lib/#{ lib }"
187
+ version = eval(name).send(:version)
188
+ end
189
+ This.version = version
190
+
191
+ abort('no lib') unless This.lib
192
+ abort('no version') unless This.version
193
+
194
+ module Util
195
+ def indent(s, n = 2)
196
+ s = unindent(s)
197
+ ws = ' ' * n
198
+ s.gsub(%r/^/, ws)
199
+ end
200
+
201
+ def unindent(s)
202
+ indent = nil
203
+ s.each do |line|
204
+ next if line =~ %r/^\s*$/
205
+ indent = line[%r/^\s*/] and break
206
+ end
207
+ indent ? s.gsub(%r/^#{ indent }/, "") : s
208
+ end
209
+ extend self
210
+ end
211
+
212
+ class Template
213
+ def initialize(&block)
214
+ @block = block
215
+ @template = block.call.to_s
216
+ end
217
+ def expand(b=nil)
218
+ ERB.new(Util.unindent(@template)).result(b||@block)
219
+ end
220
+ alias_method 'to_s', 'expand'
221
+ end
222
+ def Template(*args, &block) Template.new(*args, &block) end
223
+
224
+ Dir.chdir(This.dir)
225
+ }