open4 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -9,6 +9,10 @@ SYNOPSIS
9
9
 
10
10
  HISTORY
11
11
 
12
+ 0.4.0:
13
+ - improved error handling contributed by jordan breeding.
14
+ - introduction of background/bg method
15
+
12
16
  0.3.0 :
13
17
  - bug fix from jordan breeding. general clean up. added spawn method.
14
18
 
@@ -136,6 +140,52 @@ SAMPLES
136
140
  "42"
137
141
  ""
138
142
 
143
+
144
+ ----------------------------------------------------------------------------
145
+ the bg/background method is similar to spawn, but the process is
146
+ automatically set running in a thread. the returned thread has several
147
+ methods added dynamically which return the pid and blocking calls to the
148
+ exitstatus.
149
+ ----------------------------------------------------------------------------
150
+
151
+ harp: > cat sample/bg.rb
152
+ require 'yaml'
153
+ require 'open4'
154
+ include Open4
155
+
156
+ stdin = '42'
157
+ stdout = ''
158
+ stderr = ''
159
+
160
+ t = bg 'ruby -e"sleep 4; puts ARGF.read"', 0=>stdin, 1=>stdout, 2=>stderr
161
+
162
+ waiter = Thread.new{ y t.pid => t.exitstatus } # t.exitstatus is a blocking call!
163
+
164
+ while((status = t.status))
165
+ y "status" => status
166
+ sleep 1
167
+ end
168
+
169
+ waiter.join
170
+
171
+ y "stdout" => stdout
172
+
173
+
174
+ harp: > ruby sample/bg.rb
175
+ ---
176
+ status: run
177
+ ---
178
+ status: sleep
179
+ ---
180
+ status: sleep
181
+ ---
182
+ status: sleep
183
+ ---
184
+ 21357: 0
185
+ ---
186
+ stdout: "42\n"
187
+
188
+
139
189
  AUTHOR
140
190
 
141
191
  ara.t.howard@noaa.gov
data/gemspec.rb CHANGED
@@ -8,11 +8,11 @@ Gem::Specification::new do |spec|
8
8
  spec.platform = Gem::Platform::RUBY
9
9
  spec.summary = lib
10
10
 
11
- # spec.files = Dir[ File::join("{lib,bin}", "*") ]
12
- # spec.require_path = "lib"
13
-
14
11
  spec.files = Dir::glob "**/**"
15
12
  spec.executables = Dir::glob("bin/*").map{|exe| File::basename exe}
13
+
14
+ spec.require_path = "lib"
15
+ spec.autorequire = lib
16
16
 
17
17
  spec.has_rdoc = File::exist? "doc"
18
18
  spec.test_suite_file = "test/#{ lib }.rb" if File::directory? "test"
@@ -2,7 +2,9 @@ require 'fcntl'
2
2
 
3
3
  module Open4
4
4
  #--{{{
5
- def self.version() '0.3.0' end
5
+ def self.version
6
+ '0.4.0'
7
+ end
6
8
 
7
9
  def popen4(*cmd)
8
10
  #--{{{
@@ -46,11 +48,7 @@ module Open4
46
48
 
47
49
  begin
48
50
  e = Marshal.load ps.first
49
- if Exception === e
50
- raise e
51
- else
52
- raise "unknown failure"
53
- end
51
+ raise(Exception === e ? e : "unknown failure!")
54
52
  rescue EOFError # If we get an EOF error, then the exec was successful
55
53
  42
56
54
  end
@@ -75,6 +73,20 @@ module Open4
75
73
  module_function :popen4
76
74
  module_function :open4
77
75
 
76
+ class Error < ::StandardError; end
77
+ class SpawnError < Error
78
+ #--{{{
79
+ attr 'cmd'
80
+ attr 'status'
81
+ def exitstatus
82
+ @status.exitstatus
83
+ end
84
+ def initialize cmd, status
85
+ @cmd, @status = cmd, status
86
+ super "cmd <#{ cmd }> failed with <#{ exitstatus }>"
87
+ end
88
+ #--}}}
89
+ end
78
90
  def spawn cmd, opts = {}
79
91
  #--{{{
80
92
  getopt = lambda do |*args|
@@ -89,11 +101,13 @@ module Open4
89
101
  end
90
102
  end
91
103
 
92
- dont_raise = getopt[ 'quiet', !getopt['raise', true] ]
104
+ ignore_exit_failure = getopt[ 'ignore_exit_failure', getopt['quiet', false] ]
105
+ ignore_exec_failure = getopt[ 'ignore_exec_failure', !getopt['raise', true] ]
93
106
  exitstatus = getopt[ %w( exitstatus exit_status status ), 0 ]
94
107
  stdin = getopt[ ['stdin', 'in', '0', 0] ]
95
108
  stdout = getopt[ ['stdout', 'out', '1', 1] ]
96
109
  stderr = getopt[ ['stderr', 'err', '2', 2] ]
110
+ pid = getopt[ 'pid' ]
97
111
 
98
112
  started = false
99
113
 
@@ -102,32 +116,60 @@ module Open4
102
116
  popen4(cmd) do |c, i, o, e|
103
117
  started = true
104
118
 
105
- if stdin.respond_to? :each
106
- stdin.each{|buf| i << buf}
107
- elsif stdin.respond_to? :read
108
- i << stdin.read
109
- else
110
- i << stdin.to_s
119
+ if pid.respond_to? '<<'
120
+ pid << c
111
121
  end
112
122
 
113
- i.close
123
+ it = Thread.new(i,stdin) do |i,stdin|
124
+ if stdin
125
+ if stdin.respond_to? :each
126
+ stdin.each{|buf| i << buf}
127
+ elsif stdin.respond_to? :read
128
+ i << stdin.read
129
+ else
130
+ i << stdin.to_s
131
+ end
132
+ end
133
+ i.close
134
+ end
114
135
 
115
- ot = Thread.new(o){ o.each{|buf| stdout << buf if stdout} }
116
- et = Thread.new(e){ e.each{|buf| stderr << buf if stderr} }
136
+ ot = Thread.new(o,stdout){|o,stdout| o.each{|buf| stdout << buf if stdout}}
137
+ et = Thread.new(e,stderr){|e,stderr| e.each{|buf| stderr << buf if stderr}}
117
138
 
118
- ot.join
119
- et.join
139
+ it.join
140
+ ot.join if ot
141
+ et.join if et
120
142
  end
121
143
  rescue
122
- raise unless(not started and dont_raise)
144
+ raise unless(not started and ignore_exec_failure)
123
145
  end
124
146
 
125
- raise "cmd <#{ cmd }> failed with <#{ status }>" unless
126
- ((status == exitstatus) or dont_raise)
147
+ raise SpawnError.new(cmd, status) unless
148
+ (ignore_exit_failure or (status.nil? and ignore_exec_failure) or (status.exitstatus == exitstatus))
127
149
 
128
150
  status
129
151
  #--}}}
130
152
  end
131
153
  module_function :spawn
154
+
155
+ def background cmd, opts = {}
156
+ #--{{{
157
+ require 'thread'
158
+ q = Queue.new
159
+ opts['pid'] = opts[:pid] = q
160
+ thread = Thread.new(cmd, opts){|cmd, opts| spawn cmd, opts}
161
+ pid = q.pop
162
+ sc = class << thread; self; end
163
+ sc.module_eval {
164
+ define_method(:pid){ pid }
165
+ define_method(:spawn_status){ @spawn_status ||= value }
166
+ define_method(:exitstatus){ spawn_status.exitstatus }
167
+ }
168
+ thread
169
+ #--}}}
170
+ end
171
+ alias bg background
172
+ module_function :background
173
+ module_function :bg
132
174
  #--}}}
133
175
  end
@@ -2,7 +2,9 @@ require 'fcntl'
2
2
 
3
3
  module Open4
4
4
  #--{{{
5
- def self.version() '0.3.0' end
5
+ def self.version
6
+ '0.4.0'
7
+ end
6
8
 
7
9
  def popen4(*cmd)
8
10
  #--{{{
@@ -46,11 +48,7 @@ module Open4
46
48
 
47
49
  begin
48
50
  e = Marshal.load ps.first
49
- if Exception === e
50
- raise e
51
- else
52
- raise "unknown failure"
53
- end
51
+ raise(Exception === e ? e : "unknown failure!")
54
52
  rescue EOFError # If we get an EOF error, then the exec was successful
55
53
  42
56
54
  end
@@ -75,6 +73,20 @@ module Open4
75
73
  module_function :popen4
76
74
  module_function :open4
77
75
 
76
+ class Error < ::StandardError; end
77
+ class SpawnError < Error
78
+ #--{{{
79
+ attr 'cmd'
80
+ attr 'status'
81
+ def exitstatus
82
+ @status.exitstatus
83
+ end
84
+ def initialize cmd, status
85
+ @cmd, @status = cmd, status
86
+ super "cmd <#{ cmd }> failed with <#{ exitstatus }>"
87
+ end
88
+ #--}}}
89
+ end
78
90
  def spawn cmd, opts = {}
79
91
  #--{{{
80
92
  getopt = lambda do |*args|
@@ -89,11 +101,13 @@ module Open4
89
101
  end
90
102
  end
91
103
 
92
- dont_raise = getopt[ 'quiet', !getopt['raise', true] ]
104
+ ignore_exit_failure = getopt[ 'ignore_exit_failure', getopt['quiet', false] ]
105
+ ignore_exec_failure = getopt[ 'ignore_exec_failure', !getopt['raise', true] ]
93
106
  exitstatus = getopt[ %w( exitstatus exit_status status ), 0 ]
94
107
  stdin = getopt[ ['stdin', 'in', '0', 0] ]
95
108
  stdout = getopt[ ['stdout', 'out', '1', 1] ]
96
109
  stderr = getopt[ ['stderr', 'err', '2', 2] ]
110
+ pid = getopt[ 'pid' ]
97
111
 
98
112
  started = false
99
113
 
@@ -102,32 +116,60 @@ module Open4
102
116
  popen4(cmd) do |c, i, o, e|
103
117
  started = true
104
118
 
105
- if stdin.respond_to? :each
106
- stdin.each{|buf| i << buf}
107
- elsif stdin.respond_to? :read
108
- i << stdin.read
109
- else
110
- i << stdin.to_s
119
+ if pid.respond_to? '<<'
120
+ pid << c
111
121
  end
112
122
 
113
- i.close
123
+ it = Thread.new(i,stdin) do |i,stdin|
124
+ if stdin
125
+ if stdin.respond_to? :each
126
+ stdin.each{|buf| i << buf}
127
+ elsif stdin.respond_to? :read
128
+ i << stdin.read
129
+ else
130
+ i << stdin.to_s
131
+ end
132
+ end
133
+ i.close
134
+ end
114
135
 
115
- ot = Thread.new(o){ o.each{|buf| stdout << buf if stdout} }
116
- et = Thread.new(e){ e.each{|buf| stderr << buf if stderr} }
136
+ ot = Thread.new(o,stdout){|o,stdout| o.each{|buf| stdout << buf if stdout}}
137
+ et = Thread.new(e,stderr){|e,stderr| e.each{|buf| stderr << buf if stderr}}
117
138
 
118
- ot.join
119
- et.join
139
+ it.join
140
+ ot.join if ot
141
+ et.join if et
120
142
  end
121
143
  rescue
122
- raise unless(not started and dont_raise)
144
+ raise unless(not started and ignore_exec_failure)
123
145
  end
124
146
 
125
- raise "cmd <#{ cmd }> failed with <#{ status }>" unless
126
- ((status == exitstatus) or dont_raise)
147
+ raise SpawnError.new(cmd, status) unless
148
+ (ignore_exit_failure or (status.nil? and ignore_exec_failure) or (status.exitstatus == exitstatus))
127
149
 
128
150
  status
129
151
  #--}}}
130
152
  end
131
153
  module_function :spawn
154
+
155
+ def background cmd, opts = {}
156
+ #--{{{
157
+ require 'thread'
158
+ q = Queue.new
159
+ opts['pid'] = opts[:pid] = q
160
+ thread = Thread.new(cmd, opts){|cmd, opts| spawn cmd, opts}
161
+ pid = q.pop
162
+ sc = class << thread; self; end
163
+ sc.module_eval {
164
+ define_method(:pid){ pid }
165
+ define_method(:spawn_status){ @spawn_status ||= value }
166
+ define_method(:exitstatus){ spawn_status.exitstatus }
167
+ }
168
+ thread
169
+ #--}}}
170
+ end
171
+ alias bg background
172
+ module_function :background
173
+ module_function :bg
132
174
  #--}}}
133
175
  end
File without changes
@@ -0,0 +1,21 @@
1
+ require 'yaml'
2
+ require 'open4'
3
+ include Open4
4
+
5
+ stdin = '42'
6
+ stdout = ''
7
+ stderr = ''
8
+
9
+ t = bg 'ruby -e"sleep 4; puts ARGF.read"', 0=>stdin, 1=>stdout, 2=>stderr
10
+
11
+ waiter = Thread.new{ y t.pid => t.exitstatus } # t.exitstatus is a blocking call!
12
+
13
+ while((status = t.status))
14
+ y "status" => status
15
+ sleep 1
16
+ end
17
+
18
+ waiter.join
19
+
20
+ y "stdout" => stdout
21
+
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.11
3
3
  specification_version: 1
4
4
  name: open4
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.3.0
7
- date: 2006-04-19 00:00:00.000000 -06:00
6
+ version: 0.4.0
7
+ date: 2006-04-26 00:00:00.000000 -06:00
8
8
  summary: open4
9
9
  require_paths:
10
10
  - lib
@@ -12,7 +12,7 @@ email: ara.t.howard@noaa.gov
12
12
  homepage: http://codeforpeople.com/lib/ruby/open4/
13
13
  rubyforge_project:
14
14
  description:
15
- autorequire:
15
+ autorequire: open4
16
16
  default_executable:
17
17
  bindir: bin
18
18
  has_rdoc: false
@@ -29,7 +29,6 @@ cert_chain:
29
29
  authors:
30
30
  - Ara T. Howard
31
31
  files:
32
- - open4-0.3.0.gem
33
32
  - install.rb
34
33
  - sample
35
34
  - lib
@@ -37,11 +36,13 @@ files:
37
36
  - build
38
37
  - install
39
38
  - gemspec.rb
39
+ - open4-0.4.0.gem
40
40
  - sample/block.rb
41
41
  - sample/simple.rb
42
42
  - sample/exception.rb
43
43
  - sample/spawn.rb
44
- - lib/open4-0.3.0.rb
44
+ - sample/bg.rb
45
+ - lib/open4-0.4.0.rb
45
46
  - lib/open4.rb
46
47
  test_files: []
47
48
  rdoc_options: []