sidekiq-scheduler 5.0.3 → 5.0.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 +4 -4
- data/CHANGELOG.md +16 -0
- data/README.md +8 -3
- data/lib/sidekiq-scheduler/config.rb +11 -0
- data/lib/sidekiq-scheduler/extensions/web.rb +19 -6
- data/lib/sidekiq-scheduler/job_presenter.rb +1 -1
- data/lib/sidekiq-scheduler/manager.rb +9 -0
- data/lib/sidekiq-scheduler/redis_manager.rb +3 -3
- data/lib/sidekiq-scheduler/scheduler.rb +18 -2
- data/lib/sidekiq-scheduler/sidekiq_adapter.rb +1 -0
- data/lib/sidekiq-scheduler/utils.rb +34 -0
- data/lib/sidekiq-scheduler/version.rb +1 -1
- data/web/locales/cs.yml +1 -0
- data/web/locales/de.yml +1 -0
- data/web/locales/en.yml +1 -0
- data/web/locales/es.yml +1 -0
- data/web/locales/fr.yml +1 -0
- data/web/locales/gd.yml +1 -0
- data/web/locales/it.yml +1 -0
- data/web/locales/ja.yml +1 -0
- data/web/locales/nl.yml +1 -0
- data/web/locales/pl.yml +1 -0
- data/web/locales/pt-BR.yml +2 -1
- data/web/locales/ru.yml +1 -0
- data/web/locales/sv.yml +1 -0
- data/web/locales/zh-cn.yml +1 -0
- data/web/views/recurring_jobs.erb +3 -38
- metadata +12 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 17e9ebdeaa24ce323d7b3f94c7481d741e0f0f6d3daeded5eaef94f8b1e09f37
|
4
|
+
data.tar.gz: c8e1d62c30406b7928444bdd05a0668aac6bbb47c1bc6a752ff795dbf67b8bb2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f94c5008ef8a2ec39837adc8a25145b27c15abf2514ba22f9c2341b1306431aecfcceb877f8b07fe05c0daa09e26e6b4df3f6e10c118893fe78b11ca6b6751cb
|
7
|
+
data.tar.gz: 61e7f37615f1c575d51c0e55593c4a82362829e21533182e60ec7a125fe76273e44bc055f6449a43185c083370eb1e416a0f1031671e537b6b85cec0ff3a218b
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
# 5.0.4
|
2
|
+
- [**FIX**] Ensure rufus-scheduler has a default rufus_scheduler_options value [#434](https://github.com/sidekiq-scheduler/sidekiq-scheduler/issues/426)
|
3
|
+
- [**ENHANCEMENT**] Remove code related to sidekiq < 6 [#443](https://github.com/sidekiq-scheduler/sidekiq-scheduler/pull/443)
|
4
|
+
- [**ENHANCEMENT**] Change cache-control to `private` [#446](https://github.com/sidekiq-scheduler/sidekiq-scheduler/pull/446)
|
5
|
+
- [**ENHANCEMENT**] Increase compatibility range with tilt dependency [#458](https://github.com/sidekiq-scheduler/sidekiq-scheduler/pull/458)
|
6
|
+
- [**ENHANCEMENT**] Ensure we support Ruby 3.3 [#461](https://github.com/sidekiq-scheduler/sidekiq-scheduler/pull/461)
|
7
|
+
- [**ENHANCEMENT**] Use Redis MULTI command (https://github.com/sidekiq-scheduler/sidekiq-scheduler/pull/464)
|
8
|
+
- [**ENHANCEMENT**] Don't attempt to set jon next_time when job is nil [#466](https://github.com/sidekiq-scheduler/sidekiq-scheduler/pull/466)
|
9
|
+
- [**ENHANCEMENT**] Improvements to prevent jobs been enqueued multiple times due to a delay in job execution [#463](https://github.com/sidekiq-scheduler/sidekiq-scheduler/pull/463)
|
10
|
+
- [**FIX**] Prevent stack level too deep error by implementing `to_hash` method [#470](https://github.com/sidekiq-scheduler/sidekiq-scheduler/pull/470)
|
11
|
+
- [**ENHANCEMENT**] Support new Sidekiq model for registering UI plugins [#472](https://github.com/sidekiq-scheduler/sidekiq-scheduler/pull/472)
|
12
|
+
- [**ENHANCEMENT**] Stop testing against Ruby 2.7 and 3.0 [#472](https://github.com/sidekiq-scheduler/sidekiq-scheduler/pull/472#discussion_r1663197863)
|
13
|
+
- [**ENHANCEMENT**] Display `at` and `in` in the dashboard [#291](https://github.com/sidekiq-scheduler/sidekiq-scheduler/pull/291)
|
14
|
+
- [**ENHANCEMENT**] Docs enhancements [#442](https://github.com/sidekiq-scheduler/sidekiq-scheduler/pull/442), [#449](https://github.com/sidekiq-scheduler/sidekiq-scheduler/pull/449), [#457](https://github.com/sidekiq-scheduler/sidekiq-scheduler/pull/457), [#465](https://github.com/sidekiq-scheduler/sidekiq-scheduler/pull/465), [58e1835](https://github.com/sidekiq-scheduler/sidekiq-scheduler/commit/58e18351054fc3c264b2b5a684173316f674c386)
|
15
|
+
|
16
|
+
|
1
17
|
# 5.0.3
|
2
18
|
|
3
19
|
- [**FIX**] Fix "uppercase character in header name: Cache-Control" [#432](https://github.com/sidekiq-scheduler/sidekiq-scheduler/pull/432)
|
data/README.md
CHANGED
@@ -45,6 +45,8 @@ class HelloWorld
|
|
45
45
|
end
|
46
46
|
```
|
47
47
|
|
48
|
+
__Note:__ In Sidekiq v6.3 `Sidekiq::Job` was introduced as an alias for `Sidekiq::Worker`. `Sidekiq::Worker` has been officially deprecated in Sidekiq v7 although it still exists for backwards compatibility. It is therefore recommended to use `include Sidekiq::Job` in the above example unless an older version of Sidekiq is required.
|
49
|
+
|
48
50
|
``` yaml
|
49
51
|
# config/sidekiq.yml
|
50
52
|
|
@@ -55,6 +57,9 @@ end
|
|
55
57
|
class: HelloWorld
|
56
58
|
```
|
57
59
|
|
60
|
+
> [!NOTE]
|
61
|
+
> sidekiq-scheduler uses [fugit](https://github.com/floraison/fugit) under the hood, which supports up to six arguments as the cron string, [see](https://github.com/floraison/fugit?tab=readme-ov-file#the-second-extension).
|
62
|
+
|
58
63
|
Run sidekiq:
|
59
64
|
|
60
65
|
``` sh
|
@@ -122,7 +127,7 @@ The schedule is configured through the `:scheduler:` -> `:schedule` config entry
|
|
122
127
|
|
123
128
|
# Deconstructs a hash defined as the `args` to keyword arguments.
|
124
129
|
#
|
125
|
-
# `
|
130
|
+
# `false` by default.
|
126
131
|
#
|
127
132
|
# Example
|
128
133
|
#
|
@@ -208,7 +213,7 @@ At, and in types push jobs only once. `at` schedules in a point in time:
|
|
208
213
|
at: '3001/01/01'
|
209
214
|
```
|
210
215
|
|
211
|
-
You can specify any string that `DateTime.parse` and `Chronic` understand. To enable Chronic
|
216
|
+
You can specify any string that `DateTime.parse` and `Chronic` understand. To enable [Chronic](https://github.com/mojombo/chronic)
|
212
217
|
strings, you must add it as a dependency.
|
213
218
|
|
214
219
|
`in` triggers after a time duration has elapsed:
|
@@ -359,7 +364,7 @@ MyRegularJob:
|
|
359
364
|
Then we can conditionally load it via an initializer:
|
360
365
|
|
361
366
|
```ruby
|
362
|
-
# config/
|
367
|
+
# config/initializers/sidekiq.rb
|
363
368
|
if ENV.fetch("IS_SCHEDULER", false)
|
364
369
|
Sidekiq.configure_server do |config|
|
365
370
|
config.on(:startup) do
|
@@ -59,6 +59,17 @@ module SidekiqScheduler
|
|
59
59
|
SidekiqScheduler::SidekiqAdapter.sidekiq_queues(sidekiq_config)
|
60
60
|
end
|
61
61
|
|
62
|
+
def to_hash
|
63
|
+
{
|
64
|
+
enabled: enabled?,
|
65
|
+
dynamic: dynamic?,
|
66
|
+
dynamic_every: dynamic_every?,
|
67
|
+
shedule: schedule,
|
68
|
+
listened_queues_only: listened_queues_only?,
|
69
|
+
rufus_scheduler_options: rufus_scheduler_options
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
62
73
|
private
|
63
74
|
|
64
75
|
attr_reader :scheduler_config
|
@@ -1,14 +1,27 @@
|
|
1
1
|
require 'sidekiq/web' unless defined?(Sidekiq::Web)
|
2
2
|
|
3
|
-
|
3
|
+
if SidekiqScheduler::SidekiqAdapter::SIDEKIQ_GTE_7_3_0
|
4
4
|
|
5
|
-
|
6
|
-
Sidekiq::Web.
|
7
|
-
|
5
|
+
# Locale and asset cache is configured in `.regiester`
|
6
|
+
Sidekiq::Web.register(SidekiqScheduler::Web,
|
7
|
+
name: "recurring_jobs",
|
8
|
+
tab: ["Recurring Jobs"],
|
9
|
+
index: ["recurring-jobs"],
|
10
|
+
root_dir: File.expand_path("../../../web", File.dirname(__FILE__)),
|
11
|
+
asset_paths: ["stylesheets-scheduler"]) do |app|
|
12
|
+
# add middleware or additional settings here
|
13
|
+
end
|
14
|
+
|
15
|
+
else
|
16
|
+
|
17
|
+
ASSETS_PATH = File.expand_path('../../../web/assets', __dir__)
|
18
|
+
|
19
|
+
Sidekiq::Web.register(SidekiqScheduler::Web)
|
20
|
+
Sidekiq::Web.tabs['recurring_jobs'] = 'recurring-jobs'
|
21
|
+
Sidekiq::Web.locales << File.expand_path("#{File.dirname(__FILE__)}/../../../web/locales")
|
8
22
|
|
9
|
-
if Sidekiq::VERSION >= '6.0.0'
|
10
23
|
Sidekiq::Web.use Rack::Static, urls: ['/stylesheets-scheduler'],
|
11
24
|
root: ASSETS_PATH,
|
12
25
|
cascade: true,
|
13
|
-
header_rules: [[:all, { 'cache-control' => '
|
26
|
+
header_rules: [[:all, { 'cache-control' => 'private, max-age=86400' }]]
|
14
27
|
end
|
@@ -38,7 +38,7 @@ module SidekiqScheduler
|
|
38
38
|
#
|
39
39
|
# @return [String] with the job's interval
|
40
40
|
def interval
|
41
|
-
@attributes['cron'] || @attributes['interval'] || @attributes['every']
|
41
|
+
@attributes['cron'] || @attributes['interval'] || @attributes['every'] || @attributes['at'] || @attributes['in']
|
42
42
|
end
|
43
43
|
|
44
44
|
# Returns the queue of the job
|
@@ -24,6 +24,15 @@ module SidekiqScheduler
|
|
24
24
|
@scheduler_instance.load_schedule!
|
25
25
|
end
|
26
26
|
|
27
|
+
# This method is needed to avoid exposing unnecessary information.
|
28
|
+
# Because ActiveSupport's `as_json` traverses instance values to convert the object to a hash
|
29
|
+
# unless it responds to `to_hash`.
|
30
|
+
def to_hash
|
31
|
+
{
|
32
|
+
scheduler: @scheduler_instance.to_hash
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
27
36
|
private
|
28
37
|
|
29
38
|
def set_current_scheduler_options(config)
|
@@ -130,9 +130,9 @@ module SidekiqScheduler
|
|
130
130
|
def self.register_job_instance(job_name, time)
|
131
131
|
job_key = pushed_job_key(job_name)
|
132
132
|
registered, _ = Sidekiq.redis do |r|
|
133
|
-
r.
|
134
|
-
|
135
|
-
|
133
|
+
r.multi do |m|
|
134
|
+
m.zadd(job_key, time.to_i, time.to_i)
|
135
|
+
m.expire(job_key, REGISTERED_JOBS_THRESHOLD_IN_SECONDS)
|
136
136
|
end
|
137
137
|
end
|
138
138
|
|
@@ -58,7 +58,7 @@ module SidekiqScheduler
|
|
58
58
|
self.dynamic = config.dynamic?
|
59
59
|
self.dynamic_every = config.dynamic_every?
|
60
60
|
self.listened_queues_only = config.listened_queues_only?
|
61
|
-
self.rufus_scheduler_options = config.rufus_scheduler_options
|
61
|
+
self.rufus_scheduler_options = config.rufus_scheduler_options || {}
|
62
62
|
end
|
63
63
|
|
64
64
|
# the Rufus::Scheduler jobs that are scheduled
|
@@ -128,6 +128,8 @@ module SidekiqScheduler
|
|
128
128
|
schedule, options = SidekiqScheduler::RufusUtils.normalize_schedule_options(config_interval_type)
|
129
129
|
|
130
130
|
rufus_job = new_job(name, interval_type, config, schedule, options)
|
131
|
+
return unless rufus_job
|
132
|
+
|
131
133
|
@scheduled_jobs[name] = rufus_job
|
132
134
|
SidekiqScheduler::Utils.update_job_next_time(name, rufus_job.next_time)
|
133
135
|
|
@@ -248,6 +250,12 @@ module SidekiqScheduler
|
|
248
250
|
end
|
249
251
|
end
|
250
252
|
|
253
|
+
def to_hash
|
254
|
+
{
|
255
|
+
scheduler_config: @scheduler_config.to_hash
|
256
|
+
}
|
257
|
+
end
|
258
|
+
|
251
259
|
private
|
252
260
|
|
253
261
|
attr_reader :scheduler_config
|
@@ -256,7 +264,15 @@ module SidekiqScheduler
|
|
256
264
|
options = options.merge({ :job => true, :tags => [name] })
|
257
265
|
|
258
266
|
rufus_scheduler.send(interval_type, schedule, options) do |job, time|
|
259
|
-
|
267
|
+
if job_enabled?(name)
|
268
|
+
conf = SidekiqScheduler::Utils.sanitize_job_config(config)
|
269
|
+
|
270
|
+
if job.is_a?(Rufus::Scheduler::CronJob)
|
271
|
+
idempotent_job_enqueue(name, SidekiqScheduler::Utils.calc_cron_run_time(job.cron_line, time.utc), conf)
|
272
|
+
else
|
273
|
+
idempotent_job_enqueue(name, time, conf)
|
274
|
+
end
|
275
|
+
end
|
260
276
|
end
|
261
277
|
end
|
262
278
|
|
@@ -4,6 +4,7 @@ module SidekiqScheduler
|
|
4
4
|
class SidekiqAdapter
|
5
5
|
SIDEKIQ_GTE_6_5_0 = Gem::Version.new(Sidekiq::VERSION) >= Gem::Version.new('6.5.0')
|
6
6
|
SIDEKIQ_GTE_7_0_0 = Gem::Version.new(Sidekiq::VERSION) >= Gem::Version.new('7.0.0')
|
7
|
+
SIDEKIQ_GTE_7_3_0 = Gem::Version.new(Sidekiq::VERSION) >= Gem::Version.new('7.3.0')
|
7
8
|
|
8
9
|
def self.fetch_scheduler_config_from_sidekiq(sidekiq_config)
|
9
10
|
return {} if sidekiq_config.nil?
|
@@ -141,5 +141,39 @@ module SidekiqScheduler
|
|
141
141
|
def self.update_job_last_time(name, last_time)
|
142
142
|
SidekiqScheduler::RedisManager.set_job_last_time(name, last_time) if last_time
|
143
143
|
end
|
144
|
+
|
145
|
+
# Try to figure out when the cron job was supposed to run.
|
146
|
+
#
|
147
|
+
# Rufus calls the scheduler block with the current time and not the time the block was scheduled to run.
|
148
|
+
# This means under certain conditions you could have a job get scheduled multiple times because `time.to_i` is used
|
149
|
+
# to key the job in redis. If one server is under load and Rufus tries to run the jobs 1 seconds after the other
|
150
|
+
# server then the job will be queued twice.
|
151
|
+
# This method essentially makes a best guess at when this job was supposed to run and return that.
|
152
|
+
#
|
153
|
+
# @param [Fugit::Cron] cron
|
154
|
+
# @param [Time] time
|
155
|
+
#
|
156
|
+
# @return [Time]
|
157
|
+
def self.calc_cron_run_time(cron, time)
|
158
|
+
time = time.round # remove sub seconds to prevent rounding errors.
|
159
|
+
next_t = cron.next_time(time).utc
|
160
|
+
previous_t = cron.previous_time(time).utc
|
161
|
+
# The `time` var is some point between `previous_t` and `next_t`.
|
162
|
+
# Figure out how far off we are from each side in seconds.
|
163
|
+
next_diff = next_t - time
|
164
|
+
previous_diff = time - previous_t
|
165
|
+
|
166
|
+
if next_diff == previous_diff
|
167
|
+
# In the event `time` is exactly between `previous_t` and `next_t` the diff will not be equal to
|
168
|
+
# `cron.rough_frequency`. In that case we round down.
|
169
|
+
cron.rough_frequency == next_diff ? time : previous_t
|
170
|
+
elsif next_diff > previous_diff
|
171
|
+
# We are closer to the previous run time so return that.
|
172
|
+
previous_t
|
173
|
+
else
|
174
|
+
# We are closer to the next run time so return that.
|
175
|
+
next_t
|
176
|
+
end
|
177
|
+
end
|
144
178
|
end
|
145
179
|
end
|
data/web/locales/cs.yml
CHANGED
data/web/locales/de.yml
CHANGED
data/web/locales/en.yml
CHANGED
data/web/locales/es.yml
CHANGED
data/web/locales/fr.yml
CHANGED
data/web/locales/gd.yml
CHANGED
data/web/locales/it.yml
CHANGED
data/web/locales/ja.yml
CHANGED
data/web/locales/nl.yml
CHANGED
data/web/locales/pl.yml
CHANGED
data/web/locales/pt-BR.yml
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
pt-BR:
|
2
|
+
"Recurring Jobs": Jobs Recorrentes
|
2
3
|
recurring_jobs: Jobs Recorrentes
|
3
4
|
name: Nome
|
4
5
|
description: Descrição
|
@@ -6,7 +7,7 @@ pt-BR:
|
|
6
7
|
class: Classe
|
7
8
|
queue: Fila
|
8
9
|
arguments: Argumentos
|
9
|
-
enqueue_now: Enfileirar agora
|
10
|
+
enqueue_now: Enfileirar agora
|
10
11
|
last_time: Última execução
|
11
12
|
next_time: Próxima execução
|
12
13
|
no_next_time: Não há mais execuçoẽs
|
data/web/locales/ru.yml
CHANGED
data/web/locales/sv.yml
CHANGED
data/web/locales/zh-cn.yml
CHANGED
@@ -1,42 +1,7 @@
|
|
1
|
-
<% if
|
2
|
-
|
1
|
+
<% if SidekiqScheduler::SidekiqAdapter::SIDEKIQ_GTE_7_3_0 %>
|
2
|
+
<%= style_tag "stylesheets-scheduler/recurring_jobs.css" %>
|
3
3
|
<% else %>
|
4
|
-
|
5
|
-
.recurring-jobs { border-top-left-radius: 4px; border-top-right-radius: 4px; }
|
6
|
-
.recurring-jobs .title { margin-bottom: 5px; }
|
7
|
-
.recurring-jobs .title .name { font-weight: bold;}
|
8
|
-
.recurring-jobs .info,
|
9
|
-
.recurring-jobs .description { margin-bottom: 5px; }
|
10
|
-
.recurring-jobs .actions { margin-bottom: 5px; }
|
11
|
-
.recurring-jobs .status,
|
12
|
-
.recurring-jobs .description { font-size: 12px; }
|
13
|
-
.recurring-jobs .enqueue { margin-bottom: 0.5rem }
|
14
|
-
|
15
|
-
.list-group-item {
|
16
|
-
background-color: #f3f3f3;
|
17
|
-
color: #585454;
|
18
|
-
border: 1px solid rgba(0, 0, 0, 0.1);
|
19
|
-
}
|
20
|
-
|
21
|
-
.list-group-item-disabled {
|
22
|
-
background-color: #f3d3d3;
|
23
|
-
}
|
24
|
-
|
25
|
-
.toggle-all-buttons {
|
26
|
-
margin-top: 20px;
|
27
|
-
margin-bottom: 10px;
|
28
|
-
line-height: 45px;
|
29
|
-
text-align: right;
|
30
|
-
}
|
31
|
-
|
32
|
-
@media (max-width: 768px) {
|
33
|
-
.toggle-all-buttons {
|
34
|
-
margin-top: 0;
|
35
|
-
text-align: left;
|
36
|
-
line-height: inherit;
|
37
|
-
}
|
38
|
-
}
|
39
|
-
</style>
|
4
|
+
<link href="<%= root_path %>stylesheets-scheduler/recurring_jobs.css" media="screen" rel="stylesheet" type="text/css" />
|
40
5
|
<% end %>
|
41
6
|
|
42
7
|
<div class="row">
|
metadata
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sidekiq-scheduler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.0.
|
4
|
+
version: 5.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Morton Jonuschat
|
8
8
|
- Moove-it
|
9
9
|
- Marcelo Lauxen
|
10
|
-
autorequire:
|
10
|
+
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2024-07-04 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: sidekiq
|
@@ -53,6 +53,9 @@ dependencies:
|
|
53
53
|
- - ">="
|
54
54
|
- !ruby/object:Gem::Version
|
55
55
|
version: 1.4.0
|
56
|
+
- - "<"
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: '3'
|
56
59
|
type: :runtime
|
57
60
|
prerelease: false
|
58
61
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -60,6 +63,9 @@ dependencies:
|
|
60
63
|
- - ">="
|
61
64
|
- !ruby/object:Gem::Version
|
62
65
|
version: 1.4.0
|
66
|
+
- - "<"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3'
|
63
69
|
- !ruby/object:Gem::Dependency
|
64
70
|
name: rake
|
65
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -234,7 +240,7 @@ homepage: https://sidekiq-scheduler.github.io/sidekiq-scheduler/
|
|
234
240
|
licenses:
|
235
241
|
- MIT
|
236
242
|
metadata: {}
|
237
|
-
post_install_message:
|
243
|
+
post_install_message:
|
238
244
|
rdoc_options: []
|
239
245
|
require_paths:
|
240
246
|
- lib
|
@@ -249,8 +255,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
249
255
|
- !ruby/object:Gem::Version
|
250
256
|
version: '0'
|
251
257
|
requirements: []
|
252
|
-
rubygems_version: 3.
|
253
|
-
signing_key:
|
258
|
+
rubygems_version: 3.5.3
|
259
|
+
signing_key:
|
254
260
|
specification_version: 4
|
255
261
|
summary: Light weight job scheduling extension for Sidekiq
|
256
262
|
test_files: []
|