process-builder 1.0.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/.gitignore +19 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +24 -0
- data/README.rdoc +34 -0
- data/Rakefile +13 -0
- data/lib/process_builder.rb +217 -0
- data/process_builder.gemspec +19 -0
- data/spec/process_builder_spec.rb +156 -0
- metadata +65 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
process-builder (0.5.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: http://rubygems.org/
|
8
|
+
specs:
|
9
|
+
diff-lcs (1.1.3)
|
10
|
+
rspec (2.10.0)
|
11
|
+
rspec-core (~> 2.10.0)
|
12
|
+
rspec-expectations (~> 2.10.0)
|
13
|
+
rspec-mocks (~> 2.10.0)
|
14
|
+
rspec-core (2.10.1)
|
15
|
+
rspec-expectations (2.10.0)
|
16
|
+
diff-lcs (~> 1.1.3)
|
17
|
+
rspec-mocks (2.10.1)
|
18
|
+
|
19
|
+
PLATFORMS
|
20
|
+
ruby
|
21
|
+
|
22
|
+
DEPENDENCIES
|
23
|
+
process-builder!
|
24
|
+
rspec
|
data/README.rdoc
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
= process-builder
|
2
|
+
|
3
|
+
Simple object-oriented wrapper around Ruby's Process.spawn and Open3 library.
|
4
|
+
|
5
|
+
ProcessBuilder allows you to build a process description which can then be
|
6
|
+
used to spawn and interact with an external process.
|
7
|
+
|
8
|
+
== Usage
|
9
|
+
|
10
|
+
require 'process-builder'
|
11
|
+
|
12
|
+
# Build a process description with the ProcessBuilder.build method.
|
13
|
+
process = ProcessBuilder.build('oggenc', 'track01.cdda.wav') do |builder|
|
14
|
+
# The builder object passed to this block has methods for setting various
|
15
|
+
# attributes of the process. See the RDoc for full details.
|
16
|
+
builder.environment['LOG_DIR'] = '/var/log'
|
17
|
+
builder.directory = "#{ENV['HOME']}/Music"
|
18
|
+
end
|
19
|
+
|
20
|
+
# Once the process description is built there are a variety of ways
|
21
|
+
# to spawn the process. One way is to call spawn:
|
22
|
+
pid = process.spawn
|
23
|
+
Process.wait(pid)
|
24
|
+
|
25
|
+
# Another way is to use any of the popen or capture methods defined by Open3:
|
26
|
+
stdin, stdout, stderr, wait_thread = process.popen3
|
27
|
+
status = wait_thread.value
|
28
|
+
stdin.close; stdout.close; stderr.close
|
29
|
+
|
30
|
+
# or in block form:
|
31
|
+
process.popen3 do |stdin, stdout, stderr, wait_thread|
|
32
|
+
status = wait_thread.value
|
33
|
+
end
|
34
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
require 'rdoc/task'
|
4
|
+
|
5
|
+
RSpec::Core::RakeTask.new(:spec)
|
6
|
+
task :default => :spec
|
7
|
+
|
8
|
+
Rake::RDocTask.new do |rd|
|
9
|
+
rd.main = "README.rdoc"
|
10
|
+
rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
|
11
|
+
rd.title = 'Process Builder'
|
12
|
+
end
|
13
|
+
|
@@ -0,0 +1,217 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'open3'
|
3
|
+
|
4
|
+
# Object-oriented wrapper around Process.spawn and the Open3 library.
|
5
|
+
# ProcessBuilder is an immutable description of a process and its various
|
6
|
+
# attributes. ProcessBuilder objects are created with the
|
7
|
+
# ProcessBuilder.build method, and after having been created can be used
|
8
|
+
# to spawn the process in various ways, such as #spawn or #popen3.
|
9
|
+
#
|
10
|
+
# See the descriptions of the methods of this class for further details,
|
11
|
+
# and the documentation for Process.spawn and Open3 for full details of
|
12
|
+
# the arguments that each understands.
|
13
|
+
class ProcessBuilder
|
14
|
+
VERSION = '1.0.0'
|
15
|
+
|
16
|
+
# The command and arguments passed to Process.spawn
|
17
|
+
attr_reader :command_line
|
18
|
+
|
19
|
+
# Working directory for the process. Corresponds to the :chdir option of
|
20
|
+
# Process.spawn
|
21
|
+
attr_reader :directory
|
22
|
+
|
23
|
+
# Hash representing environment variables for the process. Passed as the
|
24
|
+
# optional [env] argument of Process.spawn.
|
25
|
+
attr_reader :environment
|
26
|
+
|
27
|
+
# If true, clear environment variables, other than specified explicitly
|
28
|
+
# in environment.
|
29
|
+
attr_reader :unsetenv_others
|
30
|
+
alias unsetenv_others? unsetenv_others
|
31
|
+
|
32
|
+
# Process group, corresponding to the :pgroup option of Process.spawn.
|
33
|
+
attr_reader :pgroup
|
34
|
+
|
35
|
+
# Hash specifying IO redirection for the child process, corresponding to
|
36
|
+
# the redirection options of Process.spawn. Generally not used if using any
|
37
|
+
# of the Open3 mechanisms for spawning the process.
|
38
|
+
attr_reader :redirection
|
39
|
+
|
40
|
+
# File descriptor inheritance, corresponding to the :close_others option of
|
41
|
+
# Process.spawn.
|
42
|
+
attr_reader :close_others
|
43
|
+
|
44
|
+
# umask for the child process, corresponding to the :umask option of
|
45
|
+
# Process.spawn.
|
46
|
+
attr_reader :umask
|
47
|
+
|
48
|
+
# Hash specifying resource limits. Process.spawn expects resource limits to
|
49
|
+
# be specified as options in the form :rlimit_resourcename, where
|
50
|
+
# resourcename is one of the resources understood by Process.setrlimit,
|
51
|
+
# such as :rlimit_core for example.
|
52
|
+
# The keys of this Hash will be used to construct the options, so setting
|
53
|
+
# \rlimit[:core] would result in an option named :rlimit_core.
|
54
|
+
attr_reader :rlimit
|
55
|
+
|
56
|
+
# Build a new process description using the given args as the process
|
57
|
+
# command line. If a block is given, this method will yield a mutable
|
58
|
+
# ProcessBuilder::Builder object to it so that the block can specify the
|
59
|
+
# attributes of the process. The object returned by this method, however,
|
60
|
+
# is an immutable (and frozen) instance of ProcessBuilder.
|
61
|
+
def self.build(*args, &block)
|
62
|
+
new(*args, &block)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Build a new process description copied from the given other ProcessBuilder.
|
66
|
+
# Like the build method, this method yields to the given block to allow for
|
67
|
+
# customization of the process attributes.
|
68
|
+
def self.copy(other)
|
69
|
+
raise ArgumentError unless other.is_a?(ProcessBuilder)
|
70
|
+
if block_given?
|
71
|
+
builder = Builder.new(other)
|
72
|
+
if block_given?
|
73
|
+
yield builder
|
74
|
+
end
|
75
|
+
new(builder)
|
76
|
+
else
|
77
|
+
new(other)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def initialize(*args)
|
82
|
+
if args.size == 1 && args.first.is_a?(ProcessBuilder)
|
83
|
+
self.copy_fields(args.first)
|
84
|
+
else
|
85
|
+
@command_line = array_copy(args)
|
86
|
+
@environment = Hash.new
|
87
|
+
@redirection = Hash.new
|
88
|
+
@rlimit = Hash.new
|
89
|
+
if block_given?
|
90
|
+
builder = Builder.new(self)
|
91
|
+
yield builder
|
92
|
+
self.copy_fields(builder)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
self.freeze
|
96
|
+
end
|
97
|
+
|
98
|
+
def initialize_copy(other)
|
99
|
+
super
|
100
|
+
self.copy_fields(other)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Spawn the process described by this ProcessBuilder using Process.spawn.
|
104
|
+
# Returns the PID of the spawned process.
|
105
|
+
def spawn
|
106
|
+
Process.spawn(*spawn_args)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Spawn the process described by this ProcessBuilder using Open3.popen2.
|
110
|
+
def popen2(&block)
|
111
|
+
Open3.popen2(*spawn_args, &block)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Spawn the process described by this ProcessBuilder using Open3.popen2e.
|
115
|
+
def popen2e(&block)
|
116
|
+
Open3.popen2e(*spawn_args, &block)
|
117
|
+
end
|
118
|
+
|
119
|
+
# Spawn the process described by this ProcessBuilder using Open3.popen3.
|
120
|
+
def popen3(&block)
|
121
|
+
Open3.popen3(*spawn_args, &block)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Execute the process described by this ProcessBuilder using Open3.capture2.
|
125
|
+
# The argument to this method is used as the :stdin_data argument of
|
126
|
+
# Open3.capture2.
|
127
|
+
def capture2(stdin_data, &block)
|
128
|
+
args = self.spawn_args
|
129
|
+
args.last[:stdin_data] = stdin_data.to_s.dup
|
130
|
+
Open3.capture2(*args, &block)
|
131
|
+
end
|
132
|
+
|
133
|
+
# Execute the process described by this ProcessBuilder using Open3.capture3.
|
134
|
+
# The argument to this method is used as the :stdin_data argument of
|
135
|
+
# Open3.capture3.
|
136
|
+
def capture3(stdin_data, &block)
|
137
|
+
args = self.spawn_args
|
138
|
+
args.last[:stdin_data] = stdin_data.to_s.dup
|
139
|
+
Open3.capture3(*args, &block)
|
140
|
+
end
|
141
|
+
|
142
|
+
# Returns an array of arguments as understood by Process.spawn.
|
143
|
+
def spawn_args
|
144
|
+
result = Array.new
|
145
|
+
unless environment.empty?
|
146
|
+
result << environment
|
147
|
+
end
|
148
|
+
result.concat(command_line)
|
149
|
+
opts = Hash.new
|
150
|
+
opts[:chdir] = directory.to_s unless directory.nil?
|
151
|
+
opts[:pgroup] = pgroup unless pgroup.nil?
|
152
|
+
opts[:umask] = umask unless umask.nil?
|
153
|
+
opts[:unsetenv_others] = unsetenv_others unless unsetenv_others.nil?
|
154
|
+
opts[:close_others] = close_others unless close_others.nil?
|
155
|
+
rlimit.each do |key, value|
|
156
|
+
opts["rlimit_#{key}".to_sym] = value
|
157
|
+
end
|
158
|
+
redirection.each do |key, value|
|
159
|
+
opts[key] = value
|
160
|
+
end
|
161
|
+
result << opts
|
162
|
+
result
|
163
|
+
end
|
164
|
+
|
165
|
+
protected
|
166
|
+
|
167
|
+
# :nodoc:
|
168
|
+
def copy_fields(other)
|
169
|
+
@command_line = array_copy(other.command_line)
|
170
|
+
@directory = Pathname(other.directory )if other.directory
|
171
|
+
@environment = hash_copy(other.environment)
|
172
|
+
@unsetenv_others = other.unsetenv_others
|
173
|
+
@pgroup = other.pgroup
|
174
|
+
@redirection = hash_copy(other.redirection)
|
175
|
+
@close_others = other.close_others
|
176
|
+
@umask = other.umask
|
177
|
+
@rlimit = hash_copy(other.rlimit)
|
178
|
+
end
|
179
|
+
|
180
|
+
private
|
181
|
+
|
182
|
+
def copy_val(val)
|
183
|
+
case val
|
184
|
+
when NilClass, TrueClass, FalseClass, Numeric, Symbol
|
185
|
+
val
|
186
|
+
else
|
187
|
+
val.dup.freeze
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def array_copy(array)
|
192
|
+
[array].flatten.compact.map { |arg|
|
193
|
+
copy_val(arg)
|
194
|
+
}
|
195
|
+
end
|
196
|
+
|
197
|
+
def hash_copy(hash)
|
198
|
+
hash ||= Hash.new
|
199
|
+
result = Hash.new
|
200
|
+
hash.each do |key, value|
|
201
|
+
result[key] = copy_val(value)
|
202
|
+
end
|
203
|
+
result
|
204
|
+
end
|
205
|
+
|
206
|
+
# Builder object that is passed to the block given to the ProcessBuilder.build
|
207
|
+
# and ProcessBuilder.copy methods. The Builder object has writable attributes
|
208
|
+
# allowing the block to customize the attributes of the process.
|
209
|
+
class Builder < ProcessBuilder
|
210
|
+
attr_writer :command_line, :directory, :environment, :pgroup,
|
211
|
+
:rlimit_resourcename, :umask, :close_others
|
212
|
+
|
213
|
+
def initialize(initial_state)
|
214
|
+
self.copy_fields(initial_state)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "process_builder"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "process-builder"
|
7
|
+
s.version = ProcessBuilder::VERSION
|
8
|
+
s.authors = ["Jason Voegele"]
|
9
|
+
s.email = ["jason@jvoegele.com"]
|
10
|
+
s.homepage = "https://github.com/jvoegele/process-builder"
|
11
|
+
s.summary = "Simple object-oriented wrapper around Process.spawn"
|
12
|
+
|
13
|
+
s.files = `git ls-files`.split("\n")
|
14
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
15
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
|
18
|
+
s.add_development_dependency "rspec"
|
19
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'process_builder'
|
2
|
+
|
3
|
+
describe ProcessBuilder do
|
4
|
+
context "build" do
|
5
|
+
it "returns a new ProcessBuilder object" do
|
6
|
+
p = ProcessBuilder.build
|
7
|
+
p.should be_kind_of(ProcessBuilder)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "optionally takes command line arguments" do
|
11
|
+
p = ProcessBuilder.build('command', 'arg1', 'arg2')
|
12
|
+
p.command_line.should == %w[command arg1 arg2]
|
13
|
+
end
|
14
|
+
|
15
|
+
it "yields a ProcessBuilder::Builder object if given a block" do
|
16
|
+
yielded = false
|
17
|
+
process = ProcessBuilder.build do |p|
|
18
|
+
yielded = true
|
19
|
+
p.should be_kind_of(ProcessBuilder::Builder)
|
20
|
+
end
|
21
|
+
yielded.should be_true
|
22
|
+
end
|
23
|
+
|
24
|
+
it "copies the values set on the builder object in the block" do
|
25
|
+
process = ProcessBuilder.build('command', 'arg1') do |builder|
|
26
|
+
builder.command_line << 'arg2'
|
27
|
+
builder.directory = "#{ENV['HOME']}/fakedir"
|
28
|
+
builder.environment['ULTIMATE_ANSWER'] = 42
|
29
|
+
builder.environment['NILVAR'] = nil
|
30
|
+
end
|
31
|
+
process.command_line.should == %w[command arg1 arg2]
|
32
|
+
process.directory.should == Pathname("#{ENV['HOME']}/fakedir")
|
33
|
+
process.environment.should == {
|
34
|
+
'ULTIMATE_ANSWER' => 42,
|
35
|
+
'NILVAR' => nil
|
36
|
+
}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context "copy" do
|
41
|
+
let(:initializer) {
|
42
|
+
->(builder) do
|
43
|
+
builder.directory = 'somedir'
|
44
|
+
builder.environment['SOMEVAR'] = 'someval'
|
45
|
+
end
|
46
|
+
}
|
47
|
+
it "copies all attributes of the other process builder" do
|
48
|
+
p1 = ProcessBuilder.build('command', 'arg1', &initializer)
|
49
|
+
p2 = ProcessBuilder.copy(p1) do |p|
|
50
|
+
p.directory = 'anotherdir'
|
51
|
+
p.environment['ANOTHERVAR'] = 'anotherval'
|
52
|
+
end
|
53
|
+
p2.command_line.should == %w[command arg1]
|
54
|
+
p2.directory.should == Pathname('anotherdir')
|
55
|
+
p2.environment.should == {
|
56
|
+
'SOMEVAR' => 'someval',
|
57
|
+
'ANOTHERVAR' => 'anotherval'
|
58
|
+
}
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context "#spawn_args" do
|
63
|
+
let(:process) {
|
64
|
+
ProcessBuilder.build('command', 'arg1') do |p|
|
65
|
+
p.directory = 'somedir'
|
66
|
+
p.environment['SOMEVAR'] = 'someval'
|
67
|
+
p.pgroup = true
|
68
|
+
p.redirection[:err] = 'error.log'
|
69
|
+
p.umask = 42
|
70
|
+
p.rlimit['core'] = [0, 100]
|
71
|
+
p.rlimit[:nice] = 20
|
72
|
+
end
|
73
|
+
}
|
74
|
+
|
75
|
+
it "converts all attributes into arguments for Process.spawn" do
|
76
|
+
spawn_args = process.spawn_args
|
77
|
+
spawn_args.should == [
|
78
|
+
{'SOMEVAR' => 'someval'},
|
79
|
+
'command', 'arg1',
|
80
|
+
{
|
81
|
+
:chdir => 'somedir',
|
82
|
+
:pgroup => true,
|
83
|
+
:err => 'error.log',
|
84
|
+
:umask => 42,
|
85
|
+
:rlimit_core => [0, 100],
|
86
|
+
:rlimit_nice => 20
|
87
|
+
}
|
88
|
+
]
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
it "can spawn a new process" do
|
93
|
+
p = ProcessBuilder.build('ruby', '--version') do |p|
|
94
|
+
p.redirection[:err] = :close
|
95
|
+
end
|
96
|
+
pid = p.spawn
|
97
|
+
pid.should be_kind_of(Integer)
|
98
|
+
end
|
99
|
+
|
100
|
+
context "popen" do
|
101
|
+
let(:process) {
|
102
|
+
ProcessBuilder.build('ruby', '--version')
|
103
|
+
}
|
104
|
+
|
105
|
+
it "supports popen2" do
|
106
|
+
stdin, stdout, wait_thread = process.popen2
|
107
|
+
stdin.should be_kind_of(IO)
|
108
|
+
stdout.should be_kind_of(IO)
|
109
|
+
wait_thread.should be_kind_of(Thread)
|
110
|
+
stdin.close
|
111
|
+
stdout.close
|
112
|
+
|
113
|
+
process.popen2 do |stdin, stdout, wait_thread|
|
114
|
+
stdin.should be_kind_of(IO)
|
115
|
+
stdout.should be_kind_of(IO)
|
116
|
+
wait_thread.should be_kind_of(Thread)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
it "supports popen3" do
|
121
|
+
stdin, stdout, stderr, wait_thread = process.popen3
|
122
|
+
stdin.should be_kind_of(IO)
|
123
|
+
stdout.should be_kind_of(IO)
|
124
|
+
stderr.should be_kind_of(IO)
|
125
|
+
wait_thread.should be_kind_of(Thread)
|
126
|
+
stdin.close
|
127
|
+
stdout.close
|
128
|
+
stderr.close
|
129
|
+
|
130
|
+
process.popen3 do |stdin, stdout, stderr, wait_thread|
|
131
|
+
stdin.should be_kind_of(IO)
|
132
|
+
stdout.should be_kind_of(IO)
|
133
|
+
stderr.should be_kind_of(IO)
|
134
|
+
wait_thread.should be_kind_of(Thread)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
context "capture" do
|
140
|
+
it "supports capture2" do
|
141
|
+
process = ProcessBuilder.build('ruby')
|
142
|
+
stdout_string, status = process.capture2("puts('Hello world!')")
|
143
|
+
stdout_string.should == "Hello world!\n"
|
144
|
+
status.should be_kind_of(Process::Status)
|
145
|
+
end
|
146
|
+
|
147
|
+
it "supports capture3" do
|
148
|
+
process = ProcessBuilder.build('ruby')
|
149
|
+
ruby_code = %Q{puts("Hello world!"); $stderr.puts("42")}
|
150
|
+
stdout_string, stderr_string, status = process.capture3(ruby_code)
|
151
|
+
stdout_string.should == "Hello world!\n"
|
152
|
+
stderr_string.should == "42\n"
|
153
|
+
status.should be_kind_of(Process::Status)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
metadata
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: process-builder
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jason Voegele
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-05-20 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: &70284807936260 !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: *70284807936260
|
25
|
+
description:
|
26
|
+
email:
|
27
|
+
- jason@jvoegele.com
|
28
|
+
executables: []
|
29
|
+
extensions: []
|
30
|
+
extra_rdoc_files: []
|
31
|
+
files:
|
32
|
+
- .gitignore
|
33
|
+
- Gemfile
|
34
|
+
- Gemfile.lock
|
35
|
+
- README.rdoc
|
36
|
+
- Rakefile
|
37
|
+
- lib/process_builder.rb
|
38
|
+
- process_builder.gemspec
|
39
|
+
- spec/process_builder_spec.rb
|
40
|
+
homepage: https://github.com/jvoegele/process-builder
|
41
|
+
licenses: []
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options: []
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
none: false
|
48
|
+
requirements:
|
49
|
+
- - ! '>='
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ! '>='
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
requirements: []
|
59
|
+
rubyforge_project:
|
60
|
+
rubygems_version: 1.8.15
|
61
|
+
signing_key:
|
62
|
+
specification_version: 3
|
63
|
+
summary: Simple object-oriented wrapper around Process.spawn
|
64
|
+
test_files:
|
65
|
+
- spec/process_builder_spec.rb
|