resque-scheduler 5.0.0 → 5.1.0

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
2
  SHA256:
3
- metadata.gz: e23e91d3c9920396d8feaad257fe4a27f4ff269f6cd0d6f9756016599d7cdf27
4
- data.tar.gz: 7c1c242fd5c2ed74f8c15924d476775f76ed4929fb65d31b965e708c5949a601
3
+ metadata.gz: 6aa6e1f1a8fe48f091032409157797e48011e473fca30c8fd12d18d05517de49
4
+ data.tar.gz: cbcc8d19930096797a1d028a9765b9c7c0b65ae023011ccec64cf8005420f183
5
5
  SHA512:
6
- metadata.gz: c90721652b59bdc1f2572975d3ef93ed9aa420f36a6a8f3a0c5a35f17a3e5145ebdd30bfacf7d62e4903f9477319282c8a33d55080f5c870c84161f4933e79d4
7
- data.tar.gz: 061fc1d3dfea48a20db7e8c11b31fe0b2e6594d9be0ed5c202c38f304941bceb8c1f15497d0c0ec886c0ddd37fc867b6dd1b5e143e9bb5961cd01b41f73d2e5e
6
+ metadata.gz: a3842dbcedbb2768e715aef2355f11333cadfa101ccb5a1475dbda155005d494408e0554c027ba5cf72afcabc7dcd3778125b9f512680850c875efbccb94f64f
7
+ data.tar.gz: 9623d1a81de5c0e1179e7be932ad6a19641dbcbc57c7b9034fc531160ae6e896374611d5d0f1dc64a710c27293cbc0e1db0eda9f6e5ad4c5ed8208398e20cdae
@@ -6,9 +6,11 @@ on:
6
6
  pull_request:
7
7
  branches: [master]
8
8
 
9
+ permissions:
10
+ contents: read
11
+
9
12
  jobs:
10
13
  test:
11
- continue-on-error: true
12
14
  runs-on: ubuntu-latest
13
15
  services:
14
16
  redis:
@@ -19,20 +21,16 @@ jobs:
19
21
  fail-fast: false
20
22
  matrix:
21
23
  ruby-version:
22
- - "3.0"
23
- - 3.1
24
24
  - 3.2
25
25
  - 3.3
26
26
  - 3.4
27
+ - "4.0"
27
28
  - head
28
29
  resque-version:
29
30
  - "master"
30
- rufus-scheduler:
31
- - "3.4"
32
- - "3.5"
33
- - "3.6"
34
31
  redis-version:
35
32
  - "4.8"
33
+ - "5.4"
36
34
  - "latest"
37
35
  rack-version:
38
36
  - 2
@@ -40,15 +38,24 @@ jobs:
40
38
  env:
41
39
  REDIS_VERSION: "${{ matrix.redis-version }}"
42
40
  RESQUE: "${{ matrix.resque-version }}"
43
- RUFUS_SCHEDULER: "${{ matrix.rufus-scheduler }}"
44
41
  RACK_VERSION: "${{ matrix.rack-version }}"
45
42
  COVERAGE: 1
46
43
 
47
44
  steps:
48
- - uses: actions/checkout@v4
45
+ - uses: actions/checkout@v7
49
46
  - uses: ruby/setup-ruby@v1
50
47
  with:
51
48
  ruby-version: "${{ matrix.ruby-version }}"
52
49
  bundler-cache: true
53
50
  - run: bundle exec rake
54
51
  - run: bin/bundle_console_test.sh
52
+
53
+ rubocop:
54
+ runs-on: ubuntu-latest
55
+ steps:
56
+ - uses: actions/checkout@v7
57
+ - uses: ruby/setup-ruby@v1
58
+ with:
59
+ ruby-version: 3.4
60
+ bundler-cache: true
61
+ - run: bundle exec rubocop
data/CHANGELOG.md CHANGED
@@ -2,6 +2,47 @@
2
2
 
3
3
  **ATTN**: This project uses [semantic versioning](http://semver.org/).
4
4
 
5
+ ## [5.1.0] - 2026-10-28
6
+ ### Breaking Changes
7
+ * Ruby requirement: `>= 3.0.0` → `>= 3.2.0` by @neuromanskie in #837
8
+ * rufus-scheduler dependency: `~> 3.2, != 3.3` → `~> 3.9` by @neuromanskie in #837
9
+ * Schedule entries (`cron`/`every`) no longer run `before_schedule`, `after_schedule` or `before_delayed_enqueue`. Move that code to `before_recurring`/`after_recurring` by @top-sigrid in #844
10
+
11
+ ### Added
12
+ * `before_recurring` and `after_recurring` hooks for jobs enqueued from the schedule by @top-sigrid in #844
13
+ * `DISABLE_DELAYED_REQUEUE_BATCHES` to opt out of batched delayed job enqueues by @onyxraven in #796
14
+ * Search delayed jobs by args in the web UI by @bmo in #839 (originally #714)
15
+ * Allow a number of seconds for `every` by @carsonreinke in #838 (originally #607)
16
+ * Railtie to load resque-scheduler automatically in Rails apps by @josh-m-sharpe in #694
17
+ * Add Ruby 4.0 and redis-rb 5.4 to the CI matrix by @neuromanskie in #837
18
+
19
+ ### Changed
20
+ * Upgrade RuboCop to 1.91 and run it as a CI job by @neuromanskie in #837
21
+
22
+ ### Fixed
23
+ * Delayed jobs no longer run `before_schedule`/`after_schedule` a second time when they become due by @top-sigrid in #844
24
+ * Skip schedule hooks when `enqueue_at` is given a timestamp that is not in the future by @top-sigrid in #817
25
+ * Schedule Active Job classes through `perform_later` by @neuromanskie and @josh-m-sharpe in #842 (originally #696)
26
+ * Schedule delayed test jobs far enough out to not race the clock by @neuromanskie in #843
27
+ * Fail CI when tests fail by @neuromanskie in #837
28
+
29
+ ## [5.0.1] - 2026-10-11
30
+ ### Changed
31
+ * Pipeline `count_all_scheduled_jobs` for significantly faster execution by @top-sigrid in #822
32
+ * Avoid unnecessary reloads in static mode by @ekmartin in #823
33
+
34
+ ### Fixed
35
+ * Skip invalid schedules without interrupting scheduler loading by @taf2 in #543
36
+ * Encode a delayed job once so its timestamps key can be cleaned up by @kamaljoshi in #827 (originally #599)
37
+ * Log to the logfile when backgrounded by @meatballhat in #828 (originally #568)
38
+ * Require redis before using its error classes by @neuromanskie in #829
39
+ * Fix flaky test by using strings instead of symbols for JSON-serialized args by @top-sigrid in #813
40
+
41
+ ### Security
42
+ * Escape job class and queue names in the scheduler web UI by @PatrickTulskie in #830
43
+ * Escape schedule names and parameters on the requeue params page by @PatrickTulskie in #831
44
+ * Escape the scheduler environment and master lock holder on the schedule page by @PatrickTulskie in #832
45
+
5
46
  ## [5.0.0] - 2026-01-15
6
47
  ### Breaking Changes
7
48
  * Ruby requirement: `>= 2.3.0` → `>= 3.0.0`
@@ -32,7 +73,7 @@
32
73
  * Fixed test compatibility with Rack 3 by @PatrickTulskie in #809
33
74
  * Fixed race condition in scheduler task tests by @PatrickTulskie in #809
34
75
 
35
- ## [4.10.3] - 2024-12-30
76
+ ## [4.11.0] - 2024-12-30
36
77
  ### Added
37
78
  * Expose timeout for lock via environment variable configuration by @pmm4654 in #786
38
79
  * Add Ruby 3.3 to the CI matrix by @santiagorodriguez96 in #797
data/Gemfile CHANGED
@@ -8,13 +8,6 @@ else
8
8
  gem 'resque', resque_version
9
9
  end
10
10
 
11
- case rufus_scheduler_version = ENV.fetch('RUFUS_SCHEDULER', '3.6')
12
- when 'master'
13
- gem 'rufus-scheduler', git: 'https://github.com/jmettraux/rufus-scheduler'
14
- else
15
- gem 'rufus-scheduler', rufus_scheduler_version
16
- end
17
-
18
11
  case redis_version = ENV.fetch('REDIS_VERSION', 'latest')
19
12
  when 'master'
20
13
  gem 'redis', git: 'https://github.com/redis/redis-rb'
data/README.md CHANGED
@@ -48,6 +48,9 @@ Adding the resque:scheduler rake task:
48
48
  require 'resque/scheduler/tasks'
49
49
  ```
50
50
 
51
+ In a Rails app, you can skip this step. A Railtie loads the
52
+ `resque:scheduler` rake task and the Resque web UI tabs automatically.
53
+
51
54
  ### Rake integration
52
55
 
53
56
  By default, `resque-scheduler` depends on the "resque:setup" rake task.
@@ -131,6 +134,8 @@ environment variables:
131
134
  * `APP_NAME` - Application name used in procline (`$0`) (default empty)
132
135
  * `BACKGROUND` - [Run in the background](#running-in-the-background) if
133
136
  non-empty (via `Process.daemon`, if supported) (default `false`)
137
+ * `DELAYED_REQUEUE_BATCH_SIZE` - Set the delayed job batch size if enabled (default `100`). If `<= 1`, this disables batching.
138
+ * `DISABLE_DELAYED_REQUEUE_BATCHES` - Disable batched delayed job queuing (default `false`) - [See section below on consequences](#batched-delayed-job-and-resque-enqueue-hooks)
134
139
  * `DYNAMIC_SCHEDULE` - Enables [dynamic scheduling](#dynamic-schedules)
135
140
  if non-empty (default `false`)
136
141
  * `RAILS_ENV` - Environment to use in procline (`$0`) (default empty)
@@ -199,6 +204,19 @@ Resque.enqueue_at_with_queue(
199
204
  )
200
205
  ```
201
206
 
207
+ #### Active Job
208
+
209
+ `ActiveJob::Base` subclasses can be passed to `enqueue_at`, `enqueue_in` and
210
+ their `_with_queue` variants, and used as the `class` of a scheduled job. They
211
+ are handed to `perform_later` (with `set(wait_until:)` for delayed jobs), so
212
+ Active Job must be using the `:resque` adapter. The job's `queue_as` applies
213
+ unless a queue is given.
214
+
215
+ Since the stored class is the adapter's `JobWrapper`, `delayed?`,
216
+ `remove_delayed` and `find_delayed_selection` won't match Active Job classes,
217
+ and [hooks](#hooks) defined on them aren't called. Use Active Job's callbacks
218
+ instead.
219
+
202
220
  The delayed queue is stored in redis and is persisted in the same way the
203
221
  standard resque jobs are persisted (redis writing to disk). Delayed jobs differ
204
222
  from scheduled jobs in that if your scheduler process is down or workers are
@@ -457,6 +475,8 @@ config[:persist] = true
457
475
  Resque.set_schedule(name, config)
458
476
  ```
459
477
 
478
+ `every` also accepts a number of seconds, so `config[:every] = 1.hour` works too.
479
+
460
480
  Schedules can later be removed by passing their name to the `remove_schedule` method:
461
481
 
462
482
  ```ruby
@@ -506,13 +526,18 @@ Similar to the `before_enqueue`- and `after_enqueue`-hooks provided in Resque
506
526
  the delayed queue. If the hook returns `false`, the job will not be placed on
507
527
  the queue.
508
528
  * `after_schedule`: Called with the job args after a job is placed on the
509
- delayed queue. Any exception raised propagates up to the code with queued the
529
+ delayed queue. Any exception raised propagates up to the code that queued the
510
530
  job.
511
531
  * `before_delayed_enqueue`: Called with the job args after the job has been
512
- removed from the delayed queue, but not yet put on a normal queue. It is
532
+ removed from the delayed queue, but not yet put on its work queue. It is
513
533
  called before `before_enqueue`-hooks, and on the same job instance as the
514
534
  `before_enqueue`-hooks will be invoked on. Return values are ignored.
515
- * `on_enqueue_failure`: Called with the job args and the exception that was raised
535
+ * `before_recurring`: Called with the job args before a job from the schedule
536
+ (`cron`, `every` or "Queue now" in resque-web) is put on its work queue. If
537
+ the hook returns `false`, the job will not be enqueued.
538
+ * `after_recurring`: Called with the job args after a job from the schedule has
539
+ been put on its work queue, after the `after_enqueue`-hooks.
540
+ * `on_enqueue_failure`: Called with the job config and the exception that was raised
516
541
  while enqueueing a job to resque or external application fails. Return
517
542
  values are ignored. For example:
518
543
 
@@ -610,6 +635,9 @@ The Delayed tab:
610
635
 
611
636
  ![The Delayed Tab](http://img.skitch.com/20100111-ne4fcqtc5emkcuwc5qtais2kwx.jpg)
612
637
 
638
+ The Delayed tab's search matches job class names, or job arguments when
639
+ "Search Args?" is checked.
640
+
613
641
  #### How do I get the schedule tabs to show up???
614
642
 
615
643
  To get these to show up you need to pass a file to `resque-web` to tell it to
@@ -752,9 +780,38 @@ This table explains the version requirements for rufus-scheduler
752
780
 
753
781
  | resque-scheduler | rufus-scheduler |
754
782
  |:-----------------|----------------:|
783
+ | `>= 5.1` | `~> 3.9` |
755
784
  | `~> 4.0` | `~> 3.0` |
756
785
  | `< 4.0` | `~> 2.0` |
757
786
 
787
+ ##### Batched delayed job and resque enqueue hooks
788
+
789
+ Batching delayed job queuing can speed up when per-second job counts grows,
790
+ avoiding situations that may cause delayed enqueues to fall behind. This
791
+ batching wraps enqueues in a `multi` pipeline, making far fewer roundtrips to
792
+ the server.
793
+
794
+ However, in `redis` gem `>= 4.0`, any operations to Redis within the `multi`
795
+ block must use the multi handle so that the actions are captured. Resque's hooks
796
+ do not currently have a way to pass this around, and so compatibility with other
797
+ Resque plugins or hooks which access Redis at enqueue time is impacted with
798
+ batch mode (A main class of plugins this affects are single runner locks which
799
+ use redis for their locking mechanism). In these cases, you should consider
800
+ disabling the batching by setting the `DISABLE_DELAYED_REQUEUE_BATCHES`
801
+ environment variable to `true`.
802
+
803
+ Detecting when this occurs can be tricky, you must watch for logs
804
+ emitted by your `resque-scheduler` process such as:
805
+
806
+ ```text
807
+ Redis::CommandError: ERR
808
+ MULTI calls can not be nested
809
+ NoMethodError: undefined method nil? for <Redis::Future
810
+ NoMethodError: undefined method `to_i' for <Redis::Future
811
+ ```
812
+
813
+ If you're in this situation, you will also see delayed jobs to not be enqueued
814
+ (they will get dropped). Disabling batching is the workaround.
758
815
 
759
816
  ### Contributing
760
817
 
@@ -16,7 +16,9 @@ module Resque
16
16
  pidfile: 'PIDFILE',
17
17
  poll_sleep_amount: 'RESQUE_SCHEDULER_INTERVAL',
18
18
  verbose: 'VERBOSE',
19
- lock_timeout: 'LOCK_TIMEOUT'
19
+ lock_timeout: 'LOCK_TIMEOUT',
20
+ delayed_requeue_batch_size: 'DELAYED_REQUEUE_BATCH_SIZE',
21
+ disable_delayed_requeue_batches: 'DISABLE_DELAYED_REQUEUE_BATCHES'
20
22
  }.freeze
21
23
 
22
24
  class Cli
@@ -74,6 +76,20 @@ module Resque
74
76
  {
75
77
  args: ['-v', '--verbose', 'Run with verbose output [VERBOSE]'],
76
78
  callback: ->(options) { ->(v) { options[:verbose] = v } }
79
+ },
80
+ {
81
+ args: [
82
+ '--delayed-requeue-batch-size [DELAYED_REQUEUE_BATCH_SIZE]',
83
+ 'Delayed requeue batch size'
84
+ ],
85
+ callback: ->(options) { ->(d) { options[:delayed_requeue_batch_size] = d } }
86
+ },
87
+ {
88
+ args: [
89
+ '--disable-delayed-requeue-batches',
90
+ 'Disable delayed requeue batches [DISABLE_DELAYED_REQUEUE_BATCHES]'
91
+ ],
92
+ callback: ->(options) { ->(d) { options[:disable_delayed_requeue_batches] = d } }
77
93
  }
78
94
  ].freeze
79
95
 
@@ -65,10 +65,23 @@ module Resque
65
65
  @app_name ||= environment['APP_NAME']
66
66
  end
67
67
 
68
+ # Batch size for requeueing delayed jobs. If <= 1, this disables batching.
69
+ # Defaults to 100.
70
+ attr_writer :delayed_requeue_batch_size
71
+
68
72
  def delayed_requeue_batch_size
69
73
  @delayed_requeue_batch_size ||= \
70
- ENV['DELAYED_REQUEUE_BATCH_SIZE'].to_i if environment['DELAYED_REQUEUE_BATCH_SIZE']
71
- @delayed_requeue_batch_size ||= 100
74
+ Integer(environment.fetch('DELAYED_REQUEUE_BATCH_SIZE', '100'))
75
+ end
76
+
77
+ attr_writer :disable_delayed_requeue_batches
78
+
79
+ def disable_delayed_requeue_batches
80
+ if @disable_delayed_requeue_batches.nil?
81
+ to_bool(environment['DISABLE_DELAYED_REQUEUE_BATCHES'])
82
+ else
83
+ @disable_delayed_requeue_batches
84
+ end
72
85
  end
73
86
 
74
87
  # Amount of time in seconds to sleep between polls of the delayed
@@ -10,6 +10,10 @@ module Resque
10
10
  # for queueing. Until timestamp is in the past, the job will
11
11
  # sit in the schedule list.
12
12
  def enqueue_at(timestamp, klass, *args)
13
+ if Resque::Scheduler::Util.active_job_class?(klass)
14
+ return enqueue_at_with_queue(nil, timestamp, klass, *args)
15
+ end
16
+
13
17
  validate(klass)
14
18
  enqueue_at_with_queue(
15
19
  queue_from_class(klass), timestamp, klass, *args
@@ -21,19 +25,29 @@ module Resque
21
25
  # timestamp has passed. It respects Resque.inline option, by
22
26
  # creating the job right away instead of adding to the queue.
23
27
  def enqueue_at_with_queue(queue, timestamp, klass, *args)
24
- plugin.process_schedule_hooks(klass, *args) do
25
- if Resque.inline? || timestamp.to_i <= Time.now.to_i
26
- # Just create the job and let resque perform it right away with
27
- # inline. If the class is a custom job class, call self#scheduled
28
- # on it. This allows you to do things like
29
- # Resque.enqueue_at(timestamp, CustomJobClass, :opt1 => val1).
30
- # Otherwise, pass off to Resque.
31
- if klass.respond_to?(:scheduled)
32
- klass.scheduled(queue, klass.to_s, *args)
33
- else
34
- Resque.enqueue_to(queue, klass, *args)
35
- end
28
+ if Resque::Scheduler::Util.active_job_class?(klass)
29
+ return klass.set(**{ wait_until: timestamp, queue: queue }.compact)
30
+ .perform_later(*args)
31
+ end
32
+
33
+ if Resque.inline? || timestamp.to_i <= Time.now.to_i
34
+ # Just create the job and let resque perform it right away with
35
+ # inline. If the class is a custom job class, call self#scheduled
36
+ # on it. This allows you to do things like
37
+ # Resque.enqueue_at(timestamp, CustomJobClass, :opt1 => val1).
38
+ # Otherwise, pass off to Resque.
39
+ #
40
+ # Note: schedule hooks are NOT called here because the job is not
41
+ # actually being added to the delayed queue.
42
+ if klass.respond_to?(:scheduled)
43
+ klass.scheduled(queue, klass.to_s, *args)
36
44
  else
45
+ Resque.enqueue_to(queue, klass, *args)
46
+ end
47
+ else
48
+ # Job is being added to the delayed queue, so we call the
49
+ # before_schedule and after_schedule hooks.
50
+ plugin.process_schedule_hooks(klass, *args) do
37
51
  delayed_push(timestamp, job_to_hash_with_queue(queue, klass, args))
38
52
  end
39
53
  end
@@ -83,11 +97,13 @@ module Resque
83
97
  # insertion time complexity is O(log(n)). Returns true if it's
84
98
  # the first job to be scheduled at that time, else false.
85
99
  def delayed_push(timestamp, item)
100
+ encoded_job = encode(item)
101
+
86
102
  # First add this item to the list for this timestamp
87
- redis.rpush("delayed:#{timestamp.to_i}", encode(item))
103
+ redis.rpush("delayed:#{timestamp.to_i}", encoded_job)
88
104
 
89
105
  # Store the timestamps at with this item occurs
90
- redis.sadd("timestamps:#{encode(item)}", ["delayed:#{timestamp.to_i}"])
106
+ redis.sadd("timestamps:#{encoded_job}", ["delayed:#{timestamp.to_i}"])
91
107
 
92
108
  # Now, add this timestamp to the zsets. The score and the value are
93
109
  # the same since we'll be querying by timestamp, and we don't have
@@ -171,14 +187,14 @@ module Resque
171
187
  def enqueue_delayed(klass, *args)
172
188
  hash = job_to_hash(klass, args)
173
189
  remove_delayed(klass, *args).times do
174
- Resque::Scheduler.enqueue_from_config(hash)
190
+ Resque::Scheduler.enqueue_from_config(hash, :delayed)
175
191
  end
176
192
  end
177
193
 
178
194
  def enqueue_delayed_with_queue(klass, queue, *args)
179
195
  hash = job_to_hash_with_queue(queue, klass, args)
180
196
  remove_delayed_in_queue(klass, queue, *args).times do
181
- Resque::Scheduler.enqueue_from_config(hash)
197
+ Resque::Scheduler.enqueue_from_config(hash, :delayed)
182
198
  end
183
199
  end
184
200
 
@@ -261,12 +277,18 @@ module Resque
261
277
  count
262
278
  end
263
279
 
280
+ # Counts all jobs across all delayed timestamps.
281
+ # Uses pipelined LLEN calls in batches to avoid blocking the Redis thread
282
+ # for too long while being significantly faster than sequential calls.
264
283
  def count_all_scheduled_jobs
265
- total_jobs = 0
266
- Array(redis.zrange(:delayed_queue_schedule, 0, -1)).each do |ts|
267
- total_jobs += redis.llen("delayed:#{ts}").to_i
284
+ timestamps = Array(redis.zrange(:delayed_queue_schedule, 0, -1))
285
+ return 0 if timestamps.empty?
286
+
287
+ timestamps.each_slice(10_000).sum do |batch|
288
+ redis.pipelined do |pipeline|
289
+ batch.each { |ts| pipeline.llen("delayed:#{ts}") }
290
+ end.sum
268
291
  end
269
- total_jobs
270
292
  end
271
293
 
272
294
  # Discover if a job has been delayed.
@@ -30,18 +30,18 @@ module Resque
30
30
  def setup_backgrounding
31
31
  return unless options[:background]
32
32
 
33
- # Need to set this here for conditional Process.daemon redirect of
34
- # stderr/stdout to /dev/null
35
- Resque::Scheduler.quiet = if options.key?(:quiet)
36
- !!options[:quiet]
37
- else
38
- true
39
- end
33
+ Resque::Scheduler.quiet = background_quiet?
40
34
 
41
35
  Process.daemon(true, !Resque::Scheduler.quiet)
42
36
  Resque.redis.reconnect
43
37
  end
44
38
 
39
+ def background_quiet?
40
+ return !!options[:quiet] if options.key?(:quiet)
41
+ return false if options.key?(:logfile)
42
+ !!options[:background]
43
+ end
44
+
45
45
  def setup_pid_file
46
46
  return unless options[:pidfile]
47
47
 
@@ -74,6 +74,14 @@ module Resque
74
74
  end
75
75
 
76
76
  c.verbose = !!options[:verbose] if options.key?(:verbose)
77
+
78
+ if options.key?(:delayed_requeue_batch_size)
79
+ c.delayed_requeue_batch_size = Integer(options[:delayed_requeue_batch_size])
80
+ end
81
+
82
+ if options.key?(:disable_delayed_requeue_batches)
83
+ c.disable_delayed_requeue_batches = !!options[:disable_delayed_requeue_batches]
84
+ end
77
85
  end
78
86
  end
79
87
  # rubocop:enable Metrics/AbcSize
@@ -3,6 +3,8 @@
3
3
  module Resque
4
4
  module Scheduler
5
5
  module Plugin
6
+ ENQUEUE_HOOKS = %i[delayed recurring].freeze
7
+
6
8
  def self.process_schedule_hooks(klass, *args)
7
9
  # the documentation states that if any before_schedule hook returns
8
10
  # false, the process should not be enqueued
@@ -12,6 +14,32 @@ module Resque
12
14
  run_after_schedule_hooks(klass, *args)
13
15
  end
14
16
 
17
+ def self.process_recurring_hooks(klass, *args)
18
+ return unless run_before_recurring_hooks(klass, *args)
19
+
20
+ enqueued = yield
21
+ run_after_recurring_hooks(klass, *args) if enqueued
22
+ enqueued
23
+ end
24
+
25
+ def self.validate_enqueue_hooks(hooks)
26
+ return if hooks.nil? || ENQUEUE_HOOKS.include?(hooks)
27
+
28
+ raise ArgumentError, "unknown hooks: #{hooks.inspect}"
29
+ end
30
+
31
+ def self.process_enqueue_hooks(hooks, klass, *args, &block)
32
+ case hooks
33
+ when :delayed
34
+ run_before_delayed_enqueue_hooks(klass, *args)
35
+ yield
36
+ when :recurring
37
+ process_recurring_hooks(klass, *args, &block)
38
+ else
39
+ yield
40
+ end
41
+ end
42
+
15
43
  def self.hooks(job, pattern)
16
44
  job.methods.grep(/^#{pattern}/).sort
17
45
  end
@@ -35,6 +63,14 @@ module Resque
35
63
  def self.run_after_schedule_hooks(klass, *args)
36
64
  run_hooks(klass, 'after_schedule', *args)
37
65
  end
66
+
67
+ def self.run_before_recurring_hooks(klass, *args)
68
+ run_hooks(klass, 'before_recurring', *args)
69
+ end
70
+
71
+ def self.run_after_recurring_hooks(klass, *args)
72
+ run_hooks(klass, 'after_recurring', *args)
73
+ end
38
74
  end
39
75
  end
40
76
  end
@@ -0,0 +1,11 @@
1
+ require 'resque/scheduler/server'
2
+
3
+ module Resque
4
+ module Scheduler
5
+ class Railtie < Rails::Railtie
6
+ rake_tasks do
7
+ require 'resque/scheduler/tasks'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -12,7 +12,7 @@
12
12
  <% jobs = resque.delayed_timestamp_peek(timestamp, start, 20) %>
13
13
  <% jobs.each do |job| %>
14
14
  <tr>
15
- <td class='class'><%= job['class'] %></td>
15
+ <td class='class'><%= h job['class'] %></td>
16
16
  <td class='args'><%= h show_job_arguments(job['args']) %></td>
17
17
  </tr>
18
18
  <% end %>
@@ -1,4 +1,4 @@
1
- <h1><%= @job_name %></h1>
1
+ <h1><%= h @job_name %></h1>
2
2
 
3
3
  <p class='intro'>
4
4
  This job requires parameters:
@@ -10,14 +10,14 @@
10
10
  <% value ||= {} %>
11
11
  <tr>
12
12
  <td>
13
- <%= key %>
13
+ <%= h key %>
14
14
  <% if value['description'] || value[:description] %>
15
- <span style="border-bottom:1px dotted;" title="<%=value ['description'] || value[:description] %>">(?)</span>
15
+ <span style="border-bottom:1px dotted;" title="<%= h(value['description'] || value[:description]) %>">(?)</span>
16
16
  <% end %>:
17
17
  </td>
18
- <td><input type="text" name="<%= key %>" value="<%= value['default'] || value[:default] %>"></td>
18
+ <td><input type="text" name="<%= h key %>" value="<%= h(value['default'] || value[:default]) %>"></td>
19
19
  <% end %>
20
20
  </table>
21
- <input type="hidden" name="job_name" value="<%= @job_name %>">
21
+ <input type="hidden" name="job_name" value="<%= h @job_name %>">
22
22
  <input type="submit" value="Queue now">
23
23
  </form>
@@ -4,8 +4,8 @@
4
4
  The list below contains all scheduled jobs. Click &quot;Queue now&quot; to queue
5
5
  a job immediately.
6
6
  <br/> Server local time: <%= Time.now %>
7
- <br/> Server Environment: <%= Resque::Scheduler.env %>
8
- <br/> Current master: <%= Resque.redis.get(Resque::Scheduler.master_lock.key) %>
7
+ <br/> Server Environment: <%= h Resque::Scheduler.env %>
8
+ <br/> Current master: <%= h Resque.redis.get(Resque::Scheduler.master_lock.key) %>
9
9
  </p>
10
10
  <p class='intro'>
11
11
  The highlighted jobs are skipped for current environment.
@@ -22,13 +22,13 @@
22
22
  <td style="padding-top: 12px; padding-bottom: 2px; width: 10px">
23
23
  <form action="<%= u "/delayed/cancel_now" %>" method="post">
24
24
  <input type="hidden" name="timestamp" value="<%= job['timestamp'].to_i %>">
25
- <input type="hidden" name="klass" value="<%= job['class'] %>">
25
+ <input type="hidden" name="klass" value="<%= h job['class'] %>">
26
26
  <input type="hidden" name="args" value="<%= h(Resque.encode job['args']) %>">
27
27
  <input type="submit" value="Cancel Job">
28
28
  </form>
29
29
  </td>
30
30
  <td class='args'><%= format_time(Time.at(job['timestamp'])) %></td>
31
- <td class='class'><%= job['class'] %></td>
31
+ <td class='class'><%= h job['class'] %></td>
32
32
  <td class='args'><%= h job['args'].inspect %></td>
33
33
  </tr>
34
34
  <% end %>
@@ -44,8 +44,8 @@
44
44
  </tr>
45
45
  <% queued.each do |job| %>
46
46
  <tr>
47
- <td class='class'><%= job['queue'] %></td>
48
- <td class='class'><%= job['class'] %></td>
47
+ <td class='class'><%= h job['queue'] %></td>
48
+ <td class='class'><%= h job['class'] %></td>
49
49
  <td class='args'><%= h job['args'].inspect %></td>
50
50
  </tr>
51
51
  <% end %>
@@ -61,8 +61,8 @@
61
61
  </tr>
62
62
  <% working.each do |job| %>
63
63
  <tr>
64
- <td class='class'><%= job['queue'] %></td>
65
- <td class='class'><%= job['class'] %></td>
64
+ <td class='class'><%= h job['queue'] %></td>
65
+ <td class='class'><%= h job['class'] %></td>
66
66
  <td class='args'><%= h job['args'].inspect %></td>
67
67
  </tr>
68
68
  <% end %>
@@ -1,4 +1,6 @@
1
1
  <form method="POST" action="<%= u 'delayed/search' %>">
2
+ <input type='checkbox' id='search_args' name='search_args' value="checked" <%= 'checked' if params[:search_args] == 'checked' %>/>
3
+ <label for="search_args"> Search Args?</label><br>
2
4
  <input type='input' name='search' value="<%= h params[:search] %>"/>
3
5
  <input type='submit' value='Search'/>
4
6
  </form>
@@ -48,7 +48,7 @@ module Resque
48
48
  if @parameters
49
49
  erb scheduler_template('requeue-params')
50
50
  else
51
- Resque::Scheduler.enqueue_from_config(config)
51
+ Resque::Scheduler.enqueue_from_config(config, :recurring)
52
52
  redirect u('/overview')
53
53
  end
54
54
  end
@@ -68,7 +68,7 @@ module Resque
68
68
 
69
69
  # Insert the args hash into config and queue the resque job
70
70
  config = config.merge('args' => config_args)
71
- Resque::Scheduler.enqueue_from_config(config)
71
+ Resque::Scheduler.enqueue_from_config(config, :recurring)
72
72
  redirect u('/overview')
73
73
  end
74
74
 
@@ -99,7 +99,8 @@ module Resque
99
99
  end
100
100
 
101
101
  def delayed_search
102
- @jobs = find_job(params[:search])
102
+ field = params[:search_args] == 'checked' ? 'args' : 'class'
103
+ @jobs = find_job(params[:search], field)
103
104
  erb scheduler_template('search')
104
105
  end
105
106
 
@@ -153,21 +154,21 @@ module Resque
153
154
  )
154
155
  end
155
156
 
156
- def find_job(worker)
157
- worker = worker.downcase
158
- results = working_jobs_for_worker(worker)
157
+ def find_job(term, field = 'class')
158
+ term = term.downcase
159
+ matches = ->(job) { job[field].to_s.downcase.include?(term) }
160
+ results = working_jobs_matching(matches)
159
161
 
160
- dels = delayed_jobs_for_worker(worker)
162
+ dels = delayed_jobs_for_worker(term)
161
163
  results += dels.select do |j|
162
- j['class'].downcase.include?(worker) &&
163
- j.merge!('where_at' => 'delayed')
164
+ matches.call(j) && j.merge!('where_at' => 'delayed')
164
165
  end
165
166
 
166
167
  Resque.queues.each do |queue|
167
168
  queued = Resque.peek(queue, 0, Resque.size(queue))
168
169
  queued = [queued] unless queued.is_a?(Array)
169
170
  results += queued.select do |j|
170
- j['class'].downcase.include?(worker) &&
171
+ matches.call(j) &&
171
172
  j.merge!('queue' => queue, 'where_at' => 'queued')
172
173
  end
173
174
  end
@@ -187,13 +188,13 @@ module Resque
187
188
 
188
189
  def schedule_interval_every(every)
189
190
  every = [*every]
190
- s = 'every: ' << every.first
191
+ s = 'every: ' << "#{every.first}#{'s' if every.first.is_a?(Numeric)}"
191
192
 
192
193
  return s unless every.length > 1
193
194
 
194
195
  s << ' ('
195
196
  meta = every.last.map do |key, value|
196
- "#{key.to_s.tr('_', ' ')} #{value}"
197
+ "#{key.to_s.tr('_', ' ')} #{value}#{'s' if value.is_a?(Numeric)}"
197
198
  end
198
199
  s << meta.join(', ') << ')'
199
200
  end
@@ -228,12 +229,11 @@ module Resque
228
229
 
229
230
  private
230
231
 
231
- def working_jobs_for_worker(worker)
232
+ def working_jobs_matching(matches)
232
233
  [].tap do |results|
233
234
  working = [*Resque.working]
234
235
  work = working.select do |w|
235
- w.job && w.job['payload'] &&
236
- w.job['payload']['class'].downcase.include?(worker)
236
+ w.job && w.job['payload'] && matches.call(w.job['payload'])
237
237
  end
238
238
  work.each do |w|
239
239
  results += [
@@ -34,6 +34,11 @@ module Resque
34
34
  def self.classify(dashed_word)
35
35
  dashed_word.split('-').map(&:capitalize).join
36
36
  end
37
+
38
+ def self.active_job_class?(klass)
39
+ defined?(::ActiveJob::Base) && klass.is_a?(Class) &&
40
+ klass < ::ActiveJob::Base
41
+ end
37
42
  end
38
43
  end
39
44
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Resque
4
4
  module Scheduler
5
- VERSION = '5.0.0'.freeze
5
+ VERSION = '5.1.0'.freeze
6
6
  end
7
7
  end
@@ -1,6 +1,6 @@
1
1
  # vim:fileencoding=utf-8
2
2
 
3
- require 'redis/errors'
3
+ require 'redis'
4
4
  require 'rufus/scheduler'
5
5
  require_relative 'scheduler/configuration'
6
6
  require_relative 'scheduler/locking'
@@ -8,6 +8,8 @@ require_relative 'scheduler/logger_builder'
8
8
  require_relative 'scheduler/signal_handling'
9
9
  require_relative 'scheduler/failure_handler'
10
10
 
11
+ require 'resque/scheduler/railtie' if defined?(Rails::Railtie)
12
+
11
13
  module Resque
12
14
  module Scheduler
13
15
  autoload :Cli, 'resque/scheduler/cli'
@@ -49,6 +51,7 @@ module Resque
49
51
  $stderr.sync = true
50
52
 
51
53
  was_master = nil
54
+ schedule_loaded = false
52
55
 
53
56
  begin
54
57
  @th = Thread.current
@@ -61,8 +64,14 @@ module Resque
61
64
  if am_master != was_master
62
65
  procline am_master ? 'Master scheduler' : 'Child scheduler'
63
66
 
64
- # Load schedule because changed
65
- reload_schedule!
67
+ # The first pass always loads the schedule - load_schedule!
68
+ # reads am_master, so it cannot run before the check above.
69
+ # After that, only a dynamic schedule can have changed while
70
+ # we were not master.
71
+ if dynamic || !schedule_loaded
72
+ reload_schedule!
73
+ schedule_loaded = true
74
+ end
66
75
  end
67
76
 
68
77
  if am_master
@@ -144,12 +153,17 @@ module Resque
144
153
  interval_defined = false
145
154
  interval_types = %w(cron every)
146
155
  interval_types.each do |interval_type|
147
- next unless !config[interval_type].nil? && !config[interval_type].empty?
156
+ next unless !config[interval_type].nil? && !config[interval_type].to_s.empty?
148
157
  args = optionizate_interval_value(config[interval_type])
149
158
  args = [args, nil, job: true] if args.is_a?(::String)
150
159
 
151
- job = rufus_scheduler.send(interval_type, *args) do
152
- enqueue_recurring(name, config)
160
+ begin
161
+ job = rufus_scheduler.send(interval_type, *args) do
162
+ enqueue_recurring(name, config)
163
+ end
164
+ rescue ArgumentError => e
165
+ log_error "Invalid #{interval_type} schedule for #{name}: #{e.message} - skipping"
166
+ return
153
167
  end
154
168
  @scheduled_jobs[name] = job
155
169
  interval_defined = true
@@ -198,31 +212,42 @@ module Resque
198
212
 
199
213
  if item
200
214
  log "queuing #{item['class']} [delayed]"
201
- enqueue(item)
215
+ enqueue(item, :delayed)
202
216
  end
203
217
 
204
218
  item
205
219
  end
206
220
 
221
+ def batch_delayed_items?
222
+ !disable_delayed_requeue_batches && delayed_requeue_batch_size > 1
223
+ end
224
+
207
225
  # Enqueues all delayed jobs for a timestamp
208
226
  def enqueue_delayed_items_for_timestamp(timestamp)
209
227
  count = 0
210
- batch_size = delayed_requeue_batch_size
211
- actual_batch_size = nil
228
+ batch_size = batch_delayed_items? ? delayed_requeue_batch_size : 1
212
229
 
213
- log "Processing delayed items for timestamp #{timestamp}, in batches of #{batch_size}"
230
+ message = "Processing delayed items for timestamp #{timestamp}"
231
+ message += ", in batches of #{batch_size}" if batch_delayed_items?
232
+ log message
214
233
 
215
234
  loop do
235
+ actual_batch_size = 0
236
+
216
237
  handle_shutdown do
217
238
  # Continually check that it is still the master
218
239
  if am_master
219
- actual_batch_size = enqueue_items_in_batch_for_timestamp(timestamp,
220
- batch_size)
240
+ if batch_delayed_items?
241
+ actual_batch_size = enqueue_items_in_batch_for_timestamp(timestamp, batch_size)
242
+ log "queued batch of #{actual_batch_size} jobs" if actual_batch_size != -1
243
+ else
244
+ item = enqueue_next_item(timestamp)
245
+ actual_batch_size = item.nil? ? 0 : 1
246
+ end
221
247
  end
222
248
  end
223
249
 
224
250
  count += actual_batch_size
225
- log "queued #{count} jobs" if actual_batch_size != -1
226
251
 
227
252
  # continue processing until there are no more items in this
228
253
  # timestamp. If we don't have a full batch, this is the last one.
@@ -231,7 +256,7 @@ module Resque
231
256
  break if actual_batch_size < batch_size
232
257
  end
233
258
 
234
- log "finished queueing #{count} total jobs for timestamp #{timestamp}" if count != -1
259
+ log "finished queueing #{count} total jobs for timestamp #{timestamp}" if count > 0
235
260
  end
236
261
 
237
262
  def timestamp_key(timestamp)
@@ -255,7 +280,7 @@ module Resque
255
280
  pipeline.srem("timestamps:#{encoded_job}", timestamp_bucket_key)
256
281
 
257
282
  decoded_job = Resque.decode(encoded_job)
258
- enqueue(decoded_job)
283
+ enqueue(decoded_job, :delayed)
259
284
  end
260
285
 
261
286
  pipeline.ltrim(timestamp_bucket_key, batch_size, -1)
@@ -282,8 +307,12 @@ module Resque
282
307
  encoded_jobs_to_requeue.count
283
308
  end
284
309
 
285
- def enqueue(config)
286
- enqueue_from_config(config)
310
+ # Enqueues a job like enqueue_from_config, but hands errors to the failure
311
+ # handler instead of raising them. Pass :delayed as hooks for a job that
312
+ # left the delayed queue or :recurring for a schedule entry to run the
313
+ # matching hooks.
314
+ def enqueue(config, hooks = nil)
315
+ enqueue_from_config(config, hooks)
287
316
  rescue => e
288
317
  Resque::Scheduler.failure_handler.on_enqueue_failure(config, e)
289
318
  end
@@ -294,8 +323,11 @@ module Resque
294
323
  exit if @shutdown
295
324
  end
296
325
 
297
- # Enqueues a job based on a config hash
298
- def enqueue_from_config(job_config)
326
+ # Enqueues a job based on a config hash. Pass :delayed as hooks for a job
327
+ # that left the delayed queue or :recurring for a schedule entry to run
328
+ # the matching hooks.
329
+ def enqueue_from_config(job_config, hooks = nil)
330
+ Resque::Scheduler::Plugin.validate_enqueue_hooks(hooks)
299
331
  args = job_config['args'] || job_config[:args]
300
332
 
301
333
  klass_name = job_config['class'] || job_config[:class]
@@ -306,9 +338,13 @@ module Resque
306
338
  end
307
339
 
308
340
  params = args.is_a?(Hash) ? [args] : Array(args)
309
- queue = job_config['queue'] ||
310
- job_config[:queue] ||
311
- Resque.queue_from_class(klass)
341
+ queue = job_config['queue'] || job_config[:queue]
342
+
343
+ if Resque::Scheduler::Util.active_job_class?(klass)
344
+ return klass.set(**{ queue: queue }.compact).perform_later(*params)
345
+ end
346
+
347
+ queue ||= Resque.queue_from_class(klass)
312
348
  # Support custom job classes like those that inherit from
313
349
  # Resque::JobWithStatus (resque-status)
314
350
  job_klass = job_config['custom_job_class']
@@ -331,8 +367,7 @@ module Resque
331
367
  # for non-existent classes (for example: running scheduler in
332
368
  # one app that schedules for another.
333
369
  if Class === klass
334
- Resque::Scheduler::Plugin.process_schedule_hooks(klass, *params) do
335
- Resque::Scheduler::Plugin.run_before_delayed_enqueue_hooks(klass, *params)
370
+ Resque::Scheduler::Plugin.process_enqueue_hooks(hooks, klass, *params) do
336
371
  # If the class is a custom job class, call self#scheduled on it.
337
372
  # This allows you to do things like Resque.enqueue_at(timestamp,
338
373
  # CustomJobClass). Otherwise, pass off to Resque.
@@ -492,7 +527,7 @@ module Resque
492
527
  def enqueue_recurring(name, config)
493
528
  if am_master
494
529
  log! "queueing #{config['class']} (#{name})"
495
- enqueue(config)
530
+ enqueue(config, :recurring)
496
531
  Resque.last_enqueued_at(name, Time.now.to_s)
497
532
  end
498
533
  end
@@ -32,7 +32,7 @@ Gem::Specification.new do |spec|
32
32
  spec.license = 'MIT'
33
33
  spec.metadata['rubygems_mfa_required'] = 'true'
34
34
 
35
- spec.required_ruby_version = '>= 3.0.0'
35
+ spec.required_ruby_version = '>= 3.2.0'
36
36
 
37
37
  spec.files = `git ls-files -z`.split("\0").reject do |f|
38
38
  f.match(%r{^(test|spec|features|examples|bin|tasks)/}) ||
@@ -43,6 +43,7 @@ Gem::Specification.new do |spec|
43
43
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
44
44
  spec.require_paths = %w(lib)
45
45
 
46
+ spec.add_development_dependency 'activejob'
46
47
  spec.add_development_dependency 'bundler'
47
48
  spec.add_development_dependency 'json'
48
49
  spec.add_development_dependency 'irb'
@@ -59,14 +60,12 @@ Gem::Specification.new do |spec|
59
60
 
60
61
  # We pin rubocop because new cops have a tendency to result in false-y
61
62
  # positives for new contributors, which is not a nice experience.
62
- spec.add_development_dependency 'rubocop', '~> 0.80'
63
+ spec.add_development_dependency 'rubocop', '~> 1.91'
63
64
 
64
65
  spec.add_runtime_dependency 'base64', '~> 0.1'
65
66
  spec.add_runtime_dependency 'logger'
66
67
  spec.add_runtime_dependency 'mono_logger', '~> 1.0'
67
68
  spec.add_runtime_dependency 'redis', '>= 4.0'
68
69
  spec.add_runtime_dependency 'resque', '>= 3.0'
69
- # rufus-scheduler v3.7 causes a failure in test/multi_process_test.rb
70
- # rufus-scheduler v3.3 is missing a to_local method which fails tests
71
- spec.add_runtime_dependency 'rufus-scheduler', '~> 3.2', '!= 3.3'
70
+ spec.add_runtime_dependency 'rufus-scheduler', '~> 3.9'
72
71
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: resque-scheduler
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.0
4
+ version: 5.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben VandenBos
@@ -10,11 +10,24 @@ authors:
10
10
  - Dan Buch
11
11
  - Michael Bianco
12
12
  - Patrick Tulskie
13
- autorequire:
14
13
  bindir: exe
15
14
  cert_chain: []
16
- date: 2026-01-15 00:00:00.000000000 Z
15
+ date: 1980-01-02 00:00:00.000000000 Z
17
16
  dependencies:
17
+ - !ruby/object:Gem::Dependency
18
+ name: activejob
19
+ requirement: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: '0'
24
+ type: :development
25
+ prerelease: false
26
+ version_requirements: !ruby/object:Gem::Requirement
27
+ requirements:
28
+ - - ">="
29
+ - !ruby/object:Gem::Version
30
+ version: '0'
18
31
  - !ruby/object:Gem::Dependency
19
32
  name: bundler
20
33
  requirement: !ruby/object:Gem::Requirement
@@ -203,14 +216,14 @@ dependencies:
203
216
  requirements:
204
217
  - - "~>"
205
218
  - !ruby/object:Gem::Version
206
- version: '0.80'
219
+ version: '1.91'
207
220
  type: :development
208
221
  prerelease: false
209
222
  version_requirements: !ruby/object:Gem::Requirement
210
223
  requirements:
211
224
  - - "~>"
212
225
  - !ruby/object:Gem::Version
213
- version: '0.80'
226
+ version: '1.91'
214
227
  - !ruby/object:Gem::Dependency
215
228
  name: base64
216
229
  requirement: !ruby/object:Gem::Requirement
@@ -287,20 +300,14 @@ dependencies:
287
300
  requirements:
288
301
  - - "~>"
289
302
  - !ruby/object:Gem::Version
290
- version: '3.2'
291
- - - "!="
292
- - !ruby/object:Gem::Version
293
- version: '3.3'
303
+ version: '3.9'
294
304
  type: :runtime
295
305
  prerelease: false
296
306
  version_requirements: !ruby/object:Gem::Requirement
297
307
  requirements:
298
308
  - - "~>"
299
309
  - !ruby/object:Gem::Version
300
- version: '3.2'
301
- - - "!="
302
- - !ruby/object:Gem::Version
303
- version: '3.3'
310
+ version: '3.9'
304
311
  description: |2
305
312
  Light weight job scheduling on top of Resque.
306
313
  Adds methods enqueue_at/enqueue_in to schedule jobs in the future.
@@ -322,9 +329,6 @@ files:
322
329
  - ".devcontainer/devcontainer.json"
323
330
  - ".devcontainer/docker-compose.yml"
324
331
  - ".github/dependabot.yml"
325
- - ".github/funding.yml"
326
- - ".github/workflows/codeql-analysis.yml"
327
- - ".github/workflows/rubocop.yml"
328
332
  - ".github/workflows/ruby.yml"
329
333
  - AUTHORS.md
330
334
  - CHANGELOG.md
@@ -350,6 +354,7 @@ files:
350
354
  - lib/resque/scheduler/locking.rb
351
355
  - lib/resque/scheduler/logger_builder.rb
352
356
  - lib/resque/scheduler/plugin.rb
357
+ - lib/resque/scheduler/railtie.rb
353
358
  - lib/resque/scheduler/scheduling_extensions.rb
354
359
  - lib/resque/scheduler/server.rb
355
360
  - lib/resque/scheduler/server/views/delayed.erb
@@ -369,7 +374,6 @@ licenses:
369
374
  - MIT
370
375
  metadata:
371
376
  rubygems_mfa_required: 'true'
372
- post_install_message:
373
377
  rdoc_options: []
374
378
  require_paths:
375
379
  - lib
@@ -377,15 +381,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
377
381
  requirements:
378
382
  - - ">="
379
383
  - !ruby/object:Gem::Version
380
- version: 3.0.0
384
+ version: 3.2.0
381
385
  required_rubygems_version: !ruby/object:Gem::Requirement
382
386
  requirements:
383
387
  - - ">="
384
388
  - !ruby/object:Gem::Version
385
389
  version: '0'
386
390
  requirements: []
387
- rubygems_version: 3.0.3.1
388
- signing_key:
391
+ rubygems_version: 4.0.16
389
392
  specification_version: 4
390
393
  summary: Light weight job scheduling on top of Resque
391
394
  test_files: []
data/.github/funding.yml DELETED
@@ -1,4 +0,0 @@
1
- # Docs for this file can be found here:
2
- # https://docs.github.com/en/github/administering-a-repository/displaying-a-sponsor-button-in-your-repository
3
-
4
- github: ["iloveitaly"]
@@ -1,59 +0,0 @@
1
- name: "CodeQL"
2
-
3
- on:
4
- push:
5
- branches: [ master, '*-stable' ]
6
- pull_request:
7
- # The branches below must be a subset of the branches above
8
- branches: [ master ]
9
- schedule:
10
- - cron: '44 16 * * 6'
11
-
12
- jobs:
13
- analyze:
14
- name: Analyze
15
- runs-on: ubuntu-latest
16
- permissions:
17
- actions: read
18
- contents: read
19
- security-events: write
20
-
21
- strategy:
22
- fail-fast: false
23
- matrix:
24
- language: [ 'ruby' ]
25
- # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
26
- # Learn more about CodeQL language support at https://git.io/codeql-language-support
27
-
28
- steps:
29
- - name: Checkout repository
30
- uses: actions/checkout@v4
31
-
32
- # Initializes the CodeQL tools for scanning.
33
- - name: Initialize CodeQL
34
- uses: github/codeql-action/init@v3
35
- with:
36
- languages: ${{ matrix.language }}
37
- # If you wish to specify custom queries, you can do so here or in a config file.
38
- # By default, queries listed here will override any specified in a config file.
39
- # Prefix the list here with "+" to use these queries and those in the config file.
40
- # queries: ./path/to/local/query, your-org/your-repo/queries@main
41
-
42
- # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
43
- # If this step fails, then you should remove it and run the build manually (see below)
44
- - name: Autobuild
45
- uses: github/codeql-action/autobuild@v3
46
-
47
- # ℹ️ Command-line programs to run using the OS shell.
48
- # 📚 https://git.io/JvXDl
49
-
50
- # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
51
- # and modify them (or add more) to build your code if your project
52
- # uses a compiled language
53
-
54
- #- run: |
55
- # make bootstrap
56
- # make release
57
-
58
- - name: Perform CodeQL Analysis
59
- uses: github/codeql-action/analyze@v3
@@ -1,27 +0,0 @@
1
- name: Rubocop
2
-
3
- on:
4
- push:
5
- branches: [master]
6
- pull_request:
7
- branches: [master]
8
-
9
- jobs:
10
- rubocop:
11
- name: Rubocop
12
- runs-on: ${{ matrix.os }}
13
- strategy:
14
- matrix:
15
- os: [ubuntu-latest]
16
- ruby: [
17
- 3.4
18
- ]
19
-
20
- steps:
21
- - uses: actions/checkout@v4
22
- - uses: ruby/setup-ruby@v1
23
- with:
24
- ruby-version: ${{ matrix.ruby }}
25
- bundler-cache: true
26
- - name: Ruby linter
27
- run: bundle exec rubocop