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.
Files changed (68) hide show
  1. data/History.txt +38 -0
  2. data/MIT-LICENSE.txt +19 -0
  3. data/README.rdoc +11 -0
  4. data/Rakefile +39 -0
  5. data/VERSION.yml +4 -0
  6. data/cucumber.yml +1 -0
  7. data/examples/sample_amqp_consumer.rb +45 -0
  8. data/examples/sample_amqp_fanout_consumer.rb +52 -0
  9. data/examples/sample_amqp_fanout_producer.rb +18 -0
  10. data/examples/sample_amqp_producer.rb +16 -0
  11. data/features/filtering.feature +31 -0
  12. data/features/messaging.feature +48 -0
  13. data/features/step_definitions/common_messaging_steps.rb +82 -0
  14. data/features/step_definitions/filtering_steps.rb +17 -0
  15. data/features/step_definitions/point_to_point_steps.rb +22 -0
  16. data/features/step_definitions/publish_subscribe_steps.rb +25 -0
  17. data/features/support/env.rb +25 -0
  18. data/features/support/sample_consumers.rb +29 -0
  19. data/lib/rosetta_queue.rb +23 -0
  20. data/lib/rosetta_queue/adapter.rb +39 -0
  21. data/lib/rosetta_queue/adapters/amqp.rb +48 -0
  22. data/lib/rosetta_queue/adapters/amqp_evented.rb +132 -0
  23. data/lib/rosetta_queue/adapters/amqp_synch.rb +123 -0
  24. data/lib/rosetta_queue/adapters/base.rb +27 -0
  25. data/lib/rosetta_queue/adapters/beanstalk.rb +56 -0
  26. data/lib/rosetta_queue/adapters/fake.rb +26 -0
  27. data/lib/rosetta_queue/adapters/null.rb +57 -0
  28. data/lib/rosetta_queue/adapters/stomp.rb +88 -0
  29. data/lib/rosetta_queue/base.rb +15 -0
  30. data/lib/rosetta_queue/consumer.rb +30 -0
  31. data/lib/rosetta_queue/consumer_managers/base.rb +24 -0
  32. data/lib/rosetta_queue/consumer_managers/evented.rb +43 -0
  33. data/lib/rosetta_queue/consumer_managers/threaded.rb +94 -0
  34. data/lib/rosetta_queue/core_ext/string.rb +22 -0
  35. data/lib/rosetta_queue/core_ext/time.rb +20 -0
  36. data/lib/rosetta_queue/destinations.rb +33 -0
  37. data/lib/rosetta_queue/exception_handler.rb +105 -0
  38. data/lib/rosetta_queue/exceptions.rb +10 -0
  39. data/lib/rosetta_queue/filters.rb +58 -0
  40. data/lib/rosetta_queue/logger.rb +27 -0
  41. data/lib/rosetta_queue/message_handler.rb +52 -0
  42. data/lib/rosetta_queue/producer.rb +21 -0
  43. data/lib/rosetta_queue/spec_helpers.rb +5 -0
  44. data/lib/rosetta_queue/spec_helpers/hash.rb +21 -0
  45. data/lib/rosetta_queue/spec_helpers/helpers.rb +47 -0
  46. data/lib/rosetta_queue/spec_helpers/publishing_matchers.rb +144 -0
  47. data/spec/rosetta_queue/adapter_spec.rb +101 -0
  48. data/spec/rosetta_queue/adapters/amqp_synchronous_spec.rb +277 -0
  49. data/spec/rosetta_queue/adapters/beanstalk_spec.rb +47 -0
  50. data/spec/rosetta_queue/adapters/fake_spec.rb +72 -0
  51. data/spec/rosetta_queue/adapters/null_spec.rb +31 -0
  52. data/spec/rosetta_queue/adapters/shared_adapter_behavior.rb +38 -0
  53. data/spec/rosetta_queue/adapters/shared_fanout_behavior.rb +20 -0
  54. data/spec/rosetta_queue/adapters/stomp_spec.rb +126 -0
  55. data/spec/rosetta_queue/consumer_managers/evented_spec.rb +56 -0
  56. data/spec/rosetta_queue/consumer_managers/shared_manager_behavior.rb +26 -0
  57. data/spec/rosetta_queue/consumer_managers/threaded_spec.rb +51 -0
  58. data/spec/rosetta_queue/consumer_spec.rb +99 -0
  59. data/spec/rosetta_queue/core_ext/string_spec.rb +15 -0
  60. data/spec/rosetta_queue/destinations_spec.rb +34 -0
  61. data/spec/rosetta_queue/exception_handler_spec.rb +106 -0
  62. data/spec/rosetta_queue/filters_spec.rb +57 -0
  63. data/spec/rosetta_queue/message_handler_spec.rb +47 -0
  64. data/spec/rosetta_queue/producer_spec.rb +77 -0
  65. data/spec/rosetta_queue/shared_messaging_behavior.rb +21 -0
  66. data/spec/spec.opts +4 -0
  67. data/spec/spec_helper.rb +47 -0
  68. 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