af_minion 0.1.15.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +106 -0
- data/Rakefile +24 -0
- data/VERSION +1 -0
- data/examples/math.rb +36 -0
- data/examples/sandwich.rb +32 -0
- data/examples/when.rb +28 -0
- data/lib/minion.rb +214 -0
- data/lib/minion/handler.rb +30 -0
- data/spec/base.rb +21 -0
- data/spec/enqueue_spec.rb +10 -0
- metadata +129 -0
data/README.rdoc
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
|
2
|
+
= Minion: super simple job queue over amqp
|
3
|
+
|
4
|
+
Minion makes processing jobs over AMQP simple and easy.
|
5
|
+
|
6
|
+
== Setup
|
7
|
+
|
8
|
+
Minion pulls the AMQP credentials out the environment via AMQP_URL.
|
9
|
+
|
10
|
+
$ export AMQP_URL="amqp://johndoe:abc123@localhost/my_vhost"
|
11
|
+
|
12
|
+
Alternativly you can explicitly set it programmatically like this:
|
13
|
+
|
14
|
+
Minion.amqp_url = "amqp://johndoe:abc123@localhost/my_vhost"
|
15
|
+
|
16
|
+
If no URL is supplied, Minion defaults to "amqp://guest:guest@localhost/" which
|
17
|
+
is the default credentials for Rabbitmq running locally.
|
18
|
+
|
19
|
+
== Principles
|
20
|
+
|
21
|
+
Minion treats your jobs with respect. The queues are durable and not
|
22
|
+
autodelete. When popping jobs off the queue, they will not receive an ack
|
23
|
+
until the job is done. You can rest assured that once queued, the job will not
|
24
|
+
be lost.
|
25
|
+
|
26
|
+
Sends are done synchronously and receives are done asynchronously. This allows
|
27
|
+
you to Minion.enqueue() from the console, or in a mongrel and you don't need to
|
28
|
+
worry about eventmachine. It also means that when enqueue returns, the AMQP
|
29
|
+
server has received your message. Daemons set to receive messages however use
|
30
|
+
eventmachine.
|
31
|
+
|
32
|
+
Message processing is done one at a time (prefetch 1). If you want tasks done
|
33
|
+
in parallel, run two minions.
|
34
|
+
|
35
|
+
== Push a job onto the queue
|
36
|
+
|
37
|
+
Its easy to push a job onto the queue.
|
38
|
+
|
39
|
+
Minion.enqueue("make.sandwich", { "for" => "me", "with" => "bread" })
|
40
|
+
|
41
|
+
Minion expects a queue name (and will create it if needed). The second argument
|
42
|
+
needs to be a hash.
|
43
|
+
|
44
|
+
== Processing a job
|
45
|
+
|
46
|
+
require 'minion'
|
47
|
+
|
48
|
+
include Minion
|
49
|
+
|
50
|
+
job "make.sandwich" do |args|
|
51
|
+
Sandwich.make(args["for"],args["with"])
|
52
|
+
end
|
53
|
+
|
54
|
+
== Chaining multiple steps
|
55
|
+
|
56
|
+
If you have a task that requires more than one step just pass an array of
|
57
|
+
queues when you enqueue.
|
58
|
+
|
59
|
+
Minion.enqueue([ "make.sandwich", "eat.sandwich" ], "for" => "me")
|
60
|
+
|
61
|
+
job "make.sandwich" do
|
62
|
+
## this return value is merged with for => me and sent to the next queue
|
63
|
+
{ "type" => "ham on rye" }
|
64
|
+
end
|
65
|
+
|
66
|
+
job "eat.sandwich" do |args|
|
67
|
+
puts "I have #{args["type"]} sandwich for #{args["me"]}"
|
68
|
+
end
|
69
|
+
|
70
|
+
== Conditional Processing
|
71
|
+
|
72
|
+
If you want a minion worker to only subscribe to a queue under specific
|
73
|
+
conditions there is a :when parameter that takes a lambda as an argument. For
|
74
|
+
example, if you had a queue that makes sandwiches but only if there is bread
|
75
|
+
on hand, it would be.
|
76
|
+
|
77
|
+
job "make.sandwich", :when => lambda { not Bread.out? } do
|
78
|
+
Sandwich.make
|
79
|
+
end
|
80
|
+
|
81
|
+
== Error handling
|
82
|
+
|
83
|
+
When an error is thrown in a job handler, the job is requeued to be done later
|
84
|
+
and the minion process exits. If you define an error handler, however, the
|
85
|
+
error handler is run and the job is removed from the queue.
|
86
|
+
|
87
|
+
error do |e|
|
88
|
+
puts "got an error! #{e}"
|
89
|
+
end
|
90
|
+
|
91
|
+
== Logging
|
92
|
+
|
93
|
+
Minion logs to stdout via "puts". You can specify a custom logger like this:
|
94
|
+
|
95
|
+
logger do |msg|
|
96
|
+
puts msg
|
97
|
+
end
|
98
|
+
|
99
|
+
== Meta
|
100
|
+
|
101
|
+
Created by Orion Henry
|
102
|
+
|
103
|
+
Patches contributed by Adam Wiggins, Kyle Drake
|
104
|
+
|
105
|
+
Released under the MIT License: www.opensource.org/licenses/mit-license.php
|
106
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'jeweler'
|
2
|
+
|
3
|
+
Jeweler::Tasks.new do |s|
|
4
|
+
s.name = "minion"
|
5
|
+
s.description = "Super simple job queue over AMQP"
|
6
|
+
s.summary = s.description
|
7
|
+
s.author = "Orion Henry"
|
8
|
+
s.email = "orion@heroku.com"
|
9
|
+
s.homepage = "http://github.com/orionz/minion"
|
10
|
+
s.rubyforge_project = "minion"
|
11
|
+
s.files = FileList["[A-Z]*", "{bin,lib,spec}/**/*"]
|
12
|
+
s.add_dependency "amqp", ">= 0.6.7"
|
13
|
+
s.add_dependency "bunny", ">= 0.6.0"
|
14
|
+
s.add_dependency "json", ">= 1.2.0"
|
15
|
+
end
|
16
|
+
|
17
|
+
Jeweler::RubyforgeTasks.new
|
18
|
+
|
19
|
+
desc 'Run specs'
|
20
|
+
task :spec do
|
21
|
+
sh 'bacon -s spec/*_spec.rb'
|
22
|
+
end
|
23
|
+
|
24
|
+
task :default => :spec
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.15
|
data/examples/math.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift File.dirname(__FILE__) + '/../lib'
|
4
|
+
require 'rubygems'
|
5
|
+
require 'minion'
|
6
|
+
|
7
|
+
include Minion
|
8
|
+
|
9
|
+
error do |exception,queue,message,headers|
|
10
|
+
puts "got an error processing queue #{queue}"
|
11
|
+
puts exception.message
|
12
|
+
puts exception.backtrace
|
13
|
+
end
|
14
|
+
|
15
|
+
logger do |msg|
|
16
|
+
puts "--> #{msg}"
|
17
|
+
end
|
18
|
+
|
19
|
+
job "math.incr" do |args|
|
20
|
+
{ "number" => (1 + args["number"].to_i) }
|
21
|
+
end
|
22
|
+
|
23
|
+
job "math.double" do |args|
|
24
|
+
{ "number" => (2 * args["number"].to_i) }
|
25
|
+
end
|
26
|
+
|
27
|
+
job "math.square" do |args|
|
28
|
+
{ "number" => (args["number"].to_i * args["number"].to_i) }
|
29
|
+
end
|
30
|
+
|
31
|
+
job "math.print" do |args|
|
32
|
+
puts "NUMBER -----> #{args["number"]}"
|
33
|
+
end
|
34
|
+
|
35
|
+
enqueue([ "math.incr", "math.double", "math.square", "math.incr", "math.double", "math.print" ], { :number => 3 })
|
36
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift File.dirname(__FILE__) + '/../lib'
|
4
|
+
require 'rubygems'
|
5
|
+
require 'minion'
|
6
|
+
|
7
|
+
include Minion
|
8
|
+
|
9
|
+
error do |exception,queue,message,headers|
|
10
|
+
puts "got an error processing queue #{queue}"
|
11
|
+
puts exception.message
|
12
|
+
puts exception.backtrace
|
13
|
+
end
|
14
|
+
|
15
|
+
job "add.bread" do |args|
|
16
|
+
{ "bread" => "sourdough" }
|
17
|
+
end
|
18
|
+
|
19
|
+
job "add.meat" do |args|
|
20
|
+
{ "meat" => "turkey" }
|
21
|
+
end
|
22
|
+
|
23
|
+
job "add.condiments" do |args|
|
24
|
+
{ "condiments" => "mayo" }
|
25
|
+
end
|
26
|
+
|
27
|
+
job "eat.sandwich" do |args|
|
28
|
+
puts "YUM! A #{args['meat']} on #{args['bread']} sandwich with #{args['condiments']}"
|
29
|
+
end
|
30
|
+
|
31
|
+
enqueue(["add.bread", "add.meat", "add.condiments", "eat.sandwich" ])
|
32
|
+
|
data/examples/when.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift File.dirname(__FILE__) + '/../lib'
|
4
|
+
require 'rubygems'
|
5
|
+
require 'minion'
|
6
|
+
|
7
|
+
include Minion
|
8
|
+
|
9
|
+
error do |exception,queue,message,headers|
|
10
|
+
puts "got an error processing queue #{queue}"
|
11
|
+
puts exception.message
|
12
|
+
puts exception.backtrace
|
13
|
+
end
|
14
|
+
|
15
|
+
logger do |msg|
|
16
|
+
puts "--> #{msg}"
|
17
|
+
end
|
18
|
+
|
19
|
+
$listen = true
|
20
|
+
|
21
|
+
job "do.once", :when => lambda { $listen } do |args|
|
22
|
+
puts "Do this one action - then unsubscribe..."
|
23
|
+
$listen = false
|
24
|
+
end
|
25
|
+
|
26
|
+
enqueue("do.once",[])
|
27
|
+
enqueue("do.once",[])
|
28
|
+
|
data/lib/minion.rb
ADDED
@@ -0,0 +1,214 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'json' unless defined? ActiveSupport::JSON
|
3
|
+
require 'mq'
|
4
|
+
require 'bunny'
|
5
|
+
require 'minion/handler'
|
6
|
+
|
7
|
+
module Minion
|
8
|
+
|
9
|
+
class AMQPServerConnectError < StandardError; end;
|
10
|
+
|
11
|
+
NUMBER_OF_RETRIES = 3
|
12
|
+
|
13
|
+
extend self
|
14
|
+
|
15
|
+
def url=(url)
|
16
|
+
@@config_url = url
|
17
|
+
end
|
18
|
+
|
19
|
+
def enqueue(jobs, data = {})
|
20
|
+
raise "cannot enqueue a nil job" if jobs.nil?
|
21
|
+
raise "cannot enqueue an empty job" if jobs.empty?
|
22
|
+
|
23
|
+
## jobs can be one or more jobs
|
24
|
+
if jobs.respond_to? :shift
|
25
|
+
queue = jobs.shift
|
26
|
+
data["next_job"] = jobs unless jobs.empty?
|
27
|
+
else
|
28
|
+
queue = jobs
|
29
|
+
end
|
30
|
+
|
31
|
+
# NOTE: we encode to yaml, Minion encodes to json. The reason is that json cannot handle Ruby symbols
|
32
|
+
yamlized_data = data.to_yaml
|
33
|
+
log "send: #{queue}:#{yamlized_data}"
|
34
|
+
|
35
|
+
# NOTE: When rabbitMQ goes down, the mongrel keeps on holding the stale connection to it. We need to establish a new
|
36
|
+
# connection when this happens.
|
37
|
+
|
38
|
+
with_bunny_connection_retry do
|
39
|
+
bunny.queue(queue, :durable => false, :auto_delete => false).publish(yamlized_data)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def log(msg)
|
44
|
+
@@logger ||= proc { |m| puts "#{Time.now} :minion: #{m}" }
|
45
|
+
@@logger.call(msg)
|
46
|
+
end
|
47
|
+
|
48
|
+
def error(&blk)
|
49
|
+
@@error_handler = blk
|
50
|
+
end
|
51
|
+
|
52
|
+
def logger(&blk)
|
53
|
+
@@logger = blk
|
54
|
+
end
|
55
|
+
|
56
|
+
def job(queue, options = {}, &blk)
|
57
|
+
handler = Minion::Handler.new queue
|
58
|
+
handler.when = options[:when] if options[:when]
|
59
|
+
handler.unsub = lambda do
|
60
|
+
log "unsubscribing to #{queue}"
|
61
|
+
MQ.queue(queue, :durable => false, :auto_delete => false).unsubscribe
|
62
|
+
end
|
63
|
+
|
64
|
+
handler.sub = lambda do
|
65
|
+
log "subscribing to #{queue}"
|
66
|
+
MQ.queue(queue, :durable => false, :auto_delete => false).subscribe(:ack => false) do |h,m|
|
67
|
+
return if AMQP.closing?
|
68
|
+
begin
|
69
|
+
log "recv: #{queue}:#{m}"
|
70
|
+
|
71
|
+
args = decode_json(m)
|
72
|
+
|
73
|
+
result = yield(args)
|
74
|
+
|
75
|
+
next_job(args, result)
|
76
|
+
rescue Object => e
|
77
|
+
raise unless error_handler
|
78
|
+
error_handler.call(e,queue,m,h)
|
79
|
+
end
|
80
|
+
check_all
|
81
|
+
end
|
82
|
+
end
|
83
|
+
@@handlers ||= []
|
84
|
+
at_exit { Minion.run } if @@handlers.size == 0
|
85
|
+
@@handlers << handler
|
86
|
+
end
|
87
|
+
|
88
|
+
def decode_json(string)
|
89
|
+
if defined? ActiveSupport::JSON
|
90
|
+
ActiveSupport::JSON.decode string
|
91
|
+
else
|
92
|
+
JSON.load string
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def check_all
|
97
|
+
@@handlers.each { |h| h.check }
|
98
|
+
end
|
99
|
+
|
100
|
+
def run
|
101
|
+
log "Starting minion"
|
102
|
+
|
103
|
+
Signal.trap('INT') { exit_handler }
|
104
|
+
Signal.trap('TERM'){ exit_handler }
|
105
|
+
|
106
|
+
EM.run do
|
107
|
+
connection = AMQP.start(amqp_config) do
|
108
|
+
MQ.prefetch(1)
|
109
|
+
check_all
|
110
|
+
end
|
111
|
+
|
112
|
+
# NOTE: By default, Minion gets into a wierd state when there is a connection error (i.e., the AMQP server is down).
|
113
|
+
# In short, it continues running without any subscriptions. We chose to detect this case, and kill the worker that
|
114
|
+
# has invoke run. This will allow it to be restarted by the monitoring system, and hopefully by then, the AMQP
|
115
|
+
# server will be back up.
|
116
|
+
if connection.error?
|
117
|
+
exit_handler
|
118
|
+
raise AMQPServerConnectError.new("Couldn't connect to RabbitMQ server at #{amqp_url}")
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def amqp_url
|
124
|
+
@@amqp_url ||= ENV["AMQP_URL"] || "amqp://guest:guest@localhost/"
|
125
|
+
end
|
126
|
+
|
127
|
+
def amqp_url=(url)
|
128
|
+
clear_bunny
|
129
|
+
@@amqp_url = url
|
130
|
+
end
|
131
|
+
|
132
|
+
def delete!(*queue_names)
|
133
|
+
EM.run do
|
134
|
+
AMQP.start(amqp_config) do
|
135
|
+
queue_names.each do |queue_name|
|
136
|
+
MQ.queue(queue_name).delete
|
137
|
+
msg = "Minion: deleting '#{queue_name}' queue from the AMQP server."
|
138
|
+
ActiveRecord::Base::logger.info(msg)
|
139
|
+
log msg
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
AMQP.stop { EM.stop }
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
|
148
|
+
private
|
149
|
+
|
150
|
+
def amqp_config
|
151
|
+
uri = URI.parse(amqp_url)
|
152
|
+
{
|
153
|
+
:vhost => uri.path,
|
154
|
+
:host => uri.host,
|
155
|
+
:user => uri.user,
|
156
|
+
:port => (uri.port || 5672),
|
157
|
+
:pass => uri.password
|
158
|
+
}
|
159
|
+
rescue Object => e
|
160
|
+
raise "invalid AMQP_URL: #{uri.inspect} (#{e})"
|
161
|
+
end
|
162
|
+
|
163
|
+
def new_bunny
|
164
|
+
b = Bunny.new(amqp_config)
|
165
|
+
b.start
|
166
|
+
b
|
167
|
+
end
|
168
|
+
|
169
|
+
def bunny
|
170
|
+
@@bunny ||= new_bunny
|
171
|
+
end
|
172
|
+
|
173
|
+
def next_job(args, response)
|
174
|
+
queue = args.delete("next_job")
|
175
|
+
enqueue(queue,args.merge(response)) if queue and not queue.empty?
|
176
|
+
end
|
177
|
+
|
178
|
+
def error_handler
|
179
|
+
@@error_handler ||= nil
|
180
|
+
end
|
181
|
+
|
182
|
+
def exit_handler
|
183
|
+
AMQP.stop { EM.stop }
|
184
|
+
end
|
185
|
+
|
186
|
+
def with_bunny_connection_retry
|
187
|
+
num_executions = 1
|
188
|
+
begin
|
189
|
+
yield
|
190
|
+
rescue Bunny::ServerDownError, Bunny::ConnectionError => e
|
191
|
+
clear_bunny
|
192
|
+
if num_executions < NUMBER_OF_RETRIES
|
193
|
+
log "Retry ##{num_executions} : #{caller.first}"
|
194
|
+
num_executions += 1
|
195
|
+
sleep 0.5
|
196
|
+
retry
|
197
|
+
else
|
198
|
+
error = e.class.new(e.message + " (Retried #{NUMBER_OF_RETRIES} times. Giving up.)")
|
199
|
+
error.set_backtrace(e.backtrace)
|
200
|
+
raise error
|
201
|
+
end
|
202
|
+
rescue => e
|
203
|
+
clear_bunny
|
204
|
+
raise e
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def clear_bunny
|
209
|
+
@@bunny = nil
|
210
|
+
end
|
211
|
+
|
212
|
+
|
213
|
+
end
|
214
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Minion
|
2
|
+
class Handler
|
3
|
+
attr_accessor :queue, :sub, :unsub, :when, :on
|
4
|
+
def initialize(queue)
|
5
|
+
@queue = queue
|
6
|
+
@when = lambda { true }
|
7
|
+
@sub = lambda {}
|
8
|
+
@unsub = lambda {}
|
9
|
+
@on = false
|
10
|
+
end
|
11
|
+
|
12
|
+
def should_sub?
|
13
|
+
@when.call
|
14
|
+
end
|
15
|
+
|
16
|
+
def check
|
17
|
+
if should_sub?
|
18
|
+
@sub.call unless @on
|
19
|
+
@on = true
|
20
|
+
else
|
21
|
+
@unsub.call if @on
|
22
|
+
@on = false
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_s
|
27
|
+
"<handler queue=#{@queue} on=#{@on}>"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/spec/base.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../lib/minion'
|
2
|
+
|
3
|
+
require 'bacon'
|
4
|
+
require 'mocha/standalone'
|
5
|
+
require 'mocha/object'
|
6
|
+
|
7
|
+
class Bacon::Context
|
8
|
+
include Mocha::API
|
9
|
+
|
10
|
+
def initialize(name, &block)
|
11
|
+
@name = name
|
12
|
+
@before, @after = [
|
13
|
+
[lambda { mocha_setup }],
|
14
|
+
[lambda { mocha_verify ; mocha_teardown }]
|
15
|
+
]
|
16
|
+
@block = block
|
17
|
+
end
|
18
|
+
|
19
|
+
def xit(desc, &bk)
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/base'
|
2
|
+
|
3
|
+
describe Minion do
|
4
|
+
it "should throw an exception when passed a nil queue" do
|
5
|
+
lambda { Minion.enqueue(nil, {}) }.should.raise(RuntimeError)
|
6
|
+
end
|
7
|
+
it "should throw an exception when passed an empty queue" do
|
8
|
+
lambda { Minion.enqueue([], {}) }.should.raise(RuntimeError)
|
9
|
+
end
|
10
|
+
end
|
metadata
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: af_minion
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 121
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 15
|
10
|
+
- 1
|
11
|
+
version: 0.1.15.1
|
12
|
+
platform: ruby
|
13
|
+
authors:
|
14
|
+
- Orion Henry
|
15
|
+
- AppFolio Dev Team
|
16
|
+
autorequire:
|
17
|
+
bindir: bin
|
18
|
+
cert_chain: []
|
19
|
+
|
20
|
+
date: 2010-08-24 00:00:00 -07:00
|
21
|
+
default_executable:
|
22
|
+
dependencies:
|
23
|
+
- !ruby/object:Gem::Dependency
|
24
|
+
name: amqp
|
25
|
+
prerelease: false
|
26
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
27
|
+
none: false
|
28
|
+
requirements:
|
29
|
+
- - ">="
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
hash: 9
|
32
|
+
segments:
|
33
|
+
- 0
|
34
|
+
- 6
|
35
|
+
- 7
|
36
|
+
version: 0.6.7
|
37
|
+
type: :runtime
|
38
|
+
version_requirements: *id001
|
39
|
+
- !ruby/object:Gem::Dependency
|
40
|
+
name: bunny
|
41
|
+
prerelease: false
|
42
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
43
|
+
none: false
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
hash: 7
|
48
|
+
segments:
|
49
|
+
- 0
|
50
|
+
- 6
|
51
|
+
- 0
|
52
|
+
version: 0.6.0
|
53
|
+
type: :runtime
|
54
|
+
version_requirements: *id002
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: json
|
57
|
+
prerelease: false
|
58
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
59
|
+
none: false
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
hash: 31
|
64
|
+
segments:
|
65
|
+
- 1
|
66
|
+
- 2
|
67
|
+
- 0
|
68
|
+
version: 1.2.0
|
69
|
+
type: :runtime
|
70
|
+
version_requirements: *id003
|
71
|
+
description: Super simple job queue over AMQP with modifications from AppFolio Inc.
|
72
|
+
email: tushar.ranka@appfolio.com orion@heroku.com
|
73
|
+
executables: []
|
74
|
+
|
75
|
+
extensions: []
|
76
|
+
|
77
|
+
extra_rdoc_files:
|
78
|
+
- README.rdoc
|
79
|
+
files:
|
80
|
+
- README.rdoc
|
81
|
+
- Rakefile
|
82
|
+
- VERSION
|
83
|
+
- lib/minion.rb
|
84
|
+
- lib/minion/handler.rb
|
85
|
+
- spec/base.rb
|
86
|
+
- spec/enqueue_spec.rb
|
87
|
+
- examples/math.rb
|
88
|
+
- examples/sandwich.rb
|
89
|
+
- examples/when.rb
|
90
|
+
has_rdoc: true
|
91
|
+
homepage: http://github.com/orionz/minion
|
92
|
+
licenses: []
|
93
|
+
|
94
|
+
post_install_message:
|
95
|
+
rdoc_options:
|
96
|
+
- --charset=UTF-8
|
97
|
+
require_paths:
|
98
|
+
- lib
|
99
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
100
|
+
none: false
|
101
|
+
requirements:
|
102
|
+
- - ">="
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
hash: 3
|
105
|
+
segments:
|
106
|
+
- 0
|
107
|
+
version: "0"
|
108
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
109
|
+
none: false
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
hash: 3
|
114
|
+
segments:
|
115
|
+
- 0
|
116
|
+
version: "0"
|
117
|
+
requirements: []
|
118
|
+
|
119
|
+
rubyforge_project: af_minion
|
120
|
+
rubygems_version: 1.3.7
|
121
|
+
signing_key:
|
122
|
+
specification_version: 3
|
123
|
+
summary: Super simple job queue over AMQP
|
124
|
+
test_files:
|
125
|
+
- spec/base.rb
|
126
|
+
- spec/enqueue_spec.rb
|
127
|
+
- examples/math.rb
|
128
|
+
- examples/sandwich.rb
|
129
|
+
- examples/when.rb
|