msgr 1.1.0.1.b302 → 1.3.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.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +8 -0
  3. data/.github/workflows/build.yml +52 -0
  4. data/.github/workflows/lint.yml +20 -0
  5. data/.rubocop.yml +9 -48
  6. data/.travis.yml +21 -35
  7. data/Appraisals +18 -0
  8. data/CHANGELOG.md +29 -11
  9. data/Gemfile +9 -15
  10. data/README.md +8 -20
  11. data/Rakefile +10 -6
  12. data/bin/msgr +1 -0
  13. data/gemfiles/rails_5.2.gemfile +14 -0
  14. data/gemfiles/rails_6.0.gemfile +14 -0
  15. data/gemfiles/rails_6.1.gemfile +14 -0
  16. data/gemfiles/rails_master.gemfile +14 -0
  17. data/lib/msgr.rb +1 -0
  18. data/lib/msgr/binding.rb +13 -8
  19. data/lib/msgr/channel.rb +5 -3
  20. data/lib/msgr/cli.rb +18 -11
  21. data/lib/msgr/client.rb +17 -20
  22. data/lib/msgr/connection.rb +13 -1
  23. data/lib/msgr/consumer.rb +2 -3
  24. data/lib/msgr/dispatcher.rb +7 -9
  25. data/lib/msgr/logging.rb +2 -0
  26. data/lib/msgr/message.rb +1 -2
  27. data/lib/msgr/railtie.rb +14 -75
  28. data/lib/msgr/route.rb +1 -4
  29. data/lib/msgr/routes.rb +2 -0
  30. data/lib/msgr/tasks/msgr/drain.rake +11 -0
  31. data/lib/msgr/test_pool.rb +1 -3
  32. data/lib/msgr/version.rb +2 -2
  33. data/msgr.gemspec +2 -6
  34. data/scripts/simple_test.rb +2 -3
  35. data/spec/fixtures/{msgr-routes-test-1.rb → msgr_routes_test_1.rb} +0 -0
  36. data/spec/integration/dummy/Rakefile +1 -1
  37. data/spec/{msgr/support/.keep → integration/dummy/app/assets/config/manifest.js} +0 -0
  38. data/spec/integration/dummy/bin/bundle +1 -1
  39. data/spec/integration/dummy/bin/rails +1 -1
  40. data/spec/integration/dummy/config/application.rb +1 -1
  41. data/spec/integration/dummy/config/boot.rb +2 -2
  42. data/spec/integration/dummy/config/environment.rb +1 -1
  43. data/spec/integration/dummy/config/rabbitmq.yml +1 -1
  44. data/spec/integration/msgr/dispatcher_spec.rb +28 -12
  45. data/spec/integration/msgr/railtie_spec.rb +10 -120
  46. data/spec/integration/spec_helper.rb +2 -3
  47. data/spec/integration/{msgr_spec.rb → test_controller_spec.rb} +1 -1
  48. data/spec/unit/msgr/client_spec.rb +88 -0
  49. data/spec/{msgr → unit}/msgr/connection_spec.rb +1 -1
  50. data/spec/{msgr → unit}/msgr/consumer_spec.rb +0 -0
  51. data/spec/unit/msgr/dispatcher_spec.rb +45 -0
  52. data/spec/{msgr → unit}/msgr/route_spec.rb +15 -14
  53. data/spec/{msgr → unit}/msgr/routes_spec.rb +32 -35
  54. data/spec/{msgr → unit}/msgr_spec.rb +25 -16
  55. data/spec/{msgr → unit}/spec_helper.rb +1 -1
  56. data/spec/unit/support/.keep +0 -0
  57. metadata +39 -36
  58. data/gemfiles/Gemfile.rails-4-2 +0 -7
  59. data/gemfiles/Gemfile.rails-5-0 +0 -7
  60. data/gemfiles/Gemfile.rails-5-1 +0 -7
  61. data/gemfiles/Gemfile.rails-5-2 +0 -7
  62. data/gemfiles/Gemfile.rails-master +0 -14
  63. data/spec/msgr/msgr/client_spec.rb +0 -60
  64. data/spec/msgr/msgr/dispatcher_spec.rb +0 -44
  65. data/spec/support/setup.rb +0 -29
@@ -4,92 +4,31 @@ module Msgr
4
4
  class Railtie < ::Rails::Railtie
5
5
  config.msgr = ActiveSupport::OrderedOptions.new
6
6
 
7
- if File.exist?("#{Rails.root}/app/consumers")
8
- config.autoload_paths << File.expand_path("#{Rails.root}/app/consumers")
9
- end
10
-
11
7
  initializer 'msgr.logger' do |app|
12
8
  app.config.msgr.logger ||= Rails.logger
13
9
  end
14
10
 
15
- # Start msgr
16
11
  initializer 'msgr.start' do
17
12
  config.after_initialize do |app|
18
13
  Msgr.logger = app.config.msgr.logger
19
-
20
- self.class.load app.config.msgr
14
+ self.class.load(app.config_for(:rabbitmq).symbolize_keys)
21
15
  end
22
16
  end
23
17
 
24
- class << self
25
- def load(rails_config)
26
- cfg = parse_config load_config rails_config
27
- return unless cfg # no config given -> does not load Msgr
28
-
29
- Msgr.config = cfg
30
- Msgr.client.connect if cfg[:checkcredentials]
31
- Msgr.start if cfg[:autostart]
32
- end
33
-
34
- def parse_config(cfg)
35
- unless cfg.is_a? Hash
36
- Rails.logger.warn '[Msgr] Could not load rabbitmq config: Config must be a Hash'
37
- return nil
38
- end
39
-
40
- unless cfg[Rails.env].is_a?(Hash)
41
- Rails.logger.warn "Could not load rabbitmq config for environment \"#{Rails.env}\": is not a Hash"
42
- return nil
43
- end
44
-
45
- cfg = HashWithIndifferentAccess.new cfg[Rails.env]
46
- unless cfg[:uri]
47
- raise ArgumentError.new('Could not load rabbitmq environment config: URI missing.')
48
- end
49
-
50
- case cfg[:autostart]
51
- when true, 'true', 'enabled'
52
- cfg[:autostart] = true
53
- when false, 'false', 'disabled', nil
54
- cfg[:autostart] = false
55
- else
56
- raise ArgumentError.new("Invalid value for rabbitmq config autostart: \"#{cfg[:autostart]}\"")
57
- end
58
-
59
- case cfg[:checkcredentials]
60
- when true, 'true', 'enabled', nil
61
- cfg[:checkcredentials] = true
62
- when false, 'false', 'disabled'
63
- cfg[:checkcredentials] = false
64
- else
65
- raise ArgumentError.new("Invalid value for rabbitmq config checkcredentials: \"#{cfg[:checkcredentials]}\"")
66
- end
67
-
68
- case cfg[:raise_exceptions]
69
- when true, 'true', 'enabled'
70
- cfg[:raise_exceptions] = true
71
- when false, 'false', 'disabled', nil
72
- cfg[:raise_exceptions] = false
73
- else
74
- raise ArgumentError.new("Invalid value for rabbitmq config raise_exceptions: \"#{cfg[:raise_exceptions]}\"")
75
- end
76
-
77
- cfg[:routing_file] ||= Rails.root.join('config/msgr.rb').to_s
78
- cfg
79
- end
80
-
81
- def load_config(options)
82
- if options.rabbitmq_config || !Rails.application.respond_to?(:config_for)
83
- load_file options.rabbitmq_config || Rails.root.join('config', 'rabbitmq.yml')
84
- else
85
- conf = Rails.application.config_for :rabbitmq
86
-
87
- {Rails.env.to_s => conf}
88
- end
89
- end
18
+ rake_tasks do
19
+ load File.expand_path('tasks/msgr/drain.rake', __dir__)
20
+ end
90
21
 
91
- def load_file(path)
92
- YAML.safe_load ERB.new(File.read(path)).result
22
+ class << self
23
+ def load(config)
24
+ # Set defaults
25
+ config.reverse_merge!(
26
+ checkcredentials: true,
27
+ routing_file: Rails.root.join('config/msgr.rb').to_s
28
+ )
29
+
30
+ Msgr.config = config
31
+ Msgr.client.connect if config.fetch(:checkcredentials)
93
32
  end
94
33
  end
95
34
  end
@@ -4,10 +4,7 @@ module Msgr
4
4
  class Route
5
5
  attr_reader :consumer, :action, :opts
6
6
 
7
- MATCH_REGEXP = /\A(?<consumer>\w+)#(?<action>\w+)\z/
8
-
9
- # rubocop:disable Metrics/AbcSize
10
- # rubocop:disable Metrics/MethodLength
7
+ MATCH_REGEXP = /\A(?<consumer>\w+)#(?<action>\w+)\z/.freeze
11
8
  def initialize(key, opts = {})
12
9
  @opts = opts
13
10
  raise ArgumentError.new 'Missing `to` options.' unless @opts[:to]
@@ -3,7 +3,9 @@
3
3
  module Msgr
4
4
  class Routes
5
5
  include Logging
6
+
6
7
  attr_reader :routes
8
+
7
9
  delegate :each, :empty?, :size, :any?, to: :@routes
8
10
 
9
11
  def initialize
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :msgr do
4
+ desc 'Drain all known queues'
5
+ task drain: :environment do
6
+ client = Msgr.client
7
+
8
+ client.connect
9
+ client.drain
10
+ end
11
+ end
@@ -31,8 +31,6 @@ module Msgr
31
31
 
32
32
  private
33
33
 
34
- # rubocop:disable Metrics/AbcSize
35
- # rubocop:disable Metrics/MethodLength
36
34
  def ns_run(count: 1, timeout: 5)
37
35
  received = 0
38
36
 
@@ -59,7 +57,7 @@ module Msgr
59
57
 
60
58
  class << self
61
59
  def new(*args)
62
- @instance ||= super(*args)
60
+ @instance ||= super(*args) # rubocop:disable Naming/MemoizedInstanceVariableName
63
61
  end
64
62
 
65
63
  def run(*args)
@@ -3,8 +3,8 @@
3
3
  module Msgr
4
4
  module VERSION
5
5
  MAJOR = 1
6
- MINOR = 1
7
- PATCH = 0
6
+ MINOR = 3
7
+ PATCH = 1
8
8
  STAGE = nil
9
9
  STRING = [MAJOR, MINOR, PATCH, STAGE].reject(&:nil?).join('.')
10
10
 
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- lib = File.expand_path('../lib', __FILE__)
3
+ lib = File.expand_path('lib', __dir__)
4
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
5
 
6
6
  require 'msgr/version'
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
9
9
  spec.name = 'msgr'
10
10
  spec.version = Msgr::VERSION
11
11
  spec.authors = ['Jan Graichen']
12
- spec.email = ['jg@altimos.de']
12
+ spec.email = ['jgraichen@altimos.de']
13
13
  spec.description = 'Msgr: Rails-like Messaging Framework'
14
14
  spec.summary = 'Msgr: Rails-like Messaging Framework'
15
15
  spec.homepage = 'https://github.com/jgraichen/msgr'
@@ -24,8 +24,4 @@ Gem::Specification.new do |spec|
24
24
  spec.add_dependency 'bunny', '>= 1.4', '< 3.0'
25
25
 
26
26
  spec.add_development_dependency 'bundler'
27
-
28
- if ENV['TRAVIS_BUILD_NUMBER']
29
- spec.version = "#{spec.version}.1.b#{ENV['TRAVIS_BUILD_NUMBER']}"
30
- end
31
27
  end
@@ -19,7 +19,6 @@ class TestConsumer < Msgr::Consumer
19
19
  end
20
20
  end
21
21
 
22
- #
23
22
  class NullPool
24
23
  def initialize(*); end
25
24
 
@@ -45,9 +44,9 @@ end
45
44
 
46
45
  begin
47
46
  sleep
48
- rescue Interrupt
47
+ rescue Interrupt # rubocop:disable Lint/SuppressedException
49
48
  ensure
50
49
  @client.stop timeout: 10, delete: true
51
50
  end
52
51
 
53
- $stderr.puts "COUNTER: #{@counter}"
52
+ warn "COUNTER: #{@counter}"
@@ -3,6 +3,6 @@
3
3
  # Add your own tasks in files placed in lib/tasks ending in .rake,
4
4
  # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
5
5
 
6
- require File.expand_path('../config/application', __FILE__)
6
+ require File.expand_path('config/application', __dir__)
7
7
 
8
8
  Dummy::Application.load_tasks
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
4
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
5
5
  load Gem.bin_path('bundler', 'bundle')
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- APP_PATH = File.expand_path('../../config/application', __FILE__)
4
+ APP_PATH = File.expand_path('../config/application', __dir__)
5
5
  require_relative '../config/boot'
6
6
  require 'rails/commands'
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require File.expand_path('../boot', __FILE__)
3
+ require File.expand_path('boot', __dir__)
4
4
 
5
5
  require 'rails/all'
6
6
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Set up gems listed in the Gemfile.
4
- ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../../Gemfile', __FILE__)
4
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../Gemfile', __dir__)
5
5
 
6
6
  require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
7
- $LOAD_PATH.unshift File.expand_path('../../../../lib', __FILE__)
7
+ $LOAD_PATH.unshift File.expand_path('../../../lib', __dir__)
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Load the Rails application.
4
- require File.expand_path('../application', __FILE__)
4
+ require File.expand_path('application', __dir__)
5
5
 
6
6
  # Initialize the Rails application.
7
7
  Dummy::Application.initialize!
@@ -1,5 +1,5 @@
1
1
  common: &common
2
- uri: amqp://localhost/
2
+ uri: <%= ENV.fetch("AMQP_SERVER", "amqp://localhost/") %>
3
3
 
4
4
  test:
5
5
  <<: *common
@@ -4,7 +4,19 @@ require 'spec_helper'
4
4
 
5
5
  class DispatcherTestConsumer < Msgr::Consumer
6
6
  def index
7
- puts "<<< #{payload}"
7
+ self.class.called!
8
+ end
9
+
10
+ class << self
11
+ def calls
12
+ @calls ||= 0
13
+ end
14
+
15
+ attr_writer :calls
16
+
17
+ def called!
18
+ self.calls += 1
19
+ end
8
20
  end
9
21
  end
10
22
 
@@ -19,55 +31,59 @@ describe Msgr::Dispatcher do
19
31
  let(:config) { {max: 1} }
20
32
  let(:consumer) { 'DispatcherTestConsumer' }
21
33
  let(:route) do
22
- double(:route).tap do |t|
34
+ instance_double('Msgr::Route').tap do |t|
23
35
  allow(t).to receive(:consumer).and_return consumer
24
36
  allow(t).to receive(:action).and_return 'index'
25
37
  end
26
38
  end
27
39
  let(:channel) do
28
- double(:channel).tap do |c|
40
+ instance_double('Msgr::Channel').tap do |c|
29
41
  allow(c).to receive(:ack)
30
42
  end
31
43
  end
32
44
  let(:delivery_info) do
33
- double(:delivery_info).tap do |ti|
45
+ instance_double('Bunny::DeliveryInfo').tap do |ti|
34
46
  allow(ti).to receive(:delivery_tag).and_return(3)
35
47
  end
36
48
  end
37
49
  let(:payload) { {} }
38
50
  let(:metadata) do
39
- double(:metadata).tap do |metadata|
51
+ instance_double('Bunny::MessageProperties').tap do |metadata|
40
52
  allow(metadata).to receive(:content_type).and_return('text/plain')
41
53
  end
42
54
  end
43
55
  let(:message) { Msgr::Message.new channel, delivery_info, metadata, payload, route }
44
56
  let(:action) { -> { dispatcher.call message } }
45
57
 
46
- it 'should consume message' do
47
- expect_any_instance_of(DispatcherTestConsumer).to receive(:index)
48
- dispatcher.call message
58
+ it 'consumes message' do
59
+ expect do
60
+ dispatcher.call message
61
+ end.to change(DispatcherTestConsumer, :calls).by(1)
49
62
  end
50
63
 
51
64
  context 'with not acknowledged message' do
52
- before { dispatcher.call message }
53
65
  subject { message }
54
- it { should be_acked }
66
+
67
+ before { dispatcher.call message }
68
+
69
+ it { is_expected.to be_acked }
55
70
  end
56
71
 
57
72
  describe 'exception swallowing' do
58
73
  let(:consumer) { 'DispatcherRaiseConsumer' }
74
+
59
75
  before do
60
76
  allow(message).to receive(:nack)
61
77
  end
62
78
 
63
- it 'should swallow exceptions by default' do
79
+ it 'swallows exceptions by default' do
64
80
  expect { dispatcher.call(message) }.not_to raise_error
65
81
  end
66
82
 
67
83
  context 'with raise_exceptions configuration option and a synchronous pool' do
68
84
  let(:config) { super().merge(raise_exceptions: true) }
69
85
 
70
- it 'should raise the exception' do
86
+ it 'raises the exception' do
71
87
  expect { dispatcher.call(message) }.to raise_error(ArgumentError)
72
88
  end
73
89
  end
@@ -6,139 +6,29 @@ describe Msgr::Railtie do
6
6
  describe 'configuration options' do
7
7
  let(:config) { Rails.configuration }
8
8
 
9
- it 'should have `msgr` key' do
9
+ it 'has `msgr` key' do
10
10
  expect(config).to respond_to :msgr
11
11
  end
12
12
  end
13
13
 
14
- describe '#parse_config' do
15
- let(:settings) { {} }
16
- let(:action) { described_class.parse_config settings }
17
- subject { action }
18
-
19
- context 'with incorrect settings' do
20
- subject { -> { action } }
21
-
22
- context 'with config without url' do
23
- let(:settings) { {'test' => {hans: 'otto'}} }
24
-
25
- it { should raise_error 'Could not load rabbitmq environment config: URI missing.' }
26
- end
27
-
28
- context 'with invalid autostart value' do
29
- let(:settings) { {'test' => {uri: 'hans', autostart: 'unvalid'}} }
30
-
31
- it { should raise_error 'Invalid value for rabbitmq config autostart: "unvalid"' }
32
- end
33
-
34
- context 'with invalid checkcredentials value' do
35
- let(:settings) { {'test' => {uri: 'hans', checkcredentials: 'unvalid'}} }
36
-
37
- it { should raise_error 'Invalid value for rabbitmq config checkcredentials: "unvalid"' }
38
- end
39
-
40
- context 'with invalid raise_exceptions value' do
41
- let(:settings) { {'test' => {uri: 'franz', raise_exceptions: 'unvalid'}} }
42
-
43
- it { should raise_error 'Invalid value for rabbitmq config raise_exceptions: "unvalid"' }
44
- end
45
- end
46
-
47
- context 'without set routes file' do
48
- let(:settings) { {'test' => {uri: 'test'}} }
49
-
50
- context '[:routing_file]' do
51
- subject { super()[:routing_file] }
52
- it { should eq Rails.root.join('config/msgr.rb').to_s }
53
- end
54
- end
55
-
56
- context 'with set routes file' do
57
- let(:settings) { {'test' => {uri: 'test', 'routing_file' => 'my fancy file'}} }
58
-
59
- context '[:routing_file]' do
60
- subject { super()[:routing_file] }
61
- it { should eq 'my fancy file' }
62
- end
63
- end
64
-
65
- context 'with uri as symbol' do
66
- let(:settings) { {'test' => {uri: 'hans'}} }
67
-
68
- context '[:uri]' do
69
- subject { super()[:uri] }
70
- it { should eq 'hans' }
71
- end
72
- end
73
-
74
- context 'with uri as string' do
75
- let(:settings) { {'test' => {'uri' => 'hans'}} }
76
-
77
- context '[:uri]' do
78
- subject { super()[:uri] }
79
- it { should eq 'hans' }
80
- end
81
- end
82
-
83
- context 'without raise_exceptions config' do
84
- let(:settings) { {'test' => {'uri' => 'hans'}, 'development' => {'uri' => 'hans_dev'}} }
85
-
86
- describe '[:raise_exceptions]' do
87
- subject { super()[:raise_exceptions] }
88
- it { should eq false }
89
- end
90
- end
91
- end
92
-
93
14
  describe '#load' do
94
- let(:config) do
95
- cfg = ActiveSupport::OrderedOptions.new
96
- cfg.rabbitmq_config = Rails.root.join 'config', 'rabbitmq.yml'
97
- cfg
98
- end
99
-
100
- context 'with autostart is true' do
101
- it 'should not start Msgr' do
102
- expect(Msgr).to receive(:start)
103
- expect(Msgr::Railtie).to receive(:load_config).and_return('test' => {uri: 'test', autostart: true})
104
- Msgr::Railtie.load config
105
- end
106
- end
107
-
108
- context 'without autostart value' do
109
- it 'should not start Msgr' do
110
- expect(Msgr).to_not receive(:start)
111
- expect(Msgr::Railtie).to receive(:load_config).and_return('test' => {uri: 'test'})
112
- Msgr::Railtie.load config
113
- end
15
+ before do
16
+ allow(Msgr).to receive(:start)
17
+ allow(Msgr.client).to receive(:connect)
114
18
  end
115
19
 
116
20
  context 'without checkcredentials value' do
117
- it 'should connect to rabbitmq directly to check credentials' do
118
- expect_any_instance_of(Msgr::Client).to receive(:connect)
119
- expect(Msgr::Railtie).to receive(:load_config).and_return('test' => {uri: 'test'})
120
- Msgr::Railtie.load config
21
+ it 'connects to rabbitmq directly to check credentials' do
22
+ described_class.load({})
23
+ expect(Msgr.client).to have_received(:connect)
121
24
  end
122
25
  end
123
26
 
124
27
  context 'with checkcredentials is false' do
125
- it 'should connect to rabbitmq directly to check credentials' do
126
- expect_any_instance_of(Msgr::Client).to_not receive(:connect)
127
- expect(Msgr::Railtie).to receive(:load_config).and_return('test' => {uri: 'test', checkcredentials: false})
128
- Msgr::Railtie.load config
28
+ it 'connects to rabbitmq directly to check credentials' do
29
+ described_class.load({checkcredentials: false})
30
+ expect(Msgr.client).not_to have_received(:connect)
129
31
  end
130
32
  end
131
33
  end
132
-
133
- # describe '#load_config'
134
- # let(:options) { {} }
135
-
136
- # subject { Msgr::Railtie.load_config options }
137
-
138
- # if Rails::Application.methods.include(:config_for)
139
- # it 'should use config_for' do
140
-
141
- # end
142
- # end
143
- # end
144
34
  end