background_bunnies 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +10 -0
- data/background_bunnies.gemspec +22 -0
- data/examples/increments.rb +36 -0
- data/examples/increments_producer.rb +13 -0
- data/examples/increments_producer_reset.rb +7 -0
- data/examples/increments_worker.rb +7 -0
- data/lib/background_bunnies.rb +56 -0
- data/lib/background_bunnies/bunny.rb +103 -0
- data/lib/background_bunnies/job.rb +13 -0
- data/lib/background_bunnies/logger.rb +15 -0
- data/lib/background_bunnies/producer.rb +23 -0
- data/lib/background_bunnies/version.rb +3 -0
- data/lib/background_bunnies/workers.rb +5 -0
- data/test/background_bunnies_test.rb +150 -0
- data/test/test_helper.rb +3 -0
- metadata +114 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Johan Hernandez
|
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,29 @@
|
|
1
|
+
# BackgroundBunnies
|
2
|
+
|
3
|
+
Background workers based on AMQP Bunny gem
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'background_bunnies'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install background_bunnies
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
See the examples ;)
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'background_bunnies/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "background_bunnies"
|
8
|
+
gem.version = BackgroundBunnies::VERSION
|
9
|
+
gem.authors = ["Firebaseco"]
|
10
|
+
gem.email = ["hello@firebase.co"]
|
11
|
+
gem.description = %q{AMQP based workers}
|
12
|
+
gem.summary = %q{Background workers based on AMQP and the bunny gem}
|
13
|
+
gem.homepage = "https://github.com/firebaseco/background_bunnies"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
gem.add_dependency "bunny", ">= 0.9.0.pre7"
|
20
|
+
gem.add_development_dependency "rake"
|
21
|
+
gem.add_development_dependency "minitest"
|
22
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require "bundler"
|
2
|
+
Bundler.setup
|
3
|
+
$:.unshift(File.expand_path("../../lib", __FILE__))
|
4
|
+
require 'background_bunnies'
|
5
|
+
require 'bunny'
|
6
|
+
|
7
|
+
class BackgroundBunnies::Workers::IncrementCounter
|
8
|
+
include BackgroundBunnies::Bunny
|
9
|
+
group :default
|
10
|
+
def process(job)
|
11
|
+
step = job.payload['step'] || 1
|
12
|
+
self.class.counter += step
|
13
|
+
p "Incrementing: #{step} for a total of #{self.class.counter}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def on_error(job, err)
|
17
|
+
super # log the error to stderr
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.counter
|
21
|
+
@counter || 0
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.counter=(c)
|
25
|
+
@counter = c
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
class BackgroundBunnies::Workers::ResetCounter
|
31
|
+
include BackgroundBunnies::Bunny
|
32
|
+
def process(job)
|
33
|
+
BackgroundBunnies::Workers::IncrementCounter.counter = 0
|
34
|
+
p "Reseting the count"
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require_relative 'increments'
|
2
|
+
require 'thread'
|
3
|
+
connection = Bunny.new
|
4
|
+
connection.start
|
5
|
+
|
6
|
+
producer = BackgroundBunnies::Workers::IncrementCounter.create_producer connection
|
7
|
+
step = 0.1
|
8
|
+
while true
|
9
|
+
step += 0.1
|
10
|
+
sleep 1
|
11
|
+
producer.enqueue({'step'=>step})
|
12
|
+
p "Enqueued #{step}"
|
13
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require "background_bunnies/version"
|
2
|
+
require "background_bunnies/logger"
|
3
|
+
require "background_bunnies/bunny"
|
4
|
+
require "background_bunnies/producer"
|
5
|
+
require "background_bunnies/job"
|
6
|
+
require "background_bunnies/workers"
|
7
|
+
require "thread"
|
8
|
+
|
9
|
+
module BackgroundBunnies
|
10
|
+
|
11
|
+
class << self
|
12
|
+
|
13
|
+
#
|
14
|
+
# Group Connection Configurations
|
15
|
+
#
|
16
|
+
def configurations
|
17
|
+
@configs || @configs = {}
|
18
|
+
end
|
19
|
+
|
20
|
+
#
|
21
|
+
# Configures the connection of a group
|
22
|
+
#
|
23
|
+
def configure(group, connection)
|
24
|
+
info "Configuring #{group} connection"
|
25
|
+
configurations[group] = connection
|
26
|
+
if connection.status == :not_connected
|
27
|
+
connection.start
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
#
|
32
|
+
# Runs all the tasks in the given group and waits.
|
33
|
+
#
|
34
|
+
def run(group)
|
35
|
+
connection = configurations[group]
|
36
|
+
klasses = describe_group(group)
|
37
|
+
info "Running #{group} workers: #{ klasses.join(',') }"
|
38
|
+
klasses.each do |klass|
|
39
|
+
instance = klass.new
|
40
|
+
instance.start connection
|
41
|
+
info "Started worker: #{klass.demodulized_class_name}"
|
42
|
+
end
|
43
|
+
Thread.current.join
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# Describe types in BackgroundBunnies::Workers::* in the given group.
|
48
|
+
#
|
49
|
+
def describe_group(group)
|
50
|
+
worker_names = BackgroundBunnies::Workers.constants.collect! { |worker_name| BackgroundBunnies::Workers.const_get(worker_name) }
|
51
|
+
worker_names.select {|klass| klass.group_name == group }
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require_relative 'job'
|
3
|
+
|
4
|
+
module BackgroundBunnies
|
5
|
+
module Bunny
|
6
|
+
|
7
|
+
module BunnyConfigurators
|
8
|
+
|
9
|
+
def group(group_name)
|
10
|
+
@group_name = group_name
|
11
|
+
end
|
12
|
+
|
13
|
+
def queue(queue_name)
|
14
|
+
@queue_name = queue_name.to_s
|
15
|
+
end
|
16
|
+
|
17
|
+
def group_name
|
18
|
+
@group_name || :default
|
19
|
+
end
|
20
|
+
|
21
|
+
def queue_name
|
22
|
+
@queue_name || demodulized_class_name
|
23
|
+
end
|
24
|
+
|
25
|
+
def demodulized_class_name
|
26
|
+
path = name
|
27
|
+
if i = path.rindex('::')
|
28
|
+
path[(i+2)..-1]
|
29
|
+
else
|
30
|
+
path
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def create_producer(connection)
|
35
|
+
BackgroundBunnies::Producer.new(connection, queue_name)
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.included(base)
|
41
|
+
base.extend(BunnyConfigurators)
|
42
|
+
end
|
43
|
+
|
44
|
+
#
|
45
|
+
# Returns the name of the queue for the Worker
|
46
|
+
#
|
47
|
+
def queue_name
|
48
|
+
self.class.queue_name
|
49
|
+
end
|
50
|
+
|
51
|
+
attr_reader :channel
|
52
|
+
attr_reader :queue
|
53
|
+
attr_reader :consumer
|
54
|
+
|
55
|
+
#
|
56
|
+
# Starts the Worker
|
57
|
+
#
|
58
|
+
def start(connection)
|
59
|
+
@channel = connection.create_channel
|
60
|
+
@queue = @channel.queue(queue_name)
|
61
|
+
@consumer = @queue.subscribe(block: false, ack: true) do |info, properties, payload|
|
62
|
+
job = Job.new(JSON.parse!(payload), info, properties)
|
63
|
+
err = nil
|
64
|
+
begin
|
65
|
+
self.process(job)
|
66
|
+
rescue =>e
|
67
|
+
err = e
|
68
|
+
end
|
69
|
+
unless err
|
70
|
+
@channel.ack(info.delivery_tag, false)
|
71
|
+
else
|
72
|
+
# processing went wrong, requeing message
|
73
|
+
@channel.reject(info.delivery_tag, true)
|
74
|
+
on_error(job, err)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def on_error(job, err)
|
80
|
+
log_error "Error processing #{job.info.delivery_tag}: #{err.message}, #{err.backtrace.join('\n')}"
|
81
|
+
end
|
82
|
+
|
83
|
+
def log_error(a)
|
84
|
+
BackgroundBunnies.error "#{queue_name}: #{a}"
|
85
|
+
end
|
86
|
+
|
87
|
+
#
|
88
|
+
# Process a Job. Implemented by the class.
|
89
|
+
#
|
90
|
+
def process(job)
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
#
|
95
|
+
# Starts the worker instance and blocks the current thread.
|
96
|
+
#
|
97
|
+
def run(connection)
|
98
|
+
start connection
|
99
|
+
Thread.current.join
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module BackgroundBunnies
|
4
|
+
class Producer
|
5
|
+
attr_reader :channel
|
6
|
+
attr_reader :queue
|
7
|
+
attr_reader :queue_name
|
8
|
+
|
9
|
+
def initialize(connection, queue_name)
|
10
|
+
@queue_name = queue_name.to_s
|
11
|
+
@channel = connection.create_channel
|
12
|
+
@queue = connection.queue(@queue_name)
|
13
|
+
end
|
14
|
+
|
15
|
+
#
|
16
|
+
# Publishes a Job for the Worker
|
17
|
+
#
|
18
|
+
def enqueue(payload)
|
19
|
+
@queue.publish(JSON.generate(payload), :routing_key => @queue.name)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require 'bunny'
|
2
|
+
require_relative 'test_helper'
|
3
|
+
|
4
|
+
describe BackgroundBunnies::Bunny do
|
5
|
+
describe :queue_name do
|
6
|
+
|
7
|
+
it "should return the default name based on the worker class name" do
|
8
|
+
class WorkerDefault
|
9
|
+
include BackgroundBunnies::Bunny
|
10
|
+
end
|
11
|
+
worker = WorkerDefault.new
|
12
|
+
worker.queue_name.must_equal "WorkerDefault"
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should return the custom name or symbol is set" do
|
16
|
+
class WorkerNonDefault
|
17
|
+
include BackgroundBunnies::Bunny
|
18
|
+
queue :explicit_queue_name
|
19
|
+
end
|
20
|
+
worker = WorkerNonDefault.new
|
21
|
+
worker.queue_name.must_equal "explicit_queue_name"
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
describe :start do
|
27
|
+
it "should subscribe to queue" do
|
28
|
+
class StartTest
|
29
|
+
include BackgroundBunnies::Bunny
|
30
|
+
end
|
31
|
+
connection_klass = Struct.new("ConnectionStub", :last_channel) do
|
32
|
+
def create_channel
|
33
|
+
channel_klass = Struct.new("StubChannel", :hook_queue_name) do
|
34
|
+
def queue(name)
|
35
|
+
self.hook_queue_name= name
|
36
|
+
queue_klass = Struct.new("StubQueue", :options) do
|
37
|
+
def subscribe(options, &block)
|
38
|
+
self.options = options
|
39
|
+
end
|
40
|
+
end
|
41
|
+
queue_klass.new
|
42
|
+
end
|
43
|
+
end
|
44
|
+
self.last_channel = channel_klass.new
|
45
|
+
end
|
46
|
+
|
47
|
+
def start
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
connection = connection_klass.new
|
52
|
+
connection.start
|
53
|
+
worker = StartTest.new
|
54
|
+
worker.start connection
|
55
|
+
worker.channel.must_equal connection.last_channel
|
56
|
+
worker.channel.hook_queue_name.must_equal "StartTest"
|
57
|
+
worker.queue.options[:block].must_equal false
|
58
|
+
worker.queue.options[:ack].must_equal true
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe :process, "success" do
|
63
|
+
it "should ack success jobs" do
|
64
|
+
class StartTest
|
65
|
+
include BackgroundBunnies::Bunny
|
66
|
+
attr_reader :product_id
|
67
|
+
|
68
|
+
def process(job)
|
69
|
+
@product_id = job.payload['product_id']
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
connection_klass = Struct.new("StubConnectionSuccess") do
|
74
|
+
def create_channel
|
75
|
+
channel_klass = Struct.new("StubChannelSuccess", :delivery_tag) do
|
76
|
+
def queue(name)
|
77
|
+
queue_klass = Struct.new("StubQueueSuccess", :options) do
|
78
|
+
def subscribe(options, &block)
|
79
|
+
self.options = options
|
80
|
+
yield(Struct.new(:delivery_tag).new("tag0"), {}, JSON.generate({:product_id=>560}))
|
81
|
+
end
|
82
|
+
end
|
83
|
+
queue_klass.new
|
84
|
+
end
|
85
|
+
|
86
|
+
def ack(delivery_tag, multiple)
|
87
|
+
self.delivery_tag = delivery_tag
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
channel_klass.new
|
92
|
+
end
|
93
|
+
def start
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
97
|
+
connection = connection_klass.new
|
98
|
+
connection.start
|
99
|
+
worker = StartTest.new
|
100
|
+
worker.start connection
|
101
|
+
worker.channel.delivery_tag.must_equal "tag0"
|
102
|
+
worker.product_id.must_equal 560
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe :process, "exceptions" do
|
107
|
+
it "should not ack failed jobs" do
|
108
|
+
class StartTest
|
109
|
+
include BackgroundBunnies::Bunny
|
110
|
+
|
111
|
+
def process(job)
|
112
|
+
raise "some error"
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
connection_klass = Struct.new("StubExceptionsConnections") do
|
117
|
+
def create_channel
|
118
|
+
channel_klass = Struct.new("StubChannelExceptions", :delivery_tag) do
|
119
|
+
def queue(name)
|
120
|
+
queue_klass = Struct.new("StubQueueExceptions", :options) do
|
121
|
+
def subscribe(options, &block)
|
122
|
+
self.options = options
|
123
|
+
yield(Struct.new(:delivery_tag).new("tag0"), {}, JSON.generate({:product_id=>1}))
|
124
|
+
end
|
125
|
+
end
|
126
|
+
queue_klass.new
|
127
|
+
end
|
128
|
+
|
129
|
+
def ack(delivery_tag, multiple)
|
130
|
+
self.delivery_tag = delivery_tag
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
channel_klass.new
|
135
|
+
end
|
136
|
+
|
137
|
+
def start
|
138
|
+
|
139
|
+
end
|
140
|
+
end
|
141
|
+
connection = connection_klass.new
|
142
|
+
connection.start
|
143
|
+
worker = StartTest.new
|
144
|
+
worker.start connection
|
145
|
+
worker.channel.delivery_tag.must_be_nil
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: background_bunnies
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Firebaseco
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-03-21 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bunny
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 0.9.0.pre7
|
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: 0.9.0.pre7
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
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: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: minitest
|
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
|
+
description: AMQP based workers
|
63
|
+
email:
|
64
|
+
- hello@firebase.co
|
65
|
+
executables: []
|
66
|
+
extensions: []
|
67
|
+
extra_rdoc_files: []
|
68
|
+
files:
|
69
|
+
- .gitignore
|
70
|
+
- Gemfile
|
71
|
+
- LICENSE.txt
|
72
|
+
- README.md
|
73
|
+
- Rakefile
|
74
|
+
- background_bunnies.gemspec
|
75
|
+
- examples/increments.rb
|
76
|
+
- examples/increments_producer.rb
|
77
|
+
- examples/increments_producer_reset.rb
|
78
|
+
- examples/increments_worker.rb
|
79
|
+
- lib/background_bunnies.rb
|
80
|
+
- lib/background_bunnies/bunny.rb
|
81
|
+
- lib/background_bunnies/job.rb
|
82
|
+
- lib/background_bunnies/logger.rb
|
83
|
+
- lib/background_bunnies/producer.rb
|
84
|
+
- lib/background_bunnies/version.rb
|
85
|
+
- lib/background_bunnies/workers.rb
|
86
|
+
- test/background_bunnies_test.rb
|
87
|
+
- test/test_helper.rb
|
88
|
+
homepage: https://github.com/firebaseco/background_bunnies
|
89
|
+
licenses: []
|
90
|
+
post_install_message:
|
91
|
+
rdoc_options: []
|
92
|
+
require_paths:
|
93
|
+
- lib
|
94
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
95
|
+
none: false
|
96
|
+
requirements:
|
97
|
+
- - ! '>='
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
101
|
+
none: false
|
102
|
+
requirements:
|
103
|
+
- - ! '>='
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
requirements: []
|
107
|
+
rubyforge_project:
|
108
|
+
rubygems_version: 1.8.25
|
109
|
+
signing_key:
|
110
|
+
specification_version: 3
|
111
|
+
summary: Background workers based on AMQP and the bunny gem
|
112
|
+
test_files:
|
113
|
+
- test/background_bunnies_test.rb
|
114
|
+
- test/test_helper.rb
|