msgr 1.1.0.1.b306 → 1.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.editorconfig +8 -0
- data/.github/workflows/build.yml +52 -0
- data/.github/workflows/lint.yml +20 -0
- data/.rubocop.yml +9 -48
- data/.travis.yml +21 -35
- data/Appraisals +18 -0
- data/CHANGELOG.md +34 -11
- data/Gemfile +9 -15
- data/README.md +8 -20
- data/Rakefile +10 -6
- data/bin/msgr +1 -0
- data/gemfiles/rails_5.2.gemfile +14 -0
- data/gemfiles/rails_6.0.gemfile +14 -0
- data/gemfiles/rails_6.1.gemfile +14 -0
- data/gemfiles/rails_master.gemfile +14 -0
- data/lib/msgr/binding.rb +13 -8
- data/lib/msgr/channel.rb +5 -3
- data/lib/msgr/cli.rb +18 -11
- data/lib/msgr/client.rb +25 -23
- data/lib/msgr/connection.rb +13 -1
- data/lib/msgr/consumer.rb +2 -3
- data/lib/msgr/dispatcher.rb +7 -9
- data/lib/msgr/logging.rb +2 -0
- data/lib/msgr/message.rb +1 -2
- data/lib/msgr/railtie.rb +14 -75
- data/lib/msgr/route.rb +1 -4
- data/lib/msgr/routes.rb +2 -0
- data/lib/msgr/tasks/msgr/drain.rake +11 -0
- data/lib/msgr/test_pool.rb +1 -3
- data/lib/msgr/version.rb +2 -2
- data/lib/msgr.rb +1 -0
- data/msgr.gemspec +2 -6
- data/scripts/simple_test.rb +2 -3
- data/spec/fixtures/{msgr-routes-test-1.rb → msgr_routes_test_1.rb} +0 -0
- data/spec/fixtures/msgr_routes_test_drain.rb +5 -0
- data/spec/integration/dummy/Rakefile +1 -1
- data/spec/{msgr/support/.keep → integration/dummy/app/assets/config/manifest.js} +0 -0
- data/spec/integration/dummy/bin/bundle +1 -1
- data/spec/integration/dummy/bin/rails +1 -1
- data/spec/integration/dummy/config/application.rb +1 -1
- data/spec/integration/dummy/config/boot.rb +2 -2
- data/spec/integration/dummy/config/environment.rb +1 -1
- data/spec/integration/dummy/config/rabbitmq.yml +1 -1
- data/spec/integration/msgr/dispatcher_spec.rb +28 -12
- data/spec/integration/msgr/railtie_spec.rb +10 -120
- data/spec/integration/spec_helper.rb +2 -3
- data/spec/integration/{msgr_spec.rb → test_controller_spec.rb} +1 -1
- data/spec/unit/msgr/client_spec.rb +83 -0
- data/spec/{msgr → unit}/msgr/connection_spec.rb +1 -1
- data/spec/{msgr → unit}/msgr/consumer_spec.rb +0 -0
- data/spec/unit/msgr/dispatcher_spec.rb +45 -0
- data/spec/{msgr → unit}/msgr/route_spec.rb +15 -14
- data/spec/{msgr → unit}/msgr/routes_spec.rb +32 -35
- data/spec/{msgr → unit}/msgr_spec.rb +25 -16
- data/spec/{msgr → unit}/spec_helper.rb +1 -1
- data/spec/unit/support/.keep +0 -0
- metadata +41 -36
- data/gemfiles/Gemfile.rails-4-2 +0 -7
- data/gemfiles/Gemfile.rails-5-0 +0 -7
- data/gemfiles/Gemfile.rails-5-1 +0 -7
- data/gemfiles/Gemfile.rails-5-2 +0 -7
- data/gemfiles/Gemfile.rails-master +0 -14
- data/spec/msgr/msgr/client_spec.rb +0 -60
- data/spec/msgr/msgr/dispatcher_spec.rb +0 -44
- data/spec/support/setup.rb +0 -29
data/lib/msgr/client.rb
CHANGED
@@ -2,15 +2,14 @@
|
|
2
2
|
|
3
3
|
require 'uri'
|
4
4
|
require 'cgi'
|
5
|
+
require 'json'
|
5
6
|
|
6
7
|
module Msgr
|
7
|
-
# rubocop:disable Metrics/ClassLength
|
8
8
|
class Client
|
9
9
|
include Logging
|
10
10
|
|
11
11
|
attr_reader :config
|
12
12
|
|
13
|
-
# rubocop:disable MethodLength
|
14
13
|
def initialize(config = {})
|
15
14
|
@config = {
|
16
15
|
host: '127.0.0.1',
|
@@ -18,21 +17,16 @@ module Msgr
|
|
18
17
|
max: 2
|
19
18
|
}
|
20
19
|
|
21
|
-
@config.merge! parse(config.delete(:uri)) if config
|
20
|
+
@config.merge! parse(config.delete(:uri)) if config[:uri]
|
22
21
|
@config.merge! config.symbolize_keys
|
23
22
|
|
24
23
|
@mutex = ::Mutex.new
|
25
|
-
@routes =
|
24
|
+
@routes = load_routes
|
26
25
|
@pid ||= ::Process.pid
|
27
26
|
|
28
27
|
log(:debug) { "Created new client on process ##{@pid}..." }
|
29
28
|
end
|
30
|
-
# rubocop:enable all
|
31
29
|
|
32
|
-
# rubocop:disable AbcSize
|
33
|
-
# rubocop:disable MethodLength
|
34
|
-
# rubocop:disable PerceivedComplexity
|
35
|
-
# rubocop:disable CyclomaticComplexity
|
36
30
|
def uri
|
37
31
|
@uri = begin
|
38
32
|
uri = ::URI.parse('amqp://localhost')
|
@@ -50,7 +44,6 @@ module Msgr
|
|
50
44
|
uri
|
51
45
|
end
|
52
46
|
end
|
53
|
-
# rubocop:enable all
|
54
47
|
|
55
48
|
def running?
|
56
49
|
mutex.synchronize do
|
@@ -59,7 +52,6 @@ module Msgr
|
|
59
52
|
end
|
60
53
|
end
|
61
54
|
|
62
|
-
# rubocop:disable AbcSize
|
63
55
|
def start
|
64
56
|
mutex.synchronize do
|
65
57
|
check_process!
|
@@ -67,12 +59,9 @@ module Msgr
|
|
67
59
|
|
68
60
|
log(:debug) { "Start on #{uri}..." }
|
69
61
|
|
70
|
-
@routes << config[:routing_file] if config[:routing_file].present?
|
71
|
-
@routes.reload
|
72
62
|
connection.bind(@routes)
|
73
63
|
end
|
74
64
|
end
|
75
|
-
# rubocop:enable all
|
76
65
|
|
77
66
|
def connect
|
78
67
|
mutex.synchronize do
|
@@ -85,7 +74,6 @@ module Msgr
|
|
85
74
|
end
|
86
75
|
end
|
87
76
|
|
88
|
-
# rubocop:disable AbcSize
|
89
77
|
def stop(opts = {})
|
90
78
|
mutex.synchronize do
|
91
79
|
check_process!
|
@@ -100,7 +88,6 @@ module Msgr
|
|
100
88
|
reset
|
101
89
|
end
|
102
90
|
end
|
103
|
-
# rubocop:enable all
|
104
91
|
|
105
92
|
def purge(release: false)
|
106
93
|
mutex.synchronize do
|
@@ -112,6 +99,15 @@ module Msgr
|
|
112
99
|
end
|
113
100
|
end
|
114
101
|
|
102
|
+
##
|
103
|
+
# Purge all queues known to Msgr, if they exist.
|
104
|
+
#
|
105
|
+
def drain
|
106
|
+
@routes.each do |route|
|
107
|
+
connection.purge_queue(route.name)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
115
111
|
def publish(payload, opts = {})
|
116
112
|
mutex.synchronize do
|
117
113
|
check_process!
|
@@ -149,6 +145,7 @@ module Msgr
|
|
149
145
|
|
150
146
|
def check_process!
|
151
147
|
return if ::Process.pid == @pid
|
148
|
+
|
152
149
|
log(:warn) do
|
153
150
|
"Fork detected. Reset internal state. (Old PID: #{@pid} / " \
|
154
151
|
"New PID: #{::Process.pid}"
|
@@ -178,22 +175,27 @@ module Msgr
|
|
178
175
|
@dispatcher = nil
|
179
176
|
end
|
180
177
|
|
181
|
-
# rubocop:disable AbcSize
|
182
178
|
def parse(uri)
|
183
179
|
# Legacy parsing of URI configuration; does not follow usual
|
184
180
|
# AMQP vhost encoding but used regular URL path
|
185
181
|
uri = ::URI.parse(uri)
|
186
182
|
|
187
183
|
config = {}
|
188
|
-
config[:user]
|
189
|
-
config[:pass]
|
190
|
-
config[:host]
|
191
|
-
config[:port]
|
192
|
-
config[:vhost] ||= uri.path
|
184
|
+
config[:user] ||= uri.user if uri.user
|
185
|
+
config[:pass] ||= uri.password if uri.password
|
186
|
+
config[:host] ||= uri.host if uri.host
|
187
|
+
config[:port] ||= uri.port if uri.port
|
188
|
+
config[:vhost] ||= uri.path unless uri.path.empty?
|
193
189
|
config[:ssl] ||= uri.scheme.casecmp('amqps').zero?
|
194
190
|
|
195
191
|
config
|
196
192
|
end
|
197
|
-
|
193
|
+
|
194
|
+
def load_routes
|
195
|
+
Routes.new.tap do |routes|
|
196
|
+
routes << config[:routing_file] if config[:routing_file].present?
|
197
|
+
routes.reload
|
198
|
+
end
|
199
|
+
end
|
198
200
|
end
|
199
201
|
end
|
data/lib/msgr/connection.rb
CHANGED
@@ -3,7 +3,6 @@
|
|
3
3
|
require 'bunny'
|
4
4
|
|
5
5
|
module Msgr
|
6
|
-
# rubocop:disable Metrics/ClassLength
|
7
6
|
class Connection
|
8
7
|
include Logging
|
9
8
|
|
@@ -49,6 +48,7 @@ module Msgr
|
|
49
48
|
|
50
49
|
def release
|
51
50
|
return if bindings.empty?
|
51
|
+
|
52
52
|
log(:debug) { "Release bindings (#{bindings.size})..." }
|
53
53
|
|
54
54
|
bindings.each(&:release)
|
@@ -56,6 +56,7 @@ module Msgr
|
|
56
56
|
|
57
57
|
def delete
|
58
58
|
return if bindings.empty?
|
59
|
+
|
59
60
|
log(:debug) { "Delete bindings (#{bindings.size})..." }
|
60
61
|
|
61
62
|
bindings.each(&:delete)
|
@@ -63,11 +64,22 @@ module Msgr
|
|
63
64
|
|
64
65
|
def purge(**kwargs)
|
65
66
|
return if bindings.empty?
|
67
|
+
|
66
68
|
log(:debug) { "Purge bindings (#{bindings.size})..." }
|
67
69
|
|
68
70
|
bindings.each {|b| b.purge(**kwargs) }
|
69
71
|
end
|
70
72
|
|
73
|
+
def purge_queue(name)
|
74
|
+
# Creating the queue in passive mode ensures that queues that do not exist
|
75
|
+
# won't be created just to purge them.
|
76
|
+
# That requires creating a new channel every time, as exceptions (on
|
77
|
+
# missing queues) invalidate the channel.
|
78
|
+
channel.queue(name, passive: true).purge
|
79
|
+
rescue Bunny::NotFound
|
80
|
+
nil
|
81
|
+
end
|
82
|
+
|
71
83
|
def bindings
|
72
84
|
@bindings ||= []
|
73
85
|
end
|
data/lib/msgr/consumer.rb
CHANGED
@@ -5,6 +5,7 @@ module Msgr
|
|
5
5
|
include Logging
|
6
6
|
|
7
7
|
attr_reader :message
|
8
|
+
|
8
9
|
delegate :payload, to: :@message
|
9
10
|
delegate :action, to: :'@message.route'
|
10
11
|
delegate :consumer, to: :'@message.consumer'
|
@@ -14,9 +15,7 @@ module Msgr
|
|
14
15
|
@auto_ack || @auto_ack.nil?
|
15
16
|
end
|
16
17
|
|
17
|
-
|
18
|
-
@auto_ack = val
|
19
|
-
end
|
18
|
+
attr_writer :auto_ack
|
20
19
|
end
|
21
20
|
|
22
21
|
def dispatch(message)
|
data/lib/msgr/dispatcher.rb
CHANGED
@@ -27,9 +27,6 @@ module Msgr
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
-
# rubocop:disable Metrics/AbcSize
|
31
|
-
# rubocop:disable Metrics/MethodLength
|
32
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
33
30
|
def dispatch(message)
|
34
31
|
consumer_class = Object.const_get message.route.consumer
|
35
32
|
|
@@ -37,17 +34,18 @@ module Msgr
|
|
37
34
|
|
38
35
|
consumer_class.new.dispatch message
|
39
36
|
|
40
|
-
# Acknowledge message
|
41
|
-
|
42
|
-
|
37
|
+
# Acknowledge message only if it is not already acknowledged and auto
|
38
|
+
# acknowledgment is enabled.
|
39
|
+
message.ack unless message.acked? || !consumer_class.auto_ack?
|
40
|
+
rescue StandardError => e
|
43
41
|
message.nack unless message.acked?
|
44
42
|
|
45
43
|
log(:error) do
|
46
|
-
"Dispatcher error: #{
|
47
|
-
|
44
|
+
"Dispatcher error: #{e.class.name}: #{e}\n" +
|
45
|
+
e.backtrace.join("\n")
|
48
46
|
end
|
49
47
|
|
50
|
-
raise
|
48
|
+
raise e if config[:raise_exceptions]
|
51
49
|
ensure
|
52
50
|
if defined?(ActiveRecord) &&
|
53
51
|
ActiveRecord::Base.connection_pool.active_connection?
|
data/lib/msgr/logging.rb
CHANGED
data/lib/msgr/message.rb
CHANGED
@@ -11,8 +11,7 @@ module Msgr
|
|
11
11
|
@payload = payload
|
12
12
|
@route = route
|
13
13
|
|
14
|
-
# rubocop:disable Style/GuardClause
|
15
|
-
if content_type == 'application/json'
|
14
|
+
if content_type == 'application/json' # rubocop:disable Style/GuardClause
|
16
15
|
@payload = JSON.parse(payload)
|
17
16
|
@payload.symbolize_keys! if @payload.respond_to? :symbolize_keys!
|
18
17
|
end
|
data/lib/msgr/railtie.rb
CHANGED
@@ -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
|
-
|
25
|
-
|
26
|
-
|
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
|
-
|
92
|
-
|
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
|
data/lib/msgr/route.rb
CHANGED
@@ -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]
|
data/lib/msgr/routes.rb
CHANGED
data/lib/msgr/test_pool.rb
CHANGED
@@ -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)
|
data/lib/msgr/version.rb
CHANGED
data/lib/msgr.rb
CHANGED
data/msgr.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
lib = File.expand_path('
|
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 = ['
|
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
|
data/scripts/simple_test.rb
CHANGED
@@ -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
|
-
|
52
|
+
warn "COUNTER: #{@counter}"
|
File without changes
|
@@ -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('
|
6
|
+
require File.expand_path('config/application', __dir__)
|
7
7
|
|
8
8
|
Dummy::Application.load_tasks
|
File without changes
|
@@ -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('
|
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('
|
7
|
+
$LOAD_PATH.unshift File.expand_path('../../../lib', __dir__)
|
@@ -4,7 +4,19 @@ require 'spec_helper'
|
|
4
4
|
|
5
5
|
class DispatcherTestConsumer < Msgr::Consumer
|
6
6
|
def index
|
7
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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 '
|
47
|
-
|
48
|
-
|
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
|
-
|
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 '
|
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 '
|
86
|
+
it 'raises the exception' do
|
71
87
|
expect { dispatcher.call(message) }.to raise_error(ArgumentError)
|
72
88
|
end
|
73
89
|
end
|