rosetta_queue 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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