belated 0.6.6 → 0.8.1

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: 628f04309c824751bd90f72163d29738f5600f7ed5b759b0106d09b9eccbf23e
4
- data.tar.gz: 1318b353fb2047ae6e185f0af720a987a3d8c784a483df04a79601fb595ff28f
3
+ metadata.gz: e9ffd4fd7fb145286210861d0b2977e20ec4ed28cf776d753b887c6b8cfe384d
4
+ data.tar.gz: e9b006476fa67f3acdcae7a8dfff4da8275f7af4e4ecbada93b462f803fab4e7
5
5
  SHA512:
6
- metadata.gz: de9793daf6f3987be47b798916dbdfa16e7be92211f46395ed4d63eabf7ae127420952c047df749de584404cd18e80a002f5fadb40896421583e80f3ebe9bf2a
7
- data.tar.gz: 3a09388e73a833d3230787f6191cd02a86cc4ebf3e58ba950d8631eba31a67fe4cf319bfaa448bc471b5fe1a933d9564c7d846d3416d91832a7b5331c78e62b9
6
+ metadata.gz: a1dd51345d62775d2920abf015e804c8cb85c01155e9df75d59e2282d13c892270e0346b661c7896c226862f808d584157c898aa2e6b6a7247f877447842307b
7
+ data.tar.gz: 57a1cd7805029a009d2d0b7495eba264325813868c0c3a30984fc6a3b4bb8fcdd400257f1d31ce8277d17848f36c61e19d223e15f429161dcd5d190258809151
@@ -7,7 +7,7 @@ jobs:
7
7
  strategy:
8
8
  fail-fast: false
9
9
  matrix:
10
- os: [ubuntu-latest, macos-latest]
10
+ os: [ubuntu-latest]
11
11
  # Due to https://github.com/actions/runner/issues/849, we have to use quotes for '3.0'
12
12
  ruby: [2.6, 2.7, '3.0']
13
13
  runs-on: ${{ matrix.os }}
data/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.8.1] - 2021-09-09
4
+ - Now you can delete jobs from the future jobs queue. This is useful if you want to delete a job that is scheduled for a future date.
5
+
6
+ ## [0.8] - 2021-09-08
7
+ - Using PStore for future jobs backup and job history. Job history is rotated daily, future jobs cannot at the moment.
8
+ - PStore has ultrasafe mode, should test whether that makes things very slow. If not, might be worth using it for the peace of mind. Or at least have it as an option.
9
+ - ActiveJob support is a bit more natural now code-wise.
10
+
11
+ ## [0.7] - 2021-09-04
12
+ - ActiveJob support! Retries, exception rescuing should work as expected.
13
+ - Second Moderna jab took me out for a while... sorry for the long wait.
14
+ ## [0.6.7] - 2021-08-25
15
+
16
+ - A bug fix for bad jobs bringing down client side.
17
+ - Heartbeat option for server side. It only affects jobs in the wait list, it's used to determine how often to check the future jobs queue.
3
18
  ## [0.6.6] - 2021-08-25
4
19
 
5
20
  - Tests now run agains Ruby 2.6, so relaxing the version constraint.
data/Gemfile.lock CHANGED
@@ -1,9 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- belated (0.6.6)
4
+ belated (0.8.1)
5
5
  drb
6
6
  dry-configurable
7
+ pstore
7
8
  ruby2_keywords
8
9
  sorted_set
9
10
 
@@ -111,6 +112,7 @@ GEM
111
112
  parallel (1.20.1)
112
113
  parser (3.0.2.0)
113
114
  ast (~> 2.4.1)
115
+ pstore (0.1.1)
114
116
  racc (1.5.2)
115
117
  rack (2.2.3)
116
118
  rack-test (1.1.0)
data/README.md CHANGED
@@ -2,11 +2,9 @@
2
2
 
3
3
  [![CodeFactor](https://www.codefactor.io/repository/github/sampokuokkanen/belated/badge)](https://www.codefactor.io/repository/github/sampokuokkanen/belated) [![Gem Version](https://badge.fury.io/rb/belated.svg)](https://badge.fury.io/rb/belated)
4
4
 
5
- This is Belated, a new Ruby backend job library! It supports running procs, lambdas and classes in the background. To deal with restarts, it uses YAML to load the queue into a file, which it then calls at startup to find the previous jobs. There is no way in Ruby to save procs or lambdas to a file, so they are discarded when the process restarts.
5
+ This is Belated, a new Ruby backend job library! It supports running procs, lambdas and classes in the background. To deal with restarts, it uses YAML for the current jobs in the queue waiting to be processed and PStore for the future jobs to load the queues into a file, which it then calls at startup to find the previous jobs. There is no way in Ruby to save procs or lambdas to a file, so they are discarded when the process restarts.
6
6
 
7
- Belated uses the Ruby Queue class, so it's First In, First Out (FIFO).
8
-
9
- Note that Belated used to be called HardWorker. That name was already in use in Sidekiq documentation and a bit too generic anyway.
7
+ Belated uses the Ruby Queue class, so it's First In, First Out (FIFO), unless of course you want to run the job in the future. In that case the order is decided by the time the job is scheduled to be executed.
10
8
 
11
9
  It uses dRuby to do the communication! Which is absolute great. No need for Redis or PostgreSQL, just Ruby standard libraries.
12
10
 
@@ -16,15 +14,14 @@ Can be used if you're on a normal instance such as EC2 or Digital Ocean drop. No
16
14
 
17
15
  TODO LIST:
18
16
 
19
- - Use GDBM for queue storage? That way could maybe get rid of YAML dumping and make things a bit safer. Not ordered though, so maybe keep a list of the jobs as YAML and update it sometimes? Just as backup. Or RocksDB? Would need to be configurable if you don't have something installed.
20
- - Maybe support ActiveJob?
21
- - Have a web UI.
22
- - Have a job history
23
- - Do some performance testing.
17
+ - Have a web UI with job history.
24
18
  - Deploy a Rails app to production that is using Belated
25
19
  and mention it in the readme. (Capistrano support?)
26
- ([Wasurechatta](https://wasurechatta.com/))
27
- - Add a section telling people to use Sidekiq if they can
20
+ ([Wasurechatta](https://wasurechatta.com/) deployed, still need to setup Capistrano)
21
+
22
+ # Why not Sidekiq?
23
+
24
+ If you can, definitely use Sidekiq!!! Belated is supposed to be used if you can't get anything else to work. Like if you want to use SQLite in a Rails app and don't want to have Redis running. Or maybe you just want to run procs in the background?
28
25
 
29
26
  ## Installation
30
27
 
@@ -48,7 +45,15 @@ Start up Belated!
48
45
 
49
46
  $ belated
50
47
 
51
- Then, in another program, connect to Belated and give it a job to do.
48
+ If you're using Rails, just set Belated to be the ActiveJob adapter like below:
49
+
50
+ ```ruby
51
+ config.active_job.adapter = :belated
52
+ ```
53
+
54
+ And you're good to go!
55
+
56
+ If not, in your non-ActiveJob using program, connect to Belated and give it a job to do.
52
57
  Sample below:
53
58
 
54
59
  ```ruby
@@ -69,8 +74,17 @@ client.perform_belated(DumDum.new, at: Time.now + 5 * 60)
69
74
  client.perform_belated(DumDum.new, max_retries: 3) # default 5
70
75
  ```
71
76
 
72
- Belated runs on localhost, port 8788.
73
- Going to make that an option in the future.
77
+ You can also fetch jobs from the future jobs queue:
78
+
79
+ ```ruby
80
+ job = client.perform_belated(proc { 0 / 0 }, at: Time.now + 5 * 60)
81
+ Belated.find job.id # Find the job if it's in the future queue
82
+ # Oh no, that job looks a bit weird!
83
+ # Let's delete it:
84
+ Belated.delete job.id
85
+ ```
86
+
87
+ Belated runs on localhost, port 8788 by default, but the port is configurable, see below.
74
88
 
75
89
  ## Rails
76
90
 
data/belated.gemspec CHANGED
@@ -34,6 +34,7 @@ Gem::Specification.new do |spec|
34
34
  spec.add_dependency 'drb'
35
35
  spec.add_dependency 'dry-configurable'
36
36
  spec.add_dependency 'ruby2_keywords'
37
+ spec.add_dependency 'pstore'
37
38
  spec.add_dependency 'sorted_set'
38
39
  spec.add_development_dependency 'byebug'
39
40
 
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'belated'
4
+
5
+ module ActiveJob # :nodoc:
6
+ module QueueAdapters # :nodoc:
7
+ # The adapter in charge of handling ActiveJob integration.
8
+ # WIP
9
+ class BelatedAdapter
10
+ def instance
11
+ @instance ||= Belated::Client.instance
12
+ rescue StandardError
13
+ @instance = Belated::Client.new
14
+ end
15
+
16
+ def enqueue(job) # :nodoc:
17
+ Rails.logger.info "Belated got job #{job}"
18
+ instance.perform(job, active_job: true)
19
+ end
20
+
21
+ def enqueue_at(job, timestamp) # :nodoc:
22
+ Rails.logger.info "Belated got job #{job} to be performed at #{Time.at(timestamp)}"
23
+ instance.perform_belated(job, at: timestamp, active_job: true)
24
+ end
25
+
26
+ # JobWrapper that overwrites perform for ActiveJob
27
+ class JobWrapper < Belated::JobWrapper
28
+ def perform
29
+ Base.execute job.serialize
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -74,10 +74,11 @@ class Belated
74
74
  # @param at [Date] - The time at which the job should be executed.
75
75
  # @param max_retries [Integer] - Times the job should be retried if it fails.
76
76
  # @return [JobWrapper] - The job wrapper for the queue.
77
- def perform(job, at: nil, max_retries: 5)
77
+ def perform(job, at: nil, max_retries: 5, active_job: false)
78
78
  start unless started?
79
- check_if_proper_job!(job)
80
- job_wrapper = wrap_job(job, at: at, max_retries: max_retries)
79
+ return unless proper_job?(job)
80
+
81
+ job_wrapper = wrap_job(job, at: at.to_f, max_retries: max_retries, active_job: active_job)
81
82
  bank.push(job_wrapper)
82
83
  @mutex.synchronize do
83
84
  proc_table[job_wrapper.object_id] = job_wrapper if job_wrapper.proc_klass
@@ -90,16 +91,22 @@ class Belated
90
91
 
91
92
  private
92
93
 
93
- def check_if_proper_job!(job)
94
- return if job.respond_to?(:call) || job.respond_to?(:perform)
94
+ def proper_job?(job)
95
+ return true if job.respond_to?(:call) || job.respond_to?(:perform)
95
96
 
96
- raise JobError, 'job does not implement .call nor .perform!'
97
+ warn 'job does not implement .call nor .perform!'
98
+ false
97
99
  end
98
100
 
99
- def wrap_job(job, at:, max_retries:)
101
+ def wrap_job(job, at:, max_retries:, active_job:)
100
102
  return job if job.is_a?(JobWrapper)
101
103
 
102
- JobWrapper.new(job: job, at: at, max_retries: max_retries)
104
+ wrapper = if active_job
105
+ ActiveJob::QueueAdapters::BelatedAdapter::JobWrapper
106
+ else
107
+ JobWrapper
108
+ end
109
+ wrapper.new(job: job, at: at, max_retries: max_retries, active_job: active_job)
103
110
  end
104
111
 
105
112
  def drb_connected?
@@ -13,47 +13,60 @@ class Belated
13
13
  class JobWrapper
14
14
  include Comparable
15
15
  include Logging
16
- attr_accessor :retries, :max_retries, :id, :job, :at, :completed, :proc_klass
16
+ attr_accessor :retries, :max_retries, :id, :job, :at,
17
+ :completed, :proc_klass, :error, :active_job
18
+
19
+ def initialize(job:, max_retries: 5, at: nil, active_job: false)
20
+ raise 'JobError' unless job.respond_to?(:call) || job.respond_to?(:perform)
17
21
 
18
- def initialize(job:, max_retries: 5, at: nil)
19
22
  self.retries = 0
20
23
  self.max_retries = max_retries
21
- self.id = SecureRandom.uuid
24
+ self.id = job.respond_to?(:job_id) ? job.job_id : SecureRandom.uuid
22
25
  self.job = job
23
26
  self.at = at
24
- self.completed = false
25
27
  self.proc_klass = job.instance_of?(Proc)
28
+ self.active_job = active_job
26
29
  end
27
30
 
28
31
  def <=>(other)
29
- at <=> other.at
32
+ at <=> (other&.at || other&.scheduled_at)
30
33
  end
31
34
 
32
35
  # rubocop:disable Lint/RescueException
33
36
  def perform
34
- resp = if job.respond_to?(:call)
35
- job.call
36
- else
37
- job.perform
38
- end
37
+ resp = execute
39
38
  self.completed = true
40
39
  resp
41
40
  rescue Exception => e
42
41
  case e.class
43
- when Interrupt, SignalException
42
+ when Interrupt, SignalException, NoMethodError
44
43
  raise e
45
44
  else
46
- retry_job
47
- "Error while executing job, #{e.inspect}. Retry #{retries} of #{max_retries}"
45
+ retry_job(e)
46
+ "Error while executing job #{job.inspect}, #{e.inspect}. Retry #{retries} of #{max_retries}"
48
47
  end
49
48
  end
49
+
50
50
  # rubocop:enable Lint/RescueException
51
+ def execute
52
+ if job.respond_to?(:call)
53
+ job.call
54
+ elsif job.respond_to?(:arguments)
55
+ job.perform(*job.arguments)
56
+ else
57
+ job.perform
58
+ end
59
+ end
51
60
 
52
- def retry_job
61
+ def retry_job(error)
53
62
  self.retries += 1
54
- return if retries > max_retries
63
+ if retries > max_retries
64
+ self.error = error
65
+ return
66
+ end
55
67
 
56
- self.at = Time.now + (retries.next**4)
68
+ seconds_to_retry = $TESTING ? 0.05 : retries.next**4
69
+ self.at = (Time.now + seconds_to_retry).to_f
57
70
  log "Job #{id} failed, retrying at #{at}"
58
71
  Belated.job_list.push(self)
59
72
  end
data/lib/belated/queue.rb CHANGED
@@ -4,7 +4,7 @@ require 'belated/job'
4
4
  require 'belated/logging'
5
5
  require 'belated/job_wrapper'
6
6
  require 'sorted_set'
7
-
7
+ require 'pstore'
8
8
  class Belated
9
9
  # Job queues that Belated uses.
10
10
  # queue is the jobs that are currenly
@@ -13,21 +13,43 @@ class Belated
13
13
  # to be added to queue at some point in the future.
14
14
  class Queue
15
15
  include Logging
16
- attr_accessor :future_jobs
16
+ attr_accessor :future_jobs, :future_jobs_db
17
17
 
18
18
  FILE_NAME = 'belated_dump'
19
19
 
20
20
  def initialize(queue: Thread::Queue.new, future_jobs: SortedSet.new)
21
21
  @queue = queue
22
+ @mutex = Mutex.new
22
23
  self.future_jobs = future_jobs
24
+ self.future_jobs_db = PStore.new('future_jobs.pstore', true) # pass true for thread safety
25
+ end
26
+
27
+ def enqueue_future_jobs
28
+ loop do
29
+ job = future_jobs.min
30
+ if job.nil?
31
+ sleep Belated.heartbeat
32
+ next
33
+ end
34
+ if job.at <= Time.now.to_f
35
+ delete_job(job)
36
+ push(job)
37
+ end
38
+ rescue DRb::DRbConnError
39
+ error 'DRb connection error!!!!!!'
40
+ log stats
41
+ end
23
42
  end
24
43
 
25
44
  def push(job)
26
45
  if job.is_a?(Symbol) || job.at.nil? ||
27
- job.at <= Time.now.utc
46
+ job.at <= Time.now.to_f
28
47
  @queue.push(job)
29
48
  else
30
- @future_jobs << job
49
+ @mutex.synchronize do
50
+ @future_jobs << job
51
+ insert_into_future_jobs_db(job) unless job.proc_klass
52
+ end
31
53
  end
32
54
  end
33
55
 
@@ -49,16 +71,16 @@ class Belated
49
71
  end
50
72
 
51
73
  def load_jobs
52
- log "reloading... if file exists #{File.exist?(FILE_NAME)}"
74
+ future_jobs_db.transaction(true) do
75
+ future_jobs_db.roots.each do |id|
76
+ future_jobs << future_jobs_db[id]
77
+ end
78
+ end
53
79
  return unless File.exist?(FILE_NAME)
54
80
 
55
81
  jobs = YAML.load(File.binread(FILE_NAME))
56
82
  jobs.each do |job|
57
- if job.at && job.at > Time.now.utc
58
- future_jobs.push(job)
59
- else
60
- @queue.push(job)
61
- end
83
+ @queue.push(job)
62
84
  end
63
85
  File.delete(FILE_NAME)
64
86
  end
@@ -70,12 +92,6 @@ class Belated
70
92
  class_array << klass
71
93
  end
72
94
  end
73
- future_jobs.each do |_job|
74
- unless proc_or_shutdown?(klass = future_jobs.pop)
75
- class_array << klass
76
- end
77
- end
78
-
79
95
  pp File.open(FILE_NAME, 'wb') { |f| f.write(YAML.dump(class_array)) }
80
96
  end
81
97
 
@@ -88,5 +104,18 @@ class Belated
88
104
  def proc_or_shutdown?(job)
89
105
  job.is_a?(Symbol) || job.job.instance_of?(Proc)
90
106
  end
107
+
108
+ def delete_job(job)
109
+ log "Deleting #{future_jobs.delete(job)} from future jobs"
110
+ future_jobs_db.transaction do
111
+ future_jobs_db.delete(job.id)
112
+ end
113
+ end
114
+
115
+ def insert_into_future_jobs_db(job)
116
+ future_jobs_db.transaction do
117
+ future_jobs_db[job.id] = job
118
+ end
119
+ end
91
120
  end
92
121
  end
@@ -22,7 +22,7 @@ class Belated
22
22
  # A client that can perform jobs inline
23
23
  class Client
24
24
  alias old_perform perform
25
- def perform(job, at: nil, max_retries: 5)
25
+ def perform(job, at: nil, max_retries: 5, active_job: false)
26
26
  if Belated::Testing.inline?
27
27
  if job.respond_to?(:call)
28
28
  job.call
@@ -30,7 +30,7 @@ class Belated
30
30
  job.perform
31
31
  end
32
32
  else
33
- old_perform(job, at: at, max_retries: max_retries)
33
+ old_perform(job, at: at, max_retries: max_retries, active_job: active_job)
34
34
  end
35
35
  end
36
36
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Belated
4
- VERSION = '0.6.6'
4
+ VERSION = '0.8.1'
5
5
  end
@@ -1,4 +1,6 @@
1
1
  require_relative 'logging'
2
+ require 'pstore'
3
+
2
4
  class Belated
3
5
  # The worker class that actually gets the jobs from the queue
4
6
  # and calls them. Expects the jobs to be procs or
@@ -20,9 +22,27 @@ class Belated
20
22
 
21
23
  log "Worker #{@number} got job: #{job.inspect}"
22
24
  log job.perform
25
+ history_insert(job) unless job.proc_klass || !job.completed
23
26
  rescue DRb::DRbConnError, Errno::ECONNREFUSED, RangeError => e
24
27
  log e
25
28
  end
26
29
  end
30
+
31
+ private
32
+
33
+ def history_insert(job)
34
+ store.transaction do
35
+ store[job.id] = job
36
+ end
37
+ rescue StandardError => e
38
+ error e
39
+ end
40
+
41
+ def store
42
+ today = Time.now.strftime('%F')
43
+ return @store if @store&.path&.include?(today)
44
+
45
+ @store = PStore.new("history-#{today}.pstore", true)
46
+ end
27
47
  end
28
48
  end
data/lib/belated.rb CHANGED
@@ -33,6 +33,7 @@ class Belated
33
33
  setting :log_level, :info, reader: true
34
34
  setting :host, 'localhost', reader: true
35
35
  setting :port, '8788', reader: true
36
+ setting :heartbeat, 1, reader: true
36
37
  setting :client_heartbeat, 5, reader: true
37
38
  URI = "druby://#{Belated.host}:#{Belated.port}"
38
39
 
@@ -50,18 +51,21 @@ class Belated
50
51
  connect!
51
52
  banner_and_info
52
53
  trap_signals
53
- enqueue_future_jobs
54
+ @@queue.enqueue_future_jobs
54
55
  end
55
56
  alias initialize start
56
57
 
57
58
  # Handles connection to DRb server.
58
59
  def connect!
60
+ i = 0
59
61
  DRb.start_service(URI, @@queue, verbose: true)
60
62
  rescue DRb::DRbConnError, Errno::EADDRINUSE
63
+ sleep 0.1 and retry if (i += 1) < 5
61
64
  error 'Could not connect to DRb server.'
62
65
  end
63
66
 
64
67
  def trap_signals
68
+ pp 'trap'
65
69
  %w[INT TERM].each do |signal|
66
70
  Signal.trap(signal) do
67
71
  @worker_list.length.times do
@@ -91,24 +95,6 @@ class Belated
91
95
  Belated.config.rails
92
96
  end
93
97
 
94
- def enqueue_future_jobs
95
- loop do
96
- sleep 0.1
97
- job = @@queue.future_jobs.min
98
- if job.nil?
99
- sleep 5
100
- next
101
- end
102
- if job.at <= Time.now
103
- log "Deleting #{@@queue.future_jobs.delete(job)} from future jobs"
104
- @@queue.push(job)
105
- end
106
- rescue DRb::DRbConnError
107
- error 'DRb connection error!!!!!!'
108
- log stats
109
- end
110
- end
111
-
112
98
  def reload
113
99
  log 'reloading...'
114
100
  @@queue.load_jobs
@@ -154,19 +140,30 @@ class Belated
154
140
  }
155
141
  end
156
142
 
157
- def self.kill_and_clear_queue!
158
- @worker_list&.each do |worker|
159
- Thread.kill(worker)
143
+ class << self
144
+ def find(job_id)
145
+ @@queue.future_jobs.find { |job| job.id == job_id }
160
146
  end
161
- clear_queue!
162
- end
163
147
 
164
- def self.clear_queue!
165
- @@queue.clear
166
- end
148
+ def delete(job_id)
149
+ job = find(job_id)
150
+ @@queue.future_jobs.delete(job)
151
+ end
152
+
153
+ def kill_and_clear_queue!
154
+ @worker_list&.each do |worker|
155
+ Thread.kill(worker)
156
+ end
157
+ clear_queue!
158
+ end
167
159
 
168
- def self.fetch_job
169
- @@queue.pop
160
+ def clear_queue!
161
+ @@queue.clear
162
+ end
163
+
164
+ def fetch_job
165
+ @@queue.pop
166
+ end
170
167
  end
171
168
 
172
169
  def job_list
@@ -177,5 +174,4 @@ class Belated
177
174
  @@queue
178
175
  end
179
176
  end
180
-
181
- require 'belated/rails' if defined?(::Rails::Engine)
177
+ require 'active_job/queue_adapters/belated_adapter' if defined?(::Rails)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: belated
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.6
4
+ version: 0.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sampo Kuokkanen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-08-25 00:00:00.000000000 Z
11
+ date: 2021-09-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: drb
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pstore
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: sorted_set
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -106,6 +120,7 @@ files:
106
120
  - bin/bundle
107
121
  - bin/console
108
122
  - bin/setup
123
+ - lib/active_job/queue_adapters/belated_adapter.rb
109
124
  - lib/belated.rb
110
125
  - lib/belated/client.rb
111
126
  - lib/belated/exceptions.rb