delayed_cron_job 0.6.0 → 0.7.4
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.
- checksums.yaml +5 -5
- data/.gitignore +2 -1
- data/.rspec +0 -1
- data/.travis.yml +10 -0
- data/{LICENSE.txt → LICENSE} +1 -1
- data/README.md +122 -3
- data/delayed_cron_job.gemspec +2 -0
- data/lib/delayed_cron_job.rb +34 -2
- data/lib/delayed_cron_job/active_job/enqueuing.rb +16 -0
- data/lib/delayed_cron_job/active_job/queue_adapter.rb +33 -0
- data/lib/delayed_cron_job/backend/active_record/railtie.rb +14 -0
- data/lib/delayed_cron_job/backend/updatable_cron.rb +29 -0
- data/lib/delayed_cron_job/cronline.rb +1 -1
- data/lib/delayed_cron_job/plugin.rb +14 -22
- data/lib/delayed_cron_job/version.rb +1 -1
- data/lib/generators/delayed_job/cron_generator.rb +13 -2
- data/lib/generators/delayed_job/templates/cron_migration.rb +2 -2
- data/spec/active_job_spec.rb +69 -0
- data/spec/delayed_cron_job_spec.rb +79 -13
- data/spec/spec_helper.rb +5 -2
- metadata +28 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 853453d17f852e33afef72373ceec5a6fe462679272e5aa8a2cee37bccf26f3b
|
4
|
+
data.tar.gz: e88869f8b206ad9acd3214eb63ee4366e61c8113fc29084e8d8edaf1fd85db22
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b7bdef74835bd41fe795bc61889361bea65521b7c1771c6e2c29ef693fe15afeeafe74fecb0152a2be7aa40483578866ceec03711e24b71eafa7c66e454c05ad
|
7
|
+
data.tar.gz: 991d3d78e52d7932e96d99dacb0bd869bf5ac314fb0a2e89fa5b9d2c0063a63f595b22fad950bf72397003d3621bfabbe79e4e2863b41fee9bac410cb1043286
|
data/.gitignore
CHANGED
data/.rspec
CHANGED
data/.travis.yml
ADDED
data/{LICENSE.txt → LICENSE}
RENAMED
data/README.md
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
# Delayed::Cron::Job
|
2
2
|
|
3
|
+
[](https://travis-ci.org/codez/delayed_cron_job)
|
4
|
+
|
3
5
|
Delayed::Cron::Job is an extension to Delayed::Job that allows you to set
|
4
6
|
cron expressions for your jobs to run repeatedly.
|
5
7
|
|
6
8
|
## Installation
|
7
9
|
|
8
|
-
Add
|
10
|
+
Add the following line to your application's Gemfile. Add it after the lines for all other `delayed_job*` gems so the gem can properly integrate with the Delayed::Job code.
|
9
11
|
|
10
12
|
gem 'delayed_cron_job'
|
11
13
|
|
@@ -26,13 +28,124 @@ There are no additional steps for `delayed_job_mongoid`.
|
|
26
28
|
|
27
29
|
When enqueuing a job, simply pass the `cron` option, e.g.:
|
28
30
|
|
29
|
-
|
31
|
+
```ruby
|
32
|
+
Delayed::Job.enqueue(MyRepeatedJob.new, cron: '15 */6 * * 1-5')
|
33
|
+
```
|
34
|
+
|
35
|
+
Or, when using ActiveJob:
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
MyJob.set(cron: '*/5 * * * *').perform_later
|
39
|
+
```
|
30
40
|
|
31
41
|
Any crontab compatible cron expressions are supported (see `man 5 crontab`).
|
32
42
|
The credits for the `Cronline` class used go to
|
33
43
|
[rufus-scheduler](https://github.com/jmettraux/rufus-scheduler).
|
34
44
|
|
35
|
-
##
|
45
|
+
## Scheduling
|
46
|
+
|
47
|
+
Usually, you want to schedule all existing cron jobs when deploying the
|
48
|
+
application. Using a common super class makes this simple.
|
49
|
+
|
50
|
+
### Custom CronJob superclass
|
51
|
+
|
52
|
+
`app/jobs/cron_job.rb`:
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
# Default configuration in `app/jobs/application_job.rb`, or subclass
|
56
|
+
# ActiveJob::Base .
|
57
|
+
class CronJob < ApplicationJob
|
58
|
+
|
59
|
+
class_attribute :cron_expression
|
60
|
+
|
61
|
+
class << self
|
62
|
+
|
63
|
+
def schedule
|
64
|
+
set(cron: cron_expression).perform_later unless scheduled?
|
65
|
+
end
|
66
|
+
|
67
|
+
def remove
|
68
|
+
delayed_job.destroy if scheduled?
|
69
|
+
end
|
70
|
+
|
71
|
+
def scheduled?
|
72
|
+
delayed_job.present?
|
73
|
+
end
|
74
|
+
|
75
|
+
def delayed_job
|
76
|
+
Delayed::Job
|
77
|
+
.where('handler LIKE ?', "%job_class: #{name}%")
|
78
|
+
.first
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
```
|
84
|
+
|
85
|
+
### Example Job inheriting from CronJob
|
86
|
+
|
87
|
+
Then, an example job that triggers E-Mail-sending with default cron time at
|
88
|
+
noon every day:
|
89
|
+
|
90
|
+
`app/jobs/noon_job.rb`:
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
|
94
|
+
# Note that it inherits from `CronJob`
|
95
|
+
class NoonJob < CronJob
|
96
|
+
# set the (default) cron expression
|
97
|
+
self.cron_expression = '0 12 * * *'
|
98
|
+
|
99
|
+
# will enqueue the mailing delivery job
|
100
|
+
def perform
|
101
|
+
UserMailer.daily_notice(User.first).deliver_later
|
102
|
+
end
|
103
|
+
end
|
104
|
+
```
|
105
|
+
|
106
|
+
### Scheduling "trigger"
|
107
|
+
|
108
|
+
Jobs with a `cron` definition are rescheduled automatically only when a job
|
109
|
+
instance finished its work. So there needs to be an initial scheduling of all
|
110
|
+
cron jobs. If you do not want to do this manually (e.g. using `rails console`)
|
111
|
+
or with your application logic, you can e.g. hook into the `rails db:*` rake
|
112
|
+
tasks:
|
113
|
+
|
114
|
+
`lib/tasks/jobs.rake`:
|
115
|
+
|
116
|
+
```ruby
|
117
|
+
namespace :db do
|
118
|
+
desc 'Schedule all cron jobs'
|
119
|
+
task :schedule_jobs => :environment do
|
120
|
+
# Need to load all jobs definitions in order to find subclasses
|
121
|
+
glob = Rails.root.join('app', 'jobs', '**', '*_job.rb')
|
122
|
+
Dir.glob(glob).each { |file| require file }
|
123
|
+
CronJob.subclasses.each { |job| job.schedule }
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# invoke schedule_jobs automatically after every migration and schema load.
|
128
|
+
%w(db:migrate db:schema:load).each do |task|
|
129
|
+
Rake::Task[task].enhance do
|
130
|
+
Rake::Task['db:schedule_jobs'].invoke
|
131
|
+
end
|
132
|
+
end
|
133
|
+
```
|
134
|
+
|
135
|
+
Now, if you run `rails db:migrate`, `rails db:schema:load` or `rails
|
136
|
+
db:schedule_jobs` all jobs inheriting from `CronJob` are scheduled.
|
137
|
+
|
138
|
+
*If you are not using ActiveJob, the same approach may be used with minor
|
139
|
+
adjustments.*
|
140
|
+
|
141
|
+
### Changing the schedule
|
142
|
+
|
143
|
+
Note that if you have a CronJob scheduled and change its `cron_expression` in
|
144
|
+
its source file, you will have to remove any scheduled instances of the Job and
|
145
|
+
reschedule it (e.g. with the snippet above: `rails db:migrate`). This is because
|
146
|
+
the `cron_expression` is already persisted in the database (as `cron`).
|
147
|
+
|
148
|
+
## Details
|
36
149
|
|
37
150
|
The initial `run_at` value is computed during the `#enqueue` method call.
|
38
151
|
If you create `Delayed::Job` database entries directly, make sure to set
|
@@ -61,3 +174,9 @@ jobs.
|
|
61
174
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
62
175
|
4. Push to the branch (`git push origin my-new-feature`)
|
63
176
|
5. Create a new Pull Request
|
177
|
+
|
178
|
+
## License
|
179
|
+
|
180
|
+
Delayed::Cron::Job is released under the terms of the MIT License.
|
181
|
+
Copyright 2014-2020 Pascal Zumkehr. See [LICENSE](LICENSE) for further
|
182
|
+
information.
|
data/delayed_cron_job.gemspec
CHANGED
data/lib/delayed_cron_job.rb
CHANGED
@@ -3,6 +3,15 @@ require 'English'
|
|
3
3
|
require 'delayed_cron_job/cronline'
|
4
4
|
require 'delayed_cron_job/plugin'
|
5
5
|
require 'delayed_cron_job/version'
|
6
|
+
require 'delayed_cron_job/backend/updatable_cron'
|
7
|
+
|
8
|
+
begin
|
9
|
+
require 'delayed_job_active_record'
|
10
|
+
rescue LoadError; end
|
11
|
+
|
12
|
+
begin
|
13
|
+
require 'delayed_job_mongoid'
|
14
|
+
rescue LoadError; end
|
6
15
|
|
7
16
|
module DelayedCronJob
|
8
17
|
|
@@ -11,10 +20,33 @@ end
|
|
11
20
|
if defined?(Delayed::Backend::Mongoid)
|
12
21
|
Delayed::Backend::Mongoid::Job.field :cron, :type => String
|
13
22
|
Delayed::Backend::Mongoid::Job.attr_accessible(:cron) if Delayed::Backend::Mongoid::Job.respond_to?(:attr_accessible)
|
23
|
+
Delayed::Backend::Mongoid::Job.send(:include, DelayedCronJob::Backend::UpdatableCron)
|
14
24
|
end
|
15
25
|
|
16
|
-
if defined?(Delayed::Backend::ActiveRecord)
|
17
|
-
|
26
|
+
if defined?(Delayed::Backend::ActiveRecord)
|
27
|
+
if defined?(Rails::Railtie)
|
28
|
+
# Postpone initialization to railtie for correct order
|
29
|
+
require 'delayed_cron_job/backend/active_record/railtie'
|
30
|
+
else
|
31
|
+
# Do the same as in the railtie
|
32
|
+
Delayed::Backend::ActiveRecord::Job.send(:include, DelayedCronJob::Backend::UpdatableCron)
|
33
|
+
if Delayed::Backend::ActiveRecord::Job.respond_to?(:attr_accessible)
|
34
|
+
Delayed::Backend::ActiveRecord::Job.attr_accessible(:cron)
|
35
|
+
end
|
36
|
+
end
|
18
37
|
end
|
19
38
|
|
20
39
|
Delayed::Worker.plugins << DelayedCronJob::Plugin
|
40
|
+
|
41
|
+
if defined?(::ActiveJob)
|
42
|
+
require 'delayed_cron_job/active_job/enqueuing'
|
43
|
+
require 'delayed_cron_job/active_job/queue_adapter'
|
44
|
+
|
45
|
+
ActiveJob::Base.send(:include, DelayedCronJob::ActiveJob::Enqueuing)
|
46
|
+
if ActiveJob::QueueAdapters::DelayedJobAdapter.respond_to?(:enqueue)
|
47
|
+
ActiveJob::QueueAdapters::DelayedJobAdapter.extend(DelayedCronJob::ActiveJob::QueueAdapter)
|
48
|
+
else
|
49
|
+
ActiveJob::QueueAdapters::DelayedJobAdapter.send(:include, DelayedCronJob::ActiveJob::QueueAdapter)
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module DelayedCronJob
|
2
|
+
module ActiveJob
|
3
|
+
module QueueAdapter
|
4
|
+
|
5
|
+
def self.included(klass)
|
6
|
+
klass.send(:alias_method, :enqueue, :enqueue_with_cron)
|
7
|
+
klass.send(:alias_method, :enqueue_at, :enqueue_at_with_cron)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.extended(klass)
|
11
|
+
meta = class << klass; self; end
|
12
|
+
meta.send(:alias_method, :enqueue, :enqueue_with_cron)
|
13
|
+
meta.send(:alias_method, :enqueue_at, :enqueue_at_with_cron)
|
14
|
+
end
|
15
|
+
|
16
|
+
def enqueue_with_cron(job)
|
17
|
+
enqueue_at(job, nil)
|
18
|
+
end
|
19
|
+
|
20
|
+
def enqueue_at_with_cron(job, timestamp)
|
21
|
+
options = { queue: job.queue_name,
|
22
|
+
cron: job.cron }
|
23
|
+
options[:run_at] = Time.at(timestamp) if timestamp
|
24
|
+
options[:priority] = job.priority if job.respond_to?(:priority)
|
25
|
+
wrapper = ::ActiveJob::QueueAdapters::DelayedJobAdapter::JobWrapper.new(job.serialize)
|
26
|
+
delayed_job = Delayed::Job.enqueue(wrapper, options)
|
27
|
+
job.provider_job_id = delayed_job.id if job.respond_to?(:provider_job_id=)
|
28
|
+
delayed_job
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module DelayedCronJob
|
2
|
+
module Backend
|
3
|
+
module ActiveRecord
|
4
|
+
class Railtie < ::Rails::Railtie
|
5
|
+
config.after_initialize do
|
6
|
+
Delayed::Backend::ActiveRecord::Job.send(:include, DelayedCronJob::Backend::UpdatableCron)
|
7
|
+
if Delayed::Backend::ActiveRecord::Job.respond_to?(:attr_accessible)
|
8
|
+
Delayed::Backend::ActiveRecord::Job.attr_accessible(:cron)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module DelayedCronJob
|
2
|
+
module Backend
|
3
|
+
module UpdatableCron
|
4
|
+
|
5
|
+
def self.included(klass)
|
6
|
+
klass.send(:before_save, :set_next_run_at, :if => :cron_changed?)
|
7
|
+
klass.attr_accessor :schedule_instead_of_destroy
|
8
|
+
end
|
9
|
+
|
10
|
+
def set_next_run_at
|
11
|
+
if cron.present?
|
12
|
+
self.run_at = Cronline.new(cron).next_time(Delayed::Job.db_time_now)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def destroy
|
17
|
+
super unless schedule_instead_of_destroy
|
18
|
+
end
|
19
|
+
|
20
|
+
def schedule_next_run
|
21
|
+
self.attempts += 1
|
22
|
+
unlock
|
23
|
+
set_next_run_at
|
24
|
+
save!
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -2,20 +2,12 @@ module DelayedCronJob
|
|
2
2
|
class Plugin < Delayed::Plugin
|
3
3
|
|
4
4
|
class << self
|
5
|
-
def next_run_at(job)
|
6
|
-
job.run_at = Cronline.new(job.cron).next_time(Delayed::Job.db_time_now)
|
7
|
-
end
|
8
|
-
|
9
5
|
def cron?(job)
|
10
6
|
job.cron.present?
|
11
7
|
end
|
12
8
|
end
|
13
9
|
|
14
10
|
callbacks do |lifecycle|
|
15
|
-
# Calculate the next run_at based on the cron attribute before enqueue.
|
16
|
-
lifecycle.before(:enqueue) do |job|
|
17
|
-
next_run_at(job) if cron?(job)
|
18
|
-
end
|
19
11
|
|
20
12
|
# Prevent rescheduling of failed jobs as this is already done
|
21
13
|
# after perform.
|
@@ -25,7 +17,6 @@ module DelayedCronJob
|
|
25
17
|
worker.job_say(job,
|
26
18
|
"FAILED with #{$ERROR_INFO.class.name}: #{$ERROR_INFO.message}",
|
27
19
|
Logger::ERROR)
|
28
|
-
job.destroy
|
29
20
|
else
|
30
21
|
# No cron job - proceed as normal
|
31
22
|
block.call(worker, job)
|
@@ -34,25 +25,26 @@ module DelayedCronJob
|
|
34
25
|
|
35
26
|
# Reset the last_error to have the correct status of the last run.
|
36
27
|
lifecycle.before(:perform) do |worker, job|
|
37
|
-
if cron?(job)
|
38
|
-
|
39
|
-
|
28
|
+
job.last_error = nil if cron?(job)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Prevent destruction of cron jobs
|
32
|
+
lifecycle.after(:invoke_job) do |job|
|
33
|
+
job.schedule_instead_of_destroy = true if cron?(job)
|
40
34
|
end
|
41
35
|
|
42
36
|
# Schedule the next run based on the cron attribute.
|
43
37
|
lifecycle.after(:perform) do |worker, job|
|
44
|
-
if cron?(job)
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
next_job.save!
|
38
|
+
if cron?(job) && !job.destroyed?
|
39
|
+
job.cron = job.class.where(:id => job.id).pluck(:cron).first
|
40
|
+
if job.cron.present?
|
41
|
+
job.schedule_next_run
|
42
|
+
else
|
43
|
+
job.schedule_instead_of_destroy = false
|
44
|
+
job.destroy
|
45
|
+
end
|
53
46
|
end
|
54
47
|
end
|
55
48
|
end
|
56
|
-
|
57
49
|
end
|
58
50
|
end
|
@@ -12,11 +12,22 @@ module DelayedJob
|
|
12
12
|
self.source_paths << File.join(File.dirname(__FILE__), 'templates')
|
13
13
|
|
14
14
|
def create_migration_file
|
15
|
-
migration_template('cron_migration.rb',
|
15
|
+
migration_template('cron_migration.rb',
|
16
|
+
'db/migrate/add_cron_to_delayed_jobs.rb',
|
17
|
+
migration_version: migration_version)
|
16
18
|
end
|
17
19
|
|
18
20
|
def self.next_migration_number(dirname)
|
19
21
|
ActiveRecord::Generators::Base.next_migration_number(dirname)
|
20
22
|
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def migration_version
|
27
|
+
if ActiveRecord::VERSION::MAJOR >= 5
|
28
|
+
"[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
21
32
|
end
|
22
|
-
end
|
33
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
class AddCronToDelayedJobs < ActiveRecord::Migration
|
1
|
+
class AddCronToDelayedJobs < ActiveRecord::Migration<%= migration_version %>
|
2
2
|
def self.up
|
3
3
|
add_column :delayed_jobs, :cron, :string
|
4
4
|
end
|
@@ -6,4 +6,4 @@ class AddCronToDelayedJobs < ActiveRecord::Migration
|
|
6
6
|
def self.down
|
7
7
|
remove_column :delayed_jobs, :cron
|
8
8
|
end
|
9
|
-
end
|
9
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActiveJob do
|
4
|
+
|
5
|
+
class CronJob < ActiveJob::Base
|
6
|
+
class_attribute :cron
|
7
|
+
|
8
|
+
def perform; end
|
9
|
+
|
10
|
+
def cron
|
11
|
+
@cron ||= self.class.cron
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
before { Delayed::Job.delete_all }
|
16
|
+
|
17
|
+
let(:cron) { '5 1 * * *' }
|
18
|
+
let(:job) { CronJob.set(cron: cron).perform_later }
|
19
|
+
let(:delayed_job) { Delayed::Job.first }
|
20
|
+
let(:worker) { Delayed::Worker.new }
|
21
|
+
let(:now) { Delayed::Job.db_time_now }
|
22
|
+
let(:next_run) do
|
23
|
+
run = now.hour * 60 + now.min >= 65 ? now + 1.day : now
|
24
|
+
Time.utc(run.year, run.month, run.day, 1, 5)
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'with cron' do
|
28
|
+
it 'sets run_at on enqueue' do
|
29
|
+
expect { job }.to change { Delayed::Job.count }.by(1)
|
30
|
+
expect(delayed_job.run_at).to eq(next_run)
|
31
|
+
expect(delayed_job.cron).to eq(cron)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'schedules a new job after success' do
|
35
|
+
job
|
36
|
+
delayed_job.update_column(:run_at, now)
|
37
|
+
delayed_job.reload
|
38
|
+
|
39
|
+
worker.work_off
|
40
|
+
|
41
|
+
expect(Delayed::Job.count).to eq(1)
|
42
|
+
j = Delayed::Job.first
|
43
|
+
expect(j.id).to eq(delayed_job.id)
|
44
|
+
expect(j.cron).to eq(delayed_job.cron)
|
45
|
+
expect(j.run_at).to eq(next_run)
|
46
|
+
expect(j.attempts).to eq(1)
|
47
|
+
expect(j.last_error).to eq(nil)
|
48
|
+
expect(j.created_at).to eq(delayed_job.created_at)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'without cron' do
|
53
|
+
let(:job) { CronJob.perform_later }
|
54
|
+
|
55
|
+
it 'sets run_at but not cron on enqueue' do
|
56
|
+
CronJob.cron = nil
|
57
|
+
expect { job }.to change { Delayed::Job.count }.by(1)
|
58
|
+
expect(delayed_job.run_at).to be <= now
|
59
|
+
expect(delayed_job.cron).to be_nil
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'uses default cron on enqueue' do
|
63
|
+
CronJob.cron = cron
|
64
|
+
expect { job }.to change { Delayed::Job.count }.by(1)
|
65
|
+
expect(delayed_job.run_at).to eq(next_run)
|
66
|
+
expect(delayed_job.cron).to eq(cron)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
require 'spec_helper'
|
2
2
|
|
3
3
|
describe DelayedCronJob do
|
4
4
|
|
@@ -6,6 +6,16 @@ describe DelayedCronJob do
|
|
6
6
|
def perform; end
|
7
7
|
end
|
8
8
|
|
9
|
+
class DatabaseDisconnectPlugin < Delayed::Plugin
|
10
|
+
|
11
|
+
callbacks do |lifecycle|
|
12
|
+
lifecycle.after(:perform) do
|
13
|
+
ActiveRecord::Base.connection.disconnect!
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
9
19
|
before { Delayed::Job.delete_all }
|
10
20
|
|
11
21
|
let(:cron) { '5 1 * * *' }
|
@@ -25,13 +35,13 @@ describe DelayedCronJob do
|
|
25
35
|
end
|
26
36
|
|
27
37
|
it 'enqueue fails with invalid cron' do
|
28
|
-
expect { Delayed::Job.enqueue(handler, cron: 'no valid cron') }
|
29
|
-
to raise_error(ArgumentError)
|
38
|
+
expect { Delayed::Job.enqueue(handler, cron: 'no valid cron') }
|
39
|
+
.to raise_error(ArgumentError)
|
30
40
|
end
|
31
41
|
|
32
42
|
it 'schedules a new job after success' do
|
33
43
|
job.update_column(:run_at, now)
|
34
|
-
job.reload
|
44
|
+
job.reload # adjusts granularity of run_at datetime
|
35
45
|
|
36
46
|
worker.work_off
|
37
47
|
|
@@ -48,7 +58,7 @@ describe DelayedCronJob do
|
|
48
58
|
it 'schedules a new job after failure' do
|
49
59
|
allow_any_instance_of(TestJob).to receive(:perform).and_raise('Fail!')
|
50
60
|
job.update(run_at: now)
|
51
|
-
job.reload
|
61
|
+
job.reload # adjusts granularity of run_at datetime
|
52
62
|
|
53
63
|
worker.work_off
|
54
64
|
|
@@ -74,19 +84,16 @@ describe DelayedCronJob do
|
|
74
84
|
expect(j.cron).to eq(job.cron)
|
75
85
|
expect(j.run_at).to eq(next_run)
|
76
86
|
expect(j.attempts).to eq(1)
|
77
|
-
expect(j.last_error).to match(
|
87
|
+
expect(j.last_error).to match('execution expired')
|
78
88
|
end
|
79
89
|
|
80
|
-
it '
|
81
|
-
Delayed::Worker.max_run_time = 1.second
|
90
|
+
it 'does not schedule new job after deserialization error' do
|
82
91
|
job.update_column(:run_at, now)
|
83
92
|
allow_any_instance_of(TestJob).to receive(:perform).and_raise(Delayed::DeserializationError)
|
84
93
|
|
85
94
|
worker.work_off
|
86
95
|
|
87
|
-
expect(Delayed::Job.count).to eq(
|
88
|
-
j = Delayed::Job.first
|
89
|
-
expect(j.last_error).to match("Delayed::DeserializationError")
|
96
|
+
expect(Delayed::Job.count).to eq(0)
|
90
97
|
end
|
91
98
|
|
92
99
|
it 'has empty last_error after success' do
|
@@ -98,7 +105,7 @@ describe DelayedCronJob do
|
|
98
105
|
expect(j.last_error).to eq(nil)
|
99
106
|
end
|
100
107
|
|
101
|
-
it 'has
|
108
|
+
it 'has updated last_error after failure' do
|
102
109
|
allow_any_instance_of(TestJob).to receive(:perform).and_raise('Fail!')
|
103
110
|
job.update(run_at: now, last_error: 'Last error')
|
104
111
|
|
@@ -116,7 +123,7 @@ describe DelayedCronJob do
|
|
116
123
|
run_at = Time.utc(run.year, run.month, run.day, hour, (now.min + 1) % 60)
|
117
124
|
expect(job.run_at).to eq(run_at)
|
118
125
|
else
|
119
|
-
pending
|
126
|
+
pending 'This test only makes sense in non-UTC time zone'
|
120
127
|
end
|
121
128
|
end
|
122
129
|
|
@@ -138,6 +145,65 @@ describe DelayedCronJob do
|
|
138
145
|
j = Delayed::Job.first
|
139
146
|
expect(j.attempts).to eq(job.attempts + 1)
|
140
147
|
end
|
148
|
+
|
149
|
+
it 'updates run_at if cron is changed' do
|
150
|
+
job.update!(cron: '1 10 * * *')
|
151
|
+
expect(job.run_at.min).to eq(1)
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'uses new cron when this is updated while job is running' do
|
155
|
+
job.update_column(:run_at, now)
|
156
|
+
allow_any_instance_of(TestJob).to receive(:perform) { job.update!(cron: '1 10 * * *') }
|
157
|
+
|
158
|
+
worker.work_off
|
159
|
+
|
160
|
+
j = Delayed::Job.first
|
161
|
+
expect(j.run_at.min).to eq(1)
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'does not reschedule job if cron is cleared while job is running' do
|
165
|
+
job.update_column(:run_at, now)
|
166
|
+
allow_any_instance_of(TestJob).to receive(:perform) { job.update!(cron: '') }
|
167
|
+
|
168
|
+
expect { worker.work_off }.to change { Delayed::Job.count }.by(-1)
|
169
|
+
end
|
170
|
+
|
171
|
+
it 'does not reschedule job if model is deleted while job is running' do
|
172
|
+
job.update_column(:run_at, now)
|
173
|
+
allow_any_instance_of(TestJob).to receive(:perform) { job.destroy! }
|
174
|
+
|
175
|
+
expect { worker.work_off }.to change { Delayed::Job.count }.by(-1)
|
176
|
+
end
|
177
|
+
|
178
|
+
context 'when database connection is lost' do
|
179
|
+
around(:each) do |example|
|
180
|
+
Delayed::Worker.plugins.unshift DatabaseDisconnectPlugin
|
181
|
+
# hold onto a connection so the in-memory database isn't lost when disconnected
|
182
|
+
temp_connection = ActiveRecord::Base.connection_pool.checkout
|
183
|
+
example.run
|
184
|
+
ActiveRecord::Base.connection_pool.checkin temp_connection
|
185
|
+
Delayed::Worker.plugins.delete DatabaseDisconnectPlugin
|
186
|
+
end
|
187
|
+
|
188
|
+
it 'does not lose the job if database connection is lost' do
|
189
|
+
job.update_column(:run_at, now)
|
190
|
+
job.reload # adjusts granularity of run_at datetime
|
191
|
+
|
192
|
+
begin
|
193
|
+
worker.work_off
|
194
|
+
rescue StandardError
|
195
|
+
# Attempting to save the clone delayed_job will raise an exception due to the database connection being closed
|
196
|
+
end
|
197
|
+
|
198
|
+
ActiveRecord::Base.connection.reconnect!
|
199
|
+
|
200
|
+
expect(Delayed::Job.count).to eq(1)
|
201
|
+
j = Delayed::Job.first
|
202
|
+
expect(j.id).to eq(job.id)
|
203
|
+
expect(j.cron).to eq(job.cron)
|
204
|
+
expect(j.attempts).to eq(0)
|
205
|
+
end
|
206
|
+
end
|
141
207
|
end
|
142
208
|
|
143
209
|
context 'without cron' do
|
data/spec/spec_helper.rb
CHANGED
@@ -15,12 +15,15 @@
|
|
15
15
|
#
|
16
16
|
|
17
17
|
require 'delayed_job_active_record'
|
18
|
+
require 'active_job'
|
18
19
|
require 'delayed_cron_job'
|
19
20
|
|
20
21
|
Delayed::Worker.logger = Logger.new('/tmp/dj.log')
|
21
22
|
ENV['RAILS_ENV'] = 'test'
|
22
23
|
|
23
|
-
|
24
|
+
ActiveJob::Base.queue_adapter = :delayed_job
|
25
|
+
|
26
|
+
ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => 'file::memory:?cache=shared'
|
24
27
|
ActiveRecord::Base.logger = Delayed::Worker.logger
|
25
28
|
ActiveRecord::Migration.verbose = false
|
26
29
|
|
@@ -45,5 +48,5 @@ end
|
|
45
48
|
|
46
49
|
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
47
50
|
RSpec.configure do |config|
|
48
|
-
|
51
|
+
|
49
52
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: delayed_cron_job
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pascal Zumkehr
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-12-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: delayed_job
|
@@ -94,6 +94,20 @@ dependencies:
|
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: activejob
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
97
111
|
description: |-
|
98
112
|
Delayed Cron Job is an extension to Delayed::Job
|
99
113
|
that allows you to set cron expressions for your
|
@@ -106,24 +120,30 @@ extra_rdoc_files: []
|
|
106
120
|
files:
|
107
121
|
- ".gitignore"
|
108
122
|
- ".rspec"
|
123
|
+
- ".travis.yml"
|
109
124
|
- Gemfile
|
110
|
-
- LICENSE
|
125
|
+
- LICENSE
|
111
126
|
- README.md
|
112
127
|
- Rakefile
|
113
128
|
- delayed_cron_job.gemspec
|
114
129
|
- lib/delayed_cron_job.rb
|
130
|
+
- lib/delayed_cron_job/active_job/enqueuing.rb
|
131
|
+
- lib/delayed_cron_job/active_job/queue_adapter.rb
|
132
|
+
- lib/delayed_cron_job/backend/active_record/railtie.rb
|
133
|
+
- lib/delayed_cron_job/backend/updatable_cron.rb
|
115
134
|
- lib/delayed_cron_job/cronline.rb
|
116
135
|
- lib/delayed_cron_job/plugin.rb
|
117
136
|
- lib/delayed_cron_job/version.rb
|
118
137
|
- lib/generators/delayed_job/cron_generator.rb
|
119
138
|
- lib/generators/delayed_job/templates/cron_migration.rb
|
139
|
+
- spec/active_job_spec.rb
|
120
140
|
- spec/delayed_cron_job_spec.rb
|
121
141
|
- spec/spec_helper.rb
|
122
142
|
homepage: https://github.com/codez/delayed_cron_job
|
123
143
|
licenses:
|
124
144
|
- MIT
|
125
145
|
metadata: {}
|
126
|
-
post_install_message:
|
146
|
+
post_install_message:
|
127
147
|
rdoc_options: []
|
128
148
|
require_paths:
|
129
149
|
- lib
|
@@ -138,12 +158,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
138
158
|
- !ruby/object:Gem::Version
|
139
159
|
version: '0'
|
140
160
|
requirements: []
|
141
|
-
|
142
|
-
|
143
|
-
signing_key:
|
161
|
+
rubygems_version: 3.0.6
|
162
|
+
signing_key:
|
144
163
|
specification_version: 4
|
145
164
|
summary: An extension to Delayed::Job that allows you to set cron expressions for
|
146
165
|
your jobs to run regularly.
|
147
166
|
test_files:
|
167
|
+
- spec/active_job_spec.rb
|
148
168
|
- spec/delayed_cron_job_spec.rb
|
149
169
|
- spec/spec_helper.rb
|