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
@@ -14,16 +14,14 @@ module MessageDriver
14
14
 
15
15
  class Subscription < MessageDriver::Subscription::Base
16
16
  def unsubscribe
17
- adapter.remove_subscription_for(destination.name)
17
+ adapter.remove_subscription_for(destination.name, self)
18
18
  end
19
19
 
20
20
  def deliver_message(message)
21
- begin
22
- consumer.call(message)
23
- rescue => e
24
- unless options[:error_handler].nil?
25
- options[:error_handler].call(e, message)
26
- end
21
+ consumer.call(message)
22
+ rescue => e
23
+ unless options[:error_handler].nil?
24
+ options[:error_handler].call(e, message)
27
25
  end
28
26
  end
29
27
  end
@@ -37,48 +35,61 @@ module MessageDriver
37
35
  message_queue.size
38
36
  end
39
37
 
40
- def pop_message(_options={})
41
- message_queue.shift
38
+ def consumer_count
39
+ adapter.consumer_count_for(name)
42
40
  end
43
41
 
44
- def subscribe(options={}, &consumer)
45
- subscription = Subscription.new(adapter, self, consumer, options)
46
- adapter.set_subscription_for(name, subscription)
47
- until (msg = pop_message).nil?
48
- subscription.deliver_message(msg)
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)
49
50
  end
51
+ end
52
+
53
+ def subscribe(options = {}, &consumer)
54
+ subscription = Subscription.new(adapter, self, consumer, options)
55
+ adapter.add_subscription_for(name, subscription)
56
+ deliver_messages(subscription)
50
57
  subscription
51
58
  end
52
59
 
53
- def publish(body, headers={}, properties={})
54
- msg = Message.new(nil, body, headers, properties)
55
- sub = subscription
56
- if sub.nil?
57
- message_queue << msg
58
- else
59
- sub.deliver_message(msg)
60
- end
60
+ def publish(body, headers = {}, properties = {})
61
+ raw_body = body
62
+ b, h, p = middleware.on_publish(body, headers, properties)
63
+ msg = Message.new(nil, b, h, p, raw_body)
64
+ message_queue << msg
65
+ deliver_messages(subscription) if subscription
61
66
  end
62
67
 
63
68
  private
64
69
 
70
+ def deliver_messages(sub)
71
+ until (msg = pop_message).nil?
72
+ sub.deliver_message(msg)
73
+ end
74
+ end
75
+
65
76
  def message_queue
66
77
  adapter.message_queue_for(name)
67
78
  end
68
79
  end
69
80
 
70
- def initialize(broker, _config={})
81
+ def initialize(broker, _config = {})
71
82
  @broker = broker
72
83
  @destinations = {}
73
- @message_store = Hash.new { |h,k| h[k] = [] }
74
- @subscriptions = {}
84
+ @message_store = Hash.new { |h, k| h[k] = [] }
85
+ @subscriptions = Hash.new { |h, k| h[k] = [] }
75
86
  end
76
87
 
77
88
  def build_context
78
89
  InMemoryContext.new(self)
79
90
  end
80
91
 
81
- def create_destination(name, dest_options={}, message_props={})
92
+ def create_destination(name, dest_options = {}, message_props = {})
82
93
  destination = Destination.new(self, name, dest_options, message_props)
83
94
  @destinations[name] = destination
84
95
  end
@@ -88,15 +99,15 @@ module MessageDriver
88
99
 
89
100
  def_delegators :adapter, :create_destination
90
101
 
91
- def publish(destination, body, headers={}, properties={})
102
+ def publish(destination, body, headers = {}, properties = {})
92
103
  destination.publish(body, headers, properties)
93
104
  end
94
105
 
95
- def pop_message(destination, options={})
106
+ def pop_message(destination, options = {})
96
107
  destination.pop_message(options)
97
108
  end
98
109
 
99
- def subscribe(destination, options={}, &consumer)
110
+ def subscribe(destination, options = {}, &consumer)
100
111
  destination.subscribe(options, &consumer)
101
112
  end
102
113
 
@@ -122,15 +133,21 @@ module MessageDriver
122
133
  end
123
134
 
124
135
  def subscription_for(name)
125
- @subscriptions[name]
136
+ sub = @subscriptions[name].shift
137
+ @subscriptions[name].push sub
138
+ sub
139
+ end
140
+
141
+ def add_subscription_for(name, subscription)
142
+ @subscriptions[name].push subscription
126
143
  end
127
144
 
128
- def set_subscription_for(name, subscription)
129
- @subscriptions[name] = subscription
145
+ def remove_subscription_for(name, subscription)
146
+ @subscriptions[name].delete(subscription)
130
147
  end
131
148
 
132
- def remove_subscription_for(name)
133
- @subscriptions.delete(name)
149
+ def consumer_count_for(name)
150
+ @subscriptions[name].size
134
151
  end
135
152
  end
136
153
  end
@@ -19,6 +19,11 @@ module MessageDriver
19
19
  end
20
20
 
21
21
  class Destination < MessageDriver::Destination::Base
22
+ def queue_path
23
+ @queue_path ||= begin
24
+ name.start_with?('/') ? name : "/queue/#{name}"
25
+ end
26
+ end
22
27
  end
23
28
 
24
29
  attr_reader :config, :poll_timeout
@@ -43,29 +48,26 @@ module MessageDriver
43
48
 
44
49
  def_delegators :adapter, :with_connection, :poll_timeout
45
50
 
46
- #def subscribe(destination, consumer)
47
- #destination.subscribe(&consumer)
48
- #end
51
+ # def subscribe(destination, consumer)
52
+ # destination.subscribe(&consumer)
53
+ # end
49
54
 
50
- def create_destination(name, dest_options={}, message_props={})
51
- unless name.start_with?('/')
52
- name = "/queue/#{name}"
53
- end
55
+ def create_destination(name, dest_options = {}, message_props = {})
54
56
  Destination.new(adapter, name, dest_options, message_props)
55
57
  end
56
58
 
57
- def publish(destination, body, headers={}, _properties={})
59
+ def publish(destination, body, headers = {}, _properties = {})
58
60
  with_connection do |connection|
59
- connection.publish(destination.name, body, headers)
61
+ connection.publish(destination.queue_path, body, headers)
60
62
  end
61
63
  end
62
64
 
63
- def pop_message(destination, options={})
65
+ def pop_message(destination, options = {})
64
66
  with_connection do |connection|
65
67
  sub_id = connection.uuid
66
68
  msg = nil
67
69
  count = 0
68
- connection.subscribe(destination.name, options, sub_id)
70
+ connection.subscribe(destination.queue_path, options, sub_id)
69
71
  while msg.nil? && count < max_poll_count
70
72
  msg = connection.poll
71
73
  if msg.nil?
@@ -73,7 +75,7 @@ module MessageDriver
73
75
  sleep 0.1
74
76
  end
75
77
  end
76
- connection.unsubscribe(destination.name, options, sub_id)
78
+ connection.unsubscribe(destination.queue_path, options, sub_id)
77
79
  Message.new(self, msg) if msg
78
80
  end
79
81
  end
@@ -90,26 +92,22 @@ module MessageDriver
90
92
  end
91
93
 
92
94
  def with_connection
93
- begin
94
- @connection ||= open_connection
95
- yield @connection
96
- rescue SystemCallError, IOError => e
97
- raise MessageDriver::ConnectionError.new(e)
98
- end
95
+ @connection ||= open_connection
96
+ yield @connection
97
+ rescue SystemCallError, IOError => e
98
+ raise MessageDriver::ConnectionError.new(e)
99
99
  end
100
100
 
101
101
  def stop
102
102
  super
103
- if @connection
104
- @connection.disconnect
105
- end
103
+ @connection.disconnect if @connection
106
104
  end
107
105
 
108
106
  private
109
107
 
110
108
  def open_connection
111
109
  conn = Stomp::Connection.new(@config)
112
- raise MessageDriver::ConnectionError, conn.connection_frame.to_s unless conn.open?
110
+ fail MessageDriver::ConnectionError, conn.connection_frame.to_s unless conn.open?
113
111
  conn
114
112
  end
115
113
 
@@ -117,7 +115,8 @@ module MessageDriver
117
115
  required = Gem::Requirement.create('~> 1.3.1')
118
116
  current = Gem::Version.create(Stomp::Version::STRING)
119
117
  unless required.satisfied_by? current
120
- raise MessageDriver::Error, 'stomp 1.3.1 or a later version of the 1.3.x series is required for the stomp adapter'
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'
121
120
  end
122
121
  end
123
122
  end
@@ -9,7 +9,7 @@ module MessageDriver
9
9
  class << self
10
10
  def configure(name = DEFAULT_BROKER_NAME, options)
11
11
  if brokers.keys.include? name
12
- raise BrokerAlreadyConfigured, "there is already a broker named #{name} configured"
12
+ fail BrokerAlreadyConfigured, "there is already a broker named #{name} configured"
13
13
  end
14
14
  brokers[name] = new(name, options)
15
15
  end
@@ -21,7 +21,8 @@ module MessageDriver
21
21
  def broker(name = DEFAULT_BROKER_NAME)
22
22
  result = brokers[name]
23
23
  if result.nil?
24
- raise BrokerNotConfigured, "There is no broker named #{name} configured. The configured brokers are #{brokers.keys}"
24
+ fail BrokerNotConfigured,
25
+ "There is no broker named #{name} configured. The configured brokers are #{brokers.keys}"
25
26
  end
26
27
  result
27
28
  end
@@ -65,11 +66,11 @@ module MessageDriver
65
66
  private
66
67
 
67
68
  def brokers
68
- @brokers ||= { }
69
+ @brokers ||= {}
69
70
  end
70
71
 
71
72
  def clients
72
- @clients ||= { }
73
+ @clients ||= {}
73
74
  end
74
75
 
75
76
  def each_broker
@@ -107,36 +108,36 @@ module MessageDriver
107
108
  end
108
109
 
109
110
  def restart
110
- unless stopped?
111
- @adapter.stop
112
- end
111
+ @adapter.stop unless stopped?
113
112
  @adapter = resolve_adapter(@configuration[:adapter], @configuration)
114
113
  @stopped = false
115
114
  end
116
115
 
117
- def dynamic_destination(dest_name, dest_options={}, message_props={})
116
+ def dynamic_destination(dest_name, dest_options = {}, message_props = {})
118
117
  client.dynamic_destination(dest_name, dest_options, message_props)
119
118
  end
120
119
 
121
- def destination(key, dest_name, dest_options={}, message_props={})
122
- dest = self.dynamic_destination(dest_name, dest_options, message_props)
120
+ def destination(key, dest_name, dest_options = {}, message_props = {})
121
+ dest = dynamic_destination(dest_name, dest_options, message_props)
123
122
  @destinations[key] = dest
124
123
  end
125
124
 
126
125
  def consumer(key, &block)
127
- raise MessageDriver::Error, 'you must provide a block' unless block_given?
126
+ fail MessageDriver::Error, 'you must provide a block' unless block_given?
128
127
  @consumers[key] = block
129
128
  end
130
129
 
131
130
  def find_destination(destination_name)
132
131
  destination = @destinations[destination_name]
133
- raise MessageDriver::NoSuchDestinationError, "no destination #{destination_name} has been configured" if destination.nil?
132
+ if destination.nil?
133
+ fail MessageDriver::NoSuchDestinationError, "no destination #{destination_name} has been configured"
134
+ end
134
135
  destination
135
136
  end
136
137
 
137
138
  def find_consumer(consumer_name)
138
139
  consumer = @consumers[consumer_name]
139
- raise MessageDriver::NoSuchConsumerError, "no consumer #{consumer_name} has been configured" if consumer.nil?
140
+ fail MessageDriver::NoSuchConsumerError, "no consumer #{consumer_name} has been configured" if consumer.nil?
140
141
  consumer
141
142
  end
142
143
 
@@ -145,7 +146,7 @@ module MessageDriver
145
146
  def resolve_adapter(adapter, options)
146
147
  case adapter
147
148
  when nil
148
- raise 'you must specify an adapter'
149
+ fail 'you must specify an adapter'
149
150
  when Symbol, String
150
151
  resolve_adapter(find_adapter_class(adapter), options)
151
152
  when Class
@@ -153,7 +154,7 @@ module MessageDriver
153
154
  when MessageDriver::Adapters::Base
154
155
  adapter
155
156
  else
156
- raise "adapter must be a MessageDriver::Adapters::Base, but this object is a #{adapter.class}"
157
+ fail "adapter must be a MessageDriver::Adapters::Base, but this object is a #{adapter.class}"
157
158
  end
158
159
  end
159
160
 
@@ -163,7 +164,11 @@ module MessageDriver
163
164
  adapter_method = "#{adapter_name}_adapter"
164
165
 
165
166
  unless respond_to?(adapter_method)
166
- raise "the adapter #{adapter_name} must provide MessageDriver::Broker##{adapter_method} that returns the adapter class"
167
+ fail ['the adapter',
168
+ adapter_name,
169
+ 'must provide',
170
+ "MessageDriver::Broker##{adapter_method}",
171
+ 'that returns the adapter class'].join(' ')
167
172
  end
168
173
 
169
174
  send(adapter_method)
@@ -5,35 +5,33 @@ module MessageDriver
5
5
  include Logging
6
6
  extend self
7
7
 
8
- def publish(destination, body, headers={}, properties={})
9
- dest = find_destination(destination)
10
- current_adapter_context.publish(dest, body, headers, properties)
8
+ def publish(destination, body, headers = {}, properties = {})
9
+ find_destination(destination).publish(body, headers, properties)
11
10
  end
12
11
 
13
- def pop_message(destination, options={})
14
- dest = find_destination(destination)
15
- current_adapter_context.pop_message(dest, options)
12
+ def pop_message(destination, options = {})
13
+ find_destination(destination).pop_message(options)
16
14
  end
17
15
 
18
- def subscribe(destination_name, consumer_name, options={})
16
+ def subscribe(destination_name, consumer_name, options = {})
19
17
  consumer = find_consumer(consumer_name)
20
18
  subscribe_with(destination_name, options, &consumer)
21
19
  end
22
20
 
23
- def subscribe_with(destination_name, options={}, &consumer)
21
+ def subscribe_with(destination_name, options = {}, &consumer)
24
22
  destination = find_destination(destination_name)
25
23
  current_adapter_context.subscribe(destination, options, &consumer)
26
24
  end
27
25
 
28
- def dynamic_destination(dest_name, dest_options={}, message_props={})
26
+ def dynamic_destination(dest_name, dest_options = {}, message_props = {})
29
27
  current_adapter_context.create_destination(dest_name, dest_options, message_props)
30
28
  end
31
29
 
32
- def ack_message(message, options={})
30
+ def ack_message(message, options = {})
33
31
  message.ack(options)
34
32
  end
35
33
 
36
- def nack_message(message, options={})
34
+ def nack_message(message, options = {})
37
35
  message.nack(options)
38
36
  end
39
37
 
@@ -54,7 +52,7 @@ module MessageDriver
54
52
  broker.find_consumer(consumer)
55
53
  end
56
54
 
57
- def with_message_transaction(options={})
55
+ def with_message_transaction(options = {})
58
56
  wrapper = fetch_context_wrapper
59
57
  wrapper.increment_transaction_depth
60
58
  begin
@@ -84,13 +82,14 @@ module MessageDriver
84
82
  end
85
83
  end
86
84
 
87
- def current_adapter_context(initialize=true)
85
+ def current_adapter_context(initialize = true)
88
86
  ctx = fetch_context_wrapper(initialize)
89
87
  ctx.nil? ? nil : ctx.ctx
90
88
  end
91
89
 
92
90
  def with_adapter_context(adapter_context)
93
- old_ctx, Thread.current[adapter_context_key] = fetch_context_wrapper(false), build_context_wrapper(adapter_context)
91
+ old_ctx = fetch_context_wrapper(false)
92
+ Thread.current[adapter_context_key] = build_context_wrapper(adapter_context)
94
93
  begin
95
94
  yield
96
95
  ensure
@@ -132,7 +131,7 @@ module MessageDriver
132
131
 
133
132
  private
134
133
 
135
- def fetch_context_wrapper(initialize=true)
134
+ def fetch_context_wrapper(initialize = true)
136
135
  wrapper = Thread.current[adapter_context_key]
137
136
  if wrapper.nil? || !wrapper.valid?
138
137
  if initialize
@@ -149,7 +148,7 @@ module MessageDriver
149
148
  Thread.current[adapter_context_key] = wrapper
150
149
  end
151
150
 
152
- def build_context_wrapper(ctx=adapter.new_context)
151
+ def build_context_wrapper(ctx = adapter.new_context)
153
152
  ContextWrapper.new(ctx)
154
153
  end
155
154
 
@@ -10,24 +10,42 @@ module MessageDriver
10
10
  @message_props = message_props
11
11
  end
12
12
 
13
- def publish(body, headers={}, properties={})
14
- adapter.broker.client.publish(self, body, headers, properties)
13
+ def publish(body, headers = {}, properties = {})
14
+ current_adapter_context.publish(self, body, headers, properties)
15
15
  end
16
16
 
17
- def pop_message(options={})
18
- adapter.broker.client.pop_message(self, options)
17
+ def pop_message(options = {})
18
+ current_adapter_context.pop_message(self, options)
19
19
  end
20
20
 
21
21
  def after_initialize(_adapter_context)
22
- #does nothing, feel free to override as needed
22
+ # does nothing, feel free to override as needed
23
23
  end
24
24
 
25
25
  def message_count
26
- raise "#message_count is not supported by #{self.class}"
26
+ fail "#message_count is not supported by #{self.class}"
27
27
  end
28
28
 
29
- def subscribe(&_consumer)
30
- raise "#subscribe is not supported by #{self.class}"
29
+ def subscribe(_options = {}, &_consumer)
30
+ fail "#subscribe is not supported by #{self.class}"
31
+ end
32
+
33
+ def consumer_count
34
+ fail "#consumer_count is not supported by #{self.class}"
35
+ end
36
+
37
+ def middleware
38
+ @middleware ||= Middleware::MiddlewareStack.new(self)
39
+ end
40
+
41
+ private
42
+
43
+ def current_adapter_context
44
+ adapter.broker.client.current_adapter_context
45
+ end
46
+
47
+ def client
48
+ @client ||= adapter.broker.client
31
49
  end
32
50
  end
33
51
  end