message-driver 0.4.0 → 0.5.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 (74) hide show
  1. checksums.yaml +4 -4
  2. data/.coveralls.yml +2 -0
  3. data/.rubocop.yml +26 -2
  4. data/.rubocop_todo.yml +15 -123
  5. data/.travis.yml +2 -1
  6. data/CHANGELOG.md +10 -1
  7. data/Gemfile +15 -6
  8. data/Rakefile +23 -12
  9. data/ci/reset_vhost +8 -3
  10. data/ci/travis_setup +0 -3
  11. data/features/.nav +6 -1
  12. data/features/CHANGELOG.md +10 -1
  13. data/features/amqp_specific_features/binding_amqp_destinations.feature +1 -0
  14. data/features/amqp_specific_features/declaring_amqp_exchanges.feature +1 -0
  15. data/features/amqp_specific_features/server_named_destinations.feature +1 -0
  16. data/features/destination_metadata.feature +26 -0
  17. data/features/logging.feature +1 -1
  18. data/features/middleware/middleware_basics.feature +91 -0
  19. data/features/middleware/middleware_ordering.feature +60 -0
  20. data/features/middleware/middleware_parameters.feature +43 -0
  21. data/features/middleware/middleware_with_blocks.feature +85 -0
  22. data/features/step_definitions/dynamic_destinations_steps.rb +1 -1
  23. data/features/step_definitions/message_consumers_steps.rb +5 -0
  24. data/features/step_definitions/middleware_steps.rb +10 -0
  25. data/features/step_definitions/steps.rb +10 -2
  26. data/features/support/env.rb +4 -3
  27. data/features/support/firewall_helper.rb +1 -1
  28. data/features/support/message_table_matcher.rb +3 -2
  29. data/features/support/no_error_matcher.rb +2 -2
  30. data/features/support/test_runner.rb +11 -57
  31. data/features/support/transforms.rb +12 -10
  32. data/lib/message_driver.rb +3 -1
  33. data/lib/message_driver/adapters/base.rb +11 -11
  34. data/lib/message_driver/adapters/bunny_adapter.rb +189 -132
  35. data/lib/message_driver/adapters/in_memory_adapter.rb +51 -34
  36. data/lib/message_driver/adapters/stomp_adapter.rb +22 -23
  37. data/lib/message_driver/broker.rb +21 -16
  38. data/lib/message_driver/client.rb +15 -16
  39. data/lib/message_driver/destination.rb +26 -8
  40. data/lib/message_driver/message.rb +5 -4
  41. data/lib/message_driver/middleware.rb +8 -0
  42. data/lib/message_driver/middleware/base.rb +19 -0
  43. data/lib/message_driver/middleware/block_middleware.rb +33 -0
  44. data/lib/message_driver/middleware/middleware_stack.rb +61 -0
  45. data/lib/message_driver/subscription.rb +2 -2
  46. data/lib/message_driver/version.rb +1 -1
  47. data/message-driver.gemspec +3 -4
  48. data/spec/integration/bunny/amqp_integration_spec.rb +21 -82
  49. data/spec/integration/bunny/bunny_adapter_spec.rb +288 -268
  50. data/spec/integration/in_memory/in_memory_adapter_spec.rb +93 -90
  51. data/spec/integration/stomp/stomp_adapter_spec.rb +126 -93
  52. data/spec/spec_helper.rb +11 -9
  53. data/spec/support/shared/adapter_examples.rb +1 -1
  54. data/spec/support/shared/client_ack_examples.rb +4 -4
  55. data/spec/support/shared/context_examples.rb +6 -4
  56. data/spec/support/shared/destination_examples.rb +54 -14
  57. data/spec/support/shared/subscription_examples.rb +33 -26
  58. data/spec/support/shared/transaction_examples.rb +12 -12
  59. data/spec/support/utils.rb +1 -1
  60. data/spec/units/message_driver/adapters/base_spec.rb +42 -40
  61. data/spec/units/message_driver/broker_spec.rb +38 -38
  62. data/spec/units/message_driver/client_spec.rb +87 -87
  63. data/spec/units/message_driver/destination_spec.rb +16 -11
  64. data/spec/units/message_driver/message_spec.rb +96 -70
  65. data/spec/units/message_driver/middleware/base_spec.rb +30 -0
  66. data/spec/units/message_driver/middleware/block_middleware_spec.rb +82 -0
  67. data/spec/units/message_driver/middleware/middleware_stack_spec.rb +165 -0
  68. data/spec/units/message_driver/subscription_spec.rb +18 -16
  69. data/test_lib/broker_config.rb +21 -5
  70. data/test_lib/coverage.rb +11 -0
  71. data/test_lib/provider/base.rb +59 -0
  72. data/test_lib/provider/in_memory.rb +6 -0
  73. data/test_lib/provider/rabbitmq.rb +67 -0
  74. metadata +46 -35
@@ -1,4 +1,5 @@
1
1
  @bunny
2
+ @read_queues_directly
2
3
  Feature: Binding AMQP destinations to exchanges
3
4
  Background:
4
5
  Given the following broker configuration
@@ -1,4 +1,5 @@
1
1
  @bunny
2
+ @read_queues_directly
2
3
  Feature: Declaring AMQP exchanges
3
4
  If you want to create an exchange that doesn't exist on the broker, you can do so by adding
4
5
  the "declare" option to your destination.
@@ -1,4 +1,5 @@
1
1
  @bunny
2
+ @read_queues_directly
2
3
  Feature: Server-Named Destinations
3
4
  AMQP brokers allow you to create queues that are named by the server. Here's
4
5
  how you do it with message_driver.
@@ -29,3 +29,29 @@ Feature: Destination Metadata
29
29
 
30
30
  Then I expect to have no errors
31
31
  And I expect to find 2 messages on :my_queue
32
+
33
+ Scenario: Check the consumer count when the queue has no consumers
34
+ When I execute the following code
35
+ """ruby
36
+ destination = MessageDriver::Client.find_destination(:my_queue)
37
+ expect(destination.consumer_count).to eq(0)
38
+ """
39
+
40
+ Then I expect to have no errors
41
+
42
+ Scenario: Check the consumer count when the queue has consumers
43
+ Given I create some subscriptions
44
+ """ruby
45
+ 3.times do
46
+ MessageDriver::Client.subscribe_with(:my_queue) do |message|
47
+ puts message.inspect
48
+ end
49
+ end
50
+ """
51
+ When I execute the following code
52
+ """ruby
53
+ destination = MessageDriver::Client.find_destination(:my_queue)
54
+ expect(destination.consumer_count).to eq(3)
55
+ """
56
+
57
+ Then I expect to have no errors
@@ -1,5 +1,5 @@
1
1
  @all_adapters
2
- Feature: Stuff gets logged if you set a logger
2
+ Feature: Logging
3
3
 
4
4
  You can configure the logger by setting it on `MessageDriver.logger`.
5
5
  If you don't provide a logger, then an info level logger will be created
@@ -0,0 +1,91 @@
1
+ @bunny
2
+ @in_memory
3
+ Feature: Middleware Basics
4
+
5
+ Middlewares can be used to transform messages that are about to be published
6
+ or that are about to be consumed. This allows for handling of things like
7
+ serializing and deserializing the message body in a way that is transparent to
8
+ your application code.
9
+
10
+ Middlewares are applied in a stack to destinations, much like Rack middleware.
11
+ As a message that is about to be consumed, it starts by coming in the top of
12
+ the middleware stack and works it's way down before it is returned by
13
+ `pop_message` or passed to a consumer.
14
+
15
+ For messages that are being published, they start at the bottom of the stack
16
+ and work their way up until they are finally passed to the underlying driver
17
+ and sent to the message broker.
18
+
19
+ Background:
20
+ Given I am connected to the broker
21
+ And I have a destination :middleware_queue with no messages on it
22
+ And I have a middleware class
23
+ """ruby
24
+ class ExampleMiddleware < MessageDriver::Middleware::Base
25
+ def on_publish(body, headers, properties)
26
+ [body+':about_to_publish', headers, properties]
27
+ end
28
+
29
+ def on_consume(body, headers, properties)
30
+ ['about_to_be_consumed:'+body, headers, properties]
31
+ end
32
+ end
33
+ """
34
+
35
+ Scenario: The middleware stack of a destination is initially empty
36
+ When I execute the following code
37
+ """ruby
38
+ destination = MessageDriver::Client.find_destination(:middleware_queue)
39
+ expect(destination.middleware).to be_empty
40
+ """
41
+ Then I expect to have no errors
42
+
43
+
44
+ Scenario: Adding a piece of middleware to a destination
45
+ When I execute the following code
46
+ """ruby
47
+ destination = MessageDriver::Client.find_destination(:middleware_queue)
48
+ destination.middleware.append ExampleMiddleware
49
+ """
50
+
51
+ Then I expect the following check to pass
52
+ """ruby
53
+ destination = MessageDriver::Client.find_destination(:middleware_queue)
54
+ expect(destination.middleware).to include(an_instance_of(ExampleMiddleware))
55
+ """
56
+
57
+
58
+ Scenario: Middleware is applied to messages as they are published
59
+ When I append middleware "ExampleMiddleware" to :middleware_queue
60
+ And I send the following messages to :middleware_queue
61
+ | body |
62
+ | Test Message 1 |
63
+ | Test Message 2 |
64
+
65
+ Then I expect to find the following 2 messages on :middleware_queue
66
+ | raw_body |
67
+ | Test Message 1:about_to_publish |
68
+ | Test Message 2:about_to_publish |
69
+
70
+
71
+ Scenario: Middleware is applied to messages as they are consumed
72
+ Given I have a destination :dest_queue with no messages on it
73
+
74
+ When I send the following messages to :middleware_queue
75
+ | body |
76
+ | Test Message 1 |
77
+ | Test Message 2 |
78
+ And I append middleware "ExampleMiddleware" to :middleware_queue
79
+ And I create a subscription
80
+ """ruby
81
+ MessageDriver::Client.subscribe_with(:middleware_queue) do |message|
82
+ MessageDriver::Client.publish(:dest_queue, message.body)
83
+ end
84
+ """
85
+ And I let the subscription process
86
+
87
+ Then I expect to find no messages on :middleware_queue
88
+ And I expect to find the following 2 messages on :dest_queue
89
+ | raw_body |
90
+ | about_to_be_consumed:Test Message 1 |
91
+ | about_to_be_consumed:Test Message 2 |
@@ -0,0 +1,60 @@
1
+ @bunny
2
+ @in_memory
3
+ Feature: Middleware Ordering
4
+
5
+ Middleware are applied based on the order they appear in the stack. For
6
+ publish operations, middlewares are applied bottom of the stack to the top.
7
+ For consume operations, middleware are applied from the top of the stack to
8
+ the bottom.
9
+
10
+ Scenario: Apply two middlewares to a destination
11
+ Given I am connected to the broker
12
+ And I have a destination :middleware_queue with no messages on it
13
+ And I have a middleware class
14
+ """ruby
15
+ class MiddlewareOne < MessageDriver::Middleware::Base
16
+ def on_publish(body, headers, properties)
17
+ [body+':publish1', headers, properties]
18
+ end
19
+
20
+ def on_consume(body, headers, properties)
21
+ ['consume1:'+body, headers, properties]
22
+ end
23
+ end
24
+ """
25
+ And I have a middleware class
26
+ """ruby
27
+ class MiddlewareTwo < MessageDriver::Middleware::Base
28
+ def on_publish(body, headers, properties)
29
+ [body+':publish2', headers, properties]
30
+ end
31
+
32
+ def on_consume(body, headers, properties)
33
+ ['consume2:'+body, headers, properties]
34
+ end
35
+ end
36
+ """
37
+ And I append middleware "MiddlewareOne" to :middleware_queue
38
+ And I append middleware "MiddlewareTwo" to :middleware_queue
39
+
40
+ And I have a destination :dest_queue with no messages on it
41
+ And I create a subscription
42
+ """ruby
43
+ MessageDriver::Client.subscribe_with(:middleware_queue) do |message|
44
+ MessageDriver::Client.publish(:dest_queue, message.body)
45
+ end
46
+ """
47
+
48
+ When I send the following messages to :middleware_queue
49
+ | body |
50
+ | Test Message 1 |
51
+ | Test Message 2 |
52
+ | Test Message 3 |
53
+ And I let the subscription process
54
+
55
+ Then I expect to find no messages on :middleware_queue
56
+ And I expect to find the following 3 messages on :dest_queue
57
+ | body |
58
+ | consume1:consume2:Test Message 1:publish1:publish2 |
59
+ | consume1:consume2:Test Message 2:publish1:publish2 |
60
+ | consume1:consume2:Test Message 3:publish1:publish2 |
@@ -0,0 +1,43 @@
1
+ @bunny
2
+ @in_memory
3
+ Feature: Middleware Parameters
4
+
5
+ You can provide parameters to your middleware by passing them as additional
6
+ paremeters to the append and prepend calls.
7
+
8
+ Background:
9
+ Given I am connected to the broker
10
+ And I have a destination :middleware_queue with no messages on it
11
+ And I have a middleware class
12
+ """ruby
13
+ class ParameterizedMiddleware < MessageDriver::Middleware::Base
14
+
15
+ attr_reader :prefix, :seperator
16
+
17
+ def initialize(destination, prefix, seperator)
18
+ super(destination)
19
+ @prefix = prefix
20
+ @seperator = seperator
21
+ end
22
+
23
+ def on_publish(body, headers, properties)
24
+ ["#{prefix}#{seperator}#{body}", headers, properties]
25
+ end
26
+ end
27
+ """
28
+
29
+ Scenario: Adding a parameterized middleware to the stack
30
+ When I execute the following code
31
+ """ruby
32
+ destination = MessageDriver::Client.find_destination(:middleware_queue)
33
+ destination.middleware.append ParameterizedMiddleware, 'pub_pre', ':'
34
+ """
35
+ And I send the following messages to :middleware_queue
36
+ | body |
37
+ | Test Message 1 |
38
+ | Test Message 2 |
39
+
40
+ Then I expect to find the following 2 messages on :middleware_queue
41
+ | raw_body |
42
+ | pub_pre:Test Message 1 |
43
+ | pub_pre:Test Message 2 |
@@ -0,0 +1,85 @@
1
+ @bunny
2
+ @in_memory
3
+ Feature: Middleware with Blocks
4
+
5
+ You can also create middleware by passing a hash with blocks as the input to
6
+ append or prepend
7
+
8
+ Background:
9
+ Given I am connected to the broker
10
+ And I have a destination :middleware_queue with no messages on it
11
+
12
+ Scenario: providing an on_publish block
13
+ When I execute the following code
14
+ """ruby
15
+ destination = MessageDriver::Client.find_destination(:middleware_queue)
16
+ destination.middleware.append on_publish: ->(body, headers, properties) { [body+' published with a block!', headers, properties] }
17
+ """
18
+
19
+ And I send the following messages to :middleware_queue
20
+ | body |
21
+ | Test Message 1 |
22
+ | Test Message 2 |
23
+
24
+ Then I expect to find the following 2 messages on :middleware_queue
25
+ | raw_body |
26
+ | Test Message 1 published with a block! |
27
+ | Test Message 2 published with a block! |
28
+
29
+
30
+ Scenario: providing an on_consume block
31
+ Given I have a destination :dest_queue with no messages on it
32
+
33
+ When I execute the following code
34
+ """ruby
35
+ destination = MessageDriver::Client.find_destination(:middleware_queue)
36
+ destination.middleware.append on_consume: ->(body, headers, properties) { [body+' consumed with a block!', headers, properties] }
37
+ """
38
+
39
+ And I send the following messages to :middleware_queue
40
+ | body |
41
+ | Test Message 1 |
42
+ | Test Message 2 |
43
+
44
+ And I create a subscription
45
+ """ruby
46
+ MessageDriver::Client.subscribe_with(:middleware_queue) do |message|
47
+ MessageDriver::Client.publish(:dest_queue, message.body)
48
+ end
49
+ """
50
+ And I let the subscription process
51
+
52
+ Then I expect to find no messages on :middleware_queue
53
+ And I expect to find the following 2 messages on :dest_queue
54
+ | raw_body |
55
+ | Test Message 1 consumed with a block! |
56
+ | Test Message 2 consumed with a block! |
57
+
58
+ Scenario: providing an on_consume block and an on_publish block
59
+ Given I have a destination :dest_queue with no messages on it
60
+
61
+ When I execute the following code
62
+ """ruby
63
+ destination = MessageDriver::Client.find_destination(:middleware_queue)
64
+ destination.middleware.append(on_consume: ->(body, headers, properties) { [body + ' consumed', headers, properties] },
65
+ on_publish: ->(body, headers, properties) { ['published ' + body, headers, properties] })
66
+ """
67
+
68
+ And I send the following messages to :middleware_queue
69
+ | body |
70
+ | Test Message 1 |
71
+ | Test Message 2 |
72
+
73
+ And I create a subscription
74
+ """ruby
75
+ MessageDriver::Client.subscribe_with(:middleware_queue) do |message|
76
+ MessageDriver::Client.publish(:dest_queue, message.body)
77
+ end
78
+ """
79
+ And I let the subscription process
80
+
81
+ Then I expect to find no messages on :middleware_queue
82
+ And I expect to find the following 2 messages on :dest_queue
83
+ | raw_body |
84
+ | published Test Message 1 consumed |
85
+ | published Test Message 2 consumed |
@@ -7,6 +7,6 @@ Then(/^I expect to find (#{NUMBER}) messages? on the dynamic destination "(#{STR
7
7
  expect(test_runner).to have_no_errors
8
8
  dest = MessageDriver::Client[test_runner.broker_name].dynamic_destination(destination, passive: true)
9
9
  messages = test_runner.fetch_messages(dest)
10
- expect(messages).to have(count).items
10
+ expect(messages.size).to eq(count)
11
11
  expect(messages).to match_message_table(table)
12
12
  end
@@ -12,6 +12,11 @@ Given 'I create a subscription' do |src|
12
12
  expect(test_runner).to have_no_errors
13
13
  end
14
14
 
15
+ Given 'I create some subscriptions' do |src|
16
+ test_runner.run_test_code(src)
17
+ expect(test_runner).to have_no_errors
18
+ end
19
+
15
20
  When 'I cancel the subscription' do
16
21
  test_runner.run_test_code('@subscription.unsubscribe')
17
22
  step 'I allow for processing'
@@ -0,0 +1,10 @@
1
+ Given('I have a middleware class') do |src|
2
+ write_file('feature_middleware.rb', src)
3
+ in_current_dir { load './feature_middleware.rb' }
4
+ end
5
+
6
+ When(/^I append middleware "(.*?)" to (#{STRING_OR_SYM})$/) do |class_name, dest_name|
7
+ klass = Object.const_get(class_name)
8
+ destination = test_runner.fetch_destination(dest_name)
9
+ destination.middleware.append klass
10
+ end
@@ -59,14 +59,14 @@ end
59
59
  Then(/^I expect to find (#{NUMBER}) messages? on (#{STRING_OR_SYM})$/) do |count, destination|
60
60
  expect(test_runner).to have_no_errors
61
61
  messages = test_runner.fetch_messages(destination)
62
- expect(messages).to have(count).items, "expected #{count} messages, but got these instead: #{messages.map(&:body)}"
62
+ expect(messages.size).to eq(count), "expected #{count} messages, but got these instead: #{messages.map(&:body)}"
63
63
  end
64
64
 
65
65
  Then(/^I expect to find the following (#{NUMBER}) messages? on (#{STRING_OR_SYM})$/) do |count, destination, table|
66
66
  expect(test_runner).to have_no_errors
67
67
  messages = test_runner.fetch_messages(destination)
68
68
  expect(messages).to match_message_table(table)
69
- expect(messages).to have(count).items
69
+ expect(messages.size).to eq(count)
70
70
  end
71
71
 
72
72
  Then(/^I expect to find the following message on (#{STRING_OR_SYM})$/) do |destination, table|
@@ -99,3 +99,11 @@ end
99
99
  Before do |current_scenario|
100
100
  test_runner.current_feature_file = current_scenario.feature.file
101
101
  end
102
+
103
+ Before '@read_queues_directly' do
104
+ test_runner.provider.read_queues_directly = true
105
+ end
106
+
107
+ After '@read_queues_directly' do
108
+ test_runner.provider.read_queues_directly = false
109
+ end
@@ -1,11 +1,12 @@
1
- require 'coveralls'
2
- Coveralls.wear!
3
-
1
+ ENV['COMMAND_NAME'] = 'features'
2
+ require File.join(File.dirname(__FILE__), '..', '..', 'test_lib', 'coverage')
4
3
  require File.join(File.dirname(__FILE__), '..', '..', 'test_lib', 'broker_config')
5
4
 
6
5
  require 'aruba/cucumber'
7
6
  require 'message_driver'
8
7
 
8
+ BrokerConfig.setup_provider
9
+
9
10
  After do
10
11
  MessageDriver::Broker.reset
11
12
  end
@@ -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
- raise "command `#{cmd}` failed!" unless result
38
+ fail "command `#{cmd}` failed!" unless result
39
39
  end
40
40
  end
41
41
 
@@ -13,10 +13,11 @@ RSpec::Matchers.define :match_message_table do |expected_tbl|
13
13
 
14
14
  match do |messages|
15
15
  @actual = messages_to_hash(messages)
16
- @actual == expected_hash
16
+ expect(@actual).to match_array(expected_hash)
17
+ true
17
18
  end
18
19
 
19
- failure_message_for_should do |_|
20
+ failure_message do |_|
20
21
  "expected #{expected_hash} and got #{@actual}"
21
22
  end
22
23