action_subscriber 2.0.1-java → 2.1.0-java

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 939204017ecb3b17925a0ceca1442798b98ddcdc
4
- data.tar.gz: 1780324c82d3dac0a2f0d36d00bfaa724296782f
3
+ metadata.gz: a8a1a10bb43fef34f63038536e121540df818b14
4
+ data.tar.gz: 78e17fea12571a3c07037a7123f2e529ee1580a3
5
5
  SHA512:
6
- metadata.gz: 258b3dcf397203650f2fe7c5b6590ddc3cf5ce0c9e81d1352d3a50505fc58ffa9b0dbcb5ec16b929d3ae9bb41580e6c15ba993da71194873eb4104ab58f44ba1
7
- data.tar.gz: 9235bf4d8f54c8616ff77f958da778d04cd4a3f6271968a74f35245ad9fbf9a52c9b7cf1cf642e04dfd71a5e3a7ea8d586d7986f06a3fd6f6e1fc94ac2ab8e86
6
+ metadata.gz: a1392baccf77a3b65c695fcb1ff5234a1ba2ef38ec83f4400f43b617be6659f0b5b31d6348e95b84e6f89bf5279643d10bcae072c4023c0d94531d8d28108e5a
7
+ data.tar.gz: c55665bfb5d487f800251d50f3737e46143092c539b81b041ffb49d5f50adf7a5a2aaade0dabc7fe3bf659f20f070b53103097958d32cfee766ffee64f76baa6
@@ -23,11 +23,13 @@ require "action_subscriber/bunny/subscriber"
23
23
  require "action_subscriber/march_hare/subscriber"
24
24
  require "action_subscriber/babou"
25
25
  require "action_subscriber/publisher"
26
+ require "action_subscriber/publisher/async"
26
27
  require "action_subscriber/route"
27
28
  require "action_subscriber/route_set"
28
29
  require "action_subscriber/router"
29
30
  require "action_subscriber/threadpool"
30
31
  require "action_subscriber/base"
32
+ require "action_subscriber/uri"
31
33
 
32
34
  module ActionSubscriber
33
35
  ##
@@ -110,6 +112,9 @@ module ActionSubscriber
110
112
  # Initialize config object
111
113
  config
112
114
 
115
+ # Intialize async publisher adapter
116
+ ::ActionSubscriber::Publisher::Async.publisher_adapter
117
+
113
118
  ::ActiveSupport.run_load_hooks(:action_subscriber, Base)
114
119
 
115
120
  ##
@@ -135,5 +140,6 @@ end
135
140
  require "action_subscriber/railtie" if defined?(Rails)
136
141
 
137
142
  at_exit do
143
+ ::ActionSubscriber::Publisher::Async.publisher_adapter.shutdown!
138
144
  ::ActionSubscriber::RabbitConnection.publisher_disconnect!
139
145
  end
@@ -1,6 +1,10 @@
1
1
  module ActionSubscriber
2
2
  class Configuration
3
3
  attr_accessor :allow_low_priority_methods,
4
+ :async_publisher,
5
+ :async_publisher_drop_messages_when_queue_full,
6
+ :async_publisher_max_queue_size,
7
+ :async_publisher_supervisor_interval,
4
8
  :decoder,
5
9
  :default_exchange,
6
10
  :error_handler,
@@ -8,17 +12,24 @@ module ActionSubscriber
8
12
  :host,
9
13
  :hosts,
10
14
  :mode,
15
+ :password,
11
16
  :pop_interval,
12
17
  :port,
13
18
  :prefetch,
14
19
  :publisher_confirms,
15
20
  :seconds_to_wait_for_graceful_shutdown,
21
+ :username,
16
22
  :threadpool_size,
17
23
  :timeout,
18
- :times_to_pop
24
+ :times_to_pop,
25
+ :virtual_host
19
26
 
20
27
  DEFAULTS = {
21
28
  :allow_low_priority_methods => false,
29
+ :async_publisher => 'memory',
30
+ :async_publisher_drop_messages_when_queue_full => false,
31
+ :async_publisher_max_queue_size => 1_000_000,
32
+ :async_publisher_supervisor_interval => 200, # in milliseconds
22
33
  :default_exchange => 'events',
23
34
  :heartbeat => 5,
24
35
  :host => 'localhost',
@@ -31,7 +42,10 @@ module ActionSubscriber
31
42
  :seconds_to_wait_for_graceful_shutdown => 30,
32
43
  :threadpool_size => 8,
33
44
  :timeout => 1,
34
- :times_to_pop => 8
45
+ :times_to_pop => 8,
46
+ :username => "guest",
47
+ :password => "guest",
48
+ :virtual_host => "/"
35
49
  }
36
50
 
37
51
  ##
@@ -81,6 +95,13 @@ module ActionSubscriber
81
95
  self.decoder.merge!(decoders)
82
96
  end
83
97
 
98
+ def connection_string=(url)
99
+ settings = ::ActionSubscriber::URI.parse_amqp_url(url)
100
+ settings.each do |key, value|
101
+ send("#{key}=", value)
102
+ end
103
+ end
104
+
84
105
  def hosts
85
106
  return @hosts if @hosts.size > 0
86
107
  [ host ]
@@ -0,0 +1,31 @@
1
+ module ActionSubscriber
2
+ module Publisher
3
+ # Publish a message asynchronously to RabbitMQ.
4
+ #
5
+ # Asynchronous is designed to do two things:
6
+ # 1. Introduce the idea of a durable retry should the RabbitMQ connection disconnect.
7
+ # 2. Provide a higher-level pattern for fire-and-forget publishing.
8
+ #
9
+ # @param [String] route The routing key to use for this message.
10
+ # @param [String] payload The message you are sending. Should already be encoded as a string.
11
+ # @param [String] exchange The exchange you want to publish to.
12
+ # @param [Hash] options hash to set message parameters (e.g. headers).
13
+ def self.publish_async(route, payload, exchange_name, options = {})
14
+ Async.publisher_adapter.publish(route, payload, exchange_name, options)
15
+ end
16
+
17
+ module Async
18
+ def self.publisher_adapter
19
+ @publisher_adapter ||= case ::ActionSubscriber.configuration.async_publisher
20
+ when /memory/i then
21
+ require "action_subscriber/publisher/async/in_memory_adapter"
22
+ InMemoryAdapter.new
23
+ when /redis/i then
24
+ fail "Not yet implemented"
25
+ else
26
+ fail "Unknown adapter '#{::ActionSubscriber.configuration.async_publisher}' provided"
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,153 @@
1
+ require "thread"
2
+
3
+ module ActionSubscriber
4
+ module Publisher
5
+ module Async
6
+ class InMemoryAdapter
7
+ include ::ActionSubscriber::Logging
8
+
9
+ attr_reader :async_queue
10
+
11
+ def initialize
12
+ logger.info "Starting in-memory publisher adapter."
13
+
14
+ @async_queue = AsyncQueue.new
15
+ end
16
+
17
+ def publish(route, payload, exchange_name, options = {})
18
+ message = Message.new(route, payload, exchange_name, options)
19
+ async_queue.push(message)
20
+ nil
21
+ end
22
+
23
+ def shutdown!
24
+ max_wait_time = ::ActionSubscriber.configuration.seconds_to_wait_for_graceful_shutdown
25
+ started_shutting_down_at = ::Time.now
26
+
27
+ logger.info "Draining async publisher in-memory adapter queue before shutdown. Current queue size: #{async_queue.size}."
28
+ while async_queue.size > 0
29
+ if (::Time.now - started_shutting_down_at) > max_wait_time
30
+ logger.info "Forcing async publisher adapter shutdown because graceful shutdown period of #{max_wait_time} seconds was exceeded. Current queue size: #{async_queue.size}."
31
+ break
32
+ end
33
+
34
+ sleep 0.1
35
+ end
36
+ end
37
+
38
+ class Message
39
+ attr_reader :route, :payload, :exchange_name, :options
40
+
41
+ def initialize(route, payload, exchange_name, options)
42
+ @route = route
43
+ @payload = payload
44
+ @exchange_name = exchange_name
45
+ @options = options
46
+ end
47
+ end
48
+
49
+ class UnableToPersistMessageError < ::StandardError
50
+ end
51
+
52
+ class AsyncQueue
53
+ include ::ActionSubscriber::Logging
54
+
55
+ attr_reader :consumer, :queue, :supervisor
56
+
57
+ if ::RUBY_PLATFORM == "java"
58
+ NETWORK_ERRORS = [::MarchHare::Exception, ::Java::ComRabbitmqClient::AlreadyClosedException, ::Java::JavaIo::IOException].freeze
59
+ else
60
+ NETWORK_ERRORS = [::Bunny::Exception, ::Timeout::Error, ::IOError].freeze
61
+ end
62
+
63
+ def initialize
64
+ @queue = ::Queue.new
65
+ create_and_supervise_consumer!
66
+ end
67
+
68
+ def push(message)
69
+ # Default of 1_000_000 messages.
70
+ if queue.size > ::ActionSubscriber.configuration.async_publisher_max_queue_size
71
+ # Drop Messages if the queue is full and we were configured to do so.
72
+ return if ::ActionSubscriber.configuration.async_publisher_drop_messages_when_queue_full
73
+
74
+ # By default we will raise an error to push the responsibility onto the caller.
75
+ fail UnableToPersistMessageError, "Queue is full, messages will be dropped."
76
+ end
77
+
78
+ queue.push(message)
79
+ end
80
+
81
+ def size
82
+ queue.size
83
+ end
84
+
85
+ private
86
+
87
+ def await_network_reconnect
88
+ sleep ::ActionSubscriber::RabbitConnection::NETWORK_RECOVERY_INTERVAL
89
+ end
90
+
91
+ def create_and_supervise_consumer!
92
+ @consumer = create_consumer
93
+ @supervisor = ::Thread.new do
94
+ loop do
95
+ unless consumer.alive?
96
+ # Why might need to requeue the last message.
97
+ queue.push(@current_message) if @current_message.present?
98
+ consumer.kill
99
+ @consumer = create_consumer
100
+ end
101
+
102
+ # Pause before checking the consumer again.
103
+ sleep supervisor_interval
104
+ end
105
+ end
106
+ end
107
+
108
+ def create_consumer
109
+ ::Thread.new do
110
+ loop do
111
+ # Write "current_message" so we can requeue should something happen to the consumer. I don't love this, but it's
112
+ # better than writing my own `#peek' method.
113
+ @current_message = message = queue.pop
114
+
115
+ begin
116
+ ::ActionSubscriber::Publisher.publish(message.route, message.payload, message.exchange_name, message.options)
117
+
118
+ # Reset
119
+ @current_message = nil
120
+ rescue *NETWORK_ERRORS
121
+ # Sleep because the connection is down.
122
+ await_network_reconnect
123
+
124
+ # Requeue and try again.
125
+ queue.push(message)
126
+ rescue => unknown_error
127
+ # Do not requeue the message because something else horrible happened.
128
+ @current_message = nil
129
+
130
+ # Log the error.
131
+ logger.info unknown_error.class
132
+ logger.info unknown_error.message
133
+ logger.info unknown_error.backtrace.join("\n")
134
+
135
+ # TODO: Find a way to bubble this out of the thread for logging purposes.
136
+ # Reraise the error out of the publisher loop. The Supervisor will restart the consumer.
137
+ raise unknown_error
138
+ end
139
+ end
140
+ end
141
+ end
142
+
143
+ def supervisor_interval
144
+ @supervisor_interval ||= begin
145
+ interval_in_milliseconds = ::ActionSubscriber.configuration.async_publisher_supervisor_interval
146
+ interval_in_milliseconds / 1000.0
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
@@ -4,6 +4,7 @@ module ActionSubscriber
4
4
  module RabbitConnection
5
5
  SUBSCRIBER_CONNECTION_MUTEX = ::Mutex.new
6
6
  PUBLISHER_CONNECTION_MUTEX = ::Mutex.new
7
+ NETWORK_RECOVERY_INTERVAL = 1.freeze
7
8
 
8
9
  def self.publisher_connected?
9
10
  publisher_connection.try(:connected?)
@@ -61,12 +62,15 @@ module ActionSubscriber
61
62
 
62
63
  def self.connection_options
63
64
  {
65
+ :continuation_timeout => ::ActionSubscriber.configuration.timeout * 1_000.0, #convert sec to ms
64
66
  :heartbeat => ::ActionSubscriber.configuration.heartbeat,
65
67
  :hosts => ::ActionSubscriber.configuration.hosts,
68
+ :pass => ::ActionSubscriber.configuration.password,
66
69
  :port => ::ActionSubscriber.configuration.port,
67
- :continuation_timeout => ::ActionSubscriber.configuration.timeout * 1_000.0, #convert sec to ms
70
+ :user => ::ActionSubscriber.configuration.username,
71
+ :vhost => ::ActionSubscriber.configuration.virtual_host,
68
72
  :automatically_recover => true,
69
- :network_recovery_interval => 1,
73
+ :network_recovery_interval => NETWORK_RECOVERY_INTERVAL,
70
74
  :recover_from_connection_close => true,
71
75
  }
72
76
  end
@@ -0,0 +1,28 @@
1
+ # Taken and adapted from https://github.com/ruby-amqp/amq-protocol/blob/master/lib/amq/uri.rb
2
+ require "cgi"
3
+ require "uri"
4
+
5
+ module ActionSubscriber
6
+ class URI
7
+ AMQP_PORTS = {"amqp" => 5672, "amqps" => 5671}.freeze
8
+
9
+ def self.parse_amqp_url(connection_string)
10
+ uri = ::URI.parse(connection_string)
11
+ raise ArgumentError.new("Connection URI must use amqp or amqps schema (example: amqp://bus.megacorp.internal:5766), learn more at http://bit.ly/ks8MXK") unless %w{amqp amqps}.include?(uri.scheme)
12
+
13
+ opts = {}
14
+
15
+ opts[:username] = ::CGI::unescape(uri.user) if uri.user
16
+ opts[:password] = ::CGI::unescape(uri.password) if uri.password
17
+ opts[:host] = uri.host if uri.host
18
+ opts[:port] = uri.port || AMQP_PORTS[uri.scheme]
19
+
20
+ if uri.path =~ %r{^/(.*)}
21
+ raise ArgumentError.new("#{uri} has multiple-segment path; please percent-encode any slashes in the vhost name (e.g. /production => %2Fproduction). Learn more at http://bit.ly/amqp-gem-and-connection-uris") if $1.index('/')
22
+ opts[:virtual_host] = ::CGI::unescape($1)
23
+ end
24
+
25
+ opts
26
+ end
27
+ end
28
+ end
@@ -1,3 +1,3 @@
1
1
  module ActionSubscriber
2
- VERSION = "2.0.1"
2
+ VERSION = "2.1.0"
3
3
  end
@@ -1,6 +1,10 @@
1
1
  describe ::ActionSubscriber::Configuration do
2
2
  describe "default values" do
3
3
  specify { expect(subject.allow_low_priority_methods).to eq(false) }
4
+ specify { expect(subject.async_publisher).to eq("memory") }
5
+ specify { expect(subject.async_publisher_drop_messages_when_queue_full).to eq(false) }
6
+ specify { expect(subject.async_publisher_max_queue_size).to eq(1_000_000) }
7
+ specify { expect(subject.async_publisher_supervisor_interval).to eq(200) }
4
8
  specify { expect(subject.default_exchange).to eq("events") }
5
9
  specify { expect(subject.heartbeat).to eq(5) }
6
10
  specify { expect(subject.host).to eq("localhost") }
@@ -34,4 +38,15 @@ describe ::ActionSubscriber::Configuration do
34
38
  }.to raise_error(/The foo decoder was given with arity of 2/)
35
39
  end
36
40
  end
41
+
42
+ describe "connection_string" do
43
+ it "explodes the connection string into the corresponding settings" do
44
+ subject.connection_string = "amqp://user:pass@host:100/vhost"
45
+ expect(subject.username).to eq("user")
46
+ expect(subject.password).to eq("pass")
47
+ expect(subject.host).to eq("host")
48
+ expect(subject.port).to eq(100)
49
+ expect(subject.virtual_host).to eq("vhost")
50
+ end
51
+ end
37
52
  end
@@ -0,0 +1,135 @@
1
+ describe ::ActionSubscriber::Publisher::Async::InMemoryAdapter do
2
+ let(:route) { "test" }
3
+ let(:payload) { "message" }
4
+ let(:exchange_name) { "place" }
5
+ let(:options) { { :test => :ok } }
6
+ let(:message) { described_class::Message.new(route, payload, exchange_name, options) }
7
+ let(:mock_queue) { double(:push => nil, :size => 0) }
8
+
9
+ describe "#publish" do
10
+ before do
11
+ allow(described_class::Message).to receive(:new).with(route, payload, exchange_name, options).and_return(message)
12
+ allow(described_class::AsyncQueue).to receive(:new).and_return(mock_queue)
13
+ end
14
+
15
+ it "can publish a message to the queue" do
16
+ expect(mock_queue).to receive(:push).with(message)
17
+ subject.publish(route, payload, exchange_name, options)
18
+ end
19
+ end
20
+
21
+ describe "#shutdown!" do
22
+ # This is called when the rspec finishes. I'm sure we can make this a better test.
23
+ end
24
+
25
+ describe "::ActionSubscriber::Publisher::Async::InMemoryAdapter::Message" do
26
+ specify { expect(message.route).to eq(route) }
27
+ specify { expect(message.payload).to eq(payload) }
28
+ specify { expect(message.exchange_name).to eq(exchange_name) }
29
+ specify { expect(message.options).to eq(options) }
30
+ end
31
+
32
+ describe "::ActionSubscriber::Publisher::Async::InMemoryAdapter::AsyncQueue" do
33
+ subject { described_class::AsyncQueue.new }
34
+
35
+ describe ".initialize" do
36
+ it "creates a supervisor" do
37
+ expect_any_instance_of(described_class::AsyncQueue).to receive(:create_and_supervise_consumer!)
38
+ subject
39
+ end
40
+ end
41
+
42
+ describe "#create_and_supervise_consumer!" do
43
+ it "creates a supervisor" do
44
+ expect_any_instance_of(described_class::AsyncQueue).to receive(:create_consumer)
45
+ subject
46
+ end
47
+
48
+ it "restarts the consumer when it dies" do
49
+ consumer = subject.consumer
50
+ consumer.kill
51
+
52
+ verify_expectation_within(0.1) do
53
+ expect(consumer).to_not be_alive
54
+ end
55
+
56
+ verify_expectation_within(0.3) do
57
+ expect(subject.consumer).to be_alive
58
+ end
59
+ end
60
+ end
61
+
62
+ describe "#create_consumer" do
63
+ it "can successfully publish a message" do
64
+ expect(::ActionSubscriber::Publisher).to receive(:publish).with(route, payload, exchange_name, options)
65
+ subject.push(message)
66
+ sleep 0.1 # Await results
67
+ end
68
+
69
+ context "when network error occurs" do
70
+ let(:error) { described_class::AsyncQueue::NETWORK_ERRORS.first }
71
+ before { allow(::ActionSubscriber::Publisher).to receive(:publish).and_raise(error) }
72
+
73
+ it "requeues the message" do
74
+ consumer = subject.consumer
75
+ expect(consumer).to be_alive
76
+ expect(subject).to receive(:await_network_reconnect).at_least(:once)
77
+ subject.push(message)
78
+ sleep 0.1 # Await results
79
+ end
80
+ end
81
+
82
+ context "when an unknown error occurs" do
83
+ before { allow(::ActionSubscriber::Publisher).to receive(:publish).and_raise(ArgumentError) }
84
+
85
+ it "kills the consumer" do
86
+ consumer = subject.consumer
87
+ expect(consumer).to be_alive
88
+ subject.push(message)
89
+ sleep 0.1 # Await results
90
+ expect(consumer).to_not be_alive
91
+ end
92
+ end
93
+ end
94
+
95
+ describe "#push" do
96
+ after { ::ActionSubscriber.configuration.async_publisher_max_queue_size = 1000 }
97
+ after { ::ActionSubscriber.configuration.async_publisher_drop_messages_when_queue_full = false }
98
+
99
+ context "when the queue has room" do
100
+ before { allow(::Queue).to receive(:new).and_return(mock_queue) }
101
+
102
+ it "successfully adds to the queue" do
103
+ expect(mock_queue).to receive(:push).with(message)
104
+ subject.push(message)
105
+ end
106
+ end
107
+
108
+ context "when the queue is full" do
109
+ before { ::ActionSubscriber.configuration.async_publisher_max_queue_size = -1 }
110
+
111
+ context "and we're dropping messages" do
112
+ before { ::ActionSubscriber.configuration.async_publisher_drop_messages_when_queue_full = true }
113
+
114
+ it "adding to the queue should not raise an error" do
115
+ expect { subject.push(message) }.to_not raise_error
116
+ end
117
+ end
118
+
119
+ context "and we're not dropping messages" do
120
+ before { ::ActionSubscriber.configuration.async_publisher_drop_messages_when_queue_full = false }
121
+
122
+ it "adding to the queue should raise error back to caller" do
123
+ expect { subject.push(message) }.to raise_error(described_class::UnableToPersistMessageError)
124
+ end
125
+ end
126
+ end
127
+ end
128
+
129
+ describe "#size" do
130
+ it "can return the size of the queue" do
131
+ expect(subject.size).to eq(0)
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,40 @@
1
+ describe ::ActionSubscriber::Publisher::Async do
2
+
3
+ before { described_class.instance_variable_set(:@publisher_adapter, nil) }
4
+ after { ::ActionSubscriber.configuration.async_publisher = "memory" }
5
+
6
+ let(:mock_adapter) { double(:publish => nil) }
7
+
8
+ describe ".publish_async" do
9
+ before { allow(described_class).to receive(:publisher_adapter).and_return(mock_adapter) }
10
+
11
+ it "calls through the adapter" do
12
+ expect(mock_adapter).to receive(:publish).with("1", "2", "3", { "four" => "five" })
13
+ ::ActionSubscriber::Publisher.publish_async("1", "2", "3", { "four" => "five" })
14
+ end
15
+ end
16
+
17
+ context "when an in-memory adapter is selected" do
18
+ before { ::ActionSubscriber.configuration.async_publisher = "memory" }
19
+
20
+ it "Creates an in-memory publisher" do
21
+ expect(described_class.publisher_adapter).to be_an(::ActionSubscriber::Publisher::Async::InMemoryAdapter)
22
+ end
23
+ end
24
+
25
+ context "when an redis adapter is selected" do
26
+ before { ::ActionSubscriber.configuration.async_publisher = "redis" }
27
+
28
+ it "raises an error" do
29
+ expect { described_class.publisher_adapter }.to raise_error("Not yet implemented")
30
+ end
31
+ end
32
+
33
+ context "when some random adapter is selected" do
34
+ before { ::ActionSubscriber.configuration.async_publisher = "yolo" }
35
+
36
+ it "raises an error" do
37
+ expect { described_class.publisher_adapter }.to raise_error("Unknown adapter 'yolo' provided")
38
+ end
39
+ end
40
+ end
data/spec/spec_helper.rb CHANGED
@@ -13,6 +13,7 @@ require 'support/user_subscriber'
13
13
  require 'action_subscriber/rspec'
14
14
 
15
15
  # Silence the Logger
16
+ $TESTING = true
16
17
  ::ActionSubscriber::Logging.initialize_logger(nil)
17
18
 
18
19
  RSpec.configure do |config|
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: action_subscriber
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 2.1.0
5
5
  platform: java
6
6
  authors:
7
7
  - Brian Stien
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2016-01-26 00:00:00.000000000 Z
15
+ date: 2016-02-08 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  requirement: !ruby/object:Gem::Requirement
@@ -213,6 +213,8 @@ files:
213
213
  - lib/action_subscriber/middleware/router.rb
214
214
  - lib/action_subscriber/middleware/runner.rb
215
215
  - lib/action_subscriber/publisher.rb
216
+ - lib/action_subscriber/publisher/async.rb
217
+ - lib/action_subscriber/publisher/async/in_memory_adapter.rb
216
218
  - lib/action_subscriber/rabbit_connection.rb
217
219
  - lib/action_subscriber/railtie.rb
218
220
  - lib/action_subscriber/route.rb
@@ -221,6 +223,7 @@ files:
221
223
  - lib/action_subscriber/rspec.rb
222
224
  - lib/action_subscriber/subscribable.rb
223
225
  - lib/action_subscriber/threadpool.rb
226
+ - lib/action_subscriber/uri.rb
224
227
  - lib/action_subscriber/version.rb
225
228
  - routing.md
226
229
  - spec/integration/around_filters_spec.rb
@@ -244,6 +247,8 @@ files:
244
247
  - spec/lib/action_subscriber/middleware/error_handler_spec.rb
245
248
  - spec/lib/action_subscriber/middleware/router_spec.rb
246
249
  - spec/lib/action_subscriber/middleware/runner_spec.rb
250
+ - spec/lib/action_subscriber/publisher/async/in_memory_adapter_spec.rb
251
+ - spec/lib/action_subscriber/publisher/async_spec.rb
247
252
  - spec/lib/action_subscriber/publisher_spec.rb
248
253
  - spec/lib/action_subscriber/router_spec.rb
249
254
  - spec/lib/action_subscriber/subscribable_spec.rb
@@ -270,7 +275,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
270
275
  version: '0'
271
276
  requirements: []
272
277
  rubyforge_project:
273
- rubygems_version: 2.5.0
278
+ rubygems_version: 2.4.8
274
279
  signing_key:
275
280
  specification_version: 4
276
281
  summary: ActionSubscriber is a DSL that allows a rails app to consume messages from a RabbitMQ broker.
@@ -296,6 +301,8 @@ test_files:
296
301
  - spec/lib/action_subscriber/middleware/error_handler_spec.rb
297
302
  - spec/lib/action_subscriber/middleware/router_spec.rb
298
303
  - spec/lib/action_subscriber/middleware/runner_spec.rb
304
+ - spec/lib/action_subscriber/publisher/async/in_memory_adapter_spec.rb
305
+ - spec/lib/action_subscriber/publisher/async_spec.rb
299
306
  - spec/lib/action_subscriber/publisher_spec.rb
300
307
  - spec/lib/action_subscriber/router_spec.rb
301
308
  - spec/lib/action_subscriber/subscribable_spec.rb