subexec 0.1.0 → 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 → README.md} +13 -10
- data/lib/subexec.rb +44 -29
- metadata +46 -35
- data/VERSION +0 -1
data/{README.rdoc → README.md}
RENAMED
@@ -1,8 +1,8 @@
|
|
1
|
-
|
1
|
+
# Subexec
|
2
2
|
by Peter Kieltyka
|
3
3
|
http://github/nulayer/subexec
|
4
4
|
|
5
|
-
|
5
|
+
## Description
|
6
6
|
|
7
7
|
Subexec is a simple library that spawns an external command with
|
8
8
|
an optional timeout parameter. It relies on Ruby 1.9's Process.spawn
|
@@ -12,18 +12,21 @@ Useful for libraries that are Ruby wrappers for CLI's. For example,
|
|
12
12
|
resizing images with ImageMagick's mogrify command sometimes stalls
|
13
13
|
and never returns control back to the original process. Enter Subexec.
|
14
14
|
|
15
|
-
|
15
|
+
Tested with MRI 1.9.3, 1.9.2, 1.8.7
|
16
16
|
|
17
|
-
|
18
|
-
puts sub.output # returns: hello
|
19
|
-
puts sub.exitstatus # returns: 0
|
17
|
+
## Usage
|
20
18
|
|
21
|
-
|
22
|
-
|
23
|
-
|
19
|
+
```ruby
|
20
|
+
sub = Subexec.run "echo 'hello' && sleep 3", :timeout => 5
|
21
|
+
puts sub.output # returns: hello
|
22
|
+
puts sub.exitstatus # returns: 0
|
24
23
|
|
24
|
+
sub = Subexec.run "echo 'hello' && sleep 3", :timeout => 1
|
25
|
+
puts sub.output # returns:
|
26
|
+
puts sub.exitstatus # returns:`
|
27
|
+
```
|
25
28
|
|
26
|
-
|
29
|
+
## Limitations
|
27
30
|
|
28
31
|
Only Ruby 1.9 can spawn non-blocking subprocesses, via Process.spawn.
|
29
32
|
So Ruby 1.8 support is sheerly for backwards compatibility.
|
data/lib/subexec.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
#
|
1
|
+
# # Subexec
|
2
2
|
# * by Peter Kieltyka
|
3
|
-
# * http://github/nulayer/
|
3
|
+
# * http://github/nulayer/subexec
|
4
4
|
#
|
5
|
-
#
|
5
|
+
# ## Description
|
6
6
|
#
|
7
7
|
# Subexec is a simple library that spawns an external command with
|
8
8
|
# an optional timeout parameter. It relies on Ruby 1.9's Process.spawn
|
@@ -11,9 +11,9 @@
|
|
11
11
|
# Useful for libraries that are Ruby wrappers for CLI's. For example,
|
12
12
|
# resizing images with ImageMagick's mogrify command sometimes stalls
|
13
13
|
# and never returns control back to the original process. Subexec
|
14
|
-
# executes mogrify and preempts if gets lost.
|
14
|
+
# executes mogrify and preempts if it gets lost.
|
15
15
|
#
|
16
|
-
#
|
16
|
+
# ## Usage
|
17
17
|
#
|
18
18
|
# # Print hello
|
19
19
|
# sub = Subexec.run "echo 'hello' && sleep 3", :timeout => 5
|
@@ -26,14 +26,15 @@
|
|
26
26
|
# puts sub.exitstatus # returns:
|
27
27
|
|
28
28
|
class Subexec
|
29
|
+
VERSION = '0.2.0'
|
29
30
|
|
30
|
-
attr_accessor :pid
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
31
|
+
attr_accessor :pid,
|
32
|
+
:command,
|
33
|
+
:lang,
|
34
|
+
:output,
|
35
|
+
:exitstatus,
|
36
|
+
:timeout,
|
37
|
+
:log_file
|
37
38
|
|
38
39
|
def self.run(command, options={})
|
39
40
|
sub = new(command, options)
|
@@ -42,13 +43,15 @@ class Subexec
|
|
42
43
|
end
|
43
44
|
|
44
45
|
def initialize(command, options={})
|
45
|
-
self.command
|
46
|
-
self.
|
47
|
-
self.
|
46
|
+
self.command = command
|
47
|
+
self.lang = options[:lang] || "C"
|
48
|
+
self.timeout = options[:timeout] || -1 # default is to never timeout
|
49
|
+
self.log_file = options[:log_file]
|
50
|
+
self.exitstatus = 0
|
48
51
|
end
|
49
52
|
|
50
53
|
def run!
|
51
|
-
if
|
54
|
+
if RUBY_VERSION >= '1.9'
|
52
55
|
spawn
|
53
56
|
else
|
54
57
|
exec
|
@@ -59,27 +62,41 @@ class Subexec
|
|
59
62
|
private
|
60
63
|
|
61
64
|
def spawn
|
62
|
-
|
63
|
-
|
65
|
+
# TODO: weak implementation for log_file support.
|
66
|
+
# Ideally, the data would be piped through to both descriptors
|
67
|
+
r, w = IO.pipe
|
68
|
+
if !log_file.nil?
|
69
|
+
self.pid = Process.spawn({'LANG' => self.lang}, command, [:out, :err] => [log_file, 'a'])
|
70
|
+
else
|
71
|
+
self.pid = Process.spawn({'LANG' => self.lang}, command, STDERR=>w, STDOUT=>w)
|
72
|
+
end
|
64
73
|
w.close
|
65
|
-
|
66
|
-
|
74
|
+
|
75
|
+
@timer = Time.now + timeout
|
67
76
|
timed_out = false
|
68
77
|
|
69
|
-
|
78
|
+
waitpid = Proc.new do
|
70
79
|
begin
|
71
80
|
flags = (timeout > 0 ? Process::WUNTRACED|Process::WNOHANG : 0)
|
72
|
-
|
81
|
+
Process.waitpid(pid, flags)
|
73
82
|
rescue Errno::ECHILD
|
74
83
|
break
|
75
84
|
end
|
85
|
+
end
|
76
86
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
break
|
87
|
+
if timeout > 0
|
88
|
+
loop do
|
89
|
+
ret = waitpid.call
|
90
|
+
|
91
|
+
break if ret == pid
|
92
|
+
sleep 0.01
|
93
|
+
if Time.now > @timer
|
94
|
+
timed_out = true
|
95
|
+
break
|
96
|
+
end
|
82
97
|
end
|
98
|
+
else
|
99
|
+
waitpid.call
|
83
100
|
end
|
84
101
|
|
85
102
|
if timed_out
|
@@ -98,8 +115,6 @@ class Subexec
|
|
98
115
|
|
99
116
|
def exec
|
100
117
|
self.output = `export LANG=#{lang} && #{command} 2>&1`
|
101
|
-
self.exitstatus = $?.exitstatus
|
102
118
|
end
|
103
119
|
|
104
120
|
end
|
105
|
-
|
metadata
CHANGED
@@ -1,59 +1,70 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: subexec
|
3
|
-
version: !ruby/object:Gem::Version
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
4
5
|
prerelease:
|
5
|
-
version: 0.1.0
|
6
6
|
platform: ruby
|
7
|
-
authors:
|
7
|
+
authors:
|
8
8
|
- Peter Kieltyka
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
12
|
+
date: 2011-11-01 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rake
|
16
|
+
requirement: &70265363645600 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70265363645600
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rspec
|
27
|
+
requirement: &70265363643540 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 2.7.0
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70265363643540
|
36
|
+
description: Subexec spawns a subprocess with an optional timeout
|
37
|
+
email:
|
19
38
|
- peter@nulayer.com
|
20
39
|
executables: []
|
21
|
-
|
22
40
|
extensions: []
|
23
|
-
|
24
41
|
extra_rdoc_files: []
|
25
|
-
|
26
|
-
|
27
|
-
- README.rdoc
|
28
|
-
- VERSION
|
42
|
+
files:
|
43
|
+
- README.md
|
29
44
|
- lib/subexec.rb
|
30
|
-
has_rdoc: true
|
31
45
|
homepage: http://github.com/nulayer/subexec
|
32
46
|
licenses: []
|
33
|
-
|
34
47
|
post_install_message:
|
35
48
|
rdoc_options: []
|
36
|
-
|
37
|
-
require_paths:
|
49
|
+
require_paths:
|
38
50
|
- lib
|
39
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
40
52
|
none: false
|
41
|
-
requirements:
|
42
|
-
- -
|
43
|
-
- !ruby/object:Gem::Version
|
44
|
-
version:
|
45
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ! '>='
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0'
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
46
58
|
none: false
|
47
|
-
requirements:
|
48
|
-
- -
|
49
|
-
- !ruby/object:Gem::Version
|
50
|
-
version:
|
59
|
+
requirements:
|
60
|
+
- - ! '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
51
63
|
requirements: []
|
52
|
-
|
53
64
|
rubyforge_project:
|
54
|
-
rubygems_version: 1.
|
65
|
+
rubygems_version: 1.8.11
|
55
66
|
signing_key:
|
56
67
|
specification_version: 3
|
57
|
-
summary: Subexec spawns
|
68
|
+
summary: Subexec spawns a subprocess with an optional timeout
|
58
69
|
test_files: []
|
59
|
-
|
70
|
+
has_rdoc:
|
data/VERSION
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
0.1.0
|