message-driver 0.6.1 → 0.7.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +20 -2
- data/.rubocop_todo.yml +15 -23
- data/.travis.yml +10 -22
- data/CHANGELOG.md +9 -0
- data/Gemfile +34 -24
- data/Guardfile +46 -29
- data/LICENSE +1 -1
- data/Rakefile +14 -6
- data/features/CHANGELOG.md +1 -0
- data/features/step_definitions/logging_steps.rb +3 -2
- data/features/support/firewall_helper.rb +2 -2
- data/features/support/no_error_matcher.rb +1 -1
- data/lib/message_driver/adapters/base.rb +115 -11
- data/lib/message_driver/adapters/bunny_adapter.rb +58 -46
- data/lib/message_driver/adapters/in_memory_adapter.rb +57 -35
- data/lib/message_driver/adapters/stomp_adapter.rb +10 -10
- data/lib/message_driver/broker.rb +16 -19
- data/lib/message_driver/client.rb +3 -7
- data/lib/message_driver/destination.rb +4 -4
- data/lib/message_driver/message.rb +3 -2
- data/lib/message_driver/middleware/block_middleware.rb +1 -1
- data/lib/message_driver/subscription.rb +1 -1
- data/lib/message_driver/version.rb +1 -1
- data/message-driver.gemspec +6 -6
- data/spec/integration/bunny/amqp_integration_spec.rb +6 -4
- data/spec/integration/bunny/bunny_adapter_spec.rb +1 -3
- data/spec/integration/in_memory/in_memory_adapter_spec.rb +46 -6
- data/spec/integration/stomp/stomp_adapter_spec.rb +0 -2
- data/spec/spec_helper.rb +6 -0
- data/spec/support/matchers/override_method_matcher.rb +7 -0
- data/spec/support/shared/adapter_examples.rb +3 -0
- data/spec/support/shared/client_ack_examples.rb +26 -4
- data/spec/support/shared/context_examples.rb +46 -0
- data/spec/support/shared/destination_examples.rb +28 -0
- data/spec/support/shared/subscription_examples.rb +6 -1
- data/spec/support/shared/transaction_examples.rb +35 -4
- data/spec/support/test_adapter.rb +19 -0
- data/spec/support/utils.rb +1 -5
- data/spec/units/message_driver/adapters/base_spec.rb +37 -31
- data/spec/units/message_driver/broker_spec.rb +1 -2
- data/spec/units/message_driver/client_spec.rb +3 -3
- data/spec/units/message_driver/destination_spec.rb +4 -2
- data/spec/units/message_driver/message_spec.rb +9 -3
- data/test_lib/broker_config.rb +0 -2
- data/test_lib/provider/base.rb +2 -6
- data/test_lib/provider/rabbitmq.rb +3 -3
- metadata +18 -16
- data/ci/travis_setup +0 -7
- data/features/CHANGELOG.md +0 -102
@@ -27,49 +27,56 @@ module MessageDriver
|
|
27
27
|
end
|
28
28
|
|
29
29
|
class Destination < MessageDriver::Destination::Base
|
30
|
-
def
|
31
|
-
adapter.
|
30
|
+
def subscriptions
|
31
|
+
adapter.subscriptions_for(name)
|
32
32
|
end
|
33
33
|
|
34
|
-
def
|
34
|
+
def handle_message_count
|
35
35
|
message_queue.size
|
36
36
|
end
|
37
37
|
|
38
|
-
def
|
39
|
-
|
38
|
+
def handle_pop_message(ctx, options = {})
|
39
|
+
_fetch_message(ctx, options)
|
40
40
|
end
|
41
41
|
|
42
|
-
def
|
43
|
-
message = message_queue.shift
|
44
|
-
if message.nil?
|
45
|
-
nil
|
46
|
-
else
|
47
|
-
raw_body = message.body
|
48
|
-
b, h, p = middleware.on_consume(message.body, message.headers, message.properties)
|
49
|
-
Message.new(nil, b, h, p, raw_body)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def subscribe(options = {}, &consumer)
|
42
|
+
def handle_subscribe(options = {}, &consumer)
|
54
43
|
subscription = Subscription.new(adapter, self, consumer, options)
|
55
44
|
adapter.add_subscription_for(name, subscription)
|
56
|
-
|
45
|
+
_deliver_messages
|
57
46
|
subscription
|
58
47
|
end
|
59
48
|
|
60
|
-
def
|
49
|
+
def handle_publish(body, headers = {}, properties = {})
|
61
50
|
raw_body = body
|
62
51
|
b, h, p = middleware.on_publish(body, headers, properties)
|
63
|
-
msg = Message.new(nil, b, h, p, raw_body)
|
52
|
+
msg = Message.new(nil, self, b, h, p, raw_body)
|
64
53
|
message_queue << msg
|
65
|
-
|
54
|
+
_deliver_messages
|
66
55
|
end
|
67
56
|
|
68
57
|
private
|
69
58
|
|
70
|
-
def
|
71
|
-
|
72
|
-
|
59
|
+
def next_subscription
|
60
|
+
adapter.next_subscription_for(name)
|
61
|
+
end
|
62
|
+
|
63
|
+
def _fetch_message(ctx, _options = {})
|
64
|
+
message = message_queue.shift
|
65
|
+
if message.nil?
|
66
|
+
nil
|
67
|
+
else
|
68
|
+
raw_body = message.body
|
69
|
+
b, h, p = middleware.on_consume(message.body, message.headers, message.properties)
|
70
|
+
Message.new(ctx, self, b, h, p, raw_body)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def _deliver_messages
|
75
|
+
unless subscriptions.empty?
|
76
|
+
until (msg = _fetch_message(current_adapter_context)).nil?
|
77
|
+
sub = next_subscription # this actually cycles through the subscriptions
|
78
|
+
sub.deliver_message(msg)
|
79
|
+
end
|
73
80
|
end
|
74
81
|
end
|
75
82
|
|
@@ -99,22 +106,31 @@ module MessageDriver
|
|
99
106
|
destination = Destination.new(self, name, dest_options, message_props)
|
100
107
|
@destinations[name] = destination
|
101
108
|
end
|
109
|
+
alias handle_create_destination create_destination
|
102
110
|
|
103
111
|
class InMemoryContext < ContextBase
|
104
112
|
extend Forwardable
|
105
113
|
|
106
|
-
def_delegators :adapter, :
|
114
|
+
def_delegators :adapter, :handle_create_destination
|
107
115
|
|
108
|
-
def
|
109
|
-
destination.
|
116
|
+
def handle_publish(destination, body, headers = {}, properties = {})
|
117
|
+
destination.handle_publish(body, headers, properties)
|
110
118
|
end
|
111
119
|
|
112
|
-
def
|
113
|
-
destination.
|
120
|
+
def handle_pop_message(destination, options = {})
|
121
|
+
destination.handle_pop_message(self, options)
|
114
122
|
end
|
115
123
|
|
116
|
-
def
|
117
|
-
destination.
|
124
|
+
def handle_subscribe(destination, options = {}, &consumer)
|
125
|
+
destination.handle_subscribe(options, &consumer)
|
126
|
+
end
|
127
|
+
|
128
|
+
def handle_message_count(destination)
|
129
|
+
destination.handle_message_count
|
130
|
+
end
|
131
|
+
|
132
|
+
def handle_consumer_count(destination)
|
133
|
+
adapter.consumer_count_for(destination.name)
|
118
134
|
end
|
119
135
|
|
120
136
|
def supports_subscriptions?
|
@@ -138,10 +154,16 @@ module MessageDriver
|
|
138
154
|
@message_store[name]
|
139
155
|
end
|
140
156
|
|
141
|
-
def
|
142
|
-
|
143
|
-
|
144
|
-
|
157
|
+
def subscriptions_for(name)
|
158
|
+
@subscriptions[name]
|
159
|
+
end
|
160
|
+
|
161
|
+
def next_subscription_for(name)
|
162
|
+
unless (subs = @subscriptions[name]).empty?
|
163
|
+
sub = subs.shift
|
164
|
+
subs.push sub
|
165
|
+
sub
|
166
|
+
end
|
145
167
|
end
|
146
168
|
|
147
169
|
def add_subscription_for(name, subscription)
|
@@ -12,9 +12,9 @@ module MessageDriver
|
|
12
12
|
class StompAdapter < Base
|
13
13
|
class Message < MessageDriver::Message::Base
|
14
14
|
attr_reader :stomp_message
|
15
|
-
def initialize(ctx, stomp_message)
|
15
|
+
def initialize(ctx, destination, stomp_message)
|
16
16
|
@stomp_message = stomp_message
|
17
|
-
super(ctx, stomp_message.body, stomp_message.headers, {})
|
17
|
+
super(ctx, destination, stomp_message.body, stomp_message.headers, {})
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
@@ -48,21 +48,21 @@ module MessageDriver
|
|
48
48
|
|
49
49
|
def_delegators :adapter, :with_connection, :poll_timeout
|
50
50
|
|
51
|
-
# def
|
51
|
+
# def handle_subscribe(destination, consumer)
|
52
52
|
# destination.subscribe(&consumer)
|
53
53
|
# end
|
54
54
|
|
55
|
-
def
|
55
|
+
def handle_create_destination(name, dest_options = {}, message_props = {})
|
56
56
|
Destination.new(adapter, name, dest_options, message_props)
|
57
57
|
end
|
58
58
|
|
59
|
-
def
|
59
|
+
def handle_publish(destination, body, headers = {}, _properties = {})
|
60
60
|
with_connection do |connection|
|
61
61
|
connection.publish(destination.queue_path, body, headers)
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
65
|
-
def
|
65
|
+
def handle_pop_message(destination, options = {})
|
66
66
|
with_connection do |connection|
|
67
67
|
sub_id = connection.uuid
|
68
68
|
msg = nil
|
@@ -76,7 +76,7 @@ module MessageDriver
|
|
76
76
|
end
|
77
77
|
end
|
78
78
|
connection.unsubscribe(destination.queue_path, options, sub_id)
|
79
|
-
Message.new(self, msg) if msg
|
79
|
+
Message.new(self, destination, msg) if msg
|
80
80
|
end
|
81
81
|
end
|
82
82
|
|
@@ -107,7 +107,7 @@ module MessageDriver
|
|
107
107
|
|
108
108
|
def open_connection
|
109
109
|
conn = Stomp::Connection.new(@config)
|
110
|
-
|
110
|
+
raise MessageDriver::ConnectionError, conn.connection_frame.to_s unless conn.open?
|
111
111
|
conn
|
112
112
|
end
|
113
113
|
|
@@ -115,8 +115,8 @@ module MessageDriver
|
|
115
115
|
required = Gem::Requirement.create('~> 1.3.1')
|
116
116
|
current = Gem::Version.create(Stomp::Version::STRING)
|
117
117
|
unless required.satisfied_by? current
|
118
|
-
|
119
|
-
|
118
|
+
raise MessageDriver::Error,
|
119
|
+
'stomp 1.3.1 or a later version of the 1.3.x series is required for the stomp adapter'
|
120
120
|
end
|
121
121
|
end
|
122
122
|
end
|
@@ -18,7 +18,7 @@ module MessageDriver
|
|
18
18
|
# @param options [Hash] options to be passed to the adapter class
|
19
19
|
def configure(name = DEFAULT_BROKER_NAME, options)
|
20
20
|
if brokers.keys.include? name
|
21
|
-
|
21
|
+
raise BrokerAlreadyConfigured, "there is already a broker named #{name} configured"
|
22
22
|
end
|
23
23
|
brokers[name] = new(name, options)
|
24
24
|
end
|
@@ -31,8 +31,8 @@ module MessageDriver
|
|
31
31
|
def broker(name = DEFAULT_BROKER_NAME)
|
32
32
|
result = brokers[name]
|
33
33
|
if result.nil?
|
34
|
-
|
35
|
-
|
34
|
+
raise BrokerNotConfigured,
|
35
|
+
"There is no broker named #{name} configured. The configured brokers are #{brokers.keys}"
|
36
36
|
end
|
37
37
|
result
|
38
38
|
end
|
@@ -58,17 +58,13 @@ module MessageDriver
|
|
58
58
|
# stops all the brokers
|
59
59
|
# @see #stop
|
60
60
|
def stop_all
|
61
|
-
each_broker
|
62
|
-
brk.stop
|
63
|
-
end
|
61
|
+
each_broker(&:stop)
|
64
62
|
end
|
65
63
|
|
66
64
|
# restarts all the brokers
|
67
65
|
# @see #restart
|
68
66
|
def restart_all
|
69
|
-
each_broker
|
70
|
-
brk.restart
|
71
|
-
end
|
67
|
+
each_broker(&:restart)
|
72
68
|
end
|
73
69
|
|
74
70
|
# Resets all the brokers for testing purposes.
|
@@ -90,6 +86,7 @@ module MessageDriver
|
|
90
86
|
end
|
91
87
|
end
|
92
88
|
brokers.clear
|
89
|
+
clients.clear
|
93
90
|
end
|
94
91
|
|
95
92
|
private
|
@@ -157,7 +154,7 @@ module MessageDriver
|
|
157
154
|
end
|
158
155
|
|
159
156
|
def consumer(key, &block)
|
160
|
-
|
157
|
+
raise MessageDriver::Error, 'you must provide a block' unless block_given?
|
161
158
|
@consumers[key] = block
|
162
159
|
end
|
163
160
|
|
@@ -168,14 +165,14 @@ module MessageDriver
|
|
168
165
|
def find_destination(destination_name)
|
169
166
|
destination = @destinations[destination_name]
|
170
167
|
if destination.nil?
|
171
|
-
|
168
|
+
raise MessageDriver::NoSuchDestinationError, "no destination #{destination_name} has been configured"
|
172
169
|
end
|
173
170
|
destination
|
174
171
|
end
|
175
172
|
|
176
173
|
def find_consumer(consumer_name)
|
177
174
|
consumer = @consumers[consumer_name]
|
178
|
-
|
175
|
+
raise MessageDriver::NoSuchConsumerError, "no consumer #{consumer_name} has been configured" if consumer.nil?
|
179
176
|
consumer
|
180
177
|
end
|
181
178
|
|
@@ -184,7 +181,7 @@ module MessageDriver
|
|
184
181
|
def resolve_adapter(adapter, options)
|
185
182
|
case adapter
|
186
183
|
when nil
|
187
|
-
|
184
|
+
raise 'you must specify an adapter'
|
188
185
|
when Symbol, String
|
189
186
|
resolve_adapter(find_adapter_class(adapter), options)
|
190
187
|
when Class
|
@@ -192,7 +189,7 @@ module MessageDriver
|
|
192
189
|
when MessageDriver::Adapters::Base
|
193
190
|
adapter
|
194
191
|
else
|
195
|
-
|
192
|
+
raise "adapter must be a MessageDriver::Adapters::Base, but this object is a #{adapter.class}"
|
196
193
|
end
|
197
194
|
end
|
198
195
|
|
@@ -202,11 +199,11 @@ module MessageDriver
|
|
202
199
|
adapter_method = "#{adapter_name}_adapter"
|
203
200
|
|
204
201
|
unless respond_to?(adapter_method)
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
202
|
+
raise ['the adapter',
|
203
|
+
adapter_name,
|
204
|
+
'must provide',
|
205
|
+
"MessageDriver::Broker##{adapter_method}",
|
206
|
+
'that returns the adapter class'].join(' ')
|
210
207
|
end
|
211
208
|
|
212
209
|
send(adapter_method)
|
@@ -21,7 +21,7 @@ module MessageDriver
|
|
21
21
|
# end
|
22
22
|
module Client
|
23
23
|
include Logging
|
24
|
-
extend self
|
24
|
+
extend self # rubocop:disable Style/ModuleFunction
|
25
25
|
|
26
26
|
# @!group Defining and Looking up Destinations
|
27
27
|
|
@@ -78,7 +78,7 @@ module MessageDriver
|
|
78
78
|
end
|
79
79
|
|
80
80
|
def subscribe(destination_name, consumer_name, options = {})
|
81
|
-
consumer =
|
81
|
+
consumer = find_consumer(consumer_name)
|
82
82
|
subscribe_with(destination_name, options, &consumer)
|
83
83
|
end
|
84
84
|
|
@@ -186,11 +186,7 @@ module MessageDriver
|
|
186
186
|
def fetch_context_wrapper(initialize = true)
|
187
187
|
wrapper = Thread.current[adapter_context_key]
|
188
188
|
if wrapper.nil? || !wrapper.valid?
|
189
|
-
if initialize
|
190
|
-
wrapper = build_context_wrapper
|
191
|
-
else
|
192
|
-
wrapper = nil
|
193
|
-
end
|
189
|
+
wrapper = (build_context_wrapper if initialize)
|
194
190
|
Thread.current[adapter_context_key] = wrapper
|
195
191
|
end
|
196
192
|
wrapper
|
@@ -23,15 +23,15 @@ module MessageDriver
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def message_count
|
26
|
-
|
26
|
+
current_adapter_context.message_count(self)
|
27
27
|
end
|
28
28
|
|
29
|
-
def subscribe(
|
30
|
-
|
29
|
+
def subscribe(options = {}, &consumer)
|
30
|
+
current_adapter_context.subscribe(self, options, &consumer)
|
31
31
|
end
|
32
32
|
|
33
33
|
def consumer_count
|
34
|
-
|
34
|
+
current_adapter_context.consumer_count(self)
|
35
35
|
end
|
36
36
|
|
37
37
|
def middleware
|
@@ -3,10 +3,11 @@ module MessageDriver
|
|
3
3
|
class Base
|
4
4
|
include Logging
|
5
5
|
|
6
|
-
attr_reader :ctx, :body, :raw_body, :headers, :properties
|
6
|
+
attr_reader :ctx, :destination, :body, :raw_body, :headers, :properties
|
7
7
|
|
8
|
-
def initialize(ctx, body, headers, properties, raw_body = nil)
|
8
|
+
def initialize(ctx, destination, body, headers, properties, raw_body = nil)
|
9
9
|
@ctx = ctx
|
10
|
+
@destination = destination
|
10
11
|
@body = body
|
11
12
|
@headers = headers
|
12
13
|
@properties = properties
|
@@ -5,7 +5,7 @@ module MessageDriver
|
|
5
5
|
|
6
6
|
def initialize(destination, opts)
|
7
7
|
super(destination)
|
8
|
-
|
8
|
+
raise ArgumentError, 'you must provide at least one of :on_publish and :on_consume' \
|
9
9
|
unless opts.keys.any? { |k| [:on_publish, :on_consume].include? k }
|
10
10
|
@on_publish_block = opts[:on_publish]
|
11
11
|
@on_consume_block = opts[:on_consume]
|
data/message-driver.gemspec
CHANGED
@@ -14,14 +14,14 @@ Gem::Specification.new do |gem|
|
|
14
14
|
gem.license = 'MIT'
|
15
15
|
|
16
16
|
gem.files = `git ls-files`.split($RS)
|
17
|
-
gem.executables = gem.files.grep(
|
18
|
-
gem.test_files = gem.files.grep(
|
17
|
+
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
18
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
19
19
|
gem.require_paths = %w(lib)
|
20
20
|
|
21
|
-
gem.required_ruby_version = '>= 1.9.
|
21
|
+
gem.required_ruby_version = '>= 1.9.3'
|
22
22
|
|
23
23
|
gem.add_development_dependency 'rake'
|
24
|
-
gem.add_development_dependency 'rspec', '~> 3.
|
25
|
-
gem.add_development_dependency 'cucumber', '
|
26
|
-
gem.add_development_dependency 'aruba', '
|
24
|
+
gem.add_development_dependency 'rspec', '~> 3.5.0'
|
25
|
+
gem.add_development_dependency 'cucumber', '~> 2.4.0'
|
26
|
+
gem.add_development_dependency 'aruba', '~> 0.14.1'
|
27
27
|
end
|
@@ -6,11 +6,13 @@ RSpec.describe 'AMQP Integration', :bunny, type: :integration do
|
|
6
6
|
context "when a queue can't be found" do
|
7
7
|
let(:queue_name) { 'my.lost.queue' }
|
8
8
|
it 'raises a MessageDriver::QueueNotFound error' do
|
9
|
+
# rubocop:disable Style/MultilineBlockChain
|
9
10
|
expect do
|
10
11
|
broker.dynamic_destination(queue_name, passive: true)
|
11
12
|
end.to raise_error(MessageDriver::QueueNotFound) do |err|
|
12
13
|
expect(err.nested).to be_a Bunny::NotFound
|
13
14
|
end
|
15
|
+
# rubocop:enable
|
14
16
|
end
|
15
17
|
end
|
16
18
|
|
@@ -54,7 +56,7 @@ RSpec.describe 'AMQP Integration', :bunny, type: :integration do
|
|
54
56
|
expect do
|
55
57
|
MessageDriver::Client.with_message_transaction do
|
56
58
|
destination.publish('Test Message')
|
57
|
-
|
59
|
+
raise 'unhandled error'
|
58
60
|
end
|
59
61
|
end.to raise_error 'unhandled error'
|
60
62
|
expect(destination.pop_message).to be_nil
|
@@ -64,7 +66,7 @@ RSpec.describe 'AMQP Integration', :bunny, type: :integration do
|
|
64
66
|
expect do
|
65
67
|
MessageDriver::Client.with_message_transaction do
|
66
68
|
destination.publish('Test Message 1')
|
67
|
-
|
69
|
+
raise 'unhandled error'
|
68
70
|
end
|
69
71
|
end.to raise_error 'unhandled error'
|
70
72
|
expect(destination.pop_message).to be_nil
|
@@ -83,7 +85,7 @@ RSpec.describe 'AMQP Integration', :bunny, type: :integration do
|
|
83
85
|
it 'does not raise an error' do
|
84
86
|
expect do
|
85
87
|
MessageDriver::Client.with_message_transaction do
|
86
|
-
#do nothing
|
88
|
+
# do nothing
|
87
89
|
end
|
88
90
|
end.not_to raise_error
|
89
91
|
end
|
@@ -92,7 +94,7 @@ RSpec.describe 'AMQP Integration', :bunny, type: :integration do
|
|
92
94
|
it 'does not raise an error' do
|
93
95
|
expect do
|
94
96
|
MessageDriver::Client.with_message_transaction(type: :confirm_and_wait) do
|
95
|
-
#do nothing
|
97
|
+
# do nothing
|
96
98
|
end
|
97
99
|
end.not_to raise_error
|
98
100
|
end
|