mb-minion 0.2.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/Rakefile +30 -0
- data/examples/batch.rb +31 -0
- data/examples/batch_wait.rb +36 -0
- data/examples/batch_wait_2.rb +52 -0
- data/examples/math.rb +42 -0
- data/examples/sandwich.rb +38 -0
- data/examples/sandwich_batch.rb +44 -0
- data/examples/when.rb +34 -0
- data/lib/mb-ext/string.rb +7 -0
- data/lib/mb-minion.rb +231 -0
- data/lib/mb-minion/handler.rb +177 -0
- data/lib/mb-minion/message.rb +58 -0
- data/lib/mb-minion/version.rb +4 -0
- data/spec/minion/handler_spec.rb +147 -0
- data/spec/minion/message_spec.rb +38 -0
- data/spec/minion_spec.rb +238 -0
- data/spec/spec_helper.rb +11 -0
- metadata +201 -0
@@ -0,0 +1,177 @@
|
|
1
|
+
require 'pp'
|
2
|
+
# encoding: utf-8
|
3
|
+
module Minion
|
4
|
+
class Handler
|
5
|
+
attr_reader :queue_name, :block, :batch_size, :wait
|
6
|
+
|
7
|
+
# Executes the handler. Will subscribe to a queue or unsubscribe to it
|
8
|
+
# depending on the conditions.
|
9
|
+
#
|
10
|
+
# @example Execute the handler.
|
11
|
+
# handler.execute
|
12
|
+
def execute
|
13
|
+
subscribable? ? subscribe : unsubscribe
|
14
|
+
end
|
15
|
+
|
16
|
+
# Instantiate the new handler. Takes a queue name and optional lambda to
|
17
|
+
# determine conditionally if a queue is subscribable.
|
18
|
+
#
|
19
|
+
# @example Create the new handler.
|
20
|
+
# Handler.new("minion.test")
|
21
|
+
#
|
22
|
+
# @param [ String ] queue_name The name of the queue.
|
23
|
+
# @param [ Hash ]
|
24
|
+
# @option options [ lambda ] :when The block for conditionally subscribing.
|
25
|
+
# @option options [ fixnum ] :batch_size The number of elements per batch
|
26
|
+
# @option options [ symbol ] :map The type of map operation: fanout or reduce
|
27
|
+
def initialize(queue_name, block, options = {})
|
28
|
+
@queue_name, @block = queue_name, block
|
29
|
+
@subscribable = options[:when]
|
30
|
+
@batch_size = options[:batch_size]
|
31
|
+
@wait = options[:wait] || false
|
32
|
+
raise ArgumentError, "wait parameter makes no sense without a batch_size" if (@wait && ! @batch_size)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# Returns true if the handler is already subscribed to the queue.
|
38
|
+
#
|
39
|
+
# @example Is the handler running?
|
40
|
+
# handler.running?
|
41
|
+
#
|
42
|
+
# @return [ true, false ] Is the handler running?
|
43
|
+
def running?
|
44
|
+
!!@running
|
45
|
+
end
|
46
|
+
|
47
|
+
# Determines if the queue is able to be subscribed to.
|
48
|
+
#
|
49
|
+
# @example Is the queue subscribable?
|
50
|
+
# handler.subscribable?
|
51
|
+
def subscribable?
|
52
|
+
@subscribable ? @subscribable.call : true
|
53
|
+
end
|
54
|
+
|
55
|
+
# Subscribe to the queue. Will do so if the handler is not already
|
56
|
+
# subscribed.
|
57
|
+
#
|
58
|
+
# @example Subscribe to the queue.
|
59
|
+
# handler.subscribe
|
60
|
+
def subscribe
|
61
|
+
unless running?
|
62
|
+
Minion.info("Subscribing to #{queue_name}")
|
63
|
+
chan = AMQP::Channel.new
|
64
|
+
chan.prefetch(1)
|
65
|
+
queue = chan.queue(queue_name, :durable => true, :auto_delete => false)
|
66
|
+
if batch_size && batch_size > 1
|
67
|
+
process_batch(queue)
|
68
|
+
else
|
69
|
+
process_single_message(queue)
|
70
|
+
end
|
71
|
+
@running = true
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
# Process a multiple messages from a queue as a batch
|
77
|
+
#
|
78
|
+
# @example Subscribe to the queue.
|
79
|
+
# handler.process_batch(queue)
|
80
|
+
#
|
81
|
+
# @param [ AMQP::Queue ]
|
82
|
+
#
|
83
|
+
def process_batch(queue)
|
84
|
+
# Our batch message will have an array for it's content
|
85
|
+
msg = Message.new
|
86
|
+
queue.subscribe(:ack => true) do |h, m|
|
87
|
+
return if AMQP.closing?
|
88
|
+
Minion.info("Received: #{queue_name}:#{m}, #{h}")
|
89
|
+
args = decode(m)
|
90
|
+
|
91
|
+
# All messages in the batch get the callbacks from
|
92
|
+
# the first message. This is why when using chained
|
93
|
+
# callbacks on batches, you always have to use the
|
94
|
+
# same combo of callback-queues!
|
95
|
+
msg.callbacks = args['callbacks']
|
96
|
+
msg.batch << args['content']
|
97
|
+
h.ack # acks are useless in batch-mode.
|
98
|
+
# You'll have to make sure you requeue manually
|
99
|
+
if (msg.batch.size == batch_size) || process_anyway?
|
100
|
+
msg.content = block.call(msg)
|
101
|
+
msg.callback
|
102
|
+
msg.batch.clear
|
103
|
+
end
|
104
|
+
Minion.execute_handlers
|
105
|
+
end
|
106
|
+
rescue Object => e
|
107
|
+
Minion.alert(e)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Process a single message from a queue
|
111
|
+
#
|
112
|
+
# @example Subscribe to the queue.
|
113
|
+
# handler.process_single_message(queue)
|
114
|
+
#
|
115
|
+
# @param [ AMQP::Queue ]
|
116
|
+
#
|
117
|
+
def process_single_message(queue)
|
118
|
+
queue.subscribe(:ack => true) do |h, m|
|
119
|
+
return if AMQP.closing?
|
120
|
+
Minion.info("Received: #{queue_name}:#{m}, #{h}")
|
121
|
+
msg = Message.new(m, h)
|
122
|
+
msg.content = block.call(msg)
|
123
|
+
h.ack
|
124
|
+
msg.callback
|
125
|
+
Minion.execute_handlers
|
126
|
+
end
|
127
|
+
rescue Object => e
|
128
|
+
Minion.alert(e)
|
129
|
+
end
|
130
|
+
|
131
|
+
# Get a string respresentation of the handler.
|
132
|
+
#
|
133
|
+
# @example Print out the string.
|
134
|
+
# handler.to_s
|
135
|
+
#
|
136
|
+
# @return [ String ] The handler as a string.
|
137
|
+
def to_s
|
138
|
+
"<handler queue_name=#{@queue_name} on=#{@on}>"
|
139
|
+
end
|
140
|
+
|
141
|
+
# Unsubscribe from the queue.
|
142
|
+
#
|
143
|
+
# @example Unsubscribe from the queue.
|
144
|
+
# handler.unsubscribe
|
145
|
+
def unsubscribe
|
146
|
+
Minion.info("Unsubscribing to #{queue_name}")
|
147
|
+
AMQP::Channel.new.queue(queue_name, :durable => true, :auto_delete => false).unsubscribe
|
148
|
+
@running = false
|
149
|
+
end
|
150
|
+
|
151
|
+
private
|
152
|
+
|
153
|
+
def decode(json)
|
154
|
+
defined?(ActiveSupport::JSON) ?
|
155
|
+
ActiveSupport::JSON.decode(json) : JSON.load(json)
|
156
|
+
end
|
157
|
+
|
158
|
+
# Determine if we should process a batch even if
|
159
|
+
# we haven't reached the batch_size
|
160
|
+
#
|
161
|
+
# @return [ Boolean ] if we should go ahead and process the batch
|
162
|
+
def process_anyway?
|
163
|
+
return false if Minion.message_count(queue_name) != 0 # there's work to be done!
|
164
|
+
case wait
|
165
|
+
when true then false # Wait indefinitely
|
166
|
+
when false then true # Don't wait at all
|
167
|
+
when Numeric
|
168
|
+
(0..wait).each do |i|
|
169
|
+
return false if Minion.message_count(queue_name) != 0
|
170
|
+
sleep 1
|
171
|
+
end
|
172
|
+
# Wait this many, then if the queue is still empty, go ahead
|
173
|
+
Minion.message_count(queue_name) == 0
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require File.join 'active_support', 'core_ext', 'module', 'delegation'
|
2
|
+
|
3
|
+
module Minion
|
4
|
+
class Message
|
5
|
+
attr_accessor :content, :callbacks, :headers, :batch
|
6
|
+
delegate :clear, :map, :each, :size, :count, :[], :each_with_index, :cycle, :shuffle, :to => :content
|
7
|
+
|
8
|
+
def initialize json="{}", header=nil
|
9
|
+
data = decode(json)
|
10
|
+
@headers = [header]
|
11
|
+
@callbacks = data['callbacks']
|
12
|
+
@content = data['content']
|
13
|
+
@batch = data['batch'] || []
|
14
|
+
end
|
15
|
+
|
16
|
+
def << data
|
17
|
+
@content << data
|
18
|
+
end
|
19
|
+
|
20
|
+
# Enqueue a job for the next callback in the chain
|
21
|
+
#
|
22
|
+
# @return void
|
23
|
+
def callback
|
24
|
+
headers.clear
|
25
|
+
if callbacks and not callbacks.empty?
|
26
|
+
queue_name = callbacks.shift
|
27
|
+
Minion.enqueue(queue_name, as_json)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# Decode the json string into a hash.
|
34
|
+
#
|
35
|
+
# @example Decode the json.
|
36
|
+
# decode("{ field : "value" }")
|
37
|
+
#
|
38
|
+
# @param [ String ] json The json string.
|
39
|
+
#
|
40
|
+
# @return [ Hash ] The json as a hash.
|
41
|
+
def decode(json)
|
42
|
+
defined?(ActiveSupport::JSON) ?
|
43
|
+
ActiveSupport::JSON.decode(json) : JSON.load(json)
|
44
|
+
end
|
45
|
+
|
46
|
+
def as_json
|
47
|
+
{ 'callbacks' => callbacks,
|
48
|
+
'headers' => headers,
|
49
|
+
'content' => content
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_json
|
54
|
+
JSON.dump(as_json || {}).force_encoding("ISO-8859-1")
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Minion::Handler do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
Minion.logger {}
|
7
|
+
end
|
8
|
+
|
9
|
+
let(:channel) do
|
10
|
+
stub.quacks_like(AMQP::Channel.allocate)
|
11
|
+
end
|
12
|
+
|
13
|
+
let(:queue) do
|
14
|
+
stub.quacks_like(AMQP::Queue.allocate)
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "#execute" do
|
18
|
+
|
19
|
+
before do
|
20
|
+
channel.expects(:prefetch).at_most_once
|
21
|
+
AMQP::Channel.stubs(:new).returns(channel)
|
22
|
+
end
|
23
|
+
|
24
|
+
context "when the queue is subscribable" do
|
25
|
+
|
26
|
+
let(:handler) do
|
27
|
+
described_class.new("minion.test", lambda{ true })
|
28
|
+
end
|
29
|
+
|
30
|
+
context "when the handler is not already running" do
|
31
|
+
|
32
|
+
before do
|
33
|
+
channel.expects(:queue).with(
|
34
|
+
"minion.test", :durable => true, :auto_delete => false
|
35
|
+
).returns(queue)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "subscribes to the queue" do
|
39
|
+
queue.expects(:subscribe)
|
40
|
+
handler.execute
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context "when the handler is running" do
|
45
|
+
|
46
|
+
before do
|
47
|
+
handler.instance_variable_set(:@running, true)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "does not subscribe again" do
|
51
|
+
channel.expects(:queue).never
|
52
|
+
handler.execute
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "when the queue is not subscribable" do
|
58
|
+
|
59
|
+
let(:handler) do
|
60
|
+
described_class.new("minion.test", lambda{ true }, :when => lambda{ false })
|
61
|
+
end
|
62
|
+
|
63
|
+
before do
|
64
|
+
channel.expects(:queue).with(
|
65
|
+
"minion.test", :durable => true, :auto_delete => false
|
66
|
+
).returns(queue)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "unsubscribes from the queue" do
|
70
|
+
queue.expects(:unsubscribe)
|
71
|
+
handler.execute
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context "when wait parameter is specified" do
|
76
|
+
it "should raise an error" do
|
77
|
+
expect do
|
78
|
+
described_class.new("minion.test", lambda{ |batch| {"content" => true} }, :wait => true)
|
79
|
+
end.to raise_error(ArgumentError)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
context "when a batch size is specified" do
|
84
|
+
let(:block) do
|
85
|
+
lambda{ |batch| {"content" => true} }
|
86
|
+
end
|
87
|
+
|
88
|
+
let(:handler) do
|
89
|
+
described_class.new("minion.test", block, :batch_size => 10)
|
90
|
+
end
|
91
|
+
|
92
|
+
let(:header) do
|
93
|
+
stub.quacks_like(AMQP::Header.allocate)
|
94
|
+
end
|
95
|
+
|
96
|
+
let(:serialized) do
|
97
|
+
'{"content":{"field":"value"}}'
|
98
|
+
end
|
99
|
+
|
100
|
+
let(:batch) do
|
101
|
+
[header, serialized] * 10
|
102
|
+
end
|
103
|
+
|
104
|
+
before do
|
105
|
+
queue.expects(:subscribe).multiple_yields(batch)
|
106
|
+
channel.expects(:queue).with(
|
107
|
+
"minion.test", :durable => true, :auto_delete => false
|
108
|
+
).returns(queue)
|
109
|
+
Minion.expects(:execute_handlers)
|
110
|
+
header.expects(:ack)
|
111
|
+
end
|
112
|
+
|
113
|
+
it "calls once for 10 messages" do
|
114
|
+
block.expects(:call).once
|
115
|
+
handler.execute
|
116
|
+
end
|
117
|
+
|
118
|
+
context "when wait parameter is specified" do
|
119
|
+
let(:handler) do
|
120
|
+
described_class.new("minion.test", block, :batch_size => 10, :wait => true)
|
121
|
+
end
|
122
|
+
|
123
|
+
it "doesn't call for 9 messages" do
|
124
|
+
block.expects(:call).never
|
125
|
+
handler.execute
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
context "when wait paramter is numeric" do
|
130
|
+
let(:batch) do
|
131
|
+
[header, serialized] * 9
|
132
|
+
end
|
133
|
+
|
134
|
+
let(:handler) do
|
135
|
+
described_class.new("minion.test", block, :batch_size => 10, :wait => 2)
|
136
|
+
end
|
137
|
+
|
138
|
+
it "calls once for 9 messages after waiting a bit" do
|
139
|
+
block.expects(:call).once
|
140
|
+
handler.execute
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
end
|
147
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Minion::Message do
|
4
|
+
let(:header) do
|
5
|
+
stub.quacks_like(AMQP::Header.allocate)
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:serialized) do
|
9
|
+
'{"content":{"field":"value"}, "callbacks":["minion.second", "minion.third"]}'
|
10
|
+
end
|
11
|
+
|
12
|
+
subject do
|
13
|
+
Minion::Message.new(serialized, header)
|
14
|
+
end
|
15
|
+
|
16
|
+
its(:content){ should eql({"field"=>"value"}) }
|
17
|
+
its(:callbacks){ should eql ["minion.second", "minion.third"] }
|
18
|
+
its(:headers){ should eql [header]}
|
19
|
+
|
20
|
+
context "when callback is executed" do
|
21
|
+
|
22
|
+
let(:data) do
|
23
|
+
{'callbacks' => ['minion.third'], 'headers' => [], 'content' => {'field' => 'value'}}
|
24
|
+
end
|
25
|
+
|
26
|
+
before do
|
27
|
+
subject.headers.clear
|
28
|
+
subject.headers.expects(:clear)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should enqueue the next job" do
|
32
|
+
Minion.expects(:enqueue).with('minion.second', data)
|
33
|
+
subject.callback
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
data/spec/minion_spec.rb
ADDED
@@ -0,0 +1,238 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "spec_helper"
|
3
|
+
|
4
|
+
describe Minion do
|
5
|
+
|
6
|
+
let(:bunny) do
|
7
|
+
Bunny.new(Minion.config).tap do |bunny|
|
8
|
+
bunny.start
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
before(:all) do
|
13
|
+
Minion.logger {}
|
14
|
+
end
|
15
|
+
|
16
|
+
describe ".alert" do
|
17
|
+
|
18
|
+
context "when an error handler is provided" do
|
19
|
+
|
20
|
+
let(:error) do
|
21
|
+
RuntimeError.new("testing")
|
22
|
+
end
|
23
|
+
|
24
|
+
before do
|
25
|
+
Minion.error do |error|
|
26
|
+
error.message
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
after do
|
31
|
+
Minion.error
|
32
|
+
end
|
33
|
+
|
34
|
+
it "delegates to the handler" do
|
35
|
+
Minion.alert(error).should == "testing"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context "when an error handler is not provided" do
|
40
|
+
|
41
|
+
let(:error) do
|
42
|
+
RuntimeError.new("testing")
|
43
|
+
end
|
44
|
+
|
45
|
+
it "raises the error" do
|
46
|
+
expect { Minion.alert(error) }.to raise_error(RuntimeError)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe ".enqueue" do
|
52
|
+
|
53
|
+
let(:queue) do
|
54
|
+
bunny.queue("minion.test", :durable => true, :auto_delete => false)
|
55
|
+
end
|
56
|
+
|
57
|
+
before do
|
58
|
+
queue.purge
|
59
|
+
end
|
60
|
+
|
61
|
+
context "when provided a string" do
|
62
|
+
|
63
|
+
context "when no data is provided" do
|
64
|
+
|
65
|
+
before do
|
66
|
+
Minion.enqueue("minion.test")
|
67
|
+
end
|
68
|
+
|
69
|
+
let(:message) do
|
70
|
+
JSON.parse(queue.pop[:payload])
|
71
|
+
end
|
72
|
+
|
73
|
+
it "adds empty json to the queue" do
|
74
|
+
message.should == {"content" => {}}
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context "when nil data is provided" do
|
79
|
+
|
80
|
+
before do
|
81
|
+
Minion.enqueue("minion.test", nil)
|
82
|
+
end
|
83
|
+
|
84
|
+
let(:message) do
|
85
|
+
JSON.parse(queue.pop[:payload])
|
86
|
+
end
|
87
|
+
|
88
|
+
it "adds empty json to the queue" do
|
89
|
+
message.should == {"content" => nil}
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
context "when data is provided" do
|
94
|
+
|
95
|
+
context "when the data has no special characters" do
|
96
|
+
|
97
|
+
let(:data) do
|
98
|
+
{"content"=>{"field"=>"value"}}
|
99
|
+
end
|
100
|
+
|
101
|
+
before do
|
102
|
+
Minion.enqueue("minion.test", data)
|
103
|
+
end
|
104
|
+
|
105
|
+
let(:message) do
|
106
|
+
JSON.parse(queue.pop[:payload])
|
107
|
+
end
|
108
|
+
|
109
|
+
it "adds the json to the queue" do
|
110
|
+
message.should == data
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
context "when the data contains special characters" do
|
115
|
+
|
116
|
+
let(:data) do
|
117
|
+
{"content"=>{"field"=>"öüäßÖÜÄ"}}
|
118
|
+
end
|
119
|
+
|
120
|
+
before do
|
121
|
+
Minion.enqueue("minion.test", data)
|
122
|
+
end
|
123
|
+
|
124
|
+
let(:message) do
|
125
|
+
JSON.parse(queue.pop[:payload])
|
126
|
+
end
|
127
|
+
|
128
|
+
it "adds the json to the queue" do
|
129
|
+
message.should == data
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
context "when provided a nil queue" do
|
136
|
+
|
137
|
+
it "raises an error" do
|
138
|
+
expect { Minion.enqueue(nil, {}) }.to raise_error(RuntimeError)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
context "when passed an array" do
|
143
|
+
|
144
|
+
let(:first) do
|
145
|
+
bunny.queue("minion.first", :durable => true, :auto_delete => false)
|
146
|
+
end
|
147
|
+
|
148
|
+
before do
|
149
|
+
first.purge
|
150
|
+
end
|
151
|
+
|
152
|
+
context "when the array is empty" do
|
153
|
+
|
154
|
+
it "raises an error" do
|
155
|
+
expect { Minion.enqueue([], {}) }.to raise_error(RuntimeError)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
context "when the array has queue names" do
|
160
|
+
|
161
|
+
let(:data) do
|
162
|
+
{"field"=>"value"}
|
163
|
+
end
|
164
|
+
|
165
|
+
let(:serialized) do
|
166
|
+
{"content"=>{"field"=>"value"}, "callbacks"=>["minion.second", "minion.third"]}
|
167
|
+
end
|
168
|
+
|
169
|
+
before do
|
170
|
+
Minion.enqueue([ "minion.first", "minion.second", "minion.third" ], data)
|
171
|
+
end
|
172
|
+
|
173
|
+
let(:message) do
|
174
|
+
JSON.parse(first.pop[:payload])
|
175
|
+
end
|
176
|
+
|
177
|
+
it "adds the serialized data to the first queue" do
|
178
|
+
message.should == serialized
|
179
|
+
end
|
180
|
+
|
181
|
+
context "when the data has already been serialized" do
|
182
|
+
before do
|
183
|
+
Minion.enqueue([ "minion.first", "minion.second", "minion.third" ], serialized)
|
184
|
+
end
|
185
|
+
|
186
|
+
let(:message) do
|
187
|
+
JSON.parse(first.pop[:payload])
|
188
|
+
end
|
189
|
+
|
190
|
+
it "adds has the same serialized data in the first queue" do
|
191
|
+
message.should == serialized
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
|
199
|
+
describe ".error_handling" do
|
200
|
+
|
201
|
+
context "when nothing has been defined" do
|
202
|
+
|
203
|
+
it "returns nil" do
|
204
|
+
Minion.send(:error_handling).should be_nil
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
describe ".error" do
|
210
|
+
|
211
|
+
let(:block) do
|
212
|
+
lambda{ "testing" }
|
213
|
+
end
|
214
|
+
|
215
|
+
before do
|
216
|
+
Minion.error(&block)
|
217
|
+
end
|
218
|
+
|
219
|
+
it "sets the error handling to the provided block" do
|
220
|
+
Minion.send(:error_handling).should == block
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
describe ".info" do
|
225
|
+
|
226
|
+
let(:block) do
|
227
|
+
lambda{ |message| message }
|
228
|
+
end
|
229
|
+
|
230
|
+
before do
|
231
|
+
Minion.logger(&block)
|
232
|
+
end
|
233
|
+
|
234
|
+
it "delegates the logging to the provided block" do
|
235
|
+
Minion.info("testing").should == "testing"
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|