delayed_cron_job 0.6.0 → 0.7.4

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
- 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