message-driver 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -20,7 +20,7 @@ module FirewallHelper
20
20
  'sudo service rabbitmq-server start'
21
21
  ]
22
22
  }
23
- }
23
+ }.freeze
24
24
 
25
25
  def block_broker_port
26
26
  run_commands(:setup)
@@ -35,7 +35,7 @@ module FirewallHelper
35
35
  def run_commands(step)
36
36
  COMMANDS[os][step].each do |cmd|
37
37
  result = system(cmd)
38
- fail "command `#{cmd}` failed!" unless result
38
+ raise "command `#{cmd}` failed!" unless result
39
39
  end
40
40
  end
41
41
 
@@ -6,7 +6,7 @@ RSpec::Matchers.define :have_no_errors do
6
6
  failure_message do |test_runner|
7
7
  err = test_runner.raised_error
8
8
  filtered = (err.backtrace || []).reject do |line|
9
- Cucumber::Ast::StepInvocation::BACKTRACE_FILTER_PATTERNS.find { |p| line =~ p }
9
+ Cucumber::Formatter::BACKTRACE_FILTER_PATTERNS.find { |p| line =~ p }
10
10
  end
11
11
  (["#{err.class}: #{err}"] + filtered).join("\n ")
12
12
  end
@@ -10,7 +10,7 @@ module MessageDriver
10
10
  end
11
11
 
12
12
  def initialize(_broker, _configuration)
13
- fail 'Must be implemented in subclass'
13
+ raise 'Must be implemented in subclass'
14
14
  end
15
15
 
16
16
  def new_context
@@ -20,7 +20,7 @@ module MessageDriver
20
20
  end
21
21
 
22
22
  def build_context
23
- fail 'Must be implemented in subclass'
23
+ raise 'Must be implemented in subclass'
24
24
  end
25
25
 
26
26
  def reset_after_tests
@@ -31,7 +31,7 @@ module MessageDriver
31
31
  if @contexts
32
32
  ctxs = @contexts
33
33
  @contexts = []
34
- ctxs.each { |ctx| ctx.invalidate }
34
+ ctxs.each(&:invalidate)
35
35
  end
36
36
  end
37
37
  end
@@ -47,20 +47,56 @@ module MessageDriver
47
47
  @valid = true
48
48
  end
49
49
 
50
- def publish(_destination, _body, _headers = {}, _properties = {})
51
- fail 'Must be implemented in subclass'
50
+ def publish(destination, body, headers = {}, properties = {})
51
+ handle_publish(destination, body, headers, properties)
52
52
  end
53
53
 
54
- def pop_message(_destination, _options = {})
55
- fail 'Must be implemented in subclass'
54
+ def pop_message(destination, options = {})
55
+ handle_pop_message(destination, options)
56
56
  end
57
57
 
58
- def subscribe(_destination, _options = {}, &_consumer)
59
- fail "#subscribe is not supported by #{adapter.class}"
58
+ def subscribe(destination, options = {}, &consumer)
59
+ handle_subscribe(destination, options, &consumer)
60
60
  end
61
61
 
62
- def create_destination(_name, _dest_options = {}, _message_props = {})
63
- fail 'Must be implemented in subclass'
62
+ def create_destination(name, dest_options = {}, message_props = {})
63
+ handle_create_destination(name, dest_options, message_props)
64
+ end
65
+
66
+ def ack_message(message, options = {})
67
+ handle_ack_message(message, options)
68
+ end
69
+
70
+ def nack_message(message, options = {})
71
+ handle_nack_message(message, options)
72
+ end
73
+
74
+ def begin_transaction(options = {})
75
+ handle_begin_transaction(options)
76
+ end
77
+
78
+ def commit_transaction(options = {})
79
+ handle_commit_transaction(options)
80
+ end
81
+
82
+ def rollback_transaction(options = {})
83
+ handle_rollback_transaction(options)
84
+ end
85
+
86
+ def message_count(destination)
87
+ handle_message_count(destination)
88
+ end
89
+
90
+ def consumer_count(destination)
91
+ handle_consumer_count(destination)
92
+ end
93
+
94
+ def in_transaction?
95
+ if supports_transactions?
96
+ raise 'must be implemented in subclass'
97
+ else
98
+ raise "#in_transaction? not supported by #{adapter.class}"
99
+ end
64
100
  end
65
101
 
66
102
  def valid?
@@ -82,6 +118,74 @@ module MessageDriver
82
118
  def supports_subscriptions?
83
119
  false
84
120
  end
121
+
122
+ def handle_create_destination(_name, _dest_options = {}, _message_props = {})
123
+ raise 'Must be implemented in subclass'
124
+ end
125
+
126
+ def handle_publish(_destination, _body, _headers = {}, _properties = {})
127
+ raise 'Must be implemented in subclass'
128
+ end
129
+
130
+ def handle_pop_message(_destination, _options = {})
131
+ raise 'Must be implemented in subclass'
132
+ end
133
+
134
+ def handle_subscribe(_destination, _options = {}, &_consumer)
135
+ if supports_subscriptions?
136
+ raise 'Must be implemented in subclass'
137
+ else
138
+ raise "#subscribe is not supported by #{adapter.class}"
139
+ end
140
+ end
141
+
142
+ def handle_ack_message(_message, _options = {})
143
+ if supports_client_acks?
144
+ raise 'Must be implemented in subclass'
145
+ else
146
+ raise "#ack_message is not supported by #{adapter.class}"
147
+ end
148
+ end
149
+
150
+ def handle_nack_message(_message, _options = {})
151
+ if supports_client_acks?
152
+ raise 'Must be implemented in subclass'
153
+ else
154
+ raise "#nack_message is not supported by #{adapter.class}"
155
+ end
156
+ end
157
+
158
+ def handle_begin_transaction(_options = {})
159
+ if supports_transactions?
160
+ raise 'Must be implemented in subclass'
161
+ else
162
+ raise "transactions are not supported by #{adapter.class}"
163
+ end
164
+ end
165
+
166
+ def handle_commit_transaction(_options = {})
167
+ if supports_transactions?
168
+ raise 'Must be implemented in subclass'
169
+ else
170
+ raise "transactions are not supported by #{adapter.class}"
171
+ end
172
+ end
173
+
174
+ def handle_rollback_transaction(_options = {})
175
+ if supports_transactions?
176
+ raise 'Must be implemented in subclass'
177
+ else
178
+ raise "transactions are not supported by #{adapter.class}"
179
+ end
180
+ end
181
+
182
+ def handle_message_count(destination)
183
+ raise "#message_count is not supported by #{destination.class}"
184
+ end
185
+
186
+ def handle_consumer_count(destination)
187
+ raise "#consumer_count is not supported by #{destination.class}"
188
+ end
85
189
  end
86
190
  end
87
191
  end
@@ -25,7 +25,7 @@ module MessageDriver
25
25
  raw_headers = properties[:headers]
26
26
  raw_headers = {} if raw_headers.nil?
27
27
  b, h, p = destination.middleware.on_consume(payload, raw_headers, properties)
28
- super(ctx, b, h, p, raw_body)
28
+ super(ctx, destination, b, h, p, raw_body)
29
29
  @delivery_info = delivery_info
30
30
  end
31
31
 
@@ -59,10 +59,10 @@ module MessageDriver
59
59
  def after_initialize(adapter_context)
60
60
  if @dest_options[:no_declare]
61
61
  if @name.empty?
62
- fail MessageDriver::Error, 'server-named queues must be declared, but you provided :no_declare => true'
62
+ raise MessageDriver::Error, 'server-named queues must be declared, but you provided :no_declare => true'
63
63
  end
64
64
  if @dest_options[:bindings]
65
- fail MessageDriver::Error, 'queues with bindings must be declared, but you provided :no_declare => true'
65
+ raise MessageDriver::Error, 'queues with bindings must be declared, but you provided :no_declare => true'
66
66
  end
67
67
  else
68
68
  adapter_context.with_channel(false) do |ch|
@@ -73,7 +73,7 @@ module MessageDriver
73
73
 
74
74
  def bunny_queue(channel, options = {})
75
75
  opts = @dest_options.dup
76
- opts.merge!(passive: options[:passive]) if options.key? :passive
76
+ opts[:passive] = options[:passive] if options.key? :passive
77
77
  queue = channel.queue(@name, opts)
78
78
  handle_queue_init(queue) if options.fetch(:init, false)
79
79
  queue
@@ -83,7 +83,7 @@ module MessageDriver
83
83
  @name = queue.name
84
84
  if (bindings = @dest_options[:bindings])
85
85
  bindings.each do |bnd|
86
- fail MessageDriver::Error, "binding #{bnd.inspect} must provide a source!" unless bnd[:source]
86
+ raise MessageDriver::Error, "binding #{bnd.inspect} must provide a source!" unless bnd[:source]
87
87
  queue.bind(bnd[:source], bnd[:args] || {})
88
88
  end
89
89
  end
@@ -97,24 +97,24 @@ module MessageDriver
97
97
  @name
98
98
  end
99
99
 
100
- def message_count
101
- adapter.broker.client.current_adapter_context.with_channel(false) do |ch|
100
+ def handle_message_count
101
+ current_adapter_context.with_channel(false) do |ch|
102
102
  bunny_queue(ch, passive: true).message_count
103
103
  end
104
104
  end
105
105
 
106
106
  def subscribe(options = {}, &consumer)
107
- adapter.broker.client.current_adapter_context.subscribe(self, options, &consumer)
107
+ current_adapter_context.subscribe(self, options, &consumer)
108
108
  end
109
109
 
110
- def consumer_count
111
- adapter.broker.client.current_adapter_context.with_channel(false) do |ch|
110
+ def handle_consumer_count
111
+ current_adapter_context.with_channel(false) do |ch|
112
112
  bunny_queue(ch, passive: true).consumer_count
113
113
  end
114
114
  end
115
115
 
116
116
  def purge
117
- adapter.broker.client.current_adapter_context.with_channel(false) do |ch|
117
+ current_adapter_context.with_channel(false) do |ch|
118
118
  bunny_queue(ch).purge
119
119
  end
120
120
  end
@@ -125,14 +125,14 @@ module MessageDriver
125
125
  if (declare = @dest_options[:declare])
126
126
  adapter_context.with_channel(false) do |ch|
127
127
  type = declare.delete(:type)
128
- fail MessageDriver::Error, 'you must provide a valid exchange type' unless type
128
+ raise MessageDriver::Error, 'you must provide a valid exchange type' unless type
129
129
  ch.exchange_declare(@name, type, declare)
130
130
  end
131
131
  end
132
132
  if (bindings = @dest_options[:bindings])
133
133
  adapter_context.with_channel(false) do |ch|
134
134
  bindings.each do |bnd|
135
- fail MessageDriver::Error, "binding #{bnd.inspect} must provide a source!" unless bnd[:source]
135
+ raise MessageDriver::Error, "binding #{bnd.inspect} must provide a source!" unless bnd[:source]
136
136
  ch.exchange_bind(bnd[:source], @name, bnd[:args] || {})
137
137
  end
138
138
  end
@@ -145,8 +145,8 @@ module MessageDriver
145
145
 
146
146
  def start
147
147
  unless destination.is_a? QueueDestination
148
- fail MessageDriver::Error,
149
- 'subscriptions are only supported with QueueDestinations'
148
+ raise MessageDriver::Error,
149
+ 'subscriptions are only supported with QueueDestinations'
150
150
  end
151
151
  @sub_ctx = adapter.new_subscription_context(self)
152
152
  @error_handler = options[:error_handler]
@@ -158,7 +158,7 @@ module MessageDriver
158
158
  when :transactional
159
159
  TransactionalAckHandler.new(self)
160
160
  else
161
- fail MessageDriver::Error, "unrecognized :ack option #{options[:ack]}"
161
+ raise MessageDriver::Error, "unrecognized :ack option #{options[:ack]}"
162
162
  end
163
163
  start_subscription
164
164
  end
@@ -305,14 +305,14 @@ module MessageDriver
305
305
  @require_commit = false
306
306
  end
307
307
 
308
- def create_destination(name, dest_options = {}, message_props = {})
308
+ def handle_create_destination(name, dest_options = {}, message_props = {})
309
309
  dest = case type = dest_options.delete(:type)
310
310
  when :exchange
311
311
  ExchangeDestination.new(adapter, name, dest_options, message_props)
312
312
  when :queue, nil
313
313
  QueueDestination.new(adapter, name, dest_options, message_props)
314
314
  else
315
- fail MessageDriver::Error, "invalid destination type #{type}"
315
+ raise MessageDriver::Error, "invalid destination type #{type}"
316
316
  end
317
317
  dest.after_initialize(self)
318
318
  dest
@@ -322,33 +322,29 @@ module MessageDriver
322
322
  true
323
323
  end
324
324
 
325
- def begin_transaction(options = {})
325
+ def handle_begin_transaction(options = {})
326
326
  if in_transaction?
327
- fail MessageDriver::TransactionError,
328
- "you can't begin another transaction, you are already in one!"
327
+ raise MessageDriver::TransactionError,
328
+ "you can't begin another transaction, you are already in one!"
329
329
  end
330
330
  @in_transaction = true
331
331
  @in_confirms_transaction = true if options[:type] == :confirm_and_wait
332
332
  end
333
333
 
334
- def commit_transaction
334
+ def handle_commit_transaction(_ = nil)
335
335
  if !in_transaction? && !@require_commit
336
- fail MessageDriver::TransactionError,
337
- "you can't finish the transaction unless you already in one!"
336
+ raise MessageDriver::TransactionError,
337
+ "you can't finish the transaction unless you already in one!"
338
338
  end
339
339
  begin
340
340
  if @in_confirms_transaction
341
- unless @rollback_only || @channel.nil?
342
- @channel.wait_for_confirms
343
- end
344
- else
345
- if is_transactional? && valid? && !@need_channel_reset && @require_commit
346
- handle_errors do
347
- if @rollback_only
348
- @channel.tx_rollback
349
- else
350
- @channel.tx_commit
351
- end
341
+ @channel.wait_for_confirms unless @rollback_only || @channel.nil?
342
+ elsif is_transactional? && valid? && !@need_channel_reset && @require_commit
343
+ handle_errors do
344
+ if @rollback_only
345
+ @channel.tx_rollback
346
+ else
347
+ @channel.tx_commit
352
348
  end
353
349
  end
354
350
  end
@@ -360,7 +356,7 @@ module MessageDriver
360
356
  end
361
357
  end
362
358
 
363
- def rollback_transaction
359
+ def handle_rollback_transaction(_ = nil)
364
360
  @rollback_only = true
365
361
  commit_transaction
366
362
  end
@@ -368,13 +364,13 @@ module MessageDriver
368
364
  def transactional?
369
365
  @is_transactional
370
366
  end
371
- alias_method :is_transactional?, :transactional?
367
+ alias is_transactional? transactional?
372
368
 
373
369
  def in_transaction?
374
370
  @in_transaction
375
371
  end
376
372
 
377
- def publish(destination, body, headers = {}, properties = {})
373
+ def handle_publish(destination, body, headers = {}, properties = {})
378
374
  body, exchange, routing_key, props = *destination.publish_params(body, headers, properties)
379
375
  confirm = props.delete(:confirm)
380
376
  confirm = false if confirm.nil?
@@ -387,8 +383,8 @@ module MessageDriver
387
383
  end
388
384
  end
389
385
 
390
- def pop_message(destination, options = {})
391
- fail MessageDriver::Error, "You can't pop a message off an exchange" if destination.is_a? ExchangeDestination
386
+ def handle_pop_message(destination, options = {})
387
+ raise MessageDriver::Error, "You can't pop a message off an exchange" if destination.is_a? ExchangeDestination
392
388
 
393
389
  with_channel(false) do |ch|
394
390
  queue = ch.queue(destination.name, passive: true)
@@ -406,13 +402,13 @@ module MessageDriver
406
402
  true
407
403
  end
408
404
 
409
- def ack_message(message, _options = {})
405
+ def handle_ack_message(message, _options = {})
410
406
  with_channel(true) do |ch|
411
407
  ch.ack(message.delivery_tag)
412
408
  end
413
409
  end
414
410
 
415
- def nack_message(message, options = {})
411
+ def handle_nack_message(message, options = {})
416
412
  requeue = options.fetch(:requeue, true)
417
413
  with_channel(true) do |ch|
418
414
  ch.reject(message.delivery_tag, requeue)
@@ -423,12 +419,28 @@ module MessageDriver
423
419
  true
424
420
  end
425
421
 
426
- def subscribe(destination, options = {}, &consumer)
422
+ def handle_subscribe(destination, options = {}, &consumer)
427
423
  sub = Subscription.new(adapter, destination, consumer, options)
428
424
  sub.start
429
425
  sub
430
426
  end
431
427
 
428
+ def handle_message_count(destination)
429
+ if destination.respond_to?(:handle_message_count)
430
+ destination.handle_message_count
431
+ else
432
+ super
433
+ end
434
+ end
435
+
436
+ def handle_consumer_count(destination)
437
+ if destination.respond_to?(:handle_consumer_count)
438
+ destination.handle_consumer_count
439
+ else
440
+ super
441
+ end
442
+ end
443
+
432
444
  def invalidate(in_unsubscribe = false)
433
445
  super()
434
446
  unless @subscription.nil? || in_unsubscribe
@@ -488,8 +500,8 @@ module MessageDriver
488
500
  end
489
501
 
490
502
  def with_channel(require_commit = true)
491
- fail MessageDriver::TransactionRollbackOnly if @rollback_only
492
- fail MessageDriver::Error, 'this adapter context is not valid!' unless valid?
503
+ raise MessageDriver::TransactionRollbackOnly if @rollback_only
504
+ raise MessageDriver::Error, 'this adapter context is not valid!' unless valid?
493
505
  ensure_channel
494
506
  @require_commit ||= require_commit
495
507
  if in_transaction?
@@ -535,7 +547,7 @@ module MessageDriver
535
547
  required = Gem::Requirement.create('>= 1.7.0')
536
548
  current = Gem::Version.create(Bunny::VERSION)
537
549
  unless required.satisfied_by? current
538
- fail MessageDriver::Error, 'bunny 1.7.0 or later is required for the bunny adapter'
550
+ raise MessageDriver::Error, 'bunny 1.7.0 or later is required for the bunny adapter'
539
551
  end
540
552
  end
541
553
  end