queueing_rabbit 0.1.3 → 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/lib/queueing_rabbit/client/amqp.rb +46 -40
- data/lib/queueing_rabbit/client/bunny.rb +20 -23
- data/lib/queueing_rabbit/extensions/direct_exchange.rb +26 -0
- data/lib/queueing_rabbit/extensions/new_relic.rb +19 -4
- data/lib/queueing_rabbit/extensions/retryable.rb +22 -0
- data/lib/queueing_rabbit/job.rb +56 -11
- data/lib/queueing_rabbit/jobs/abstract_job.rb +27 -0
- data/lib/queueing_rabbit/jobs/json_job.rb +19 -0
- data/lib/queueing_rabbit/version.rb +1 -1
- data/lib/queueing_rabbit/worker.rb +14 -4
- data/lib/queueing_rabbit.rb +21 -6
- data/queueing_rabbit.gemspec +6 -17
- data/spec/integration/asynchronous_publishing_and_consuming_spec.rb +36 -16
- data/spec/integration/asynchronous_publishing_and_consuming_with_retries_spec.rb +103 -0
- data/spec/integration/direct_exchange_asynchronous_publishing_and_consuming_spec.rb +93 -0
- data/spec/integration/jobs/print_line_job.rb +5 -11
- data/spec/integration/json_job_asynchronous_publishing_and_consuming_spec.rb +99 -0
- data/spec/integration/persistent_asynchronous_publishing_and_consuming_spec.rb +96 -0
- data/spec/integration/synchronous_publishing_and_asynchronous_consuming_spec.rb +13 -17
- data/spec/integration/synchronous_publishing_spec.rb +6 -2
- data/spec/spec_helper.rb +4 -1
- data/spec/unit/queueing_rabbit/client/amqp_spec.rb +80 -51
- data/spec/unit/queueing_rabbit/client/bunny_spec.rb +53 -8
- data/spec/unit/queueing_rabbit/extensions/direct_exchange_spec.rb +20 -0
- data/spec/unit/queueing_rabbit/extensions/new_relic_spec.rb +36 -0
- data/spec/unit/queueing_rabbit/extensions/retryable_spec.rb +36 -0
- data/spec/unit/queueing_rabbit/job_spec.rb +76 -4
- data/spec/unit/queueing_rabbit/jobs/abstract_job_spec.rb +73 -0
- data/spec/unit/queueing_rabbit/jobs/json_job_spec.rb +22 -0
- data/spec/unit/queueing_rabbit/worker_spec.rb +33 -23
- data/spec/unit/queueing_rabbit_spec.rb +55 -25
- metadata +111 -90
- data/spec/support/shared_examples.rb +0 -37
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe QueueingRabbit::JobExtensions::DirectExchange do
|
4
|
+
|
5
|
+
let(:test_job) {
|
6
|
+
Class.new(QueueingRabbit::AbstractJob) do
|
7
|
+
include QueueingRabbit::JobExtensions::DirectExchange
|
8
|
+
|
9
|
+
exchange 'test_job'
|
10
|
+
queue 'test_queue'
|
11
|
+
end
|
12
|
+
}
|
13
|
+
|
14
|
+
subject { test_job }
|
15
|
+
|
16
|
+
its(:exchange_name) { should == 'test_job' }
|
17
|
+
its(:exchange_options) { should include(:type => :direct) }
|
18
|
+
its(:binding_options) { should include(:routing_key => 'test_queue') }
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe QueueingRabbit::JobExtensions::NewRelic do
|
4
|
+
let(:installation) {
|
5
|
+
Proc.new do
|
6
|
+
job.class_eval { include QueueingRabbit::JobExtensions::NewRelic }
|
7
|
+
end
|
8
|
+
}
|
9
|
+
let(:new_relic) { Module.new }
|
10
|
+
|
11
|
+
before do
|
12
|
+
stub_const('NewRelic::Agent::Instrumentation::ControllerInstrumentation',
|
13
|
+
new_relic)
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'when is being installed into an instantiated job' do
|
17
|
+
let(:job) { Class.new(QueueingRabbit::AbstractJob) }
|
18
|
+
|
19
|
+
it 'registers a transaction tracer' do
|
20
|
+
job.should_receive(:add_transaction_tracer).
|
21
|
+
with(:perform, :category => :task)
|
22
|
+
installation.call
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Don't know how to get this test working on Ruby 1.8.7
|
27
|
+
context 'when is being installed into a class based job', :ruby => '1.8.7' do
|
28
|
+
let(:job) { Class.new { def self.perform; end } }
|
29
|
+
|
30
|
+
it 'registers a transaction tracer' do
|
31
|
+
job.class.should_receive(:add_transaction_tracer).
|
32
|
+
with(:perform, :category => :task)
|
33
|
+
installation.call
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe QueueingRabbit::JobExtensions::Retryable do
|
4
|
+
|
5
|
+
let(:test_job) {
|
6
|
+
Class.new(QueueingRabbit::AbstractJob) do
|
7
|
+
include QueueingRabbit::JobExtensions::Retryable
|
8
|
+
|
9
|
+
exchange 'test_job'
|
10
|
+
queue 'test_queue'
|
11
|
+
|
12
|
+
def perform
|
13
|
+
end
|
14
|
+
end
|
15
|
+
}
|
16
|
+
let(:payload) { mock }
|
17
|
+
let(:headers) { {'qr_retries' => 2} }
|
18
|
+
let(:metadata) { mock(:headers => headers)}
|
19
|
+
|
20
|
+
subject { test_job.new(payload, metadata) }
|
21
|
+
|
22
|
+
its(:retries) { should == 2 }
|
23
|
+
|
24
|
+
describe '#retry_upto' do
|
25
|
+
it 'returns nil and does not retry if attempts exceeded' do
|
26
|
+
subject.retry_upto(2).should_not be
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'retries with increased number of attempts' do
|
30
|
+
test_job.should_receive(:enqueue).
|
31
|
+
with(payload, :headers => {'qr_retries' => 3}).and_return(true)
|
32
|
+
subject.retry_upto(3).should be_true
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -1,15 +1,39 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe QueueingRabbit::AbstractJob do
|
4
|
-
|
5
|
-
|
4
|
+
let(:job_class) {
|
5
|
+
Class.new(QueueingRabbit::AbstractJob) do
|
6
|
+
queue 'test_queue', :durable => true
|
7
|
+
exchange 'test_exchange', :durable => false
|
8
|
+
bind :routing_key => 'test.*'
|
9
|
+
end
|
10
|
+
}
|
11
|
+
|
12
|
+
subject { job_class }
|
13
|
+
|
14
|
+
it { should respond_to(:exchange).with(1).argument }
|
15
|
+
it { should respond_to(:exchange).with(2).arguments }
|
16
|
+
it { should respond_to(:exchange_name) }
|
17
|
+
it { should respond_to(:exchange_options) }
|
18
|
+
it { should respond_to(:queue).with(1).argument }
|
6
19
|
it { should respond_to(:queue).with(2).arguments }
|
7
20
|
it { should respond_to(:queue_name) }
|
8
21
|
it { should respond_to(:queue_options) }
|
9
22
|
it { should respond_to(:channel_options) }
|
10
23
|
it { should respond_to(:channel).with(1).argument }
|
24
|
+
it { should respond_to(:enqueue).with(1).argument }
|
25
|
+
it { should respond_to(:enqueue).with(2).arguments }
|
26
|
+
it { should respond_to(:listening_options) }
|
27
|
+
it { should respond_to(:listen) }
|
28
|
+
it { should respond_to(:listen).with(1).argument }
|
29
|
+
it { should respond_to(:publishing_defaults) }
|
30
|
+
it { should respond_to(:publishing_defaults).with(1).argument }
|
11
31
|
|
12
|
-
its(:queue_name) { should == '
|
32
|
+
its(:queue_name) { should == 'test_queue' }
|
33
|
+
its(:queue_options) { should include(:durable => true) }
|
34
|
+
its(:exchange_options) { should include(:durable => false) }
|
35
|
+
its(:binding_options) { should include(:routing_key => 'test.*') }
|
36
|
+
its(:publishing_defaults) { should include(:routing_key => 'test_queue') }
|
13
37
|
|
14
38
|
describe ".queue_size" do
|
15
39
|
let(:size) { mock }
|
@@ -20,4 +44,52 @@ describe QueueingRabbit::AbstractJob do
|
|
20
44
|
|
21
45
|
its(:queue_size) { should == size }
|
22
46
|
end
|
23
|
-
|
47
|
+
|
48
|
+
describe '.enqueue' do
|
49
|
+
let(:payload) { mock }
|
50
|
+
let(:options) { {:persistent => true} }
|
51
|
+
let(:result_options) { options.merge(job_class.publishing_defaults) }
|
52
|
+
|
53
|
+
it 'enqueues a job of its own type with given argument' do
|
54
|
+
QueueingRabbit.should_receive(:enqueue).
|
55
|
+
with(subject, payload, result_options)
|
56
|
+
subject.enqueue(payload, options)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'instance methods' do
|
61
|
+
let(:payload) { mock }
|
62
|
+
let(:headers) { mock }
|
63
|
+
let(:metadata) { stub(:headers => headers) }
|
64
|
+
|
65
|
+
subject { job_class.new(payload, metadata) }
|
66
|
+
|
67
|
+
its(:payload) { should == payload }
|
68
|
+
its(:metadata) { should == metadata }
|
69
|
+
its(:headers) { should == headers }
|
70
|
+
|
71
|
+
it { should respond_to(:perform) }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe QueueingRabbit::JSONJob do
|
76
|
+
let(:json_job) { QueueingRabbit::JSONJob }
|
77
|
+
let(:payload) { JSON.dump(:foo => 'bar') }
|
78
|
+
let(:metadata) { mock }
|
79
|
+
|
80
|
+
subject { json_job.new(payload, metadata) }
|
81
|
+
|
82
|
+
its(:payload) { should include(:foo => 'bar') }
|
83
|
+
|
84
|
+
describe '.enqueue' do
|
85
|
+
let(:options) { {:persistent => true} }
|
86
|
+
let(:result_options) { options.merge(:routing_key => 'JSONJob') }
|
87
|
+
|
88
|
+
it 'dumps payload to JSON' do
|
89
|
+
QueueingRabbit.should_receive(:enqueue).
|
90
|
+
with(json_job, payload, result_options)
|
91
|
+
json_job.enqueue({:foo => 'bar'}, options)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe QueueingRabbit::AbstractJob do
|
4
|
+
let(:job_class) {
|
5
|
+
Class.new(QueueingRabbit::AbstractJob) do
|
6
|
+
queue 'test_queue', :durable => true
|
7
|
+
exchange 'test_exchange', :durable => false
|
8
|
+
bind :routing_key => 'test.*'
|
9
|
+
end
|
10
|
+
}
|
11
|
+
|
12
|
+
subject { job_class }
|
13
|
+
|
14
|
+
it { should respond_to(:exchange).with(1).argument }
|
15
|
+
it { should respond_to(:exchange).with(2).arguments }
|
16
|
+
it { should respond_to(:exchange_name) }
|
17
|
+
it { should respond_to(:exchange_options) }
|
18
|
+
it { should respond_to(:queue).with(1).argument }
|
19
|
+
it { should respond_to(:queue).with(2).arguments }
|
20
|
+
it { should respond_to(:queue_name) }
|
21
|
+
it { should respond_to(:queue_options) }
|
22
|
+
it { should respond_to(:channel_options) }
|
23
|
+
it { should respond_to(:channel).with(1).argument }
|
24
|
+
it { should respond_to(:enqueue).with(1).argument }
|
25
|
+
it { should respond_to(:enqueue).with(2).arguments }
|
26
|
+
it { should respond_to(:listening_options) }
|
27
|
+
it { should respond_to(:listen) }
|
28
|
+
it { should respond_to(:listen).with(1).argument }
|
29
|
+
it { should respond_to(:publishing_defaults) }
|
30
|
+
it { should respond_to(:publishing_defaults).with(1).argument }
|
31
|
+
|
32
|
+
its(:queue_name) { should == 'test_queue' }
|
33
|
+
its(:queue_options) { should include(:durable => true) }
|
34
|
+
its(:exchange_options) { should include(:durable => false) }
|
35
|
+
its(:binding_options) { should include(:routing_key => 'test.*') }
|
36
|
+
its(:publishing_defaults) { should include(:routing_key => 'test_queue') }
|
37
|
+
|
38
|
+
describe ".queue_size" do
|
39
|
+
let(:size) { mock }
|
40
|
+
|
41
|
+
before do
|
42
|
+
QueueingRabbit.should_receive(:queue_size).with(subject).and_return(size)
|
43
|
+
end
|
44
|
+
|
45
|
+
its(:queue_size) { should == size }
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '.enqueue' do
|
49
|
+
let(:payload) { mock }
|
50
|
+
let(:options) { {:persistent => true} }
|
51
|
+
let(:result_options) { options.merge(job_class.publishing_defaults) }
|
52
|
+
|
53
|
+
it 'enqueues a job of its own type with given argument' do
|
54
|
+
QueueingRabbit.should_receive(:enqueue).
|
55
|
+
with(subject, payload, result_options)
|
56
|
+
subject.enqueue(payload, options)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'instance methods' do
|
61
|
+
let(:payload) { mock }
|
62
|
+
let(:headers) { mock }
|
63
|
+
let(:metadata) { stub(:headers => headers) }
|
64
|
+
|
65
|
+
subject { job_class.new(payload, metadata) }
|
66
|
+
|
67
|
+
its(:payload) { should == payload }
|
68
|
+
its(:metadata) { should == metadata }
|
69
|
+
its(:headers) { should == headers }
|
70
|
+
|
71
|
+
it { should respond_to(:perform) }
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe QueueingRabbit::JSONJob do
|
4
|
+
let(:json_job) { QueueingRabbit::JSONJob }
|
5
|
+
let(:payload) { JSON.dump(:foo => 'bar') }
|
6
|
+
let(:metadata) { mock }
|
7
|
+
|
8
|
+
subject { json_job.new(payload, metadata) }
|
9
|
+
|
10
|
+
its(:payload) { should include(:foo => 'bar') }
|
11
|
+
|
12
|
+
describe '.enqueue' do
|
13
|
+
let(:options) { {:persistent => true} }
|
14
|
+
let(:result_options) { options.merge(:routing_key => 'JSONJob') }
|
15
|
+
|
16
|
+
it 'dumps payload to JSON' do
|
17
|
+
QueueingRabbit.should_receive(:enqueue).
|
18
|
+
with(json_job, payload, result_options)
|
19
|
+
json_job.enqueue({:foo => 'bar'}, options)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -4,17 +4,23 @@ describe QueueingRabbit::Worker do
|
|
4
4
|
include_context "StringIO logger"
|
5
5
|
|
6
6
|
subject { QueueingRabbit::Worker }
|
7
|
-
let
|
8
|
-
|
9
|
-
def self.perform(
|
7
|
+
let(:class_based_job) {
|
8
|
+
Class.new(QueueingRabbit::AbstractJob) do
|
9
|
+
def self.perform(payload, metadata); end
|
10
10
|
end
|
11
11
|
}
|
12
|
+
let(:instance_based_job) { Class.new(QueueingRabbit::AbstractJob) }
|
12
13
|
let(:creation) {
|
13
|
-
Proc.new
|
14
|
-
|
15
|
-
|
16
|
-
creation.call
|
14
|
+
Proc.new do
|
15
|
+
QueueingRabbit::Worker.new('QueueingRabbitClassJob', QueueingRabbitInstanceJob)
|
16
|
+
end
|
17
17
|
}
|
18
|
+
let(:worker) { creation.call }
|
19
|
+
|
20
|
+
before do
|
21
|
+
stub_const("QueueingRabbitClassJob", class_based_job)
|
22
|
+
stub_const("QueueingRabbitInstanceJob", instance_based_job)
|
23
|
+
end
|
18
24
|
|
19
25
|
after(:each) do
|
20
26
|
QueueingRabbit.client = QueueingRabbit::Client::Bunny
|
@@ -60,38 +66,42 @@ describe QueueingRabbit::Worker do
|
|
60
66
|
|
61
67
|
context 'instance methods' do
|
62
68
|
let(:connection) { mock }
|
63
|
-
let(:
|
64
|
-
let(:
|
69
|
+
let(:queue) { mock }
|
70
|
+
let(:payload) { mock }
|
71
|
+
let(:metadata) { mock }
|
65
72
|
|
66
73
|
subject { worker }
|
67
74
|
|
68
75
|
describe '#work' do
|
69
76
|
before do
|
70
77
|
QueueingRabbit.should_receive(:connection).and_return(connection)
|
71
|
-
|
72
|
-
|
78
|
+
[class_based_job, instance_based_job].each do |job|
|
79
|
+
QueueingRabbit.should_receive(:follow_job_requirements).
|
80
|
+
with(job).
|
81
|
+
and_yield(nil, nil, queue)
|
82
|
+
connection.should_receive(:listen_queue).
|
83
|
+
with(queue, job.listening_options).
|
84
|
+
and_yield(payload, metadata)
|
85
|
+
end
|
86
|
+
|
87
|
+
class_based_job.should_receive(:perform).with(payload, metadata)
|
88
|
+
instance_based_job.should_receive(:new).
|
89
|
+
with(payload, metadata).
|
90
|
+
and_return(mock(:perform => nil))
|
73
91
|
end
|
74
92
|
|
75
93
|
it 'listens to queues specified by jobs' do
|
76
94
|
subject.work
|
77
95
|
end
|
78
96
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
end
|
83
|
-
|
84
|
-
it 'writes to the log' do
|
85
|
-
subject.work
|
86
|
-
end
|
97
|
+
it 'writes to the log' do
|
98
|
+
subject.should_receive(:info).twice
|
99
|
+
subject.work
|
87
100
|
end
|
88
101
|
|
89
102
|
describe '#work!' do
|
90
|
-
before do
|
91
|
-
EM.should_receive(:run).and_yield
|
92
|
-
end
|
93
|
-
|
94
103
|
it 'runs #work and joins the eventmachine thread' do
|
104
|
+
EM.should_receive(:run).and_yield
|
95
105
|
subject.work!
|
96
106
|
end
|
97
107
|
end
|
@@ -5,17 +5,21 @@ describe QueueingRabbit do
|
|
5
5
|
|
6
6
|
let(:connection) { mock }
|
7
7
|
let(:queue_name) { mock }
|
8
|
-
let(:queue_options) { {
|
8
|
+
let(:queue_options) { {:durable => true} }
|
9
9
|
let(:channel) { mock }
|
10
|
-
let(:channel_options) { {
|
11
|
-
let(:
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
10
|
+
let(:channel_options) { {:prefetch => 1, :auto_recovery => true} }
|
11
|
+
let(:exchange_name) { mock }
|
12
|
+
let(:exchange_options) { {:type => :direct, :durable => true} }
|
13
|
+
let(:binding_options) { {:key => 'routing_key'} }
|
14
|
+
let(:job) {
|
15
|
+
stub(:queue_name => queue_name,
|
16
|
+
:queue_options => queue_options,
|
17
|
+
:channel_options => channel_options,
|
18
|
+
:exchange_name => exchange_name,
|
19
|
+
:exchange_options => exchange_options,
|
20
|
+
:binding_options => binding_options,
|
21
|
+
:bind_queue? => true)
|
22
|
+
}
|
19
23
|
|
20
24
|
before(:each) { subject.drop_connection }
|
21
25
|
|
@@ -33,34 +37,60 @@ describe QueueingRabbit do
|
|
33
37
|
|
34
38
|
its(:connect) { should == connection }
|
35
39
|
its(:connection) { should == connection }
|
40
|
+
its(:conn) { should == connection }
|
36
41
|
end
|
37
42
|
|
38
43
|
describe ".enqueue" do
|
39
|
-
let(:
|
44
|
+
let(:payload) { mock(:to_s => 'payload') }
|
45
|
+
let(:options) { mock }
|
46
|
+
let(:exchange) { mock }
|
47
|
+
let(:channel) { mock }
|
40
48
|
|
41
49
|
before do
|
42
50
|
subject.instance_variable_set(:@connection, connection)
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
queue_options)
|
48
|
-
connection.should_receive(:enqueue).with(channel,
|
49
|
-
queue_name, arguments)
|
51
|
+
subject.should_receive(:follow_job_requirements).
|
52
|
+
with(job).
|
53
|
+
and_yield(channel, exchange, nil)
|
54
|
+
connection.should_receive(:enqueue).with(exchange, payload, options)
|
50
55
|
channel.should_receive(:close)
|
51
56
|
end
|
52
57
|
|
53
58
|
it 'returns true when a message was enqueued successfully' do
|
54
|
-
subject.enqueue(job,
|
59
|
+
subject.enqueue(job, payload, options).should be_true
|
55
60
|
end
|
56
61
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
62
|
+
it 'keeps the record of enqueued job at info level' do
|
63
|
+
subject.should_receive(:info).and_return(nil)
|
64
|
+
subject.enqueue(job, payload, options).should be_true
|
65
|
+
end
|
66
|
+
end
|
61
67
|
|
62
|
-
|
63
|
-
|
68
|
+
describe '.follow_job_requirements' do
|
69
|
+
let(:channel) { mock }
|
70
|
+
let(:exchange) { mock }
|
71
|
+
let(:queue) { mock }
|
72
|
+
|
73
|
+
before do
|
74
|
+
subject.instance_variable_set(:@connection, connection)
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'opens a channel, defines an exchange and a queue, binds the queue to ' \
|
78
|
+
'the exchange, yields' do
|
79
|
+
connection.should_receive(:open_channel).with(job.channel_options).
|
80
|
+
and_yield(channel, nil)
|
81
|
+
connection.should_receive(:define_exchange).
|
82
|
+
with(channel, job.exchange_name, job.exchange_options).
|
83
|
+
and_yield(exchange)
|
84
|
+
connection.should_receive(:define_queue).
|
85
|
+
with(channel, job.queue_name, job.queue_options).
|
86
|
+
and_yield(queue)
|
87
|
+
connection.should_receive(:bind_queue).
|
88
|
+
with(queue, exchange, job.binding_options)
|
89
|
+
|
90
|
+
subject.follow_job_requirements(job) do |ch, ex, q|
|
91
|
+
ch.should == channel
|
92
|
+
ex.should == exchange
|
93
|
+
q.should == q
|
64
94
|
end
|
65
95
|
end
|
66
96
|
end
|