bmabey-rosetta_queue 0.2.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.
- data/History.txt +6 -3
- data/README.rdoc +1 -193
- data/Rakefile +7 -1
- data/cucumber.yml +1 -1
- data/examples/sample_amqp_consumer.rb +13 -3
- data/examples/sample_amqp_fanout_consumer.rb +6 -3
- data/examples/sample_amqp_fanout_producer.rb +3 -2
- data/examples/sample_amqp_producer.rb +2 -1
- data/features/filtering.feature +13 -13
- data/features/messaging.feature +28 -20
- data/features/step_definitions/common_messaging_steps.rb +54 -17
- data/features/step_definitions/filtering_steps.rb +2 -2
- data/features/step_definitions/point_to_point_steps.rb +19 -9
- data/features/step_definitions/publish_subscribe_steps.rb +22 -8
- data/features/support/env.rb +2 -0
- data/features/support/sample_consumers.rb +6 -6
- data/lib/rosetta_queue.rb +1 -0
- data/lib/rosetta_queue/adapter.rb +6 -6
- data/lib/rosetta_queue/adapters/amqp.rb +6 -3
- data/lib/rosetta_queue/adapters/amqp_evented.rb +22 -22
- data/lib/rosetta_queue/adapters/amqp_synch.rb +42 -36
- data/lib/rosetta_queue/adapters/base.rb +3 -3
- data/lib/rosetta_queue/adapters/beanstalk.rb +5 -5
- data/lib/rosetta_queue/adapters/fake.rb +5 -5
- data/lib/rosetta_queue/adapters/null.rb +9 -9
- data/lib/rosetta_queue/adapters/stomp.rb +25 -12
- data/lib/rosetta_queue/base.rb +2 -2
- data/lib/rosetta_queue/consumer.rb +4 -4
- data/lib/rosetta_queue/consumer_managers/base.rb +8 -6
- data/lib/rosetta_queue/consumer_managers/evented.rb +5 -5
- data/lib/rosetta_queue/consumer_managers/threaded.rb +4 -4
- data/lib/rosetta_queue/core_ext/string.rb +3 -3
- data/lib/rosetta_queue/core_ext/time.rb +20 -0
- data/lib/rosetta_queue/destinations.rb +6 -6
- data/lib/rosetta_queue/filters.rb +8 -8
- data/lib/rosetta_queue/logger.rb +2 -2
- data/lib/rosetta_queue/message_handler.rb +10 -4
- data/lib/rosetta_queue/producer.rb +2 -2
- data/lib/rosetta_queue/spec_helpers/hash.rb +3 -3
- data/lib/rosetta_queue/spec_helpers/helpers.rb +8 -8
- data/lib/rosetta_queue/spec_helpers/publishing_matchers.rb +26 -26
- data/spec/rosetta_queue/adapter_spec.rb +27 -27
- data/spec/rosetta_queue/adapters/amqp_synchronous_spec.rb +21 -1
- data/spec/rosetta_queue/adapters/beanstalk_spec.rb +3 -3
- data/spec/rosetta_queue/adapters/fake_spec.rb +6 -6
- data/spec/rosetta_queue/adapters/null_spec.rb +5 -5
- data/spec/rosetta_queue/adapters/shared_adapter_behavior.rb +4 -4
- data/spec/rosetta_queue/adapters/shared_fanout_behavior.rb +1 -1
- data/spec/rosetta_queue/adapters/stomp_spec.rb +39 -18
- data/spec/rosetta_queue/consumer_managers/evented_spec.rb +6 -6
- data/spec/rosetta_queue/consumer_managers/shared_manager_behavior.rb +3 -3
- data/spec/rosetta_queue/consumer_managers/threaded_spec.rb +5 -5
- data/spec/rosetta_queue/consumer_spec.rb +13 -13
- data/spec/rosetta_queue/core_ext/string_spec.rb +3 -3
- data/spec/rosetta_queue/destinations_spec.rb +8 -8
- data/spec/rosetta_queue/filters_spec.rb +16 -16
- data/spec/rosetta_queue/producer_spec.rb +15 -15
- data/spec/rosetta_queue/shared_messaging_behavior.rb +6 -6
- metadata +3 -2
@@ -2,7 +2,7 @@ module RosettaQueue
|
|
2
2
|
module Gateway
|
3
3
|
|
4
4
|
class BaseAdapter
|
5
|
-
|
5
|
+
|
6
6
|
protected
|
7
7
|
|
8
8
|
def options_for(message_handler)
|
@@ -13,7 +13,7 @@ module RosettaQueue
|
|
13
13
|
raise DestinationNotFound.new("Missing destination!") unless message_handler.destination
|
14
14
|
@dest ||= Destinations.lookup(message_handler.destination.to_sym)
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
def filter_receiving(msg)
|
18
18
|
Filters.process_receiving(msg)
|
19
19
|
end
|
@@ -21,7 +21,7 @@ module RosettaQueue
|
|
21
21
|
def filter_sending(msg)
|
22
22
|
Filters.process_sending(msg)
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
@@ -2,7 +2,7 @@ require 'beanstalk-client'
|
|
2
2
|
|
3
3
|
module RosettaQueue
|
4
4
|
module Gateway
|
5
|
-
|
5
|
+
|
6
6
|
class BeanstalkAdapter < BaseAdapter
|
7
7
|
|
8
8
|
def ack(msg)
|
@@ -22,7 +22,7 @@ module RosettaQueue
|
|
22
22
|
msg.delete
|
23
23
|
msg
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
def receive_once(destination=nil, opts={})
|
27
27
|
receive.body
|
28
28
|
end
|
@@ -39,14 +39,14 @@ module RosettaQueue
|
|
39
39
|
message_handler.on_message(filter_receiving(msg))
|
40
40
|
end
|
41
41
|
end
|
42
|
-
|
42
|
+
|
43
43
|
def send_message(destination, message, options)
|
44
|
-
RosettaQueue.logger.info("Publishing to #{destination} :: #{message}")
|
44
|
+
RosettaQueue.logger.info("Publishing to #{destination} :: #{message}")
|
45
45
|
@conn.put(message)
|
46
46
|
end
|
47
47
|
|
48
48
|
private
|
49
|
-
|
49
|
+
|
50
50
|
def running(&block)
|
51
51
|
loop(&block)
|
52
52
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module RosettaQueue
|
2
2
|
module Gateway
|
3
|
-
|
3
|
+
|
4
4
|
class FakeAdapter
|
5
5
|
|
6
6
|
def initialize
|
@@ -10,17 +10,17 @@ module RosettaQueue
|
|
10
10
|
def send_message(queue, message, headers)
|
11
11
|
@messages << {'queue' => queue, 'message' => RosettaQueue::Filters::process_receiving(message), 'headers' => headers}
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
def messages_sent_to(queue)
|
15
15
|
(queue ? @messages.select{|message| message['queue'] == queue} : @messages).map{|m| m['message']}
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
def queues
|
19
19
|
@messages.map {|message| message['queue']}
|
20
20
|
end
|
21
21
|
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
module RosettaQueue
|
2
2
|
module Gateway
|
3
|
-
|
3
|
+
|
4
4
|
# The null adapter lets all send messages enter into the ether and so is ideal for modes
|
5
|
-
# when you do not want to incur the overhead of a real adapter. You can not consume with
|
5
|
+
# when you do not want to incur the overhead of a real adapter. You can not consume with
|
6
6
|
# this adapter however.
|
7
7
|
#
|
8
8
|
# In your RosettaQueue definition block, and your using rails, you could base your adapter type on Rails.env:
|
@@ -18,11 +18,11 @@ module RosettaQueue
|
|
18
18
|
# a.type = "null"
|
19
19
|
# end
|
20
20
|
# end
|
21
|
-
#
|
22
|
-
# (if you follow this example and are using stories be sure
|
21
|
+
#
|
22
|
+
# (if you follow this example and are using stories be sure
|
23
23
|
# to set ENV["RUNNING_STORIES"] = "true" in your helper.rb or env.rb file)
|
24
24
|
class NullAdapter
|
25
|
-
|
25
|
+
|
26
26
|
def initialize(adapter_settings)
|
27
27
|
# no-op
|
28
28
|
end
|
@@ -30,15 +30,15 @@ module RosettaQueue
|
|
30
30
|
def disconnect
|
31
31
|
# no-op
|
32
32
|
end
|
33
|
-
|
33
|
+
|
34
34
|
def receive
|
35
35
|
raise "Null Adpater is in use, you can not consume messages!"
|
36
36
|
end
|
37
|
-
|
37
|
+
|
38
38
|
def receive_with(message_handler)
|
39
39
|
raise "Null Adpater is in use, you can not consume messages!"
|
40
40
|
end
|
41
|
-
|
41
|
+
|
42
42
|
def send_message(queue, message, options)
|
43
43
|
# no-op
|
44
44
|
end
|
@@ -46,7 +46,7 @@ module RosettaQueue
|
|
46
46
|
def subscribe(queue, options)
|
47
47
|
# no-op
|
48
48
|
end
|
49
|
-
|
49
|
+
|
50
50
|
def unsubscribe(queue)
|
51
51
|
# no-op
|
52
52
|
end
|
@@ -2,19 +2,20 @@ require 'stomp'
|
|
2
2
|
|
3
3
|
module RosettaQueue
|
4
4
|
module Gateway
|
5
|
-
|
5
|
+
|
6
6
|
class StompAdapter < BaseAdapter
|
7
7
|
|
8
8
|
def ack(msg)
|
9
|
+
raise AdapterException, "Unable to ack client because message-id is blank. Are your message handler options correct? (i.e., :ack => 'client')" if msg.headers["message-id"].nil?
|
9
10
|
@conn.ack(msg.headers["message-id"])
|
10
11
|
end
|
11
12
|
|
12
13
|
def initialize(adapter_settings = {})
|
13
|
-
raise "Missing adapter settings" if adapter_settings.empty?
|
14
|
-
@conn = Stomp::Connection.open(adapter_settings[:user],
|
15
|
-
adapter_settings[:password],
|
16
|
-
adapter_settings[:host],
|
17
|
-
adapter_settings[:port],
|
14
|
+
raise AdapterException, "Missing adapter settings" if adapter_settings.empty?
|
15
|
+
@conn = Stomp::Connection.open(adapter_settings[:user],
|
16
|
+
adapter_settings[:password],
|
17
|
+
adapter_settings[:host],
|
18
|
+
adapter_settings[:port],
|
18
19
|
true)
|
19
20
|
end
|
20
21
|
|
@@ -28,7 +29,7 @@ module RosettaQueue
|
|
28
29
|
ack(msg) unless options[:ack].nil?
|
29
30
|
msg
|
30
31
|
end
|
31
|
-
|
32
|
+
|
32
33
|
def receive_once(destination, opts)
|
33
34
|
subscribe(destination, opts)
|
34
35
|
msg = receive(opts).body
|
@@ -50,26 +51,38 @@ module RosettaQueue
|
|
50
51
|
Thread.current[:processing] = false
|
51
52
|
end
|
52
53
|
end
|
53
|
-
|
54
|
+
|
54
55
|
def send_message(destination, message, options)
|
55
|
-
RosettaQueue.logger.info("Publishing to #{destination} :: #{message}")
|
56
|
+
RosettaQueue.logger.info("Publishing to #{destination} :: #{message}")
|
56
57
|
@conn.send(destination, message, options)
|
57
58
|
end
|
58
59
|
|
59
60
|
def subscribe(destination, options)
|
60
61
|
@conn.subscribe(destination, options)
|
61
62
|
end
|
62
|
-
|
63
|
+
|
63
64
|
def unsubscribe(destination)
|
64
65
|
@conn.unsubscribe(destination)
|
65
66
|
end
|
66
|
-
|
67
|
+
|
67
68
|
private
|
68
|
-
|
69
|
+
|
69
70
|
def running(&block)
|
70
71
|
loop(&block)
|
71
72
|
end
|
72
73
|
|
73
74
|
end
|
75
|
+
|
76
|
+
class StompAdapterProxy
|
77
|
+
|
78
|
+
def initialize(adapter, msg)
|
79
|
+
@adapter, @msg = adapter, msg
|
80
|
+
end
|
81
|
+
|
82
|
+
def ack
|
83
|
+
@adapter.ack(@msg)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
74
87
|
end
|
75
88
|
end
|
data/lib/rosetta_queue/base.rb
CHANGED
@@ -3,17 +3,17 @@ module RosettaQueue
|
|
3
3
|
|
4
4
|
def self.receive(destination, options = {})
|
5
5
|
RosettaQueue::Adapter.instance.receive_once(Destinations.lookup(destination), options)
|
6
|
-
|
6
|
+
|
7
7
|
rescue Exception=>e
|
8
8
|
RosettaQueue.logger.error("Caught exception in Consumer.receive: #{$!}\n" + e.backtrace.join("\n\t"))
|
9
9
|
end
|
10
10
|
|
11
11
|
def self.delete(destination, options={})
|
12
12
|
RosettaQueue::Adapter.instance.delete(Destinations.lookup(destination), options)
|
13
|
-
|
13
|
+
|
14
14
|
rescue Exception=>e
|
15
15
|
RosettaQueue.logger.error("Caught exception in Consumer.delete: #{$!}\n" + e.backtrace.join("\n\t"))
|
16
|
-
end
|
16
|
+
end
|
17
17
|
|
18
18
|
def initialize(message_handler)
|
19
19
|
@message_handler = message_handler
|
@@ -21,7 +21,7 @@ module RosettaQueue
|
|
21
21
|
|
22
22
|
def receive
|
23
23
|
connection.receive_with(@message_handler)
|
24
|
-
|
24
|
+
|
25
25
|
rescue Exception=>e
|
26
26
|
RosettaQueue.logger.error("Caught exception in Consumer#receive: #{$!}\n" + e.backtrace.join("\n\t"))
|
27
27
|
end
|
@@ -1,22 +1,24 @@
|
|
1
1
|
module RosettaQueue
|
2
|
-
|
2
|
+
|
3
3
|
class BaseManager
|
4
4
|
attr_reader :consumers
|
5
5
|
|
6
6
|
class << self
|
7
7
|
def create
|
8
|
-
|
8
|
+
manager = self.new
|
9
|
+
yield manager
|
10
|
+
manager
|
9
11
|
end
|
10
12
|
end
|
11
|
-
|
13
|
+
|
12
14
|
def initialize
|
13
15
|
@consumers = {}
|
14
16
|
end
|
15
|
-
|
17
|
+
|
16
18
|
def add(message_handler)
|
17
19
|
key = message_handler.class.to_s.underscore.to_sym
|
18
20
|
@consumers[key] = Consumer.new(message_handler)
|
19
21
|
end
|
20
|
-
|
21
|
-
end
|
22
|
+
|
23
|
+
end
|
22
24
|
end
|
@@ -2,9 +2,9 @@ require 'rosetta_queue/consumer_managers/base'
|
|
2
2
|
require 'mq'
|
3
3
|
|
4
4
|
module RosettaQueue
|
5
|
-
|
5
|
+
|
6
6
|
class EventedManager < BaseManager
|
7
|
-
|
7
|
+
|
8
8
|
def start
|
9
9
|
EM.run {
|
10
10
|
trap_interruptions
|
@@ -19,20 +19,20 @@ module RosettaQueue
|
|
19
19
|
end
|
20
20
|
}
|
21
21
|
end
|
22
|
-
|
22
|
+
|
23
23
|
def stop
|
24
24
|
RosettaQueue.logger.info("Shutting down event machine...")
|
25
25
|
EM.stop
|
26
26
|
end
|
27
27
|
|
28
28
|
private
|
29
|
-
|
29
|
+
|
30
30
|
def trap_interruptions
|
31
31
|
trap("INT") {
|
32
32
|
RosettaQueue.logger.warn("Interrupt received. Shutting down...")
|
33
33
|
EM.stop
|
34
34
|
}
|
35
|
-
|
35
|
+
|
36
36
|
trap("TERM") {
|
37
37
|
RosettaQueue.logger.warn("Interrupt received. Shutting down...")
|
38
38
|
EM.stop
|
@@ -25,10 +25,10 @@ module RosettaQueue
|
|
25
25
|
def join_threads
|
26
26
|
@threads.each { |thread| thread.join }
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
29
|
def shutdown_requested
|
30
30
|
RosettaQueue.logger.error "Shutdown requested, starting to prune threads..."
|
31
|
-
|
31
|
+
|
32
32
|
while @threads.any? { |n, t| t.alive? }
|
33
33
|
RosettaQueue.logger.info "Calling stop_threads"
|
34
34
|
stop_threads
|
@@ -73,7 +73,7 @@ module RosettaQueue
|
|
73
73
|
Thread.pass
|
74
74
|
end
|
75
75
|
end
|
76
|
-
end
|
76
|
+
end
|
77
77
|
end
|
78
78
|
|
79
79
|
def stop_threads
|
@@ -86,8 +86,8 @@ module RosettaQueue
|
|
86
86
|
next
|
87
87
|
end
|
88
88
|
RosettaQueue.logger.debug("#{key} Stopping thread #{thread} and disconnecting the consumer")
|
89
|
-
@consumers[key].disconnect
|
90
89
|
thread.kill
|
90
|
+
@consumers[key].disconnect
|
91
91
|
end
|
92
92
|
end
|
93
93
|
end
|
@@ -7,11 +7,11 @@ class String
|
|
7
7
|
self.first.downcase + camelize(self)[1..-1]
|
8
8
|
end
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
def classify
|
12
12
|
camelize(self.sub(/.*\./, ''))
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
15
|
def underscore
|
16
16
|
self.gsub(/::/, '/').
|
17
17
|
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
@@ -19,4 +19,4 @@ class String
|
|
19
19
|
tr("-", "_").
|
20
20
|
downcase
|
21
21
|
end
|
22
|
-
end
|
22
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'time'
|
2
|
+
|
3
|
+
class Time
|
4
|
+
|
5
|
+
DATE_FORMATS = {
|
6
|
+
:db => "%Y-%m-%d %H:%M:%S",
|
7
|
+
:number => "%Y%m%d%H%M%S",
|
8
|
+
:time => "%H:%M",
|
9
|
+
:short => "%d %b %H:%M",
|
10
|
+
:long => "%B %d, %Y %H:%M",
|
11
|
+
:long_ordinal => lambda { |time| time.strftime("%B #{time.day.ordinalize}, %Y %H:%M") },
|
12
|
+
:rfc822 => lambda { |time| time.strftime("%a, %d %b %Y %H:%M:%S #{time.formatted_offset(false)}") }
|
13
|
+
}
|
14
|
+
|
15
|
+
def to_formatted_s(format = :default)
|
16
|
+
return to_default_s unless formatter = DATE_FORMATS[format]
|
17
|
+
formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module RosettaQueue
|
2
2
|
|
3
3
|
class Destinations
|
4
|
-
|
4
|
+
|
5
5
|
@dest = {}
|
6
6
|
|
7
7
|
class << self
|
@@ -10,25 +10,25 @@ module RosettaQueue
|
|
10
10
|
def define
|
11
11
|
yield self
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
def clear
|
15
15
|
@dest.clear
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
def lookup(dest_name)
|
19
19
|
mapping = dest[dest_name.to_sym]
|
20
20
|
raise "No destination mapping for '#{dest_name}' has been defined!" unless mapping
|
21
21
|
return mapping
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
def map(key, dest)
|
25
25
|
@dest[key] = dest
|
26
26
|
end
|
27
|
-
|
27
|
+
|
28
28
|
def queue_names
|
29
29
|
@dest.values
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
33
|
end
|
34
|
-
end
|
34
|
+
end
|