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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.metrics +5 -0
  4. data/.reek +3 -0
  5. data/.rspec +1 -0
  6. data/.rubocop.yml +2 -1
  7. data/.ruby-version +1 -1
  8. data/Guardfile +5 -4
  9. data/README.md +10 -8
  10. data/basquiat.gemspec +10 -8
  11. data/basquiat_docker.sh +35 -0
  12. data/docker-compose.yml +5 -1
  13. data/docker/Dockerfile +2 -3
  14. data/docker/guard_start.sh +3 -0
  15. data/lib/basquiat.rb +5 -0
  16. data/lib/basquiat/adapters/base_adapter.rb +21 -11
  17. data/lib/basquiat/adapters/base_message.rb +29 -0
  18. data/lib/basquiat/adapters/rabbitmq/configuration.rb +52 -0
  19. data/lib/basquiat/adapters/rabbitmq/connection.rb +89 -0
  20. data/lib/basquiat/adapters/rabbitmq/events.rb +49 -0
  21. data/lib/basquiat/adapters/rabbitmq/message.rb +33 -0
  22. data/lib/basquiat/adapters/rabbitmq/requeue_strategies.rb +3 -0
  23. data/lib/basquiat/adapters/rabbitmq/requeue_strategies/base_strategy.rb +33 -0
  24. data/lib/basquiat/adapters/rabbitmq/requeue_strategies/basic_acknowledge.rb +12 -0
  25. data/lib/basquiat/adapters/rabbitmq/requeue_strategies/dead_lettering.rb +58 -0
  26. data/lib/basquiat/adapters/rabbitmq/requeue_strategies/delayed_delivery.rb +27 -0
  27. data/lib/basquiat/adapters/rabbitmq/session.rb +47 -0
  28. data/lib/basquiat/adapters/rabbitmq_adapter.rb +39 -95
  29. data/lib/basquiat/adapters/test_adapter.rb +4 -3
  30. data/lib/basquiat/errors.rb +2 -0
  31. data/lib/basquiat/errors/strategy_not_registered.rb +14 -0
  32. data/lib/basquiat/errors/subclass_responsibility.rb +9 -0
  33. data/lib/basquiat/interfaces/base.rb +0 -1
  34. data/lib/basquiat/support/configuration.rb +4 -4
  35. data/lib/basquiat/support/hash_refinements.rb +2 -1
  36. data/lib/basquiat/version.rb +1 -1
  37. data/spec/lib/adapters/base_adapter_spec.rb +24 -6
  38. data/spec/lib/adapters/base_message_spec.rb +16 -0
  39. data/spec/lib/adapters/rabbitmq/configuration_spec.rb +47 -0
  40. data/spec/lib/adapters/rabbitmq/connection_spec.rb +45 -0
  41. data/spec/lib/adapters/rabbitmq/events_spec.rb +78 -0
  42. data/spec/lib/adapters/rabbitmq/message_spec.rb +26 -0
  43. data/spec/lib/adapters/rabbitmq/requeue_strategies/basic_acknowledge_spec.rb +38 -0
  44. data/spec/lib/adapters/rabbitmq/requeue_strategies/dead_lettering_spec.rb +102 -0
  45. data/spec/lib/adapters/rabbitmq_adapter_spec.rb +39 -49
  46. data/spec/lib/adapters/test_adapter_spec.rb +15 -19
  47. data/spec/lib/support/configuration_spec.rb +1 -1
  48. data/spec/lib/support/hash_refinements_spec.rb +8 -2
  49. data/spec/spec_helper.rb +8 -5
  50. data/spec/support/rabbitmq_queue_matchers.rb +53 -0
  51. data/spec/support/shared_examples/basquiat_adapter_shared_examples.rb +9 -20
  52. metadata +65 -6
  53. data/.travis.yml +0 -3
  54. data/docker/basquiat_start.sh +0 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4d297e7591c1ea6247edd40cf816fd9886c9f8d7
4
- data.tar.gz: 93b240d5a7353ed4ceb65ded88e3de66f64a18d9
3
+ metadata.gz: f2ece36298347faf2fb2607326545c2d35f78d08
4
+ data.tar.gz: e461978745bacda7cbf9e276327a91acdf814701
5
5
  SHA512:
6
- metadata.gz: 2fffd158b4d20998d28e100351892571bd952874affb8a18fb83b7cf64d3815b6fbad980ea2826905eda70c88afa33249b10b23d3025a18697e1a1478ed67368
7
- data.tar.gz: 45366c9864d178391f97ba52caad3bc6a2c77375a2c360ee66da325a37a3fb75c5066703582f50074acfb5bc2e1a29ab64a0b82168c0d22ed491c437242e2d8a
6
+ metadata.gz: 88cfcff653f37e3f25519df53f4ca434af685cd6bc3146f4519e5b00c6efaea54c55fc2a051cbc131b8c92c734c77cc0d784bc83a2148aaccee463eb2857da15
7
+ data.tar.gz: 7c500da60290323968215415e264b2bbc698d5c1ac703212bf4bb2745b81ea704d78dae3cf371cab415cbac704032a0c8c23920417dfe1614bba41859bec901c
data/.gitignore CHANGED
@@ -17,3 +17,6 @@ test/version_tmp
17
17
  tmp
18
18
  .idea
19
19
  bin
20
+ docker/Gemfile
21
+ log
22
+ .tags*
data/.metrics CHANGED
@@ -1,5 +1,10 @@
1
1
  MetricFu::Configuration.run do |config|
2
2
  config.configure_metric(:cane) do |cane|
3
3
  cane.line_length = 110
4
+ cane.no_doc = 'y'
5
+ end
6
+
7
+ config.configure_metric(:reek) do |reek|
8
+ reek.config_file_pattern = '.reek'
4
9
  end
5
10
  end
data/.reek ADDED
@@ -0,0 +1,3 @@
1
+ ---
2
+ IrresponsibleModule:
3
+ enabled: false
data/.rspec CHANGED
@@ -2,3 +2,4 @@
2
2
  --color
3
3
  --fail-fast
4
4
  --order rand
5
+ --require spec_helper
data/.rubocop.yml CHANGED
@@ -2,7 +2,8 @@ AllCops:
2
2
  Exclude:
3
3
  - 'bin/*'
4
4
  - 'docker/*'
5
- Style/LineLength:
5
+ - 'Guardfile'
6
+ Metrics/LineLength:
6
7
  Max: 109
7
8
 
8
9
  Style/AlignHash:
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- ruby-2.1.5
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, { all_on_start: true, keep: true, all_after_pass: true, run_all: { cmd: 'rspec -f progress'} } do
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') { 'spec' }
11
- watch(%r{spec/support/.+\.rb}) { 'spec' }
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
- :servers:
83
+ servers:
84
84
  -
85
85
  :host: 'localhost'
86
86
  :port: '5672'
87
- :failover:
87
+ failover:
88
88
  :default_timeout: 5
89
89
  :max_retries: 5
90
- :publisher:
91
- :confirm: true
92
- :persistent: true
93
- :auth:
94
- :user: 'guest'
95
- :password: 'guest'
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 = '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
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 = 'A pluggable library that aims to hide message queue complexity'
15
- spec.homepage = 'http://www.vagas.com.br/'
16
- spec.license = 'MIT'
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'
@@ -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/basquiat_start.sh
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
@@ -5,7 +5,6 @@ RUN useradd -m -g users dev
5
5
  VOLUME /basquiat
6
6
  WORKDIR /basquiat
7
7
 
8
- USER dev
9
8
  ENV REFRESHED_AT 2015-03-29
10
- COPY ../Gemfile /tmp/Gemfile
11
- RUN bundle install
9
+ COPY Gemfile /tmp/Gemfile
10
+ RUN cd /tmp && bundle install
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/bash
2
+ cd /basquiat
3
+ bin/guard start -c --no-notify -p
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
- module Base
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
- end
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