subexec 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|