process_pool 0.1.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 ADDED
@@ -0,0 +1,5 @@
1
+ .idea/*
2
+ vendor/*
3
+ bin/*
4
+ tmp/*
5
+ scratch_directory/*
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ disable_system_gems
2
+
3
+ gem 'json'
4
+
5
+ only :test do
6
+ gem 'shoulda'
7
+ gem 'mocha'
8
+ end
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ == MIT License
2
+
3
+ Copyright (c) 2010, Adam Pohorecki
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README ADDED
@@ -0,0 +1,21 @@
1
+ Process Pool is something I created to replace a thread pool that had major performance problems caused by Ruby's (MRI)
2
+ poor thread implementation.
3
+
4
+ The basic usage is:
5
+
6
+ class SomeTask
7
+ def initialize(x, y, z)
8
+ end
9
+
10
+ def run
11
+ end
12
+ end
13
+
14
+ pool = ProcessPool.new(5) # first constructor argument is a number of worker processes
15
+ pool.schedule SomeTask, 1, 2, 3 # adds task SomeTask.new(1,2,3) to the queue (instantiation happens just before calling run())
16
+ pool.start # starts the workers
17
+ pool.schedule SomeTask, 3, 2, 1 # tasks can be added also after calling start
18
+ pool.shutdown # waits for all the tasks in the queue to be processed and kills all the worker processes
19
+
20
+ For now there is only one, very naive, queue implementation, which uses file locking and JSON storage format. I plan on adding
21
+ some other (better) queue backend soon.
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ begin
2
+ require 'jeweler'
3
+ Jeweler::Tasks.new do |gemspec|
4
+ gemspec.name = "process_pool"
5
+ gemspec.summary = "ProcessPool with interchangeable job queue backends for Ruby"
6
+ gemspec.email = "adam@pohorecki.pl"
7
+ gemspec.homepage = "http://github.com/psyho/process_pool"
8
+ gemspec.authors = ["Adam Pohorecki"]
9
+ gemspec.add_dependency 'json'
10
+ end
11
+ rescue LoadError
12
+ puts "Jeweler not available. Install it with: gem install jeweler"
13
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,3 @@
1
+ #!/home/psyho/.rvm/ruby-1.8.7-p174/bin/ruby
2
+ require File.join(File.dirname(__FILE__), "../vendor/gems/environment")
3
+ load File.join(File.dirname(__FILE__), "../vendor/gems/gems/shoulda-2.10.3/bin/convert_to_should_syntax")
data/bin/edit_json.rb ADDED
@@ -0,0 +1,3 @@
1
+ #!/home/psyho/.rvm/ruby-1.8.7-p174/bin/ruby
2
+ require File.join(File.dirname(__FILE__), "../vendor/gems/environment")
3
+ load File.join(File.dirname(__FILE__), "../vendor/gems/gems/json-1.2.0/bin/edit_json.rb")
data/bin/flay ADDED
@@ -0,0 +1,3 @@
1
+ #!/home/psyho/.rvm/ruby-1.8.7-p174/bin/ruby
2
+ require File.join(File.dirname(__FILE__), "../vendor/gems/environment")
3
+ load File.join(File.dirname(__FILE__), "../vendor/gems/gems/flay-1.4.0/bin/flay")
data/bin/flog ADDED
@@ -0,0 +1,3 @@
1
+ #!/home/psyho/.rvm/ruby-1.8.7-p174/bin/ruby -w
2
+ require File.join(File.dirname(__FILE__), "../vendor/gems/environment")
3
+ load File.join(File.dirname(__FILE__), "../vendor/gems/gems/flog-2.2.0/bin/flog")
@@ -0,0 +1,3 @@
1
+ #!/home/psyho/.rvm/ruby-1.8.7-p174/bin/ruby
2
+ require File.join(File.dirname(__FILE__), "../vendor/gems/environment")
3
+ load File.join(File.dirname(__FILE__), "../vendor/gems/gems/json-1.2.0/bin/prettify_json.rb")
data/bin/rake ADDED
@@ -0,0 +1,3 @@
1
+ #!/home/psyho/.rvm/ruby-1.8.7-p174/bin/ruby
2
+ require File.join(File.dirname(__FILE__), "../vendor/gems/environment")
3
+ load File.join(File.dirname(__FILE__), "../vendor/gems/gems/rake-0.8.7/bin/rake")
data/bin/rcov ADDED
@@ -0,0 +1,3 @@
1
+ #!/home/psyho/.rvm/ruby-1.8.7-p174/bin/ruby
2
+ require File.join(File.dirname(__FILE__), "../vendor/gems/environment")
3
+ load File.join(File.dirname(__FILE__), "../vendor/gems/gems/rcov-0.9.7.1/bin/rcov")
data/bin/ruby_parse ADDED
@@ -0,0 +1,3 @@
1
+ #!/home/psyho/.rvm/ruby-1.8.7-p174/bin/ruby -s
2
+ require File.join(File.dirname(__FILE__), "../vendor/gems/environment")
3
+ load File.join(File.dirname(__FILE__), "../vendor/gems/gems/ruby_parser-2.0.4/bin/ruby_parse")
data/bin/rubyforge ADDED
@@ -0,0 +1,3 @@
1
+ #!/home/psyho/.rvm/ruby-1.8.7-p174/bin/ruby
2
+ require File.join(File.dirname(__FILE__), "../vendor/gems/environment")
3
+ load File.join(File.dirname(__FILE__), "../vendor/gems/gems/rubyforge-2.0.3/bin/rubyforge")
data/bin/sow ADDED
@@ -0,0 +1,3 @@
1
+ #!/home/psyho/.rvm/ruby-1.8.7-p174/bin/ruby -ws
2
+ require File.join(File.dirname(__FILE__), "../vendor/gems/environment")
3
+ load File.join(File.dirname(__FILE__), "../vendor/gems/gems/hoe-2.5.0/bin/sow")
data/lib/init.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'rubygems'
2
+ require 'json'
3
+
4
+ require File.expand_path(File.join(File.dirname(__FILE__), 'simple_logger'))
5
+ require File.expand_path(File.join(File.dirname(__FILE__), 'simple_queue'))
6
+ require File.expand_path(File.join(File.dirname(__FILE__), 'process_pool'))
@@ -0,0 +1,98 @@
1
+ class ProcessPool
2
+
3
+ class InvalidStateError < StandardError;
4
+ end
5
+
6
+ attr_reader :workers_count
7
+
8
+ def initialize(workers_count, queue = SimpleQueue.create, logger = SimpleLogger.new)
9
+ self.state = :stopped
10
+ self.logger = logger
11
+ self.workers_count = workers_count
12
+ self.queue = queue
13
+ self.worker_pids = []
14
+ end
15
+
16
+ def schedule(job_class, *args)
17
+ raise InvalidStateError.new('Can not add more jobs after shut down was called') if is_shutdown?
18
+ logger.debug("Scheduling task #{job_class}(#{args})")
19
+ push_task(job_class, args)
20
+ end
21
+
22
+ def start
23
+ raise InvalidStateError.new('Can not start a pool more than once') unless is_stopped?
24
+ logger.info("Starting process pool")
25
+ self.state = :running
26
+
27
+ workers_count.times do
28
+ pid = fork do
29
+ child_queue = get_child_queue()
30
+ while true
31
+ task_class, args = child_queue.pop
32
+ begin
33
+ task = get_task_class(task_class).new(*args)
34
+ task.run
35
+ rescue => e
36
+ logger.warn("Exception occurred while executing task #{task_class}(#{args}): #{e}")
37
+ end
38
+ end
39
+ end
40
+ self.worker_pids << pid
41
+ end
42
+ end
43
+
44
+ def shutdown
45
+ raise InvalidStateError.new('Can not shut down pool that is not running') unless is_running?
46
+ logger.info("Shutting down process pool")
47
+ self.state = :shutdown
48
+
49
+ workers_count.times do
50
+ push_task(EndTask, [])
51
+ end
52
+
53
+ worker_pids.each do |pid|
54
+ Process.wait(pid)
55
+ end
56
+ end
57
+
58
+ def is_running?
59
+ return state == :running
60
+ end
61
+
62
+ def is_stopped?
63
+ return state == :stopped
64
+ end
65
+
66
+ def is_shutdown?
67
+ return state == :shutdown
68
+ end
69
+
70
+ protected
71
+
72
+ attr_accessor :state, :logger, :queue, :worker_pids
73
+ attr_writer :workers_count
74
+
75
+ def push_task(job_class, args)
76
+ queue.push([job_class.name.to_s, args])
77
+ end
78
+
79
+ def get_child_queue
80
+ queue.class.get(queue.uri)
81
+ end
82
+
83
+ # this is taken from ActiveSupport (String#constantize)
84
+ def get_task_class(class_name)
85
+ unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ class_name
86
+ raise NameError, "#{class_name.inspect} is not a valid constant name!"
87
+ end
88
+
89
+ Object.module_eval("::#{$1}", __FILE__, __LINE__)
90
+ end
91
+
92
+ class EndTask
93
+ def run
94
+ exit(0)
95
+ end
96
+ end
97
+
98
+ end
@@ -0,0 +1,19 @@
1
+ class SimpleLogger
2
+ LEVELS = [:debug, :info, :warn, :error, :fatal]
3
+
4
+ attr_accessor :level
5
+
6
+ def initialize(level = :info)
7
+ self.level = level
8
+ end
9
+
10
+ LEVELS.each do |level|
11
+ define_method(level) do |msg|
12
+ idx = LEVELS.index(level)
13
+ if idx >= LEVELS.index(self.level)
14
+ puts "Process #{Process.pid}: [#{level.to_s.upcase}] #{msg}"
15
+ end
16
+ end
17
+ end
18
+
19
+ end
@@ -0,0 +1,93 @@
1
+ require 'rubygems'
2
+ require 'json'
3
+ require 'tempfile'
4
+
5
+ class SimpleQueue
6
+
7
+ attr_reader :uri
8
+
9
+ def close
10
+ File.delete(uri)
11
+ end
12
+
13
+ def push(value)
14
+ with_queue_file do |file|
15
+ contents = file.read
16
+ queue = JSON.parse(contents)
17
+ queue.push(value)
18
+ store_queue(file, queue)
19
+ end
20
+ end
21
+
22
+ def pop
23
+ result = nil
24
+ queue_empty = true
25
+
26
+ while queue_empty
27
+ queue_empty, result = pop_nowait()
28
+ sleep(0.1) if queue_empty
29
+ end
30
+
31
+ return result
32
+ end
33
+
34
+ def size
35
+ contents = ''
36
+ with_queue_file do |file|
37
+ contents = file.read
38
+ end
39
+ return JSON.parse(contents).size
40
+ end
41
+
42
+ def self.create
43
+ file = Tempfile.new('simple_queue')
44
+ file.puts [].to_json
45
+ uri = file.path
46
+ file.close
47
+ return new(uri)
48
+ end
49
+
50
+ def self.get(uri)
51
+ raise ArgumentError.new("Queue file must exist: #{uri}") unless File.exists?(uri)
52
+ return new(uri)
53
+ end
54
+
55
+ protected
56
+
57
+ attr_writer :uri
58
+
59
+ def initialize(uri)
60
+ self.uri = uri
61
+ end
62
+
63
+ def with_queue_file(&block)
64
+ file = File.new(uri, 'r+')
65
+ if file.flock(File::LOCK_EX)
66
+ block.call(file)
67
+ end
68
+ file.close
69
+ end
70
+
71
+ def store_queue(file, queue)
72
+ file.truncate(0)
73
+ file.seek(0)
74
+
75
+ file.puts queue.to_json
76
+ end
77
+
78
+ def pop_nowait
79
+ queue_empty = true
80
+ result = nil
81
+ with_queue_file do |file|
82
+ contents = file.read
83
+ queue = JSON.parse(contents)
84
+ unless queue.empty?
85
+ queue_empty = false
86
+ result = queue.shift
87
+ store_queue(file, queue)
88
+ end
89
+ end
90
+ return queue_empty, result
91
+ end
92
+
93
+ end
@@ -0,0 +1,218 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'test_helper'))
2
+
3
+ class SampleQueue
4
+
5
+ attr_accessor :data
6
+
7
+ def initialize
8
+ self.data = []
9
+ end
10
+
11
+ def uri
12
+ 'test-queue'
13
+ end
14
+
15
+ def push(value)
16
+ self.data << value
17
+ end
18
+
19
+ def pop
20
+ self.data.shift
21
+ end
22
+
23
+ def size
24
+ self.data.size
25
+ end
26
+
27
+ def self.create
28
+ new
29
+ end
30
+
31
+ def self.get(uri)
32
+ new
33
+ end
34
+
35
+ end
36
+
37
+ class SampleTask
38
+
39
+ attr_accessor :args
40
+
41
+ def initialize(*args)
42
+ self.args = args
43
+ end
44
+
45
+ def run
46
+ return args
47
+ end
48
+
49
+ end
50
+
51
+ class ExceptionTask
52
+ def run
53
+ raise ArgumentError.new("something went wrong")
54
+ end
55
+ end
56
+
57
+ class WrongArgumentsTask
58
+ def initialize(x, y, z)
59
+ end
60
+
61
+ def run
62
+ end
63
+ end
64
+
65
+ class ProcessPoolTest < Test::Unit::TestCase
66
+
67
+ context "state" do
68
+ setup do
69
+ @pool = ProcessPool.new(10, SampleQueue.new, SimpleLogger.new(:debug))
70
+ @pool.stubs(:fork => 1)
71
+ Process.stubs(:wait => 0)
72
+ end
73
+
74
+ should "be stopped after initialization" do
75
+ assert @pool.is_stopped?
76
+ assert !@pool.is_running?
77
+ assert !@pool.is_shutdown?
78
+ end
79
+
80
+ should "be running after start" do
81
+ @pool.start
82
+ assert !@pool.is_stopped?
83
+ assert @pool.is_running?
84
+ assert !@pool.is_shutdown?
85
+ end
86
+
87
+ should "be shutdown after shutdown" do
88
+ @pool.start
89
+ @pool.shutdown
90
+ assert !@pool.is_stopped?
91
+ assert !@pool.is_running?
92
+ assert @pool.is_shutdown?
93
+ end
94
+
95
+ context "invalid actions" do
96
+ should "raise InvalidStateError when calling shutdown on a not started pool" do
97
+ assert_raises ProcessPool::InvalidStateError do
98
+ @pool.shutdown
99
+ end
100
+ end
101
+
102
+ should "raise InvalidStateError when calling start twice" do
103
+ @pool.start
104
+ assert_raises ProcessPool::InvalidStateError do
105
+ @pool.start
106
+ end
107
+ end
108
+
109
+ should "raise InvalidStateError when calling shutdown twice" do
110
+ @pool.start
111
+ @pool.shutdown
112
+ assert_raises ProcessPool::InvalidStateError do
113
+ @pool.shutdown
114
+ end
115
+ end
116
+
117
+ should "raise InvalidStateError when calling schedule on a shutdown pool" do
118
+ @pool.start
119
+ @pool.shutdown
120
+ assert_raises ProcessPool::InvalidStateError do
121
+ @pool.schedule(SampleTask)
122
+ end
123
+ end
124
+ end
125
+ end
126
+
127
+ context :schedule do
128
+ setup do
129
+ @queue = SampleQueue.new
130
+ @pool = ProcessPool.new(10, @queue, SimpleLogger.new(:debug))
131
+ @pool.stubs(:fork => 1)
132
+ end
133
+
134
+ should "add jobs to the queue" do
135
+ assert_equal 0, @queue.size
136
+ @pool.schedule(SampleTask, 1, 2, 3)
137
+ assert_equal 1, @queue.size
138
+ assert_equal ["SampleTask", [1, 2, 3]], @queue.pop
139
+ end
140
+ end
141
+
142
+ context :start do
143
+ setup do
144
+ @queue = SampleQueue.new
145
+ @workers_count = 10
146
+ @pool = ProcessPool.new(@workers_count, @queue, SimpleLogger.new(:debug))
147
+ end
148
+
149
+ should "fork workers_count workers" do
150
+ @pool.expects(:fork).times(@workers_count).returns(0)
151
+ @pool.start
152
+ end
153
+ end
154
+
155
+ context :shutdown do
156
+ setup do
157
+ @queue = SampleQueue.new
158
+ @pool = ProcessPool.new(3, @queue, SimpleLogger.new(:debug)) # no workers so that nothing processes the queue
159
+ @pool.schedule(SampleTask)
160
+ @pool.stubs(:fork => 1)
161
+ @pool.start
162
+ end
163
+
164
+ should "schedule same number od EndTasks as worker_count" do
165
+ Process.stubs(:wait => 0)
166
+ @pool.shutdown
167
+ assert_equal 4, @queue.size
168
+ assert_equal "SampleTask", @queue.data[0].first
169
+ (1..3).each do |n|
170
+ assert_equal "ProcessPool::EndTask", @queue.data[n].first
171
+ end
172
+ end
173
+
174
+ should "wait on all of the worker processes to end" do
175
+ Process.expects(:wait).with(1).times(3).returns(0)
176
+ @pool.shutdown
177
+ end
178
+ end
179
+
180
+ context "with default queue and two workers" do
181
+ setup do
182
+ @pool = ProcessPool.new(1)
183
+ 2.times { |n| @pool.schedule(SampleTask, n) }
184
+ end
185
+
186
+ should "empty the queue before returning from shutdown" do
187
+ assert_equal 2, @pool.send(:queue).size
188
+ @pool.start
189
+ @pool.shutdown
190
+ assert_equal 0, @pool.send(:queue).size
191
+ end
192
+
193
+ should "empty the queue even if some tasks result in exception" do
194
+ @pool.schedule(ExceptionTask)
195
+ assert_equal 3, @pool.send(:queue).size
196
+ @pool.start
197
+ @pool.shutdown
198
+ assert_equal 0, @pool.send(:queue).size
199
+ end
200
+
201
+ should "empty the queue even if some tasks can not be loaded" do
202
+ @pool.schedule(String)
203
+ assert_equal 3, @pool.send(:queue).size
204
+ @pool.start
205
+ @pool.shutdown
206
+ assert_equal 0, @pool.send(:queue).size
207
+ end
208
+
209
+ should "empty the queue even if some tasks can not be initialized" do
210
+ @pool.schedule(WrongArgumentsTask, 1)
211
+ assert_equal 3, @pool.send(:queue).size
212
+ @pool.start
213
+ @pool.shutdown
214
+ assert_equal 0, @pool.send(:queue).size
215
+ end
216
+ end
217
+
218
+ end
@@ -0,0 +1,101 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'test_helper'))
2
+
3
+ class SimpleQueueTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ @queue = SimpleQueue.create
7
+ end
8
+
9
+ def teardown
10
+ @queue.close if @queue
11
+ end
12
+
13
+ context :create do
14
+ should "return a queue" do
15
+ assert @queue.is_a?(SimpleQueue)
16
+ end
17
+
18
+ should "return queues with different URIs every time" do
19
+ new_queue = SimpleQueue.create
20
+ new_queue.close
21
+ assert new_queue.uri != @queue.uri
22
+ end
23
+
24
+ should "return a queue that is empty" do
25
+ assert_equal 0, @queue.size
26
+ end
27
+ end
28
+
29
+ context :get do
30
+ setup do
31
+ @uri = @queue.uri
32
+ end
33
+
34
+ should "raise an ArgumentError if queue does not exist" do
35
+ assert_raises(ArgumentError) { SimpleQueue.get('not existing') }
36
+ end
37
+
38
+ should "return a queue if it does exist" do
39
+ queue = SimpleQueue.get(@uri)
40
+ assert queue
41
+ assert_equal @uri, queue.uri
42
+ end
43
+ end
44
+
45
+ context :pop do
46
+ context "on a queue that contains something" do
47
+ setup do
48
+ @queue.push('hello')
49
+ end
50
+
51
+ context "pop" do
52
+ setup do
53
+ @result = @queue.pop
54
+ end
55
+
56
+ should "return 'hello'" do
57
+ assert_equal 'hello', @result
58
+ end
59
+
60
+ should_change('queue size', :by => -1) { @queue.size }
61
+ end
62
+ end
63
+
64
+ context 'on an empty queue' do
65
+ context 'pop' do
66
+ should 'block until something gets pushed' do
67
+ pid = fork do
68
+ queue = SimpleQueue.get(@queue.uri)
69
+ value = queue.pop
70
+ exit(value || 0)
71
+ end
72
+ sleep(0.5) # give the child process some time to start
73
+ @queue.push(19)
74
+ pid, status = Process.waitpid2(pid)
75
+ assert_equal 19, status.exitstatus
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ context :push do
82
+ context "on a queue that contains something" do
83
+ setup do
84
+ @queue.push('hello')
85
+ end
86
+
87
+ should_change('queue size', :by => 1) { @queue.size }
88
+ end
89
+ end
90
+
91
+ should "work on a FIFO basis" do
92
+ elements = [1, 2, 3, 4, 5]
93
+ elements.each { |e| @queue.push(e) }
94
+ popped = []
95
+ elements.size.times do
96
+ popped << @queue.pop
97
+ end
98
+ assert_equal elements, popped
99
+ end
100
+
101
+ end
@@ -0,0 +1,5 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'vendor', 'gems', 'environment'))
2
+
3
+ Bundler.require_env :test
4
+
5
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'init'))
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: process_pool
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Adam Pohorecki
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-01-17 00:00:00 +01:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: json
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ description:
26
+ email: adam@pohorecki.pl
27
+ executables:
28
+ - convert_to_should_syntax
29
+ - rake
30
+ - rubyforge
31
+ - flog
32
+ - ruby_parse
33
+ - flay
34
+ - rcov
35
+ - edit_json.rb
36
+ - sow
37
+ - prettify_json.rb
38
+ extensions: []
39
+
40
+ extra_rdoc_files:
41
+ - LICENSE
42
+ - README
43
+ files:
44
+ - .gitignore
45
+ - Gemfile
46
+ - LICENSE
47
+ - README
48
+ - Rakefile
49
+ - VERSION
50
+ - lib/init.rb
51
+ - lib/process_pool.rb
52
+ - lib/simple_logger.rb
53
+ - lib/simple_queue.rb
54
+ - test/process_pool_test.rb
55
+ - test/simple_queue_test.rb
56
+ - test/test_helper.rb
57
+ has_rdoc: true
58
+ homepage: http://github.com/psyho/process_pool
59
+ licenses: []
60
+
61
+ post_install_message:
62
+ rdoc_options:
63
+ - --charset=UTF-8
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: "0"
71
+ version:
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: "0"
77
+ version:
78
+ requirements: []
79
+
80
+ rubyforge_project:
81
+ rubygems_version: 1.3.5
82
+ signing_key:
83
+ specification_version: 3
84
+ summary: ProcessPool with interchangeable job queue backends for Ruby
85
+ test_files:
86
+ - test/test_helper.rb
87
+ - test/simple_queue_test.rb
88
+ - test/process_pool_test.rb