basquiat 1.2.0 → 1.3.0.pre.1
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.
- 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
|