drb_queue 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ drb_queue
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-2.0.0-p195
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.9.2"
4
+ - "1.9.3"
5
+ - "2.0.0"
6
+ services:
7
+ - redis-server
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Andrew Warner
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # DRbQueue
2
+
3
+ ![](https://travis-ci.org/a-warner/drb_queue.png)
4
+
5
+ Simple separate-process queue system using DRb
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'drb_queue'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install drb_queue
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Contributing
26
+
27
+ 1. Fork it
28
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
29
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
30
+ 4. Push to the branch (`git push origin my-new-feature`)
31
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/drb_queue.gemspec ADDED
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'drb_queue/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "drb_queue"
8
+ spec.version = DRbQueue::VERSION
9
+ spec.authors = ["Andrew Warner"]
10
+ spec.email = ["wwarner.andrew@gmail.com"]
11
+ spec.description = %q{Simple drb-based queue/worker system}
12
+ spec.summary = %q{Simple drb-based queue/worker system}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_runtime_dependency "uuid", "~> 2.3.7"
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.3"
24
+ spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "rspec"
26
+ spec.add_development_dependency "pry"
27
+ spec.add_development_dependency "pry-doc"
28
+ spec.add_development_dependency "redis"
29
+ spec.add_development_dependency "redis-namespace"
30
+ end
Binary file
@@ -0,0 +1,52 @@
1
+ module DRbQueue
2
+ class Configuration
3
+ attr_accessor :socket_location, :num_workers, :logger, :error_handler, :immediate, :persistence_store
4
+
5
+ def initialize
6
+ self.socket_location = '/tmp/drb_queue'
7
+ self.num_workers = 1
8
+ self.logger = Logger.new(STDOUT)
9
+ self.error_handler = lambda { |e| logger.error(([e.message] + e.backtrace).join("\n")) }
10
+ end
11
+
12
+ def store(klass, options = {})
13
+ raise "Whoops, that's not a #{DRbQueue::Store}" unless klass <= DRbQueue::Store
14
+
15
+ self.persistence_store = [klass, options]
16
+ end
17
+
18
+ def construct_persistence_store
19
+ return unless persistence_store
20
+
21
+ persistence_store[0].new(persistence_store[1])
22
+ end
23
+
24
+ def immediate!
25
+ self.immediate = true
26
+ end
27
+
28
+ def on_error(&block)
29
+ self.error_handler = block
30
+ end
31
+
32
+ def after_fork(&block)
33
+ after_fork_callbacks << block
34
+ end
35
+
36
+ def before_fork(&block)
37
+ before_fork_callbacks << block
38
+ end
39
+
40
+ def after_fork_callbacks
41
+ @after_fork_callbacks ||= []
42
+ end
43
+
44
+ def before_fork_callbacks
45
+ @before_fork_callbacks ||= []
46
+ end
47
+
48
+ def server_uri
49
+ "drbunix:#{socket_location}"
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,130 @@
1
+ require "uuid"
2
+ require "thread"
3
+
4
+ module DRbQueue
5
+ class Server
6
+ NotStarted = Class.new(StandardError)
7
+ AlreadyStarted = Class.new(StandardError)
8
+
9
+ class UnableToStart < StandardError
10
+ attr_reader :cause
11
+ def initialize(message, cause)
12
+ super("#{message}: Cause is #{cause.inspect}")
13
+ @cause = cause
14
+ end
15
+ end
16
+
17
+ class Work < Struct.new(:worker, :args)
18
+ def perform
19
+ worker.perform(*args)
20
+ end
21
+
22
+ def self.unserialize(serialized)
23
+ hash = JSON.parse(serialized)
24
+ worker = hash['worker'].split('::').inject(Object) { |o, k| o.const_get(k) }
25
+
26
+ new(worker, hash['args'])
27
+ end
28
+
29
+ def serialize
30
+ {'worker' => worker.to_s, 'args' => args}.to_json
31
+ end
32
+ end
33
+
34
+ def initialize(configuration)
35
+ [:logger, :error_handler, :immediate].each do |p|
36
+ __send__("#{p}=", configuration.__send__(p))
37
+ end
38
+
39
+ self.queue = Queue.new
40
+ self.running = true
41
+
42
+ self.store = configuration.construct_persistence_store
43
+
44
+ start_workers(configuration.num_workers)
45
+
46
+ if store
47
+ store.each_persisted_work do |serialized_work|
48
+ enqueue_work(Work.unserialize(serialized_work))
49
+ end
50
+ end
51
+ end
52
+
53
+ def enqueue(worker, *args)
54
+ uuid.generate.tap do |id|
55
+ enqueue_work(Work.new(worker, args))
56
+ end
57
+ end
58
+
59
+ def uuid
60
+ @uuid ||= UUID.new
61
+ end
62
+
63
+ def ping
64
+ 'pong'
65
+ end
66
+
67
+ def shutdown!
68
+ self.running = false
69
+
70
+ if store
71
+ begin
72
+ while work = queue.pop(:dont_block)
73
+ store.persist(work)
74
+ end
75
+ rescue ThreadError => e
76
+ rescue => e
77
+ error_handler.call(e)
78
+ end
79
+ elsif queue.size > 0
80
+ logger.error("Queue is non-empty and we're shutting down...probably better to configure a persistence store\n")
81
+ end
82
+
83
+ workers.each(&:join)
84
+ end
85
+
86
+ private
87
+ attr_accessor :queue, :logger, :error_handler, :immediate, :store, :running
88
+ alias_method :immediate?, :immediate
89
+ alias_method :running?, :running
90
+
91
+ def enqueue_work(work)
92
+ if immediate?
93
+ work.perform
94
+ else
95
+ queue << work
96
+ end
97
+ end
98
+
99
+ def start_workers(num)
100
+ num.times.map { start_worker }
101
+ end
102
+
103
+ def workers
104
+ @workers ||= []
105
+ end
106
+
107
+ def start_worker
108
+ thread = Thread.new do
109
+ loop do
110
+ begin
111
+ break unless running?
112
+
113
+ work = queue.pop(:non_blocking)
114
+ work.perform
115
+ rescue ThreadError => e
116
+ sleep 0.05
117
+ rescue => e
118
+ error_handler.call(e)
119
+ start_worker
120
+ break
121
+ end
122
+ end
123
+
124
+ workers.delete(thread)
125
+ end
126
+
127
+ workers << thread
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,31 @@
1
+ require 'redis'
2
+ require 'redis-namespace'
3
+
4
+ module DRbQueue
5
+ class Store
6
+ class Redis < Store
7
+ def initialize(options = {})
8
+ @redis = ::Redis::Namespace.new(:DRbQueue, :redis => options.fetch(:redis, lambda { ::Redis.new }).call)
9
+ end
10
+
11
+ def persist(work)
12
+ redis.rpush(persistence_key, work.serialize)
13
+ end
14
+
15
+ def each_persisted_work
16
+ return enum_for(:each_persisted_work) unless block_given?
17
+
18
+ while serialized_work = redis.lpop(persistence_key)
19
+ yield(serialized_work)
20
+ end
21
+ end
22
+
23
+ private
24
+ attr_reader :redis
25
+
26
+ def persistence_key
27
+ 'persisted:work'
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,13 @@
1
+ module DRbQueue
2
+ class Store
3
+ NotImplementedError = Class.new(StandardError)
4
+
5
+ def persist(work)
6
+ raise NotImplementedError, "Must implement #persist"
7
+ end
8
+
9
+ def each_persisted_work
10
+ raise NotImplementedError, "Must implement #each_persisted_work"
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,3 @@
1
+ module DRbQueue
2
+ VERSION = "0.0.1"
3
+ end
data/lib/drb_queue.rb ADDED
@@ -0,0 +1,135 @@
1
+ require "drb_queue/version"
2
+ require "drb_queue/store"
3
+ require 'drb/drb'
4
+ require 'drb/unix'
5
+ require "fileutils"
6
+ require 'timeout'
7
+ require 'json'
8
+ require 'monitor'
9
+
10
+ module DRbQueue
11
+ extend self
12
+ extend Forwardable
13
+
14
+ autoload :Server, 'drb_queue/server'
15
+ autoload :Configuration, 'drb_queue/configuration'
16
+
17
+ ConfiguredAfterStarted = Class.new(StandardError)
18
+
19
+ attr_reader :started
20
+ alias_method :started?, :started
21
+
22
+ def enqueue(worker, *args)
23
+ raise Server::NotStarted, "You must start the server first" unless started?
24
+ raise ArgumentError, "#{worker} is not a module" unless worker.is_a?(Module)
25
+ raise ArgumentError, "#{worker} does not respond to perform" unless worker.respond_to?(:perform)
26
+
27
+ server.enqueue(worker, *args)
28
+ end
29
+
30
+ def start!
31
+ raise Server::AlreadyStarted, "The server is already started" if started?
32
+
33
+ synchronize do
34
+ return if started?
35
+
36
+ @pid = fork_server
37
+ connect_client!
38
+
39
+ at_exit { shutdown! }
40
+ @started = true
41
+ end
42
+ end
43
+
44
+ def configure
45
+ raise ConfiguredAfterStarted, "You must configure #{self.name} BEFORE starting the server" if started?
46
+
47
+ synchronize { yield configuration }
48
+ end
49
+
50
+ def shutdown!(immediately = false)
51
+ return unless started?
52
+
53
+ synchronize do
54
+ return unless started?
55
+
56
+ Process.kill(immediately ? 'KILL' : 'TERM', pid)
57
+
58
+ begin
59
+ ::Timeout.timeout(20) { Process.wait }
60
+ rescue Timeout::Error
61
+ Process.kill('KILL', pid)
62
+ Process.wait
63
+ logger.error("#{self}: forced shutdown")
64
+ ensure
65
+ cleanup_socket
66
+ @started = false
67
+ @pid = nil
68
+ end
69
+ end
70
+ end
71
+
72
+ def connect_client!
73
+ synchronize do
74
+ tries = 0
75
+
76
+ begin
77
+ @server = DRbObject.new_with_uri(server_uri)
78
+ @server.ping
79
+ rescue DRb::DRbConnError => e
80
+ raise Server::UnableToStart.new("Couldn't start up the queue server", e) if tries > 3
81
+ tries += 1
82
+ sleep 0.2
83
+ retry
84
+ end
85
+ end
86
+ end
87
+
88
+ private
89
+ attr_reader :pid, :server
90
+
91
+ def fork_server
92
+ cleanup_socket
93
+
94
+ execute_before_fork_callbacks
95
+
96
+ fork do
97
+ execute_after_fork_callbacks
98
+
99
+ server = Server.new(configuration)
100
+ DRb.start_service(server_uri, server)
101
+
102
+ shutting_down = false
103
+ trap('TERM') { shutting_down = true }
104
+ sleep 0.1 until shutting_down
105
+
106
+ server.shutdown!
107
+ DRb.stop_service
108
+ end
109
+ end
110
+
111
+ def execute_before_fork_callbacks
112
+ before_fork_callbacks.each(&:call)
113
+ end
114
+
115
+ def execute_after_fork_callbacks
116
+ after_fork_callbacks.each(&:call)
117
+ end
118
+
119
+ def configuration
120
+ @configuration ||= Configuration.new
121
+ end
122
+ def_delegators :configuration, :server_uri, :socket_location, :before_fork_callbacks, :after_fork_callbacks, :num_workers, :logger
123
+
124
+ def synchronize(&block)
125
+ synchronization_monitor.synchronize(&block)
126
+ end
127
+
128
+ def synchronization_monitor
129
+ @synchronization_monitor ||= Monitor.new
130
+ end
131
+
132
+ def cleanup_socket
133
+ FileUtils.rm(socket_location) if File.exist?(socket_location)
134
+ end
135
+ end
@@ -0,0 +1,161 @@
1
+ require 'spec_helper'
2
+
3
+ describe DRbQueue do
4
+ it { should_not be_nil }
5
+
6
+ def connect_to_redis!
7
+ Redis.current = Redis::Namespace.new(described_class.to_s, :redis => Redis.new(:db => 8))
8
+ end
9
+
10
+ before(:all) do
11
+ DRbQueue.configure do |c|
12
+ c.after_fork do
13
+ connect_to_redis!
14
+ end
15
+ end
16
+ end
17
+
18
+ def per_test_configuration(config); end
19
+
20
+ before do
21
+ connect_to_redis!
22
+
23
+ DRbQueue.configure do |c|
24
+ @old_config = c.dup
25
+ per_test_configuration(c)
26
+ end
27
+
28
+ DRbQueue.start!
29
+ end
30
+
31
+ let(:shutdown_immediately) { false }
32
+ after do
33
+ DRbQueue.shutdown!(shutdown_immediately)
34
+ DRbQueue.instance_variable_set('@configuration', @old_config)
35
+ Redis.current.flushall
36
+ end
37
+
38
+ class SetKeyToValueWorker
39
+ def self.perform(key, value, options = {})
40
+ sleep options[:sleep_time_before_working] if options[:sleep_time_before_working]
41
+ Redis.current.set(key, value)
42
+ end
43
+ end
44
+
45
+ context SetKeyToValueWorker do
46
+ let(:key) { 'foo' }
47
+ let(:value) { 'bar' }
48
+
49
+ it 'should do work asynchronously' do
50
+ DRbQueue.enqueue(SetKeyToValueWorker, key, value, :sleep_time_before_working => 0.1)
51
+ expect(Redis.current.get(key)).to be_nil
52
+ sleep 0.2
53
+ expect(Redis.current.get(key)).to eq(value)
54
+ end
55
+
56
+ it 'should do work in order' do
57
+ DRbQueue.enqueue(SetKeyToValueWorker, key, 'nottherightanswer')
58
+ DRbQueue.enqueue(SetKeyToValueWorker, key, value)
59
+ sleep 0.2
60
+ expect(Redis.current.get(key)).to eq(value)
61
+ end
62
+
63
+ context 'in parallel' do
64
+ def per_test_configuration(config)
65
+ config.num_workers = 5
66
+ end
67
+
68
+ it 'should do work in parallel' do
69
+ time = Benchmark.realtime do
70
+ 5.times { |i| DRbQueue.enqueue(SetKeyToValueWorker, i, i.to_s, :sleep_time_before_working => 0.1) }
71
+ sleep 0.2
72
+ 5.times { |i| expect(Redis.current.get(i)).to eq(i.to_s) }
73
+ end
74
+
75
+ expect(time).to be < 0.4
76
+ end
77
+ end
78
+ end
79
+
80
+ class SleepyWorker
81
+ def self.perform
82
+ sleep 100
83
+ end
84
+ end
85
+
86
+ context SleepyWorker do
87
+ let(:shutdown_immediately) { true }
88
+
89
+ it 'should not block regular execution' do
90
+ expect(Benchmark.realtime { DRbQueue.enqueue(SleepyWorker) }).to be < 1
91
+ end
92
+ end
93
+
94
+ class ExceptionWorker
95
+ def self.perform
96
+ raise "Get me outta here!"
97
+ end
98
+ end
99
+
100
+ context ExceptionWorker do
101
+ it 'should continue operating normally' do
102
+ DRbQueue.enqueue(ExceptionWorker)
103
+ sleep 0.1
104
+ DRbQueue.enqueue(SetKeyToValueWorker, 'a', 'b')
105
+ sleep 0.1
106
+ expect(Redis.current.get('a')).to eq('b')
107
+ end
108
+
109
+ def per_test_configuration(config)
110
+ config.on_error do |e|
111
+ Redis.current.set('error', e.message)
112
+ end
113
+ end
114
+
115
+ it 'allows custom exception handlers' do
116
+ DRbQueue.enqueue(ExceptionWorker)
117
+ sleep 0.1
118
+ expect(Redis.current.get('error')).not_to be_nil
119
+ end
120
+ end
121
+
122
+ context 'immediate mode' do
123
+ def per_test_configuration(config)
124
+ config.immediate!
125
+ end
126
+
127
+ it 'should do work synchronously' do
128
+ DRbQueue.enqueue(SetKeyToValueWorker, 'immediate', 'results', :sleep_time_before_working => 0.1)
129
+ expect(Redis.current.get('immediate')).to eq('results')
130
+ end
131
+ end
132
+
133
+ context 'error cases' do
134
+ it 'should blow up if something besides a module is passed in' do
135
+ expect { DRbQueue.enqueue('hahaha', 1, 2) }.to raise_error ArgumentError
136
+ end
137
+
138
+ it 'should blow up unless the module responds to perform' do
139
+ expect { DRbQueue.enqueue(Class.new) }.to raise_error ArgumentError
140
+ end
141
+ end
142
+
143
+ context 'job persistence' do
144
+ def per_test_configuration(config)
145
+ require 'drb_queue/store/redis'
146
+ config.store DRbQueue::Store::Redis
147
+ end
148
+
149
+ it 'should persist jobs on clean shutdown' do
150
+ 5.times { |i| DRbQueue.enqueue(SetKeyToValueWorker, i, i.to_s, :sleep_time_before_working => 0.1) }
151
+ DRbQueue.shutdown!
152
+
153
+ expect(0.upto(4).all? { |i| Redis.current.get(i) == i.to_s }).to be_false
154
+
155
+ DRbQueue.start!
156
+ sleep 0.5
157
+
158
+ expect(0.upto(4).all? { |i| Redis.current.get(i) == i.to_s }).to be_true
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,8 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+
4
+ require 'rubygems'
5
+ require 'bundler/setup'
6
+ Bundler.require(:development)
7
+ require 'drb_queue'
8
+ require 'benchmark'
metadata ADDED
@@ -0,0 +1,202 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: drb_queue
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Andrew Warner
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-11-29 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: uuid
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 2.3.7
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 2.3.7
30
+ - !ruby/object:Gem::Dependency
31
+ name: bundler
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '1.3'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '1.3'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rake
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rspec
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: pry
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: pry-doc
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: redis
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: redis-namespace
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ description: Simple drb-based queue/worker system
143
+ email:
144
+ - wwarner.andrew@gmail.com
145
+ executables: []
146
+ extensions: []
147
+ extra_rdoc_files: []
148
+ files:
149
+ - .gitignore
150
+ - .rspec
151
+ - .ruby-gemset
152
+ - .ruby-version
153
+ - .travis.yml
154
+ - Gemfile
155
+ - LICENSE.txt
156
+ - README.md
157
+ - Rakefile
158
+ - drb_queue.gemspec
159
+ - lib/drb_queue.rb
160
+ - lib/drb_queue/.configuration.swp
161
+ - lib/drb_queue/configuration.rb
162
+ - lib/drb_queue/server.rb
163
+ - lib/drb_queue/store.rb
164
+ - lib/drb_queue/store/redis.rb
165
+ - lib/drb_queue/version.rb
166
+ - spec/lib/drb_queue_spec.rb
167
+ - spec/spec_helper.rb
168
+ homepage: ''
169
+ licenses:
170
+ - MIT
171
+ post_install_message:
172
+ rdoc_options: []
173
+ require_paths:
174
+ - lib
175
+ required_ruby_version: !ruby/object:Gem::Requirement
176
+ none: false
177
+ requirements:
178
+ - - '>='
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ segments:
182
+ - 0
183
+ hash: -713522776135624504
184
+ required_rubygems_version: !ruby/object:Gem::Requirement
185
+ none: false
186
+ requirements:
187
+ - - '>='
188
+ - !ruby/object:Gem::Version
189
+ version: '0'
190
+ segments:
191
+ - 0
192
+ hash: -713522776135624504
193
+ requirements: []
194
+ rubyforge_project:
195
+ rubygems_version: 1.8.24
196
+ signing_key:
197
+ specification_version: 3
198
+ summary: Simple drb-based queue/worker system
199
+ test_files:
200
+ - spec/lib/drb_queue_spec.rb
201
+ - spec/spec_helper.rb
202
+ has_rdoc: