rosetta_queue 0.4.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/History.txt +38 -0
- data/MIT-LICENSE.txt +19 -0
- data/README.rdoc +11 -0
- data/Rakefile +39 -0
- data/VERSION.yml +4 -0
- data/cucumber.yml +1 -0
- data/examples/sample_amqp_consumer.rb +45 -0
- data/examples/sample_amqp_fanout_consumer.rb +52 -0
- data/examples/sample_amqp_fanout_producer.rb +18 -0
- data/examples/sample_amqp_producer.rb +16 -0
- data/features/filtering.feature +31 -0
- data/features/messaging.feature +48 -0
- data/features/step_definitions/common_messaging_steps.rb +82 -0
- data/features/step_definitions/filtering_steps.rb +17 -0
- data/features/step_definitions/point_to_point_steps.rb +22 -0
- data/features/step_definitions/publish_subscribe_steps.rb +25 -0
- data/features/support/env.rb +25 -0
- data/features/support/sample_consumers.rb +29 -0
- data/lib/rosetta_queue.rb +23 -0
- data/lib/rosetta_queue/adapter.rb +39 -0
- data/lib/rosetta_queue/adapters/amqp.rb +48 -0
- data/lib/rosetta_queue/adapters/amqp_evented.rb +132 -0
- data/lib/rosetta_queue/adapters/amqp_synch.rb +123 -0
- data/lib/rosetta_queue/adapters/base.rb +27 -0
- data/lib/rosetta_queue/adapters/beanstalk.rb +56 -0
- data/lib/rosetta_queue/adapters/fake.rb +26 -0
- data/lib/rosetta_queue/adapters/null.rb +57 -0
- data/lib/rosetta_queue/adapters/stomp.rb +88 -0
- data/lib/rosetta_queue/base.rb +15 -0
- data/lib/rosetta_queue/consumer.rb +30 -0
- data/lib/rosetta_queue/consumer_managers/base.rb +24 -0
- data/lib/rosetta_queue/consumer_managers/evented.rb +43 -0
- data/lib/rosetta_queue/consumer_managers/threaded.rb +94 -0
- data/lib/rosetta_queue/core_ext/string.rb +22 -0
- data/lib/rosetta_queue/core_ext/time.rb +20 -0
- data/lib/rosetta_queue/destinations.rb +33 -0
- data/lib/rosetta_queue/exception_handler.rb +105 -0
- data/lib/rosetta_queue/exceptions.rb +10 -0
- data/lib/rosetta_queue/filters.rb +58 -0
- data/lib/rosetta_queue/logger.rb +27 -0
- data/lib/rosetta_queue/message_handler.rb +52 -0
- data/lib/rosetta_queue/producer.rb +21 -0
- data/lib/rosetta_queue/spec_helpers.rb +5 -0
- data/lib/rosetta_queue/spec_helpers/hash.rb +21 -0
- data/lib/rosetta_queue/spec_helpers/helpers.rb +47 -0
- data/lib/rosetta_queue/spec_helpers/publishing_matchers.rb +144 -0
- data/spec/rosetta_queue/adapter_spec.rb +101 -0
- data/spec/rosetta_queue/adapters/amqp_synchronous_spec.rb +277 -0
- data/spec/rosetta_queue/adapters/beanstalk_spec.rb +47 -0
- data/spec/rosetta_queue/adapters/fake_spec.rb +72 -0
- data/spec/rosetta_queue/adapters/null_spec.rb +31 -0
- data/spec/rosetta_queue/adapters/shared_adapter_behavior.rb +38 -0
- data/spec/rosetta_queue/adapters/shared_fanout_behavior.rb +20 -0
- data/spec/rosetta_queue/adapters/stomp_spec.rb +126 -0
- data/spec/rosetta_queue/consumer_managers/evented_spec.rb +56 -0
- data/spec/rosetta_queue/consumer_managers/shared_manager_behavior.rb +26 -0
- data/spec/rosetta_queue/consumer_managers/threaded_spec.rb +51 -0
- data/spec/rosetta_queue/consumer_spec.rb +99 -0
- data/spec/rosetta_queue/core_ext/string_spec.rb +15 -0
- data/spec/rosetta_queue/destinations_spec.rb +34 -0
- data/spec/rosetta_queue/exception_handler_spec.rb +106 -0
- data/spec/rosetta_queue/filters_spec.rb +57 -0
- data/spec/rosetta_queue/message_handler_spec.rb +47 -0
- data/spec/rosetta_queue/producer_spec.rb +77 -0
- data/spec/rosetta_queue/shared_messaging_behavior.rb +21 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +47 -0
- metadata +142 -0
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
2
|
+
require 'rosetta_queue/adapters/null'
|
3
|
+
|
4
|
+
module RosettaQueue
|
5
|
+
module Gateway
|
6
|
+
|
7
|
+
|
8
|
+
describe NullAdapter do
|
9
|
+
|
10
|
+
def null_adapter
|
11
|
+
NullAdapter.new({:user => 'user', :password => 'password', :host => 'host', :port => 'port'})
|
12
|
+
end
|
13
|
+
|
14
|
+
%w[disconnect receive receive_with send_message subscribe unsubscribe].each do |adapter_method|
|
15
|
+
it "should respond to ##{adapter_method}" do
|
16
|
+
null_adapter.should respond_to(adapter_method)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should raise an error when #receive is called" do
|
21
|
+
running { null_adapter.receive }.should raise_error
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should raise an error when #receive_with is called" do
|
25
|
+
running { null_adapter.receive_with('consumer') }.should raise_error
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module RosettaQueue
|
2
|
+
module Gateway
|
3
|
+
|
4
|
+
describe "an adapter", :shared => true do
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
::RosettaQueue::Destinations.stub!(:lookup).and_return("foo")
|
8
|
+
end
|
9
|
+
|
10
|
+
def do_publishing
|
11
|
+
@adapter.send_message('queue', 'message', 'options')
|
12
|
+
end
|
13
|
+
|
14
|
+
def do_receiving_with_handler
|
15
|
+
@adapter.receive_with(@handler)
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#receive_once" do
|
19
|
+
|
20
|
+
it "should return the message from the connection" do
|
21
|
+
@adapter.receive_once("foo", {:persistent => false}).should == @msg
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "#receive_with" do
|
27
|
+
|
28
|
+
it "should look up the destination defined on the class" do
|
29
|
+
when_receiving_with_handler {
|
30
|
+
Destinations.should_receive(:lookup).with(:foo).and_return("foo")
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module RosettaQueue
|
2
|
+
module Gateway
|
3
|
+
|
4
|
+
describe "a fanout exchange adapter", :shared => true do
|
5
|
+
|
6
|
+
# it "should discover fanout name for destation" do
|
7
|
+
# when_receiving_exchange {
|
8
|
+
# @channel.should_receive(:fanout).with(@exchange).and_return(@bound_queue)
|
9
|
+
# }
|
10
|
+
# end
|
11
|
+
|
12
|
+
it "should bind to fanout exchange" do
|
13
|
+
when_receiving_exchange {
|
14
|
+
@queue.should_receive(:bind).with(@exchange).and_return(@bound_queue)
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
2
|
+
require File.dirname(__FILE__) + '/shared_adapter_behavior'
|
3
|
+
require 'rosetta_queue/adapters/stomp'
|
4
|
+
|
5
|
+
module RosettaQueue
|
6
|
+
module Gateway
|
7
|
+
|
8
|
+
describe StompAdapter do
|
9
|
+
|
10
|
+
before(:each) do
|
11
|
+
@msg = "Hello World!"
|
12
|
+
@handler = mock('handler', :destination => :foo, :options_hash => {:persistent => false, :ack => "client"}, :handle_message => "")
|
13
|
+
@msg_obj = mock("message", :body => @msg, :headers => {"message-id" => 2})
|
14
|
+
@conn = mock("Stomp::Connection", :ack => true, :send => true, :subscribe => true, :receive => @msg_obj, :unsubscribe => true, :disconnect => true)
|
15
|
+
::Stomp::Connection.stub!(:open).and_return(@conn)
|
16
|
+
@adapter = StompAdapter.new({:user => "user", :password => "password", :host => "host", :port => "port"})
|
17
|
+
@adapter.stub!(:running).and_yield
|
18
|
+
end
|
19
|
+
|
20
|
+
it_should_behave_like "an adapter"
|
21
|
+
|
22
|
+
describe "#send_message" do
|
23
|
+
it "should delegate to the connection" do
|
24
|
+
# need this hack since the stomp client overrides #send
|
25
|
+
def @conn.send(*args)
|
26
|
+
@args = args
|
27
|
+
end
|
28
|
+
def @conn.sent_args ; @args end
|
29
|
+
|
30
|
+
after_publishing {
|
31
|
+
@conn.sent_args.should == ['queue', 'message', 'options']
|
32
|
+
}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "#receive_once" do
|
37
|
+
|
38
|
+
def do_receiving_once
|
39
|
+
@adapter.receive_once("/queue/foo", {:persistent => false})
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should subscribe to queue" do
|
43
|
+
when_receiving_once {
|
44
|
+
@conn.should_receive("subscribe").with("/queue/foo", {:persistent => false})
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should unsubscribe from queue" do
|
49
|
+
when_receiving_once {
|
50
|
+
@conn.should_receive("unsubscribe").with("/queue/foo")
|
51
|
+
}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "#receive_with" do
|
56
|
+
|
57
|
+
it "should subscribe to queue defined by the class with the options defined on the class" do
|
58
|
+
when_receiving_with_handler {
|
59
|
+
@conn.should_receive("subscribe").with('foo', :persistent => false, :ack => "client")
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should acknowledge client" do
|
64
|
+
when_receiving_with_handler {
|
65
|
+
@conn.should_receive(:ack)
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "no ack" do
|
70
|
+
|
71
|
+
before(:each) do
|
72
|
+
@handler = mock('handler', :destination => :foo, :options_hash => {:persistent => false}, :handle_message => "")
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should not acknowledge client" do
|
76
|
+
when_receiving_with_handler {
|
77
|
+
@conn.should_not_receive(:ack)
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "disconnect" do
|
85
|
+
|
86
|
+
def do_disconnecting
|
87
|
+
@adapter.disconnect(@handler)
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should unsubscribe connection" do
|
91
|
+
when_disconnecting {
|
92
|
+
@conn.should_receive("unsubscribe").with("foo")
|
93
|
+
}
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should disconnect connection" do
|
97
|
+
when_disconnecting {
|
98
|
+
@conn.should_receive("disconnect")
|
99
|
+
}
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
describe StompAdapterProxy do
|
105
|
+
|
106
|
+
before(:each) do
|
107
|
+
@adapter = mock("StompAdapter", :ack => nil)
|
108
|
+
@proxy = StompAdapterProxy.new(@adapter, "foo")
|
109
|
+
end
|
110
|
+
|
111
|
+
context "#ack" do
|
112
|
+
|
113
|
+
it "should delegate to AMQP queue object" do
|
114
|
+
# expect
|
115
|
+
@adapter.should_receive(:ack).with("foo")
|
116
|
+
|
117
|
+
# when
|
118
|
+
@proxy.ack
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
2
|
+
require File.dirname(__FILE__) + '/shared_manager_behavior'
|
3
|
+
|
4
|
+
module RosettaQueue
|
5
|
+
|
6
|
+
describe EventedManager do
|
7
|
+
|
8
|
+
before(:each) do
|
9
|
+
Stomp::Connection.stub!(:open).and_return(nil)
|
10
|
+
@consumer = mock("test_consumer_1", :receive => true,
|
11
|
+
:connection => mock("AmqpAdapter", :subscribe => true, :unsubscribe => nil, :disconnect => nil),
|
12
|
+
:unsubscribe => true, :disconnect => true)
|
13
|
+
Consumer.stub!(:new).and_return(@consumer)
|
14
|
+
@manager = EventedManager.new
|
15
|
+
@manager.add(@message_handler = mock("message_handler", :destination => "/queue/foo", :option_hash => {}))
|
16
|
+
end
|
17
|
+
|
18
|
+
it_should_behave_like "a consumer manager"
|
19
|
+
|
20
|
+
describe "running" do
|
21
|
+
|
22
|
+
before(:each) do
|
23
|
+
EM.stub!(:run).and_yield
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "starting" do
|
27
|
+
|
28
|
+
def do_process
|
29
|
+
@manager.start
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should start consumers" do
|
33
|
+
during_process {
|
34
|
+
@manager.consumers.each_value { |cons| cons.should_receive(:receive) }
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "stopping" do
|
41
|
+
|
42
|
+
def do_process
|
43
|
+
@manager.stop
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should stop consumers" do
|
47
|
+
during_process {
|
48
|
+
EM.should_receive(:stop)
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module RosettaQueue
|
2
|
+
|
3
|
+
describe "a consumer manager", :shared => true do
|
4
|
+
|
5
|
+
def do_process
|
6
|
+
@manager.start
|
7
|
+
end
|
8
|
+
|
9
|
+
describe ".add" do
|
10
|
+
|
11
|
+
def do_process
|
12
|
+
@manager.add(@message_handler)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should load message_handler into consumer" do
|
16
|
+
during_process { Consumer.should_receive(:new).with(@message_handler) }
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should allow user to add new consumers" do
|
20
|
+
@manager.consumers.size.should == 1
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
2
|
+
require File.dirname(__FILE__) + '/shared_manager_behavior'
|
3
|
+
|
4
|
+
module RosettaQueue
|
5
|
+
describe ThreadedManager do
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
@mutex = mock("Mutex")
|
9
|
+
@mutex.stub!(:synchronize).and_yield
|
10
|
+
Mutex.stub!(:new).and_return(@mutex)
|
11
|
+
Stomp::Connection.stub!(:open).and_return(nil)
|
12
|
+
@consumer = mock("test_consumer_1", :receive => true, :connection => mock("StompAdapter", :subscribe => true, :unsubscribe => nil, :disconnect => nil),
|
13
|
+
:unsubscribe => true, :disconnect => true)
|
14
|
+
Consumer.stub!(:new).and_return(@consumer)
|
15
|
+
@manager = ThreadedManager.new
|
16
|
+
@manager.add(@message_handler = mock("message_handler", :destination => "/queue/foo", :option_hash => {}))
|
17
|
+
end
|
18
|
+
|
19
|
+
it_should_behave_like "a consumer manager"
|
20
|
+
|
21
|
+
describe "threading" do
|
22
|
+
|
23
|
+
before do
|
24
|
+
@manager.stub!(:join_threads)
|
25
|
+
@manager.stub!(:monitor_threads)
|
26
|
+
# we stub brackets because we check a Thread variable
|
27
|
+
@thread = mock(Thread, :kill => nil, :alive? => true, :[] => false)
|
28
|
+
Thread.stub!(:new).and_return(@thread)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should load subscriptions into threads on start" do
|
32
|
+
during_process {Thread.should_receive(:new).with(:"spec/mocks/mock", @consumer).and_return(@thread)}
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "shutting down" do
|
36
|
+
|
37
|
+
def do_process
|
38
|
+
@manager.start
|
39
|
+
@manager.stop
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should shut threaded subscriptions down on stop" do
|
43
|
+
during_process do
|
44
|
+
@consumer.should_receive(:disconnect)
|
45
|
+
@thread.should_receive(:kill)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
module RosettaQueue
|
4
|
+
describe Consumer do
|
5
|
+
|
6
|
+
class TestConsumer
|
7
|
+
include MessageHandler
|
8
|
+
|
9
|
+
subscribes_to :test_queue
|
10
|
+
options :persistent => false, :ack => "client"
|
11
|
+
|
12
|
+
def on_message(msg)
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
before(:each) do
|
18
|
+
@message = mock("message", "headers" => "foo", "body" => "message body")
|
19
|
+
@adapter = mock("adpater", :subscribe => true, :unsubscribe => true, :disconnect => true, :receive_with => TestConsumer.new,
|
20
|
+
:receive_once => @message.body, :ack => true)
|
21
|
+
Adapter.stub!(:instance).and_return(@adapter)
|
22
|
+
Destinations.stub!(:lookup).and_return("/queue/foo")
|
23
|
+
end
|
24
|
+
|
25
|
+
it_should_behave_like "a messaging gateway object"
|
26
|
+
|
27
|
+
attr_reader :adapter
|
28
|
+
def gateway
|
29
|
+
@gateway ||= Consumer.new(TestConsumer.new)
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "#receive" do
|
33
|
+
before(:each) do
|
34
|
+
@consumer = Consumer.new( @message_handler = TestConsumer.new)
|
35
|
+
end
|
36
|
+
|
37
|
+
def when_receiving
|
38
|
+
yield if block_given?
|
39
|
+
@consumer.receive
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should pass message handler onto the adpater with #receive" do
|
43
|
+
when_receiving {
|
44
|
+
@adapter.should_receive("receive_with").with(@message_handler)
|
45
|
+
}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
describe ".delete" do
|
51
|
+
|
52
|
+
before(:each) do
|
53
|
+
@adapter.stub!(:delete)
|
54
|
+
Destinations.stub!(:lookup).and_return("/queue/foo")
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should look up the destination" do
|
58
|
+
# expect
|
59
|
+
Destinations.should_receive(:lookup).with(:test_queue_passed_in).and_return("/queue/foo")
|
60
|
+
|
61
|
+
# when
|
62
|
+
Consumer.delete(:test_queue_passed_in)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should delegate to the adapter" do
|
66
|
+
# expect
|
67
|
+
@adapter.should_receive(:delete).with("/queue/foo", {})
|
68
|
+
|
69
|
+
# when
|
70
|
+
Consumer.delete(:test_queue_passed_in)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe ".receive" do
|
75
|
+
|
76
|
+
def when_receiving
|
77
|
+
yield if block_given?
|
78
|
+
Consumer.receive(:test_queue_passed_in, {:persistent => false})
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should look up the destination" do
|
82
|
+
when_receiving {
|
83
|
+
Destinations.should_receive(:lookup).with(:test_queue_passed_in).and_return("/queue/foo")
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should pass destination and options to adapter" do
|
88
|
+
when_receiving {
|
89
|
+
@adapter.should_receive(:receive_once).with("/queue/foo", {:persistent => false}).and_return(@message)
|
90
|
+
}
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should return the body of the message received" do
|
94
|
+
Consumer.receive(:test_queue, {:persistent => false}).should == @message.body
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
end
|