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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 52ec65c4ec0ca66d57f30457b6bf0327f43ee479
4
- data.tar.gz: c311cfb9241e8145f324386833366b764cc73c1a
2
+ SHA256:
3
+ metadata.gz: 853453d17f852e33afef72373ceec5a6fe462679272e5aa8a2cee37bccf26f3b
4
+ data.tar.gz: e88869f8b206ad9acd3214eb63ee4366e61c8113fc29084e8d8edaf1fd85db22
5
5
  SHA512:
6
- metadata.gz: 2d9b317975e2914d4ba8f3a6fd12981fccd9341d8432bd9508d6cdadf322f786fb0bb9cda5dd5015c9b053300ab3f1f5dc00ae88b568de9f22296dc416e1542a
7
- data.tar.gz: 670ec7558441a32ede722f66c1821ee524498ee595fc9c03f230f1b313b9bdd3dabb58a7f8d883726a735b9007c4783635fdcad222b7234d8c6799e32b30a794
6
+ metadata.gz: b7bdef74835bd41fe795bc61889361bea65521b7c1771c6e2c29ef693fe15afeeafe74fecb0152a2be7aa40483578866ceec03711e24b71eafa7c66e454c05ad
7
+ data.tar.gz: 991d3d78e52d7932e96d99dacb0bd869bf5ac314fb0a2e89fa5b9d2c0063a63f595b22fad950bf72397003d3621bfabbe79e4e2863b41fee9bac410cb1043286
data/.gitignore CHANGED
@@ -20,4 +20,5 @@ tmp
20
20
  *.o
21
21
  *.a
22
22
  mkmf.log
23
- .project
23
+ .project
24
+ file::memory:*
data/.rspec CHANGED
@@ -1,3 +1,2 @@
1
1
  --color
2
- --warnings
3
2
  --require spec_helper
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - "2.5.8"
5
+
6
+ sudo: false
7
+
8
+ cache: bundler
9
+
10
+ script: bundle exec rake
@@ -1,4 +1,4 @@
1
- Copyright (c) 2014 Pascal Zumkehr
1
+ Copyright (c) 2014-2017 Pascal Zumkehr
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -1,11 +1,13 @@
1
1
  # Delayed::Cron::Job
2
2
 
3
+ [![Build Status](https://travis-ci.org/codez/delayed_cron_job.svg)](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 this line to your application's Gemfile:
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
- Delayed::Job.enqueue(MyRepeatedJob.new, cron: '15 */6 * * 1-5')
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
- ## Details
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.
@@ -28,4 +28,6 @@ Gem::Specification.new do |spec|
28
28
  spec.add_development_dependency "rspec"
29
29
  spec.add_development_dependency "sqlite3"
30
30
  spec.add_development_dependency "delayed_job_active_record"
31
+ spec.add_development_dependency "activejob"
32
+
31
33
  end
@@ -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) && Delayed::Backend::ActiveRecord::Job.respond_to?(:attr_accessible)
17
- Delayed::Backend::ActiveRecord::Job.attr_accessible(:cron)
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,16 @@
1
+ module DelayedCronJob
2
+ module ActiveJob
3
+ module Enqueuing
4
+
5
+ def self.included(klass)
6
+ klass.send(:attr_accessor, :cron)
7
+ end
8
+
9
+ def enqueue(options = {})
10
+ self.cron = options[:cron] if options[:cron]
11
+ super
12
+ end
13
+
14
+ end
15
+ end
16
+ 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
@@ -74,7 +74,7 @@ module DelayedCronJob
74
74
 
75
75
  raise ArgumentError.new(
76
76
  "invalid cronline: '#{line}'"
77
- ) if es && es.find { |e| ! e.is_a?(Fixnum) }
77
+ ) if es && es.find { |e| ! e.is_a?(Integer) }
78
78
  end
79
79
  end
80
80
 
@@ -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
- job.last_error = nil
39
- end
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
- next_job = job.dup
46
- next_job.id = job.id
47
- next_job.created_at = job.created_at
48
- next_job.locked_at = nil
49
- next_job.locked_by = nil
50
- next_job.attempts += 1
51
- next_run_at(next_job)
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
@@ -1,3 +1,3 @@
1
1
  module DelayedCronJob
2
- VERSION = '0.6.0'
2
+ VERSION = '0.7.4'
3
3
  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', 'db/migrate/add_cron_to_delayed_jobs.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("execution expired")
87
+ expect(j.last_error).to match('execution expired')
78
88
  end
79
89
 
80
- it 'schedules new job after deserialization error' do
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(1)
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 correct last_error after success' do
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 "This test only makes sense in non-UTC time zone"
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
@@ -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
- ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => ':memory:'
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.6.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: 2016-01-11 00:00:00.000000000 Z
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.txt
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
- rubyforge_project:
142
- rubygems_version: 2.4.5.1
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