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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +20 -2
  3. data/.rubocop_todo.yml +15 -23
  4. data/.travis.yml +10 -22
  5. data/CHANGELOG.md +9 -0
  6. data/Gemfile +34 -24
  7. data/Guardfile +46 -29
  8. data/LICENSE +1 -1
  9. data/Rakefile +14 -6
  10. data/features/CHANGELOG.md +1 -0
  11. data/features/step_definitions/logging_steps.rb +3 -2
  12. data/features/support/firewall_helper.rb +2 -2
  13. data/features/support/no_error_matcher.rb +1 -1
  14. data/lib/message_driver/adapters/base.rb +115 -11
  15. data/lib/message_driver/adapters/bunny_adapter.rb +58 -46
  16. data/lib/message_driver/adapters/in_memory_adapter.rb +57 -35
  17. data/lib/message_driver/adapters/stomp_adapter.rb +10 -10
  18. data/lib/message_driver/broker.rb +16 -19
  19. data/lib/message_driver/client.rb +3 -7
  20. data/lib/message_driver/destination.rb +4 -4
  21. data/lib/message_driver/message.rb +3 -2
  22. data/lib/message_driver/middleware/block_middleware.rb +1 -1
  23. data/lib/message_driver/subscription.rb +1 -1
  24. data/lib/message_driver/version.rb +1 -1
  25. data/message-driver.gemspec +6 -6
  26. data/spec/integration/bunny/amqp_integration_spec.rb +6 -4
  27. data/spec/integration/bunny/bunny_adapter_spec.rb +1 -3
  28. data/spec/integration/in_memory/in_memory_adapter_spec.rb +46 -6
  29. data/spec/integration/stomp/stomp_adapter_spec.rb +0 -2
  30. data/spec/spec_helper.rb +6 -0
  31. data/spec/support/matchers/override_method_matcher.rb +7 -0
  32. data/spec/support/shared/adapter_examples.rb +3 -0
  33. data/spec/support/shared/client_ack_examples.rb +26 -4
  34. data/spec/support/shared/context_examples.rb +46 -0
  35. data/spec/support/shared/destination_examples.rb +28 -0
  36. data/spec/support/shared/subscription_examples.rb +6 -1
  37. data/spec/support/shared/transaction_examples.rb +35 -4
  38. data/spec/support/test_adapter.rb +19 -0
  39. data/spec/support/utils.rb +1 -5
  40. data/spec/units/message_driver/adapters/base_spec.rb +37 -31
  41. data/spec/units/message_driver/broker_spec.rb +1 -2
  42. data/spec/units/message_driver/client_spec.rb +3 -3
  43. data/spec/units/message_driver/destination_spec.rb +4 -2
  44. data/spec/units/message_driver/message_spec.rb +9 -3
  45. data/test_lib/broker_config.rb +0 -2
  46. data/test_lib/provider/base.rb +2 -6
  47. data/test_lib/provider/rabbitmq.rb +3 -3
  48. metadata +18 -16
  49. data/ci/travis_setup +0 -7
  50. 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 subscription
31
- adapter.subscription_for(name)
30
+ def subscriptions
31
+ adapter.subscriptions_for(name)
32
32
  end
33
33
 
34
- def message_count
34
+ def handle_message_count
35
35
  message_queue.size
36
36
  end
37
37
 
38
- def consumer_count
39
- adapter.consumer_count_for(name)
38
+ def handle_pop_message(ctx, options = {})
39
+ _fetch_message(ctx, options)
40
40
  end
41
41
 
42
- def pop_message(_options = {})
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
- deliver_messages(subscription)
45
+ _deliver_messages
57
46
  subscription
58
47
  end
59
48
 
60
- def publish(body, headers = {}, properties = {})
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
- deliver_messages(subscription) if subscription
54
+ _deliver_messages
66
55
  end
67
56
 
68
57
  private
69
58
 
70
- def deliver_messages(sub)
71
- until (msg = pop_message).nil?
72
- sub.deliver_message(msg)
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, :create_destination
114
+ def_delegators :adapter, :handle_create_destination
107
115
 
108
- def publish(destination, body, headers = {}, properties = {})
109
- destination.publish(body, headers, properties)
116
+ def handle_publish(destination, body, headers = {}, properties = {})
117
+ destination.handle_publish(body, headers, properties)
110
118
  end
111
119
 
112
- def pop_message(destination, options = {})
113
- destination.pop_message(options)
120
+ def handle_pop_message(destination, options = {})
121
+ destination.handle_pop_message(self, options)
114
122
  end
115
123
 
116
- def subscribe(destination, options = {}, &consumer)
117
- destination.subscribe(options, &consumer)
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 subscription_for(name)
142
- sub = @subscriptions[name].shift
143
- @subscriptions[name].push sub
144
- sub
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 subscribe(destination, consumer)
51
+ # def handle_subscribe(destination, consumer)
52
52
  # destination.subscribe(&consumer)
53
53
  # end
54
54
 
55
- def create_destination(name, dest_options = {}, message_props = {})
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 publish(destination, body, headers = {}, _properties = {})
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 pop_message(destination, options = {})
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
- fail MessageDriver::ConnectionError, conn.connection_frame.to_s unless conn.open?
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
- fail MessageDriver::Error,
119
- 'stomp 1.3.1 or a later version of the 1.3.x series is required for the stomp adapter'
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
- fail BrokerAlreadyConfigured, "there is already a broker named #{name} configured"
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
- fail BrokerNotConfigured,
35
- "There is no broker named #{name} configured. The configured brokers are #{brokers.keys}"
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 do |brk|
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 do |brk|
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
- fail MessageDriver::Error, 'you must provide a block' unless block_given?
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
- fail MessageDriver::NoSuchDestinationError, "no destination #{destination_name} has been configured"
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
- fail MessageDriver::NoSuchConsumerError, "no consumer #{consumer_name} has been configured" if consumer.nil?
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
- fail 'you must specify an adapter'
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
- fail "adapter must be a MessageDriver::Adapters::Base, but this object is a #{adapter.class}"
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
- fail ['the adapter',
206
- adapter_name,
207
- 'must provide',
208
- "MessageDriver::Broker##{adapter_method}",
209
- 'that returns the adapter class'].join(' ')
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 = find_consumer(consumer_name)
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
- fail "#message_count is not supported by #{self.class}"
26
+ current_adapter_context.message_count(self)
27
27
  end
28
28
 
29
- def subscribe(_options = {}, &_consumer)
30
- fail "#subscribe is not supported by #{self.class}"
29
+ def subscribe(options = {}, &consumer)
30
+ current_adapter_context.subscribe(self, options, &consumer)
31
31
  end
32
32
 
33
33
  def consumer_count
34
- fail "#consumer_count is not supported by #{self.class}"
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
- fail ArgumentError, 'you must provide at least one of :on_publish and :on_consume' \
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]
@@ -12,7 +12,7 @@ module MessageDriver
12
12
  end
13
13
 
14
14
  def unsubscribe
15
- fail 'must be implemented in subclass'
15
+ raise 'must be implemented in subclass'
16
16
  end
17
17
  end
18
18
  end
@@ -1,4 +1,4 @@
1
1
  module MessageDriver
2
2
  # @return [String] version of the library
3
- VERSION = '0.6.1'
3
+ VERSION = '0.7.0'.freeze
4
4
  end
@@ -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(/^bin\//).map { |f| File.basename(f) }
18
- gem.test_files = gem.files.grep(/^(test|spec|features)\//)
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.2'
21
+ gem.required_ruby_version = '>= 1.9.3'
22
22
 
23
23
  gem.add_development_dependency 'rake'
24
- gem.add_development_dependency 'rspec', '~> 3.1.0'
25
- gem.add_development_dependency 'cucumber', '< 2'
26
- gem.add_development_dependency 'aruba', '< 0.9'
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
- fail 'unhandled error'
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
- fail 'unhandled error'
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