advanced-sneakers-activejob 0.2.3 → 0.3.0

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
  SHA256:
3
- metadata.gz: 2ae17fa99114e9d196dfb9c0597250dd67fabb59bbf5bd687a16e0ec08876457
4
- data.tar.gz: 655068e7718b730183051637ba4a9532e906655a811721df371eb7ba2b68ad41
3
+ metadata.gz: 6d7e686de7c5fe9a22c265854adf1c8e9afecbdcd8b23e8bffcf669738e2c23f
4
+ data.tar.gz: 60f16a0f4cb3492d1ad216a25cc4cef1d51cf4c5e13eef9cb5b47f672d7c4943
5
5
  SHA512:
6
- metadata.gz: 354d6f85a842408cb17e214b20ab5a0eb25815ce2e54ba727e8a4ac3309cd220e6a2e33c777f2bf13a97381d41777d8e50d63d1ab95a2b4c0a67680b3f992330
7
- data.tar.gz: 9a3552f273d1da9ace5d808aab2b519f1c21d2dbebcecb72c272c699f8914b2381e44e4c8e023f939391aa86f6648bc8673a1a763a705d8f4141024151c8437f
6
+ metadata.gz: c039b2fc27c32064c34ae8071b3c5ae8e19766e3f71283b7052eeb4b0031300ec42c3d3a04e025674111e7160d59d7840cbeb348407e7a92e2b14f14cedf4cba
7
+ data.tar.gz: 47f9d05c9b1d9386faa8c3186868353665327701c11fb79542f3aeff2964b086ac6b84c476e95d313861311f275f18ca31131972de75d0743c064d15ae4269ba
data/.travis.yml CHANGED
@@ -3,17 +3,17 @@ dist: xenial
3
3
  language: ruby
4
4
  cache: bundler
5
5
  rvm:
6
- - 2.4.9
7
- - 2.5.7
8
- - 2.6.5
6
+ - 2.5.8
7
+ - 2.6.6
8
+ - 2.7.1
9
9
  env:
10
10
  - RAILS_VERSION="~> 4.2.11"
11
11
  - RAILS_VERSION="~> 5.2.4"
12
12
  - RAILS_VERSION="~> 6.0.2"
13
13
  jobs:
14
14
  exclude:
15
- - rvm: 2.4.9
16
- env: RAILS_VERSION="~> 6.0.2"
15
+ - rvm: 2.7.1
16
+ env: RAILS_VERSION="~> 4.2.11"
17
17
 
18
18
  before_install: gem install bundler -v 1.17.3
19
19
  before_script:
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## Changes Between 0.2.3 and 0.3
2
+
3
+ This release does not change the observed behavior, but replaces the publisher with completely new implementation.
4
+
5
+ ### Reusable parts of publisher are extracted to [bunny-publisher](https://github.com/veeqo/bunny-publisher)
6
+
1
7
  ## Changes Between 0.2.2 and 0.2.3
2
8
 
3
9
  ### Refactored support for ActiveJob prefix https://github.com/veeqo/advanced-sneakers-activejob/pull/3
data/README.md CHANGED
@@ -49,7 +49,7 @@ If message is published before routing has been configured (e.g. by consumer), i
49
49
 
50
50
  There is a setting `handle_unrouted_messages` in [configuration](#configuration) to disable this behavior. If it is disabled, publisher will only log unrouted messages.
51
51
 
52
- Take into accout that **this process is asynchronous**. It means that in case of network failures or process exit unrouted messages could be lost. The adapter tries to postpone application exit up to 30 seconds in case if there are unrouted messages, but it does not provide any guarantees.
52
+ Take into accout that **this process is asynchronous**. It means that in case of network failures or process exit unrouted messages could be lost. The adapter tries to postpone application exit up to 5 seconds in case if there are unrouted messages, but it does not provide any guarantees.
53
53
 
54
54
  **Delayed messages are not handled!** If job is delayed `GuestsCleanupJob.set(wait: 1.week).perform_later(guest)` and there is no proper routing defined at the moment of job execution, it would be lost.
55
55
 
@@ -152,12 +152,19 @@ AdvancedSneakersActiveJob.configure do |config|
152
152
 
153
153
  # Custom sneakers configuration for ActiveJob publisher & runner
154
154
  config.sneakers = {
155
+ connection: Bunny.new('CUSTOM_URL', with: { other: 'options' }),
155
156
  exchange: 'activejob',
156
157
  handler: AdvancedSneakersActiveJob::Handler
157
158
  }
158
159
 
159
160
  # Define custom delay for retries, but remember - each unique delay leads to new queue on RabbitMQ side
160
161
  config.retry_delay_proc = ->(count) { AdvancedSneakersActiveJob::EXPONENTIAL_BACKOFF[count] }
162
+
163
+ # Connection for publisher (fallbacks to connection of consumers)
164
+ config.publish_connection = Bunny.new('CUSTOM_URL', with: { other: 'options' })
165
+
166
+ # Unrouted messages republish requires extra connection and will try to "clone" publish_connection unless it is provided
167
+ config.republish_connection = Bunny.new('CUSTOM_URL', with: { other: 'options' })
161
168
  end
162
169
  ```
163
170
 
@@ -168,3 +175,5 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/veeqo/
168
175
  ## License
169
176
 
170
177
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
178
+
179
+ ## Sponsored by [Veeqo](https://veeqo.com/)
@@ -27,6 +27,7 @@ Gem::Specification.new do |spec|
27
27
  spec.require_paths = ['lib']
28
28
 
29
29
  spec.add_dependency 'activejob', '>= 4.2'
30
+ spec.add_dependency 'bunny-publisher', '~> 0.1'
30
31
  spec.add_dependency 'sneakers', '~> 2.7'
31
32
 
32
33
  spec.add_development_dependency 'bundler'
@@ -26,7 +26,10 @@ module ActiveJob
26
26
  delay = AdvancedSneakersActiveJob.config.delay_proc.call(timestamp).to_i
27
27
 
28
28
  if delay.positive?
29
- AdvancedSneakersActiveJob.publisher.publish_delayed(*publish_params(job).tap { |params| params.last[:delay] = delay })
29
+ message, options = publish_params(job)
30
+ options[:headers] = { 'delay' => delay.to_i } # do not use x- prefix because headers exchanges ignore such headers
31
+
32
+ AdvancedSneakersActiveJob.delayed_publisher.publish(message, options)
30
33
  else
31
34
  enqueue(job)
32
35
  end
@@ -38,7 +41,7 @@ module ActiveJob
38
41
  @monitor.synchronize do
39
42
  [
40
43
  Sneakers::ContentType.serialize(job.serialize, AdvancedSneakersActiveJob::CONTENT_TYPE),
41
- build_publish_params(job)
44
+ build_publish_params(job).merge(content_type: AdvancedSneakersActiveJob::CONTENT_TYPE)
42
45
  ]
43
46
  end
44
47
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'active_support'
4
4
  require 'active_support/core_ext'
5
+ require 'bunny_publisher'
5
6
 
6
7
  require 'sneakers'
7
8
  require 'advanced_sneakers_activejob/workers_registry'
@@ -15,6 +16,7 @@ require 'advanced_sneakers_activejob/handler'
15
16
  require 'advanced_sneakers_activejob/configuration'
16
17
  require 'advanced_sneakers_activejob/errors'
17
18
  require 'advanced_sneakers_activejob/publisher'
19
+ require 'advanced_sneakers_activejob/delayed_publisher'
18
20
  require 'advanced_sneakers_activejob/active_job_patch'
19
21
  require 'advanced_sneakers_activejob/railtie' if defined?(::Rails::Railtie)
20
22
  require 'active_job/queue_adapters/advanced_sneakers_adapter'
@@ -38,13 +40,17 @@ module AdvancedSneakersActiveJob
38
40
  klass = Class.new(ActiveJob::QueueAdapters::AdvancedSneakersAdapter::JobWrapper)
39
41
  const_set(name, klass)
40
42
  klass.include Sneakers::Worker
41
- klass.from_queue(queue_name, AdvancedSneakersActiveJob.config.sneakers)
43
+ klass.from_queue(queue_name, config.sneakers)
42
44
 
43
45
  klass
44
46
  end
45
47
 
46
48
  def publisher
47
- @publisher ||= AdvancedSneakersActiveJob::Publisher.new
49
+ @publisher ||= AdvancedSneakersActiveJob::Publisher.new(config.publisher_config)
50
+ end
51
+
52
+ def delayed_publisher
53
+ @delayed_publisher ||= AdvancedSneakersActiveJob::DelayedPublisher.new(config.publisher_config)
48
54
  end
49
55
 
50
56
  # Based on ActiveSupport::Inflector#parameterize
@@ -17,6 +17,9 @@ module AdvancedSneakersActiveJob
17
17
  config_accessor(:delayed_queue_prefix) { 'delayed' }
18
18
  config_accessor(:retry_delay_proc) { ->(count) { AdvancedSneakersActiveJob::EXPONENTIAL_BACKOFF[count] } } # seconds
19
19
 
20
+ config_accessor(:publish_connection)
21
+ config_accessor(:republish_connection)
22
+
20
23
  def sneakers
21
24
  custom_config = DEFAULT_SNEAKERS_CONFIG.deep_merge(config.sneakers || {})
22
25
 
@@ -30,5 +33,9 @@ module AdvancedSneakersActiveJob
30
33
  def sneakers=(custom)
31
34
  config.sneakers = custom
32
35
  end
36
+
37
+ def publisher_config
38
+ sneakers.merge(publish_connection: publish_connection, republish_connection: republish_connection)
39
+ end
33
40
  end
34
41
  end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AdvancedSneakersActiveJob
4
+ # This publisher relies on TTL to keep messages in a queue.
5
+ # When TTL is reached, messages go to another exchange (see dlx_exchange_name param).
6
+ class DelayedPublisher < ::BunnyPublisher::Base
7
+ include ::BunnyPublisher::Mandatory
8
+
9
+ delegate :logger, to: :'::ActiveJob::Base'
10
+
11
+ delegate :name_prefix, :delayed_queue_prefix,
12
+ to: :'AdvancedSneakersActiveJob.config',
13
+ prefix: :config
14
+
15
+ before_publish :log_message
16
+
17
+ attr_reader :dlx_exchange_name
18
+
19
+ def initialize(exchange:, **options)
20
+ super(options.merge(exchange: [exchange, 'delayed'].join('-'), exchange_options: { type: 'headers', durable: true }))
21
+
22
+ @dlx_exchange_name = exchange
23
+ end
24
+
25
+ private
26
+
27
+ def log_message(publisher, message, options = {})
28
+ logger.debug do
29
+ delay = options.dig(:headers, 'delay')
30
+
31
+ "Publishing <#{message}> to [#{publisher.exchange.name}] with routing_key [#{options[:routing_key]}] and delay [#{delay}]"
32
+ end
33
+ end
34
+
35
+ def declare_republish_queue(_return_info, properties, _message)
36
+ delay = properties.headers.fetch('delay')
37
+
38
+ queue_name = delayed_queue_name(delay: delay)
39
+
40
+ queue_arguments = {
41
+ 'x-queue-mode' => 'lazy', # tell RabbitMQ not to use RAM for this queue as it won't be consumed
42
+ 'x-message-ttl' => delay * 1000, # make messages die after requested time
43
+ 'x-dead-letter-exchange' => dlx_exchange_name # dead messages go to original exchange and then routed to proper queues
44
+ }
45
+
46
+ logger.debug { "Creating delayed queue [#{queue_name}]" }
47
+
48
+ republish_channel.queue(queue_name, durable: true, arguments: queue_arguments)
49
+ end
50
+
51
+ def declare_republish_queue_binding(queue, _return_info, properties, _message)
52
+ queue.bind(republish_exchange, arguments: { delay: properties.headers.fetch('delay') })
53
+ end
54
+
55
+ def delayed_queue_name(delay:)
56
+ [
57
+ ::ActiveJob::Base.queue_name_prefix,
58
+ [config_delayed_queue_prefix, delay].join(':')
59
+ ].compact.join(::ActiveJob::Base.queue_name_delimiter)
60
+ end
61
+ end
62
+ end
@@ -5,11 +5,10 @@ module AdvancedSneakersActiveJob
5
5
  class Handler < Sneakers::Handlers::Oneshot
6
6
  def error(delivery_info, properties, message, error)
7
7
  params = properties.to_h
8
- params[:headers] = patch_headers(params[:headers], delivery_info, error)
8
+ params[:headers] = patch_headers(params[:headers] || {}, delivery_info, error)
9
9
  params[:routing_key] = delivery_info.routing_key
10
- params[:delay] = calculate_delay(params[:headers], delivery_info)
11
10
 
12
- AdvancedSneakersActiveJob.publisher.publish_delayed(message, params)
11
+ AdvancedSneakersActiveJob.delayed_publisher.publish(message, params)
13
12
 
14
13
  acknowledge(delivery_info, properties, message)
15
14
  end
@@ -23,6 +22,7 @@ module AdvancedSneakersActiveJob
23
22
 
24
23
  track_error_in_headers(headers, error)
25
24
  track_death_in_headers(headers, queue, exchange, routing_key)
25
+ set_delay_in_headers(headers, delivery_info)
26
26
 
27
27
  headers
28
28
  end
@@ -41,6 +41,10 @@ module AdvancedSneakersActiveJob
41
41
  end
42
42
  end
43
43
 
44
+ def set_delay_in_headers(headers, delivery_info)
45
+ headers['delay'] = calculate_delay(headers, delivery_info)
46
+ end
47
+
44
48
  def build_death_row(queue, exchange, routing_key)
45
49
  {
46
50
  'count' => 1,
@@ -1,210 +1,33 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AdvancedSneakersActiveJob
4
- # Based on Sneakers::Publisher, but refactored to utilize :mandatory option to handle unrouted messages
5
- # http://rubybunny.info/articles/exchanges.html#publishing_messages_as_mandatory
6
- class Publisher
7
- WAIT_FOR_UNROUTED_MESSAGES_AT_EXIT_TIMEOUT = 30
4
+ class Publisher < ::BunnyPublisher::Base
5
+ include ::BunnyPublisher::Mandatory
8
6
 
9
- delegate :sneakers, :handle_unrouted_messages, :delayed_queue_prefix,
10
- to: :'AdvancedSneakersActiveJob.config', prefix: :config
7
+ before_publish :log_message
11
8
 
12
- delegate :logger, to: :'ActiveJob::Base'
9
+ delegate :logger, to: :'::ActiveJob::Base'
13
10
 
14
- attr_reader :publish_channel, :republish_channel,
15
- :publish_exchange, :republish_exchange,
16
- :publish_delayed_exchange, :republish_delayed_exchange
17
-
18
- def initialize
19
- @mutex = Mutex.new
20
- at_exit { wait_for_unrouted_messages_processing(timeout: WAIT_FOR_UNROUTED_MESSAGES_AT_EXIT_TIMEOUT) }
21
- end
22
-
23
- def publish(message, routing_key: nil, headers: {}, **properties)
24
- ensure_connection!
25
-
26
- logger.debug "Publishing <#{message}> to [#{publish_exchange.name}] with routing_key [#{routing_key}]"
27
-
28
- params = properties.deep_symbolize_keys.merge(
29
- routing_key: routing_key,
30
- mandatory: true,
31
- content_type: AdvancedSneakersActiveJob::CONTENT_TYPE,
32
- headers: headers
33
- )
34
-
35
- publish_exchange.publish(message, params)
36
- end
37
-
38
- def publish_delayed(message, delay:, routing_key: nil, headers: {}, **properties)
39
- ensure_connection!
40
-
41
- logger.debug "Publishing <#{message}> to [#{publish_delayed_exchange.name}] with routing_key [#{routing_key}] and delay [#{delay}]"
42
-
43
- params = properties.deep_symbolize_keys.merge(
44
- routing_key: routing_key,
45
- mandatory: true,
46
- content_type: AdvancedSneakersActiveJob::CONTENT_TYPE,
47
- headers: headers.deep_symbolize_keys.merge(delay: delay.to_i) # do not use x- prefix because headers exchanges ignore such headers
48
- )
49
-
50
- publish_delayed_exchange.publish(message, params)
51
- end
11
+ delegate :handle_unrouted_messages,
12
+ to: :'AdvancedSneakersActiveJob.config',
13
+ prefix: :config
52
14
 
53
15
  private
54
16
 
55
- def ensure_connection!
56
- @mutex.synchronize do
57
- unless connected?
58
- start_connections!
59
- create_channels!
60
- configure_exchanges!
61
- end
17
+ def log_message(publisher, message, options = {})
18
+ logger.debug do
19
+ "Publishing <#{message}> to [#{publisher.exchange.name}] with routing_key [#{options[:routing_key]}]"
62
20
  end
63
21
  end
64
22
 
65
- def start_connections!
66
- @publish_connection ||= create_bunny_connection
67
- @publish_connection.start
68
-
69
- @republish_connection ||= create_bunny_connection
70
- @republish_connection.start
71
- end
72
-
73
- def create_channels!
74
- @publish_channel = @publish_connection.create_channel
75
- @republish_channel = @republish_connection.create_channel
76
- end
77
-
78
- def configure_exchanges!
79
- @publish_exchange = build_exchange(@publish_channel)
80
- @publish_exchange.on_return { |*attrs| handle_unrouted_messages(*attrs) }
81
-
82
- @publish_delayed_exchange = build_delayed_exchange(@publish_channel)
83
- @publish_delayed_exchange.on_return { |*attrs| handle_unrouted_delayed_messages(*attrs) }
84
-
85
- @republish_exchange = build_exchange(republish_channel)
86
- @republish_delayed_exchange = build_delayed_exchange(republish_channel)
87
- end
88
-
89
- def connected?
90
- @publish_connection&.connected? &&
91
- @republish_connection&.connected? &&
92
- @publish_channel &&
93
- @republish_channel
94
- end
95
-
96
- # Returned messages are processed asynchronously and there is a probability for messages loses on program exit or network failure.
97
- # Second connection is required because `on_return` is called within a frameset of amqp connection.
98
- # Any interaction within the connection (even by another channel) can lead to connection error.
99
- # https://github.com/ruby-amqp/bunny/blob/7fb05abf36637557f75a69790be78f9cc1cea807/lib/bunny/session.rb#L683
100
- def handle_unrouted_messages(return_info, properties, message)
101
- @unrouted_message = true
102
-
103
- params = { message: message, return_info: return_info, properties: properties }
104
-
105
- raise(PublishError, params) if return_info.reply_code != 312 # NO_ROUTE
106
-
23
+ def on_message_return(return_info, properties, message)
107
24
  if config_handle_unrouted_messages
108
- setup_routing_and_republish_message(params)
25
+ super
109
26
  else
110
- logger.warn("Message is not routed! #{params}")
111
- end
112
-
113
- @unrouted_message = false
114
- end
115
-
116
- def handle_unrouted_delayed_messages(return_info, properties, message)
117
- @unrouted_delayed_message = true
118
-
119
- params = { message: message, return_info: return_info, properties: properties }
120
-
121
- raise(PublishError, params) if return_info.reply_code != 312 # NO_ROUTE
122
-
123
- setup_routing_and_republish_delayed_message(params)
124
-
125
- @unrouted_delayed_message = false
126
- end
127
-
128
- # TODO: introduce more reliable way to wait for handling of unrouted messages at exit
129
- def wait_for_unrouted_messages_processing(timeout:)
130
- sleep(0.05) # gives publish_exchange some time to receive retuned message
131
-
132
- return unless @unrouted_message || @unrouted_delayed_message
133
-
134
- logger.warn("Waiting up to #{timeout} seconds for unrouted messages handling")
135
-
136
- Timeout.timeout(timeout) { sleep 0.01 while @unrouted_message || @unrouted_delayed_message }
137
- rescue Timeout::Error
138
- logger.warn('Some unrouted messages are lost on process exit!')
139
- end
140
-
141
- def setup_routing_and_republish_message(message:, return_info:, properties:)
142
- logger.debug("Performing queue/binding setup & re-publish for unrouted message. #{{ message: message, return_info: return_info }}")
143
-
144
- routing_key = return_info.routing_key
145
-
146
- create_queue_and_binding(queue_name: deserialize(message).fetch('queue_name'), routing_key: routing_key)
147
-
148
- logger.debug "Re-publishing <#{message}> to [#{republish_exchange.name}] with routing_key [#{routing_key}]"
149
- republish_exchange.publish(message, properties.to_h.merge(routing_key: routing_key))
150
- end
151
-
152
- def create_queue_and_binding(queue_name:, routing_key:)
153
- logger.debug "Creating queue [#{queue_name}] and binding with routing_key [#{routing_key}] to [#{republish_exchange.name}]"
154
- republish_channel.queue(queue_name, config_sneakers[:queue_options]).tap do |queue|
155
- queue.bind(republish_exchange, routing_key: routing_key)
156
- republish_channel.deregister_queue(queue) # we are not going to work with this queue in this channel
157
- end
158
- end
159
-
160
- def setup_routing_and_republish_delayed_message(message:, return_info:, properties:)
161
- delay = properties.headers.fetch('delay').to_i
162
- queue_name = delayed_queue_name(delay: delay)
163
-
164
- logger.debug "Creating delayed queue [#{queue_name}]"
165
-
166
- create_delayed_queue_and_binding(queue_name: queue_name, delay: delay)
167
-
168
- republish_delayed_exchange.publish message, properties.to_h.merge(routing_key: return_info.routing_key)
169
- end
170
-
171
- def delayed_queue_name(delay:)
172
- [
173
- ::ActiveJob::Base.queue_name_prefix,
174
- [config_delayed_queue_prefix, delay].join(':')
175
- ].compact.join(::ActiveJob::Base.queue_name_delimiter)
176
- end
177
-
178
- def create_delayed_queue_and_binding(queue_name:, delay:)
179
- queue_arguments = {
180
- 'x-queue-mode' => 'lazy', # tell RabbitMQ not to use RAM for this queue as it won't be consumed
181
- 'x-message-ttl' => delay * 1000, # make messages die after requested time
182
- 'x-dead-letter-exchange' => republish_exchange.name # died messages go to original exchange and then routed to consumers
183
- }
184
-
185
- republish_channel.queue(queue_name, durable: true, arguments: queue_arguments).tap do |queue|
186
- queue.bind(republish_delayed_exchange, arguments: { delay: delay })
187
- republish_channel.deregister_queue(queue) # we are not going to work with this queue in this channel
27
+ logger.warn do
28
+ "Message is not routed! #{{ message: message, return_info: return_info, properties: properties }}"
29
+ end
188
30
  end
189
31
  end
190
-
191
- def build_exchange(channel)
192
- channel.exchange(config_sneakers[:exchange], config_sneakers[:exchange_options])
193
- end
194
-
195
- def build_delayed_exchange(channel)
196
- channel.exchange([config_sneakers[:exchange], 'delayed'].join('-'), type: 'headers', durable: true)
197
- end
198
-
199
- def create_bunny_connection
200
- Bunny.new config_sneakers[:amqp],
201
- vhost: config_sneakers[:vhost],
202
- heartbeat: config_sneakers[:heartbeat],
203
- properties: config_sneakers.fetch(:properties, {})
204
- end
205
-
206
- def deserialize(message)
207
- Sneakers::ContentType.deserialize(message, AdvancedSneakersActiveJob::CONTENT_TYPE)
208
- end
209
32
  end
210
33
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AdvancedSneakersActiveJob
4
- VERSION = '0.2.3'
4
+ VERSION = '0.3.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: advanced-sneakers-activejob
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rustam Sharshenov
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-04-06 00:00:00.000000000 Z
12
+ date: 2020-05-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activejob
@@ -25,6 +25,20 @@ dependencies:
25
25
  - - ">="
26
26
  - !ruby/object:Gem::Version
27
27
  version: '4.2'
28
+ - !ruby/object:Gem::Dependency
29
+ name: bunny-publisher
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '0.1'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '0.1'
28
42
  - !ruby/object:Gem::Dependency
29
43
  name: sneakers
30
44
  requirement: !ruby/object:Gem::Requirement
@@ -147,6 +161,7 @@ files:
147
161
  - lib/advanced_sneakers_activejob/active_job_patch.rb
148
162
  - lib/advanced_sneakers_activejob/configuration.rb
149
163
  - lib/advanced_sneakers_activejob/content_type.rb
164
+ - lib/advanced_sneakers_activejob/delayed_publisher.rb
150
165
  - lib/advanced_sneakers_activejob/errors.rb
151
166
  - lib/advanced_sneakers_activejob/exponential_backoff.rb
152
167
  - lib/advanced_sneakers_activejob/handler.rb