basquiat 1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
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