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.
- checksums.yaml +4 -4
- data/.codeclimate.yml +50 -0
- data/.gitignore +1 -0
- data/.metrics +1 -5
- data/.reek +0 -1
- data/.rubocop.yml +2 -1
- data/.ruby-version +1 -1
- data/Guardfile +4 -0
- data/README.md +77 -61
- data/basquiat.gemspec +5 -4
- data/basquiat_docker.sh +1 -1
- data/docker/Dockerfile +8 -3
- data/docker-compose.yml +3 -3
- data/lib/basquiat/adapters/base_adapter.rb +42 -7
- data/lib/basquiat/adapters/base_message.rb +14 -9
- data/lib/basquiat/adapters/rabbitmq/configuration.rb +31 -16
- data/lib/basquiat/adapters/rabbitmq/connection.rb +33 -56
- data/lib/basquiat/adapters/rabbitmq/events.rb +1 -1
- data/lib/basquiat/adapters/rabbitmq/message.rb +10 -9
- data/lib/basquiat/adapters/rabbitmq/requeue_strategies/auto_acknowledge.rb +15 -0
- data/lib/basquiat/adapters/rabbitmq/requeue_strategies/base_strategy.rb +8 -4
- data/lib/basquiat/adapters/rabbitmq/requeue_strategies/basic_acknowledge.rb +1 -1
- data/lib/basquiat/adapters/rabbitmq/requeue_strategies/dead_lettering.rb +16 -15
- data/lib/basquiat/adapters/rabbitmq/requeue_strategies/delayed_delivery.rb +80 -16
- data/lib/basquiat/adapters/rabbitmq/requeue_strategies.rb +7 -0
- data/lib/basquiat/adapters/rabbitmq/session.rb +12 -14
- data/lib/basquiat/adapters/rabbitmq_adapter.rb +35 -16
- data/lib/basquiat/adapters/test_adapter.rb +2 -5
- data/lib/basquiat/errors/strategy_not_registered.rb +1 -9
- data/lib/basquiat/interfaces/base.rb +28 -7
- data/lib/basquiat/support/configuration.rb +33 -4
- data/lib/basquiat/support/hash_refinements.rb +9 -0
- data/lib/basquiat/support/json.rb +9 -0
- data/lib/basquiat/version.rb +1 -1
- data/lib/basquiat.rb +6 -1
- data/spec/lib/adapters/base_adapter_spec.rb +1 -1
- data/spec/lib/adapters/base_message_spec.rb +0 -6
- data/spec/lib/adapters/rabbitmq/configuration_spec.rb +2 -2
- data/spec/lib/adapters/rabbitmq/connection_spec.rb +8 -13
- data/spec/lib/adapters/rabbitmq/events_spec.rb +8 -1
- data/spec/lib/adapters/rabbitmq/requeue_strategies/auto_acknowledge_spec.rb +24 -0
- data/spec/lib/adapters/rabbitmq/requeue_strategies/basic_acknowledge_spec.rb +4 -4
- data/spec/lib/adapters/rabbitmq/requeue_strategies/dead_lettering_spec.rb +23 -28
- data/spec/lib/adapters/rabbitmq/requeue_strategies/delayed_delivery_spec.rb +105 -0
- data/spec/lib/adapters/rabbitmq_adapter_spec.rb +21 -17
- data/spec/lib/basquiat_spec.rb +0 -6
- data/spec/lib/interfaces/base_spec.rb +11 -19
- data/spec/lib/support/configuration_spec.rb +0 -8
- data/spec/lib/support/hash_refinements_spec.rb +2 -2
- data/spec/spec_helper.rb +2 -6
- data/spec/support/rabbitmq_queue_matchers.rb +9 -3
- 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
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
-
|
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
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
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
|
@@ -20,13 +20,17 @@ module Basquiat
|
|
20
20
|
fail Basquiat::Errors::SubclassResponsibility
|
21
21
|
end
|
22
22
|
|
23
|
-
def ack(
|
24
|
-
@session.channel.ack(delivery_tag)
|
23
|
+
def ack(message)
|
24
|
+
@session.channel.ack(message.delivery_tag)
|
25
25
|
end
|
26
26
|
|
27
|
-
def
|
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
|
@@ -7,10 +7,11 @@ module Basquiat
|
|
7
7
|
|
8
8
|
def setup(opts)
|
9
9
|
@options = {
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
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
|
34
|
+
public_send(message.action, message)
|
34
35
|
end
|
35
36
|
|
36
37
|
private
|
37
38
|
|
38
|
-
def check_incoming_messages(
|
39
|
-
|
40
|
-
|
41
|
-
|
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 =
|
50
|
-
queue =
|
51
|
-
|
52
|
-
|
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
|
-
|
5
|
-
class
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
6
|
-
|
7
|
-
|
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(
|
22
|
-
|
23
|
-
|
24
|
-
yield
|
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:
|
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:
|
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
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
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
|
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(
|
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 <
|
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
|