basquiat 1.1.1 → 1.2.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.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/.metrics +5 -0
- data/.reek +3 -0
- data/.rspec +1 -0
- data/.rubocop.yml +2 -1
- data/.ruby-version +1 -1
- data/Guardfile +5 -4
- data/README.md +10 -8
- data/basquiat.gemspec +10 -8
- data/basquiat_docker.sh +35 -0
- data/docker-compose.yml +5 -1
- data/docker/Dockerfile +2 -3
- data/docker/guard_start.sh +3 -0
- data/lib/basquiat.rb +5 -0
- data/lib/basquiat/adapters/base_adapter.rb +21 -11
- data/lib/basquiat/adapters/base_message.rb +29 -0
- data/lib/basquiat/adapters/rabbitmq/configuration.rb +52 -0
- data/lib/basquiat/adapters/rabbitmq/connection.rb +89 -0
- data/lib/basquiat/adapters/rabbitmq/events.rb +49 -0
- data/lib/basquiat/adapters/rabbitmq/message.rb +33 -0
- data/lib/basquiat/adapters/rabbitmq/requeue_strategies.rb +3 -0
- data/lib/basquiat/adapters/rabbitmq/requeue_strategies/base_strategy.rb +33 -0
- data/lib/basquiat/adapters/rabbitmq/requeue_strategies/basic_acknowledge.rb +12 -0
- data/lib/basquiat/adapters/rabbitmq/requeue_strategies/dead_lettering.rb +58 -0
- data/lib/basquiat/adapters/rabbitmq/requeue_strategies/delayed_delivery.rb +27 -0
- data/lib/basquiat/adapters/rabbitmq/session.rb +47 -0
- data/lib/basquiat/adapters/rabbitmq_adapter.rb +39 -95
- data/lib/basquiat/adapters/test_adapter.rb +4 -3
- data/lib/basquiat/errors.rb +2 -0
- data/lib/basquiat/errors/strategy_not_registered.rb +14 -0
- data/lib/basquiat/errors/subclass_responsibility.rb +9 -0
- data/lib/basquiat/interfaces/base.rb +0 -1
- data/lib/basquiat/support/configuration.rb +4 -4
- data/lib/basquiat/support/hash_refinements.rb +2 -1
- data/lib/basquiat/version.rb +1 -1
- data/spec/lib/adapters/base_adapter_spec.rb +24 -6
- data/spec/lib/adapters/base_message_spec.rb +16 -0
- data/spec/lib/adapters/rabbitmq/configuration_spec.rb +47 -0
- data/spec/lib/adapters/rabbitmq/connection_spec.rb +45 -0
- data/spec/lib/adapters/rabbitmq/events_spec.rb +78 -0
- data/spec/lib/adapters/rabbitmq/message_spec.rb +26 -0
- data/spec/lib/adapters/rabbitmq/requeue_strategies/basic_acknowledge_spec.rb +38 -0
- data/spec/lib/adapters/rabbitmq/requeue_strategies/dead_lettering_spec.rb +102 -0
- data/spec/lib/adapters/rabbitmq_adapter_spec.rb +39 -49
- data/spec/lib/adapters/test_adapter_spec.rb +15 -19
- data/spec/lib/support/configuration_spec.rb +1 -1
- data/spec/lib/support/hash_refinements_spec.rb +8 -2
- data/spec/spec_helper.rb +8 -5
- data/spec/support/rabbitmq_queue_matchers.rb +53 -0
- data/spec/support/shared_examples/basquiat_adapter_shared_examples.rb +9 -20
- metadata +65 -6
- data/.travis.yml +0 -3
- data/docker/basquiat_start.sh +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f2ece36298347faf2fb2607326545c2d35f78d08
|
4
|
+
data.tar.gz: e461978745bacda7cbf9e276327a91acdf814701
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 88cfcff653f37e3f25519df53f4ca434af685cd6bc3146f4519e5b00c6efaea54c55fc2a051cbc131b8c92c734c77cc0d784bc83a2148aaccee463eb2857da15
|
7
|
+
data.tar.gz: 7c500da60290323968215415e264b2bbc698d5c1ac703212bf4bb2745b81ea704d78dae3cf371cab415cbac704032a0c8c23920417dfe1614bba41859bec901c
|
data/.gitignore
CHANGED
data/.metrics
CHANGED
data/.reek
ADDED
data/.rspec
CHANGED
data/.rubocop.yml
CHANGED
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
ruby-2.
|
1
|
+
ruby-2.2
|
data/Guardfile
CHANGED
@@ -3,15 +3,16 @@ guard :bundler do
|
|
3
3
|
watch('basquiat.gemspec')
|
4
4
|
end
|
5
5
|
|
6
|
-
guard :rspec, {
|
6
|
+
guard :rspec, { cmd: 'bundle exec rspec', all_on_start: true,
|
7
|
+
keep: true, all_after_pass: true, run_all: { cmd: 'rspec -f progress' } } do
|
7
8
|
watch(%r{^spec/.+_spec.rb$})
|
8
9
|
watch(%r{^spec/lib/.+_spec.rb$})
|
9
10
|
watch(%r{^lib/basquiat/(.+)\.rb$}) { |matchdata| "spec/lib/#{matchdata[1]}_spec.rb" }
|
10
|
-
watch('spec/spec_helper.rb')
|
11
|
-
watch(%r{spec/support/.+\.rb})
|
11
|
+
watch('spec/spec_helper.rb') { 'spec' }
|
12
|
+
watch(%r{spec/support/.+\.rb}) { 'spec' }
|
12
13
|
end
|
13
14
|
|
14
|
-
guard :rubocop, { cli: '-fs -c./.rubocop.yml'} do
|
15
|
+
guard :rubocop, { cmd: 'rubocop', cli: '-fs -c./.rubocop.yml' } do
|
15
16
|
#watch(%r{.+\.rb$})
|
16
17
|
#watch(%r{(?:.+/)?\.rubocop\.yml$}) { |m| File.dirname(m[0]) }
|
17
18
|
end
|
data/README.md
CHANGED
@@ -80,16 +80,18 @@ Yaml File configuration example:
|
|
80
80
|
queue_name: 'my.queue'
|
81
81
|
default_adapter: Basquiat::Adapters::RabbitMq
|
82
82
|
adapter_options:
|
83
|
-
|
83
|
+
servers:
|
84
84
|
-
|
85
85
|
:host: 'localhost'
|
86
86
|
:port: '5672'
|
87
|
-
|
87
|
+
failover:
|
88
88
|
:default_timeout: 5
|
89
89
|
:max_retries: 5
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
90
|
+
publisher:
|
91
|
+
confirm: true
|
92
|
+
persistent: true
|
93
|
+
auth:
|
94
|
+
user: 'guest'
|
95
|
+
password: 'guest'
|
96
|
+
requeue:
|
97
|
+
enabled: false
|
data/basquiat.gemspec
CHANGED
@@ -4,16 +4,16 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require 'basquiat/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name
|
8
|
-
spec.version
|
9
|
-
spec.authors
|
10
|
-
spec.email
|
11
|
-
spec.description
|
7
|
+
spec.name = 'basquiat'
|
8
|
+
spec.version = Basquiat::VERSION
|
9
|
+
spec.authors = ['Marcello "mereghost" Rocha']
|
10
|
+
spec.email = %w(marcello.rocha@gmail.com.br)
|
11
|
+
spec.description = <<EOD
|
12
12
|
Basquiat is a library that intends to abstract all the complexity of working with message queues
|
13
13
|
EOD
|
14
|
-
spec.summary
|
15
|
-
spec.homepage
|
16
|
-
spec.license
|
14
|
+
spec.summary = 'A pluggable library that aims to hide message queue complexity'
|
15
|
+
spec.homepage = 'http://github.com/VAGAScom/basquiat'
|
16
|
+
spec.license = 'MIT'
|
17
17
|
|
18
18
|
spec.files = `git ls-files`.split($RS)
|
19
19
|
spec.executables = spec.files.grep(/^bin\//) { |f| File.basename(f) }
|
@@ -33,6 +33,8 @@ EOD
|
|
33
33
|
spec.add_development_dependency 'simplecov'
|
34
34
|
spec.add_development_dependency 'metric_fu'
|
35
35
|
spec.add_development_dependency 'rubocop'
|
36
|
+
spec.add_development_dependency 'byebug'
|
37
|
+
spec.add_development_dependency 'pry-byebug'
|
36
38
|
|
37
39
|
spec.add_dependency 'multi_json'
|
38
40
|
spec.add_dependency 'naught'
|
data/basquiat_docker.sh
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
function bundle_list() {
|
3
|
+
bundle list
|
4
|
+
}
|
5
|
+
|
6
|
+
function generate_gemfile() {
|
7
|
+
bundle_list | awk '
|
8
|
+
BEGIN {
|
9
|
+
FS=" "
|
10
|
+
print "source \"https://rubygems.org\"";
|
11
|
+
format = "gem \"%s\", \"%s\"\r\n";
|
12
|
+
}
|
13
|
+
{
|
14
|
+
if ($2 == "bundler") {
|
15
|
+
next;
|
16
|
+
}
|
17
|
+
if ($1 == "*") {
|
18
|
+
match($3, "[^()]+");
|
19
|
+
version = substr($3,RSTART,RLENGTH);
|
20
|
+
printf format, $2, version;
|
21
|
+
}
|
22
|
+
}' > docker/Gemfile
|
23
|
+
}
|
24
|
+
|
25
|
+
function stop_and_remove_containers {
|
26
|
+
docker-compose stop rabbitmq
|
27
|
+
docker rm basquiat_basquiat_run_1
|
28
|
+
docker rm --volumes=true basquiat_rabbitmq_1
|
29
|
+
}
|
30
|
+
|
31
|
+
generate_gemfile
|
32
|
+
docker-compose start rabbitmq
|
33
|
+
docker-compose run basquiat
|
34
|
+
|
35
|
+
trap stop_and_remove_containers EXIT SIGINT SIGTERM SIGKILL
|
data/docker-compose.yml
CHANGED
@@ -1,11 +1,15 @@
|
|
1
1
|
basquiat:
|
2
2
|
build: docker
|
3
|
-
command: /basquiat/docker/
|
3
|
+
command: /basquiat/docker/guard_start.sh
|
4
4
|
volumes:
|
5
5
|
- ./:/basquiat
|
6
6
|
links:
|
7
7
|
- rabbitmq
|
8
8
|
tty: true
|
9
|
+
stdin_open: true
|
9
10
|
|
10
11
|
rabbitmq:
|
11
12
|
image: mereghost/rabbitmq:latest
|
13
|
+
ports:
|
14
|
+
- "15672"
|
15
|
+
- "5672"
|
data/docker/Dockerfile
CHANGED
data/lib/basquiat.rb
CHANGED
@@ -3,6 +3,7 @@ require 'naught'
|
|
3
3
|
require 'yaml'
|
4
4
|
|
5
5
|
require 'basquiat/support'
|
6
|
+
require 'basquiat/errors'
|
6
7
|
require 'basquiat/adapters'
|
7
8
|
require 'basquiat/version'
|
8
9
|
require 'basquiat/interfaces/base'
|
@@ -21,6 +22,10 @@ module Basquiat
|
|
21
22
|
def configure
|
22
23
|
yield configuration
|
23
24
|
end
|
25
|
+
|
26
|
+
def logger
|
27
|
+
configuration.logger
|
28
|
+
end
|
24
29
|
end
|
25
30
|
end
|
26
31
|
|
@@ -1,15 +1,32 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
require 'basquiat/adapters/base_message'
|
3
|
+
|
1
4
|
module Basquiat
|
2
5
|
module Adapters
|
3
6
|
# Base implementation for an adapter
|
4
|
-
|
7
|
+
class Base
|
5
8
|
using Basquiat::HashRefinements
|
6
9
|
|
10
|
+
class << self
|
11
|
+
def strategies
|
12
|
+
@strategies ||= {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def register_strategy(config_name, klass)
|
16
|
+
strategies.merge!(config_name.to_sym => klass)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
7
20
|
def initialize
|
8
21
|
@options = base_options
|
9
22
|
@procs = {}
|
10
23
|
@retries = 0
|
11
24
|
end
|
12
25
|
|
26
|
+
def strategies
|
27
|
+
self.class.strategies
|
28
|
+
end
|
29
|
+
|
13
30
|
# Used to set the options for the adapter. It is merged in
|
14
31
|
# to the default_options hash.
|
15
32
|
# @param [Hash] opts an adapter dependant hash of options
|
@@ -27,28 +44,21 @@ module Basquiat
|
|
27
44
|
{}
|
28
45
|
end
|
29
46
|
|
30
|
-
def update_config
|
31
|
-
end
|
32
|
-
|
33
47
|
def publish
|
48
|
+
fail Basquiat::Errors::SubclassResponsibility
|
34
49
|
end
|
35
50
|
|
36
51
|
def subscribe_to
|
52
|
+
fail Basquiat::Errors::SubclassResponsibility
|
37
53
|
end
|
38
54
|
|
39
55
|
def disconnect
|
40
|
-
|
41
|
-
|
42
|
-
def disconnected?
|
56
|
+
fail Basquiat::Errors::SubclassResponsibility
|
43
57
|
end
|
44
58
|
|
45
59
|
private
|
46
60
|
|
47
61
|
attr_reader :procs, :options
|
48
|
-
|
49
|
-
def logger
|
50
|
-
Basquiat.configuration.logger
|
51
|
-
end
|
52
62
|
end
|
53
63
|
end
|
54
64
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Basquiat
|
2
|
+
module Adapters
|
3
|
+
class BaseMessage < SimpleDelegator
|
4
|
+
attr_reader :action
|
5
|
+
|
6
|
+
def initialize(message)
|
7
|
+
@message = Basquiat::Json.decode(message)
|
8
|
+
super(@message)
|
9
|
+
@action = :ack
|
10
|
+
end
|
11
|
+
|
12
|
+
def ack?
|
13
|
+
fail Basquiat::Errors::SubclassResponsibility
|
14
|
+
end
|
15
|
+
|
16
|
+
def unack
|
17
|
+
fail Basquiat::Errors::SubclassResponsibility
|
18
|
+
end
|
19
|
+
|
20
|
+
def requeue
|
21
|
+
fail Basquiat::Errors::SubclassResponsibility
|
22
|
+
end
|
23
|
+
|
24
|
+
def delay_redelivery
|
25
|
+
fail Basquiat::Errors::SubclassResponsibility
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Basquiat
|
2
|
+
module Adapters
|
3
|
+
class RabbitMq
|
4
|
+
class Configuration
|
5
|
+
using Basquiat::HashRefinements
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@options = { failover: { default_timeout: 5, max_retries: 5 },
|
9
|
+
servers: [{ host: 'localhost', port: 5672 }],
|
10
|
+
queue: {
|
11
|
+
name: Basquiat.configuration.queue_name,
|
12
|
+
options: { durable: true } },
|
13
|
+
exchange: {
|
14
|
+
name: Basquiat.configuration.exchange_name,
|
15
|
+
options: { durable: true } },
|
16
|
+
publisher: { confirm: true, persistent: false },
|
17
|
+
auth: { user: 'guest', password: 'guest' },
|
18
|
+
requeue: { enabled: false } }
|
19
|
+
end
|
20
|
+
|
21
|
+
def base_options
|
22
|
+
@options
|
23
|
+
end
|
24
|
+
|
25
|
+
def merge_user_options(user_opts)
|
26
|
+
@options.merge!(user_opts)
|
27
|
+
end
|
28
|
+
|
29
|
+
def connection_options
|
30
|
+
{ servers: @options[:servers],
|
31
|
+
failover: @options[:failover],
|
32
|
+
auth: @options[:auth] }
|
33
|
+
end
|
34
|
+
|
35
|
+
def session_options
|
36
|
+
{ exchange: @options[:exchange],
|
37
|
+
publisher: @options[:publisher],
|
38
|
+
queue: @options[:queue] }.deep_merge(strategy.session_options)
|
39
|
+
end
|
40
|
+
|
41
|
+
def strategy
|
42
|
+
return BasicAcknowledge unless @options[:requeue][:enabled]
|
43
|
+
@strategy ||= RabbitMq.strategies.fetch(@options[:requeue][:strategy].to_sym)
|
44
|
+
@strategy.setup(@options[:requeue][:options] || {})
|
45
|
+
@strategy
|
46
|
+
rescue KeyError
|
47
|
+
fail Basquiat::Errors::StrategyNotRegistered.new(@options[:requeue][:strategy])
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Basquiat
|
2
|
+
module Adapters
|
3
|
+
class RabbitMq
|
4
|
+
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)
|
9
|
+
end
|
10
|
+
|
11
|
+
def start
|
12
|
+
with_network_failure_handler do
|
13
|
+
connection.start
|
14
|
+
current_server[:retries] = 0
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def connected?
|
19
|
+
connection.status == :started
|
20
|
+
end
|
21
|
+
|
22
|
+
def disconnect
|
23
|
+
connection.close_all_channels
|
24
|
+
connection.close
|
25
|
+
reset
|
26
|
+
end
|
27
|
+
|
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
|
+
private
|
44
|
+
|
45
|
+
def reset
|
46
|
+
@connection = nil
|
47
|
+
end
|
48
|
+
|
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
|
+
def connection
|
64
|
+
Basquiat.logger.info("Connecting to #{current_server_uri}")
|
65
|
+
@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)
|
72
|
+
__setobj__(@connection)
|
73
|
+
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
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|