basquiat 1.2.0 → 1.3.0.pre.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +50 -0
  3. data/.gitignore +1 -0
  4. data/.metrics +1 -5
  5. data/.reek +0 -1
  6. data/.rubocop.yml +2 -1
  7. data/.ruby-version +1 -1
  8. data/Guardfile +4 -0
  9. data/README.md +77 -61
  10. data/basquiat.gemspec +5 -4
  11. data/basquiat_docker.sh +1 -1
  12. data/docker/Dockerfile +8 -3
  13. data/docker-compose.yml +3 -3
  14. data/lib/basquiat/adapters/base_adapter.rb +42 -7
  15. data/lib/basquiat/adapters/base_message.rb +14 -9
  16. data/lib/basquiat/adapters/rabbitmq/configuration.rb +31 -16
  17. data/lib/basquiat/adapters/rabbitmq/connection.rb +33 -56
  18. data/lib/basquiat/adapters/rabbitmq/events.rb +1 -1
  19. data/lib/basquiat/adapters/rabbitmq/message.rb +10 -9
  20. data/lib/basquiat/adapters/rabbitmq/requeue_strategies/auto_acknowledge.rb +15 -0
  21. data/lib/basquiat/adapters/rabbitmq/requeue_strategies/base_strategy.rb +8 -4
  22. data/lib/basquiat/adapters/rabbitmq/requeue_strategies/basic_acknowledge.rb +1 -1
  23. data/lib/basquiat/adapters/rabbitmq/requeue_strategies/dead_lettering.rb +16 -15
  24. data/lib/basquiat/adapters/rabbitmq/requeue_strategies/delayed_delivery.rb +80 -16
  25. data/lib/basquiat/adapters/rabbitmq/requeue_strategies.rb +7 -0
  26. data/lib/basquiat/adapters/rabbitmq/session.rb +12 -14
  27. data/lib/basquiat/adapters/rabbitmq_adapter.rb +35 -16
  28. data/lib/basquiat/adapters/test_adapter.rb +2 -5
  29. data/lib/basquiat/errors/strategy_not_registered.rb +1 -9
  30. data/lib/basquiat/interfaces/base.rb +28 -7
  31. data/lib/basquiat/support/configuration.rb +33 -4
  32. data/lib/basquiat/support/hash_refinements.rb +9 -0
  33. data/lib/basquiat/support/json.rb +9 -0
  34. data/lib/basquiat/version.rb +1 -1
  35. data/lib/basquiat.rb +6 -1
  36. data/spec/lib/adapters/base_adapter_spec.rb +1 -1
  37. data/spec/lib/adapters/base_message_spec.rb +0 -6
  38. data/spec/lib/adapters/rabbitmq/configuration_spec.rb +2 -2
  39. data/spec/lib/adapters/rabbitmq/connection_spec.rb +8 -13
  40. data/spec/lib/adapters/rabbitmq/events_spec.rb +8 -1
  41. data/spec/lib/adapters/rabbitmq/requeue_strategies/auto_acknowledge_spec.rb +24 -0
  42. data/spec/lib/adapters/rabbitmq/requeue_strategies/basic_acknowledge_spec.rb +4 -4
  43. data/spec/lib/adapters/rabbitmq/requeue_strategies/dead_lettering_spec.rb +23 -28
  44. data/spec/lib/adapters/rabbitmq/requeue_strategies/delayed_delivery_spec.rb +105 -0
  45. data/spec/lib/adapters/rabbitmq_adapter_spec.rb +21 -17
  46. data/spec/lib/basquiat_spec.rb +0 -6
  47. data/spec/lib/interfaces/base_spec.rb +11 -19
  48. data/spec/lib/support/configuration_spec.rb +0 -8
  49. data/spec/lib/support/hash_refinements_spec.rb +2 -2
  50. data/spec/spec_helper.rb +2 -6
  51. data/spec/support/rabbitmq_queue_matchers.rb +9 -3
  52. metadata +21 -12
@@ -1,88 +1,65 @@
1
1
  module Basquiat
2
2
  module Adapters
3
3
  class RabbitMq
4
+ # Control the connection to the RabitMQ server. Delegates calls to {Bunny::Connection}
4
5
  class Connection < SimpleDelegator
5
- def initialize(servers:, failover: {}, auth: {})
6
- @servers = Array(servers)
7
- @failover = { default_timeout: 5, max_retries: 5 }.merge(failover)
8
- @auth = { user: 'guest', password: 'guest' }.merge(auth)
6
+ # @param hosts: [Array<String>] IPs or FQDN of the RabbitMQ instances
7
+ # @param port: [Fixnum] Port that the RabbitMQ instances run
8
+ # @param failover: [Hash]
9
+ # @option failover: [Fixnum] :max_retries (5) Maximum number of reconnection retries
10
+ # @option failover: [Fixnum] :default_timeout (5) Interval between to reconnect attempts
11
+ # @option failover: [Fixnum] :connection_timeout (5) Allowed time before a connection attempt timeouts
12
+ # @param auth: [Hash]
13
+ # @option auth: [String] :user ('guest')
14
+ # @option auth: [String] :password ('guest')
15
+ def initialize(hosts:, port: 5672, failover: {}, auth: {})
16
+ @hosts = hosts
17
+ @port = port
18
+ @failover = failover
19
+ @auth = auth
9
20
  end
10
21
 
22
+ # Creates a channel
23
+ # @return [Bunny::Channel]
24
+ def create_channel
25
+ connection.start
26
+ connection.create_channel
27
+ end
28
+
29
+ # Starts the connection if needed
11
30
  def start
12
- with_network_failure_handler do
13
- connection.start
14
- current_server[:retries] = 0
15
- end
31
+ connection.start unless connection.connected?
16
32
  end
17
33
 
18
34
  def connected?
19
35
  connection.status == :started
20
36
  end
21
37
 
38
+ # Closes the channels and the connection.
22
39
  def disconnect
23
40
  connection.close_all_channels
24
41
  connection.close
25
42
  reset
26
43
  end
27
44
 
28
- def current_server_uri
29
- "amqp://#{current_server[:host]}:#{current_server[:port]}#{current_server[:vhost]}"
30
- end
31
-
32
- def with_network_failure_handler
33
- yield if block_given?
34
- rescue Bunny::ConnectionForced, Bunny::TCPConnectionFailed, Bunny::NetworkFailure => error
35
- if current_server.fetch(:retries, 0) <= @failover.fetch(:max_retries)
36
- handle_network_failures
37
- retry
38
- else
39
- raise(error)
40
- end
41
- end
42
-
43
45
  private
44
46
 
45
47
  def reset
46
48
  @connection = nil
47
49
  end
48
50
 
49
- def handle_network_failures
50
- Basquiat.logger.warn "Failed to connect to #{current_server_uri}"
51
- retries = current_server.fetch(:retries, 0)
52
- current_server[:retries] = retries + 1
53
- if retries < @failover.fetch(:max_retries)
54
- Basquiat.logger.warn("Retrying connection to #{current_server_uri} in #{@failover.fetch(:default_timeout)} seconds")
55
- sleep(@failover.fetch(:default_timeout))
56
- else
57
- Basquiat.logger.warn("Total number of retries exceeded for #{current_server_uri}")
58
- rotate
59
- end
60
- reset
61
- end
62
-
63
51
  def connection
64
- Basquiat.logger.info("Connecting to #{current_server_uri}")
65
52
  @connection ||= Bunny.new(
66
- current_server_uri,
67
- user: auth[:user],
68
- password: auth[:password],
69
- automatic_recovery: false,
70
- threaded: @failover.fetch(:threaded, true),
71
- logger: Basquiat.logger)
53
+ hosts: @hosts,
54
+ port: @port,
55
+ username: @auth.fetch(:user, 'guest'),
56
+ password: @auth.fetch(:password, 'guest'),
57
+ recovery_attempts: @failover.fetch(:max_retries, 5),
58
+ network_recovery_interval: @failover.fetch(:default_timeout, 5),
59
+ connection_timeout: @failover.fetch(:connection_timeout, 5),
60
+ logger: Basquiat.logger)
72
61
  __setobj__(@connection)
73
62
  end
74
-
75
- def current_server
76
- @servers.first
77
- end
78
-
79
- def auth
80
- current_server[:auth] || @auth
81
- end
82
-
83
- def rotate
84
- @servers.rotate!
85
- end
86
63
  end
87
64
  end
88
65
  end
@@ -27,10 +27,10 @@ module Basquiat
27
27
  raise KeyError, "No event handler found for #{key}"
28
28
  end
29
29
 
30
- # event.for.the.win, event.for.everyone, event.for.*
31
30
  private
32
31
 
33
32
  def set_pattern_key(key, value)
33
+ key = key.gsub('.', '\.')
34
34
  key = if key =~ /\*/
35
35
  /^#{key.gsub('*', '[^.]+')}$/
36
36
  else
@@ -4,6 +4,10 @@ module Basquiat
4
4
  class Message < Basquiat::Adapters::BaseMessage
5
5
  attr_reader :delivery_info, :props
6
6
  alias_method :di, :delivery_info
7
+ # @!attribute [r] delivery_info
8
+ # @return [Hash] RabbitMQ delivery_info.
9
+ # @!attribute [r] props
10
+ # @return [Hash] RabbitMQ message properties, such as headers.
7
11
 
8
12
  def initialize(message, delivery_info = {}, props = {})
9
13
  super(message)
@@ -12,21 +16,18 @@ module Basquiat
12
16
  @action = :ack
13
17
  end
14
18
 
19
+ # @!attribute [rw] routing_key
20
+ # It overrides (but not overwrites) the delivery_info routing_key
21
+ # @return [String] returns either the set routing_key or the delivery_info routing_key
22
+ attr_writer :routing_key
15
23
  def routing_key
16
- delivery_info.routing_key
24
+ @routing_key || delivery_info.routing_key
17
25
  end
18
26
 
27
+ # Shorthand for delivery_info.delivery_tag
19
28
  def delivery_tag
20
29
  delivery_info.delivery_tag
21
30
  end
22
-
23
- def ack
24
- @action = :ack
25
- end
26
-
27
- def unack
28
- @action = :unack
29
- end
30
31
  end
31
32
  end
32
33
  end
@@ -0,0 +1,15 @@
1
+ module Basquiat
2
+ module Adapters
3
+ class RabbitMq
4
+ class AutoAcknowledge < BaseStrategy
5
+ def self.session_options
6
+ { consumer: { manual_ack: false } }
7
+ end
8
+
9
+ def run(*)
10
+ yield
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -20,13 +20,17 @@ module Basquiat
20
20
  fail Basquiat::Errors::SubclassResponsibility
21
21
  end
22
22
 
23
- def ack(delivery_tag)
24
- @session.channel.ack(delivery_tag)
23
+ def ack(message)
24
+ @session.channel.ack(message.delivery_tag)
25
25
  end
26
26
 
27
- def unack(delivery_tag)
28
- @session.channel.nack(delivery_tag, false)
27
+ def nack(message)
28
+ @session.channel.nack(message.delivery_tag, false)
29
29
  end
30
+
31
+ private
32
+
33
+ attr_reader :session
30
34
  end
31
35
  end
32
36
  end
@@ -4,7 +4,7 @@ module Basquiat
4
4
  class BasicAcknowledge < BaseStrategy
5
5
  def run(message)
6
6
  yield
7
- send(message.action, message.di.delivery_tag)
7
+ public_send(message.action, message)
8
8
  end
9
9
  end
10
10
  end
@@ -7,10 +7,11 @@ module Basquiat
7
7
 
8
8
  def setup(opts)
9
9
  @options = {
10
- session: { queue: {
11
- options: { 'x-dead-letter-exchange' => opts.fetch(:exchange, 'basquiat.dlx') }
12
- } },
13
- dlx: { ttl: opts.fetch(:ttl, 1_000) } }
10
+ session: {
11
+ queue: {
12
+ options: {
13
+ 'x-dead-letter-exchange' => opts.fetch(:exchange, 'basquiat.dlx') } } },
14
+ dlx: { ttl: opts.fetch(:ttl, 1_000) } }
14
15
  end
15
16
 
16
17
  def session_options
@@ -27,18 +28,18 @@ module Basquiat
27
28
 
28
29
  def run(message)
29
30
  catch :skip_processing do
30
- check_incoming_messages(message)
31
+ check_incoming_messages(message.props.headers)
31
32
  yield
32
33
  end
33
- public_send(message.action, message.delivery_tag)
34
+ public_send(message.action, message)
34
35
  end
35
36
 
36
37
  private
37
38
 
38
- def check_incoming_messages(message)
39
- message.props.headers and
40
- message.props.headers['x-death'][1]['queue'] != @session.queue.name and
41
- throw(:skip_processing)
39
+ def check_incoming_messages(headers)
40
+ headers &&
41
+ headers['x-death'][1]['queue'] != session.queue.name &&
42
+ throw(:skip_processing)
42
43
  end
43
44
 
44
45
  def options
@@ -46,11 +47,11 @@ module Basquiat
46
47
  end
47
48
 
48
49
  def setup_dead_lettering
49
- dlx = @session.channel.topic('basquiat.dlx')
50
- queue = @session.channel.queue('basquiat.dlq',
51
- arguments: { 'x-dead-letter-exchange' => @session.exchange.name,
52
- 'x-message-ttl' => options[:dlx][:ttl] })
53
- queue.bind(dlx, routing_key: '*.#')
50
+ dlx = session.channel.topic('basquiat.dlx')
51
+ queue = session.channel.queue('basquiat.dlq',
52
+ arguments: { 'x-dead-letter-exchange' => session.exchange.name,
53
+ 'x-message-ttl' => options[:dlx][:ttl] })
54
+ queue.bind(dlx, routing_key: '#')
54
55
  end
55
56
  end
56
57
  end
@@ -1,24 +1,88 @@
1
1
  module Basquiat
2
2
  module Adapters
3
3
  class RabbitMq
4
- module Strategies
5
- class DelayedDeliveryWIP
6
- def initialize(channel, message)
7
- @channel = channel
8
- @message = message
4
+ class DelayedDelivery < BaseStrategy
5
+ class << self
6
+ using HashRefinements
7
+ attr_reader :options
8
+
9
+ def setup(opts)
10
+ @options = { ddl: { retries: 5,
11
+ exchange_name: 'basquiat.dlx',
12
+ queue_name_preffix: 'basquiat.ddlq' } }.deep_merge(opts)
13
+ end
14
+ end
15
+
16
+ def initialize(session)
17
+ super
18
+ setup_delayed_delivery
19
+ end
20
+
21
+ def run(message)
22
+ message.routing_key = extract_event_info(message.routing_key)[0]
23
+ yield
24
+ public_send(message.action, message)
25
+ end
26
+
27
+ # @param [Message] message the, well, message to be requeued
28
+ def requeue(message)
29
+ @exchange.publish(Basquiat::Json.encode(message), routing_key: requeue_route_for(message.di.routing_key))
30
+ ack(message)
31
+ end
32
+
33
+ private
34
+
35
+ # @param [#match] key the current routing key of the message
36
+ # @return [String] the calculated routing key for a republish / requeue
37
+ def requeue_route_for(key)
38
+ event_name, timeout = extract_event_info(key)
39
+ if timeout == 2**options[:retries] * 1_000
40
+ "rejected.#{session.queue.name}.#{event_name}"
41
+ else
42
+ "#{timeout * 2}.#{session.queue.name}.#{event_name}"
9
43
  end
44
+ end
45
+
46
+ # @param [#match] key the current routing key of the message
47
+ # @return [Array<String, Integer>] a 2 item array composed of the event.name (aka original routing_key) and
48
+ # the current timeout
49
+ def extract_event_info(key)
50
+ md = key.match(/^(\d+)\.#{session.queue.name}\.(.+)$/)
51
+ if md
52
+ [md.captures[1], md.captures[0].to_i]
53
+ else
54
+ # So timeout can turn into 1 second, weird but spares some checking
55
+ [key, 500]
56
+ end
57
+ end
58
+
59
+ def options
60
+ self.class.options[:ddl]
61
+ end
62
+
63
+ def setup_delayed_delivery
64
+ @exchange = session.channel.topic(options[:exchange_name], durable: true)
65
+ session.bind_queue("*.#{session.queue.name}.#")
66
+ prepare_timeout_queues
67
+ queue = session.channel.queue("#{options[:queue_name_preffix]}_rejected", durable: true)
68
+ queue.bind(@exchange, routing_key: 'rejected.#')
69
+ end
70
+
71
+ def prepare_timeout_queues
72
+ queues = (0..options[:retries] - 1).map do |iteration|
73
+ timeout = 2**iteration
74
+ session.channel.queue("#{options[:queue_name_preffix]}_#{timeout}", durable: true,
75
+ arguments: {
76
+ 'x-dead-letter-exchange' => session.exchange.name,
77
+ 'x-message-ttl' => timeout * 1_000 })
78
+ end
79
+ bind_timeout_queues(queues)
80
+ end
10
81
 
11
- # Criar um exchange
12
- # Criar o queue (ou redeclara-lo)
13
- # O queue tem que ter um dlx para o exchange padrão
14
- # Publicar a mensagem no exchange com um ttl igual ao anterior **2
15
- # dar um unack caso o tempo estoure o maximo.
16
- def message_handler
17
- delay = message[:headers][0][:expiration]**2
18
- exchange = channel.topic('basquiat.dd')
19
- queue = channel.queue('delay', ttl: delay * 2)
20
- queue.bind(exchange, 'original_queue.delay.message_name')
21
- exchange.publish('original_queue.delay.message_name', message, ttl: delay, dlx: default_exchange)
82
+ def bind_timeout_queues(queues)
83
+ queues.each do |queue|
84
+ timeout = queue.arguments['x-message-ttl'].to_i
85
+ queue.bind(@exchange, routing_key: "#{timeout}.#")
22
86
  end
23
87
  end
24
88
  end
@@ -1,3 +1,10 @@
1
1
  require 'basquiat/adapters/rabbitmq/requeue_strategies/base_strategy'
2
+ require 'basquiat/adapters/rabbitmq/requeue_strategies/auto_acknowledge'
2
3
  require 'basquiat/adapters/rabbitmq/requeue_strategies/basic_acknowledge'
3
4
  require 'basquiat/adapters/rabbitmq/requeue_strategies/dead_lettering'
5
+ require 'basquiat/adapters/rabbitmq/requeue_strategies/delayed_delivery'
6
+
7
+ Basquiat::Adapters::RabbitMq.register_strategy(:auto_ack, Basquiat::Adapters::RabbitMq::AutoAcknowledge)
8
+ Basquiat::Adapters::RabbitMq.register_strategy(:basic_ack, Basquiat::Adapters::RabbitMq::BasicAcknowledge)
9
+ Basquiat::Adapters::RabbitMq.register_strategy(:dead_lettering, Basquiat::Adapters::RabbitMq::DeadLettering)
10
+ Basquiat::Adapters::RabbitMq.register_strategy(:delayed_delivery, Basquiat::Adapters::RabbitMq::DelayedDelivery)
@@ -2,9 +2,11 @@ module Basquiat
2
2
  module Adapters
3
3
  class RabbitMq
4
4
  class Session
5
- def initialize(connection, session_options = {})
6
- @connection = connection
7
- @options = session_options
5
+ attr_reader :channel
6
+
7
+ def initialize(channel, session_options = {})
8
+ @channel = channel
9
+ @options = session_options
8
10
  end
9
11
 
10
12
  def bind_queue(routing_key)
@@ -15,30 +17,26 @@ module Basquiat
15
17
  channel.confirm_select if @options[:publisher][:confirm]
16
18
  exchange.publish(Basquiat::Json.encode(message),
17
19
  { routing_key: routing_key,
20
+ persistent: true,
18
21
  timestamp: Time.now.to_i }.merge(props))
19
22
  end
20
23
 
21
- def subscribe(lock, &_block)
22
- queue.subscribe(block: lock, manual_ack: true) do |di, props, msg|
23
- message = Basquiat::Adapters::RabbitMq::Message.new(msg, di, props)
24
- yield message
24
+ def subscribe(block: true, manual_ack: @options[:consumer][:manual_ack])
25
+ channel.prefetch(@options[:consumer][:prefetch])
26
+ queue.subscribe(block: block, manual_ack: manual_ack) do |di, props, msg|
27
+ yield Basquiat::Adapters::RabbitMq::Message.new(msg, di, props)
25
28
  end
26
29
  end
27
30
 
28
- def channel
29
- @connection.start unless @connection.connected?
30
- @channel ||= @connection.create_channel
31
- end
32
-
33
31
  def queue
34
32
  @queue ||= channel.queue(@options[:queue][:name],
35
- durable: true,
33
+ durable: @options[:queue][:durable],
36
34
  arguments: (@options[:queue][:options] || {}))
37
35
  end
38
36
 
39
37
  def exchange
40
38
  @exchange ||= channel.topic(@options[:exchange][:name],
41
- durable: true,
39
+ durable: @options[:exchange][:durable],
42
40
  arguments: (@options[:exchange][:options] || {}))
43
41
  end
44
42
  end
@@ -7,7 +7,6 @@ module Basquiat
7
7
  class RabbitMq < Basquiat::Adapters::Base
8
8
  using Basquiat::HashRefinements
9
9
 
10
-
11
10
  # Avoid superclass mismatch errors
12
11
  require 'basquiat/adapters/rabbitmq/events'
13
12
  require 'basquiat/adapters/rabbitmq/message'
@@ -16,56 +15,76 @@ module Basquiat
16
15
  require 'basquiat/adapters/rabbitmq/session'
17
16
  require 'basquiat/adapters/rabbitmq/requeue_strategies'
18
17
 
18
+ # Initializes the superclass using a {Events} object as the procs instance variable
19
19
  def initialize
20
- super
21
- @procs = Events.new
20
+ super(procs: Events.new)
22
21
  end
23
22
 
23
+ # Since the RabbitMQ configuration options are quite vast and it's interations with the requeue strategies a bit
24
+ # convoluted it uses a {Configuration} object to handle it all
24
25
  def base_options
25
26
  @configuration ||= Configuration.new
26
27
  @configuration.merge_user_options(Basquiat.configuration.adapter_options)
27
28
  end
28
29
 
30
+ # Adds the subscription and register the proc to the event.
31
+ # @param event_name [String] routing key to be matched (and bound to) when listening
32
+ # @param proc [#call] callable object to be run when a message with the said routing_key is received
29
33
  def subscribe_to(event_name, proc)
30
34
  procs[event_name] = proc
31
35
  end
32
36
 
33
- def publish(event, message, persistent: options[:publisher][:persistent], props: {})
34
- connection.with_network_failure_handler do
35
- session.publish(event, message, props)
36
- disconnect unless persistent
37
- end
37
+ # Publishes the event to the exchange configured.
38
+ # @param event [String] routing key to be used
39
+ # @param message [Hash] the message to be publish
40
+ # @param props [Hash] other properties you wish to publish with the message, such as custom headers etc.
41
+ def publish(event, message, props: {})
42
+ session.publish(event, message, props)
43
+ disconnect unless options[:publisher][:persistent]
38
44
  end
39
45
 
40
- def listen(block: true)
41
- connection.with_network_failure_handler do
42
- procs.keys.each { |key| session.bind_queue(key) }
43
- session.subscribe(block) do |message|
44
- strategy.run(message) do
45
- procs[message.routing_key].call(message)
46
- end
46
+ # Binds the queues and start the event lopp.
47
+ # @param block [Boolean] block the thread
48
+ def listen(block: true, rescue_proc: Basquiat.configuration.rescue_proc)
49
+ procs.keys.each { |key| session.bind_queue(key) }
50
+ session.subscribe(block: block) do |message|
51
+ strategy.run(message) do
52
+ process_message(message, rescue_proc)
47
53
  end
48
54
  end
49
55
  end
50
56
 
57
+ def process_message(message, rescue_proc)
58
+ procs[message.routing_key].call(message)
59
+ rescue => ex
60
+ rescue_proc.call(ex, message)
61
+ end
62
+
63
+ # Reset the connection to RabbitMQ.
51
64
  def reset_connection
52
65
  connection.disconnect
53
66
  @connection = nil
54
67
  @session = nil
68
+ @strategy = nil
55
69
  end
56
70
 
57
71
  alias_method :disconnect, :reset_connection
58
72
 
73
+ # Lazy initializes the requeue strategy configured for the adapter
74
+ # @return [BaseStrategy]
59
75
  def strategy
60
76
  @strategy ||= @configuration.strategy.new(session)
61
77
  end
62
78
 
79
+ # Lazy initializes and return the session
80
+ # @return [Session]
63
81
  def session
64
- @session ||= Session.new(connection, @configuration.session_options)
82
+ @session ||= Session.new(connection.create_channel, @configuration.session_options)
65
83
  end
66
84
 
67
85
  private
68
86
 
87
+ # Lazy initializes the connection
69
88
  def connection
70
89
  @connection ||= Connection.new(@configuration.connection_options)
71
90
  end
@@ -2,9 +2,6 @@ module Basquiat
2
2
  module Adapters
3
3
  # An adapter to be used in testing
4
4
  class Test < Basquiat::Adapters::Base
5
- class Message < BaseMessage
6
- end
7
-
8
5
  class << self
9
6
  def events
10
7
  @events ||= Hash.new { |hash, key| hash[key] = [] }
@@ -17,7 +14,7 @@ module Basquiat
17
14
 
18
15
  attr_reader :options
19
16
 
20
- def default_options
17
+ def base_options
21
18
  @event_names = []
22
19
  { host: '127.0.0.1', port: 123_456, durable: true }
23
20
  end
@@ -38,7 +35,7 @@ module Basquiat
38
35
  def listen(*)
39
36
  event = subscribed_event
40
37
  msg = self.class.events[event].shift
41
- msg ? procs[event].call(Message.new(msg)) : nil
38
+ msg ? procs[event].call(BaseMessage.new(msg)) : nil
42
39
  end
43
40
 
44
41
  private
@@ -1,14 +1,6 @@
1
1
  module Basquiat
2
2
  module Errors
3
- class StrategyNotRegistered < StandardError
4
- def initialize(symbol)
5
- super()
6
- @symbol = symbol
7
- end
8
-
9
- def message
10
- "No matching requeue strategy registered as :#{@symbol}"
11
- end
3
+ class StrategyNotRegistered < KeyError
12
4
  end
13
5
  end
14
6
  end