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,101 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ module RosettaQueue
4
+
5
+ describe Adapter do
6
+
7
+ before(:each) do
8
+ @stomp_adapter = mock("Gateway::StompAdapter")
9
+ Adapter.reset
10
+ end
11
+
12
+ describe ".reset" do
13
+ it "should clear all definitions" do
14
+ Adapter.define { |a| a.type = "null" }
15
+ Adapter.instance.should be_instance_of(RosettaQueue::Gateway::NullAdapter)
16
+ Adapter.reset
17
+ running { Adapter.instance }.should raise_error(AdapterException)
18
+ end
19
+ end
20
+
21
+ describe ".type=" do
22
+
23
+ it "should raise error when adapter does not exist" do
24
+ running {
25
+ Adapter.define do |a|
26
+ a.type = "foo"
27
+ end
28
+ }.should raise_error(AdapterException)
29
+ end
30
+
31
+ end
32
+
33
+ describe "adapter not type set" do
34
+ it "should raise an error when .instance is called" do
35
+ # given
36
+ Adapter.define { |a| }
37
+ # then & when
38
+ running { Adapter.instance }.should raise_error(AdapterException)
39
+ end
40
+ end
41
+
42
+ describe "adapter type set" do
43
+
44
+ before(:each) do
45
+ Adapter.define { |a| a.type = "null" }
46
+ end
47
+
48
+ it "should return adapter instance" do
49
+ Adapter.instance.class.should == RosettaQueue::Gateway::NullAdapter
50
+ end
51
+
52
+ end
53
+
54
+ describe "adapter instantiation" do
55
+
56
+ before(:each) do
57
+ Adapter.define do |a|
58
+ a.user = "foo"
59
+ a.password = "bar"
60
+ a.host = "localhost"
61
+ a.port = "9000"
62
+ a.type = "fake"
63
+ end
64
+ end
65
+
66
+ def do_process
67
+ Adapter.instance
68
+ end
69
+
70
+ it "should set opts as an empty has unless variable is set" do
71
+ during_process {
72
+ RosettaQueue::Gateway::FakeAdapter.should_receive(:new).with({:user => "foo", :password => "bar", :host => "localhost", :port => "9000", :opts => {}})
73
+ }
74
+ end
75
+
76
+ describe "when setting options" do
77
+ before(:each) do
78
+ Adapter.define { |a| a.options = {:vhost => "baz"} }
79
+ end
80
+
81
+ it "should map adapter_settings to a hash" do
82
+ during_process {
83
+ RosettaQueue::Gateway::FakeAdapter.should_receive(:new).with({:user => "foo", :password => "bar", :host => "localhost", :port => "9000", :opts => {:vhost => "baz"}})
84
+ }
85
+ end
86
+ end
87
+
88
+ describe "setting options incorrectly (options should always be set as a Hash)" do
89
+
90
+ before(:each) do
91
+ Adapter.define { |a| a.options = "baz" }
92
+ end
93
+
94
+ it "should raise an adapter exception" do
95
+ running { Adapter.instance }.should raise_error("Adapter options should be a hash")
96
+ end
97
+ end
98
+
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,277 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+ require File.dirname(__FILE__) + '/shared_adapter_behavior'
3
+ require File.dirname(__FILE__) + '/shared_fanout_behavior'
4
+
5
+ begin
6
+ require 'rosetta_queue/adapters/amqp_synch'
7
+ rescue LoadError => ex
8
+ if ex.message =~ /bunny/i
9
+ warn "--WARNING-- Skipping all of tha AmqpSynchAdapter code examples since bunny is not present. Install bunny if you need to be testing this adapter!"
10
+ else
11
+ raise ex
12
+ end
13
+ end
14
+
15
+ if defined? Bunny
16
+
17
+ module RosettaQueue::Gateway
18
+
19
+ describe "an exchange", :shared => true do
20
+
21
+ describe "#do_exchange" do
22
+
23
+ it "should delegate message handling to the message handler" do
24
+ when_receiving_exchange {
25
+ @handler.should_receive(:handle_message).with("Hello World!")
26
+ }
27
+ end
28
+ end
29
+ end
30
+
31
+ describe "AmqpSynch adapter and components" do
32
+
33
+ before(:each) do
34
+ RosettaQueue.logger.stub!(:info)
35
+ @msg = "Hello World!"
36
+ @adapter = AmqpSynchAdapter.new({:user => "foo", :password => "bar", :host => "localhost"})
37
+ @handler = mock("handler", :handle_message => true, :destination => :foo, :options_hash => {:durable => true})
38
+ end
39
+
40
+ describe AmqpSynchAdapter do
41
+
42
+ before(:each) do
43
+ @exchange_strategy = mock('DirectExchange', :receive_once => @msg, :receive => @msg, :send_message => true)
44
+ SynchExchange::DirectExchange.stub!(:new).and_return(@exchange_strategy)
45
+ end
46
+
47
+ it_should_behave_like "an adapter"
48
+
49
+ describe "#receive_once" do
50
+
51
+ def do_receiving_once
52
+ @adapter.receive_once("queue.foo", {:durable => false})
53
+ end
54
+
55
+ it "should pass destination and options to exchange strategy" do
56
+ when_receiving_once {
57
+ @exchange_strategy.should_receive(:receive_once).with("queue.foo")
58
+ }
59
+ end
60
+
61
+ end
62
+
63
+ describe "#receive_with" do
64
+
65
+ def do_receiving_with_handler
66
+ @adapter.receive_with(@handler)
67
+ end
68
+
69
+ before(:each) do
70
+ @handler = mock("handler", :handle_message => true, :destination => :foo, :options_hash => {:durable => true })
71
+ end
72
+
73
+ it "should pass message handler to exchange strategy" do
74
+ when_receiving_with_handler {
75
+ @exchange_strategy.should_receive(:receive).with("foo", @handler)
76
+ }
77
+ end
78
+
79
+ end
80
+
81
+ describe "#send_message" do
82
+
83
+ it "should pass message handler to exchange strategy" do
84
+ when_publishing {
85
+ @exchange_strategy.should_receive(:publish).with('queue', 'message')
86
+ }
87
+ end
88
+
89
+ end
90
+ end
91
+
92
+
93
+ describe SynchExchange::DirectExchange do
94
+
95
+ before(:each) do
96
+ @queue = mock("Bunny::Queue", :pop => @msg, :publish => true, :unsubscribe => true)
97
+ Bunny.stub!(:new).and_return(@conn = mock("Bunny::Client", :queue => @queue, :exchange => @exchange, :status => :connected, :stop => nil))
98
+ @queue.stub!(:subscribe).and_yield(@msg)
99
+ @handler = mock("handler", :handle_message => true, :destination => :foo)
100
+ @exchange = SynchExchange::DirectExchange.new({:user => 'user', :password => 'pass', :host => 'host', :opts => {:vhost => "foo"}})
101
+ end
102
+
103
+
104
+ def do_receiving_exchange
105
+ @exchange.receive("queue.foo", @handler)
106
+ end
107
+
108
+ it_should_behave_like "an exchange"
109
+
110
+ describe "#receive_once" do
111
+
112
+ def do_receiving_single_exchange
113
+ @exchange.receive_once("queue.foo") { |msg| }
114
+
115
+ end
116
+
117
+ it "should return the message from the connection" do
118
+ @exchange.receive_once("queue.foo") do |msg|
119
+ msg.should == @msg
120
+ end
121
+ end
122
+
123
+ it "should subscribe to queue" do
124
+ when_receiving_single_exchange {
125
+ @queue.should_receive(:pop)
126
+ }
127
+ end
128
+
129
+ end
130
+
131
+ describe "#receive" do
132
+
133
+ it "should subscribe to queue" do
134
+ when_receiving_exchange {
135
+ @queue.should_receive(:subscribe).and_yield(@msg)
136
+ }
137
+ end
138
+
139
+ end
140
+
141
+
142
+ describe "#publish" do
143
+
144
+ def do_publishing
145
+ @exchange.publish('queue.foo', 'message')
146
+ end
147
+
148
+ it "should instantiate queue" do
149
+ when_publishing {
150
+ @conn.should_receive(:queue).and_return(@queue)
151
+ }
152
+ end
153
+
154
+ it "should publish message to queue" do
155
+ when_publishing {
156
+ @conn.queue.should_receive(:publish).with("message", {})
157
+ }
158
+ end
159
+
160
+ end
161
+
162
+ end
163
+
164
+
165
+ describe SynchExchange::FanoutExchange do
166
+
167
+ before(:each) do
168
+ @exchange = SynchExchange::FanoutExchange.new({:user => 'user', :password => 'pass', :host => 'host', :opts => {:vhost => 'foo'}})
169
+ @queue = mock("Bunny::Queue", :pop => @msg, :bind => @bound_queue = mock("Bunny::Queue", :pop => @msg), :publish => true, :unbind => true)
170
+ Bunny.stub!(:new).and_return(@conn = mock("Bunny::Client", :queue => @queue, :exchange => @exchange, :status => :connected))
171
+ @queue.stub!(:subscribe).and_yield(@msg)
172
+ @handler = mock("handler", :handle_message => true, :destination => :foo, :options => {:durable => false})
173
+ end
174
+
175
+ def do_receiving_exchange
176
+ @exchange.receive("topic.foo", @handler)
177
+ end
178
+
179
+ it_should_behave_like "an exchange"
180
+
181
+ describe "#receive_once" do
182
+
183
+ def do_receiving_exchange
184
+ @exchange.receive_once("topic.foo") { |msg| }
185
+ end
186
+
187
+ it_should_behave_like "a fanout exchange adapter"
188
+
189
+ it "should return the message from the connection" do
190
+ @exchange.receive_once("topic.foo") do |msg|
191
+ msg.should == @msg
192
+ end
193
+ end
194
+
195
+ it "should subscribe to queue" do
196
+ when_receiving_exchange {
197
+ @queue.should_receive(:pop)
198
+ }
199
+ end
200
+
201
+ it "should unbind queue from exchange" do
202
+ pending
203
+ when_receiving_single_exchange {
204
+ @queue.should_receive(:unbind)
205
+ }
206
+ end
207
+
208
+ end
209
+
210
+ describe "#receive" do
211
+
212
+ it_should_behave_like "a fanout exchange adapter"
213
+
214
+ it "should forward the message body onto the handler" do
215
+ when_receiving_exchange {
216
+ @handler.should_receive(:handle_message).with("Hello World!")
217
+ }
218
+ end
219
+
220
+ it "should subscribe to queue" do
221
+ when_receiving_exchange {
222
+ @queue.should_receive(:subscribe).and_yield(@msg)
223
+ }
224
+ end
225
+
226
+ end
227
+
228
+ # describe "#publish_to_exchange" do
229
+ #
230
+ # def do_publishing
231
+ # @exchange.publish_to_exchange('/queue/foo', 'message', {:durable => false})
232
+ # end
233
+ #
234
+ # it "should instantiate queue" do
235
+ # when_publishing {
236
+ # @channel.should_receive(:queue).and_return(@queue)
237
+ # }
238
+ # end
239
+ #
240
+ # it "should publish message to queue" do
241
+ # when_publishing {
242
+ # @channel.queue.should_receive(:publish).with('message')
243
+ # }
244
+ # end
245
+ #
246
+ # it "should stop event loop" do
247
+ # when_publishing {
248
+ # EM.should_receive(:stop_event_loop)
249
+ # }
250
+ # end
251
+ # end
252
+
253
+ # describe SynchExchange::AmqpAdapterProxy do
254
+
255
+ # before(:each) do
256
+ # @queue = mock("Queue", :ack => nil)
257
+ # @proxy = SynchExchange::AmqpAdapterProxy.new(@queue)
258
+ # end
259
+
260
+ # context "#ack" do
261
+
262
+ # it "should delegate to AMQP queue object" do
263
+ # # expect
264
+ # @queue.should_receive(:ack)
265
+
266
+ # # when
267
+ # @proxy.ack
268
+ # end
269
+
270
+ # end
271
+ # end
272
+
273
+ end
274
+ end
275
+ end
276
+
277
+ end # for Guard up top to prevent this spec from running if Bunny not loaded from amqp_synch
@@ -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', :handle_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