cwyckoff-rosetta_queue 0.3.0 → 0.3.3

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.
Files changed (51) hide show
  1. data/History.txt +26 -3
  2. data/README.rdoc +4 -149
  3. data/Rakefile +3 -0
  4. data/VERSION.yml +2 -2
  5. data/cucumber.yml +1 -1
  6. data/examples/sample_amqp_consumer.rb +45 -0
  7. data/examples/sample_amqp_fanout_consumer.rb +52 -0
  8. data/examples/sample_amqp_fanout_producer.rb +18 -0
  9. data/examples/sample_amqp_producer.rb +16 -0
  10. data/features/filtering.feature +31 -0
  11. data/features/messaging.feature +47 -0
  12. data/features/step_definitions/common_messaging_steps.rb +82 -0
  13. data/features/step_definitions/filtering_steps.rb +17 -0
  14. data/features/step_definitions/point_to_point_steps.rb +22 -0
  15. data/features/step_definitions/publish_subscribe_steps.rb +25 -0
  16. data/features/support/env.rb +25 -0
  17. data/features/support/sample_consumers.rb +29 -0
  18. data/lib/rosetta_queue.rb +3 -2
  19. data/lib/rosetta_queue/adapter.rb +1 -1
  20. data/lib/rosetta_queue/adapters/amqp.rb +48 -0
  21. data/lib/rosetta_queue/adapters/amqp_evented.rb +132 -0
  22. data/lib/rosetta_queue/adapters/amqp_synch.rb +48 -69
  23. data/lib/rosetta_queue/adapters/beanstalk.rb +56 -0
  24. data/lib/rosetta_queue/adapters/stomp.rb +16 -1
  25. data/lib/rosetta_queue/consumer_managers/base.rb +3 -1
  26. data/lib/rosetta_queue/consumer_managers/threaded.rb +23 -4
  27. data/lib/rosetta_queue/core_ext/string.rb +22 -0
  28. data/lib/rosetta_queue/core_ext/time.rb +20 -0
  29. data/lib/rosetta_queue/filters.rb +1 -1
  30. data/lib/rosetta_queue/logger.rb +1 -1
  31. data/lib/rosetta_queue/message_handler.rb +6 -0
  32. data/spec/rosetta_queue/adapter_spec.rb +101 -0
  33. data/spec/rosetta_queue/adapters/amqp_synchronous_spec.rb +278 -0
  34. data/spec/rosetta_queue/adapters/beanstalk_spec.rb +47 -0
  35. data/spec/rosetta_queue/adapters/fake_spec.rb +72 -0
  36. data/spec/rosetta_queue/adapters/null_spec.rb +31 -0
  37. data/spec/rosetta_queue/adapters/shared_adapter_behavior.rb +38 -0
  38. data/spec/rosetta_queue/adapters/shared_fanout_behavior.rb +20 -0
  39. data/spec/rosetta_queue/adapters/stomp_spec.rb +126 -0
  40. data/spec/rosetta_queue/consumer_managers/evented_spec.rb +56 -0
  41. data/spec/rosetta_queue/consumer_managers/shared_manager_behavior.rb +26 -0
  42. data/spec/rosetta_queue/consumer_managers/threaded_spec.rb +51 -0
  43. data/spec/rosetta_queue/consumer_spec.rb +99 -0
  44. data/spec/rosetta_queue/core_ext/string_spec.rb +15 -0
  45. data/spec/rosetta_queue/destinations_spec.rb +34 -0
  46. data/spec/rosetta_queue/filters_spec.rb +44 -0
  47. data/spec/rosetta_queue/producer_spec.rb +66 -0
  48. data/spec/rosetta_queue/shared_messaging_behavior.rb +21 -0
  49. data/spec/spec.opts +4 -0
  50. data/spec/spec_helper.rb +47 -0
  51. metadata +68 -19
@@ -0,0 +1,47 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+ require File.dirname(__FILE__) + '/shared_adapter_behavior'
3
+ require 'rosetta_queue/adapters/beanstalk'
4
+
5
+ module RosettaQueue
6
+ module Gateway
7
+
8
+ describe BeanstalkAdapter do
9
+
10
+ before(:each) do
11
+ @msg = "Hello World!"
12
+ @handler = mock('handler', :on_message => "", :destination => :foo)
13
+ @msg_obj = mock("message", :body => @msg, :delete => true)
14
+ @conn = mock("Beanstalk::Pool", :put => true, :reserve => @msg_obj)
15
+ ::Beanstalk::Pool.stub!(:new).and_return(@conn)
16
+ @adapter = BeanstalkAdapter.new({:host => "host", :port => "port"})
17
+ @adapter.stub!(:running).and_yield
18
+ end
19
+
20
+ it_should_behave_like "an adapter"
21
+
22
+ describe "#receive_once" do
23
+ def do_receiving_once
24
+ @adapter.receive_once
25
+ end
26
+
27
+ it "should delete messages once received" do
28
+ when_receiving_once {
29
+ @msg_obj.should_receive(:delete)
30
+ }
31
+ end
32
+ end
33
+
34
+ describe "#receive" do
35
+ def do_receiving
36
+ @adapter.receive
37
+ end
38
+
39
+ it "should delete message during receive" do
40
+ when_receiving {
41
+ @msg_obj.should_receive(:delete)
42
+ }
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,72 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+ require 'rosetta_queue/adapters/fake'
3
+
4
+ module RosettaQueue
5
+ module Gateway
6
+
7
+ describe FakeAdapter do
8
+
9
+ describe "#queues" do
10
+ it "should return all the queues that messages were delivered to" do
11
+ # given
12
+ adapter = FakeAdapter.new
13
+ adapter.send_message('queue 1', 'message 1', 'headers 1')
14
+ adapter.send_message('queue 2', 'message 2', 'headers 2')
15
+ # then
16
+ adapter.queues.should == ['queue 1', 'queue 2']
17
+ end
18
+ end
19
+
20
+ describe "#messages_sent_to" do
21
+
22
+ it "should return the message bodies that were delivered to the specified queue" do
23
+ # given
24
+ adapter = FakeAdapter.new
25
+ adapter.send_message('queue 1', 'message 1', 'headers 1')
26
+ adapter.send_message('queue 2', 'message 2', 'headers 2')
27
+ adapter.send_message('queue 1', 'message 3', 'headers 3')
28
+ # when
29
+ results = adapter.messages_sent_to('queue 1')
30
+ # then
31
+ results.should == ['message 1', 'message 3']
32
+ end
33
+
34
+ it "should return the bodies of the messages after they have been filtered" do
35
+ # given
36
+ adapter = FakeAdapter.new
37
+ # expect
38
+ ::RosettaQueue::Filters.should_receive(:process_receiving).with('message').and_return("Filtered Message")
39
+ # when
40
+ adapter.send_message('queue', 'message', 'headers')
41
+ # then
42
+ adapter.messages_sent_to('queue').should == ['Filtered Message']
43
+ end
44
+
45
+ it "should return all the message's bodies when nil is passed in at the queue" do
46
+ # given
47
+ adapter = FakeAdapter.new
48
+ adapter.send_message('queue 1', 'message 1', 'headers 1')
49
+ adapter.send_message('queue 2', 'message 2', 'headers 2')
50
+ # when
51
+ results = adapter.messages_sent_to(nil)
52
+ # then
53
+ results.should == ['message 1', 'message 2']
54
+ end
55
+
56
+ it "should return an empty array when no messages have been delivered" do
57
+ # given
58
+ adapter = FakeAdapter.new
59
+ adapter.send_message('queue 1', 'message 1', 'headers 1')
60
+ # when
61
+ results = adapter.messages_sent_to('queue 2')
62
+ # then
63
+ results.should == []
64
+ end
65
+
66
+ end
67
+
68
+ end
69
+
70
+ end
71
+
72
+ end
@@ -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"}, :on_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}, :on_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