procrastinator 2.0.0 → 2.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 +4 -4
- data/.rubocop.yml +32 -1
- data/.ruby-version +1 -1
- data/Gemfile +5 -2
- data/README.md +40 -22
- data/RELEASE_NOTES.md +35 -5
- data/lib/procrastinator/config.rb +23 -2
- data/lib/procrastinator/logged_task.rb +1 -1
- data/lib/procrastinator/queue.rb +3 -1
- data/lib/procrastinator/scheduler.rb +25 -7
- data/lib/procrastinator/task_store/simple_comma_store.rb +1 -1
- data/lib/procrastinator/test/mocks.rb +44 -0
- data/lib/procrastinator/version.rb +1 -1
- data/procrastinator.gemspec +4 -1
- metadata +19 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 49bfb07d03e59388e8deefc6fa6eaea6342cf973943f3ce7ccbaa3835095e768
|
4
|
+
data.tar.gz: 498838544ce3371270b8a71f076820811b9a8c51b6b8227725be5ab4c59f8dac
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e779c44fbd3edab3d32f387d20563a7c2a7d00acb270e567370692a78b0520bf11aa9fd9ce65cb205894ffd7a7f18e09839a4ca72571e9b0e5b2cf814d3a0470
|
7
|
+
data.tar.gz: c7235fec5d0db4dbf2d201e55e36d719750ea2a3875eca9ede878d054c55e1abd627319a686b082a402d7c62745831f2bee180f87a85b78c0c0c74ab1285bf57
|
data/.rubocop.yml
CHANGED
@@ -4,7 +4,7 @@ AllCops:
|
|
4
4
|
Exclude:
|
5
5
|
- 'bin/*'
|
6
6
|
|
7
|
-
TargetRubyVersion: 3.
|
7
|
+
TargetRubyVersion: 3.3
|
8
8
|
|
9
9
|
Layout/LineLength:
|
10
10
|
Exclude:
|
@@ -14,6 +14,10 @@ Layout/LineLength:
|
|
14
14
|
Layout/FirstArrayElementIndentation:
|
15
15
|
IndentationWidth: 6
|
16
16
|
|
17
|
+
# setting to 6 to match RubyMine autoformat
|
18
|
+
Layout/MultilineMethodCallIndentation:
|
19
|
+
EnforcedStyle: indented_relative_to_receiver
|
20
|
+
IndentationWidth: 6
|
17
21
|
|
18
22
|
# rspec blocks are huge by design
|
19
23
|
Metrics/BlockLength:
|
@@ -23,3 +27,30 @@ Metrics/BlockLength:
|
|
23
27
|
Metrics/ModuleLength:
|
24
28
|
Exclude:
|
25
29
|
- 'spec/**/*.rb'
|
30
|
+
|
31
|
+
# Disabling because it's marginally better, if at all, and not worth rewriting all step descripts right now
|
32
|
+
RSpec/ExampleWording:
|
33
|
+
Enabled: false
|
34
|
+
|
35
|
+
# My style is often to use multiple smaller expectations in a row instead of one giant one
|
36
|
+
RSpec/MultipleExpectations:
|
37
|
+
Enabled: false
|
38
|
+
|
39
|
+
# My style is often to abuse contexts for logical grouping
|
40
|
+
RSpec/ContextWording:
|
41
|
+
Enabled: false
|
42
|
+
|
43
|
+
# TODO: re-enable when FakeFS is removed
|
44
|
+
RSpec/AnyInstance:
|
45
|
+
Enabled: false
|
46
|
+
|
47
|
+
# Not convinced described_class is better. Makes everything harder to track mentally.
|
48
|
+
RSpec/DescribedClass:
|
49
|
+
Enabled: false
|
50
|
+
|
51
|
+
# TODO: re-enable and replace generic doubles with verifying ones
|
52
|
+
RSpec/VerifiedDoubles:
|
53
|
+
Enabled: false
|
54
|
+
|
55
|
+
RSpec/MultipleMemoizedHelpers:
|
56
|
+
Max: 7
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
ruby-3.
|
1
|
+
ruby-3.4.5
|
data/Gemfile
CHANGED
@@ -5,10 +5,13 @@ source 'https://rubygems.org'
|
|
5
5
|
group :development do
|
6
6
|
gem 'bundler', '~> 2.3'
|
7
7
|
gem 'fakefs', '~> 1.8'
|
8
|
+
gem 'mutex_m', '~> 0.3' # for 'debase' debugger
|
8
9
|
gem 'rake', '~> 13.0'
|
9
10
|
gem 'rspec', '~> 3.12'
|
10
|
-
gem 'rubocop', '~> 1.
|
11
|
-
gem 'rubocop-performance', '~> 1.
|
11
|
+
gem 'rubocop', '~> 1.79'
|
12
|
+
gem 'rubocop-performance', '~> 1.25'
|
13
|
+
gem 'rubocop-rake', '~> 0.7'
|
14
|
+
gem 'rubocop-rspec', '~> 3.6'
|
12
15
|
gem 'simplecov', '~> 0.22.0'
|
13
16
|
gem 'timecop', '~> 0.9'
|
14
17
|
gem 'yard', '~> 0.9'
|
data/README.md
CHANGED
@@ -53,15 +53,15 @@ scheduler.defer(:birthday, run_at: Time.now + 3600, data: {user_id: 5})
|
|
53
53
|
- [CSV Task Store](#csv-task-store)
|
54
54
|
- [Shared Task Stores](#shared-task-stores)
|
55
55
|
+ [Task Container](#task-container)
|
56
|
+
+ [Process Priority](#process-priority)
|
56
57
|
* [Deferring Tasks](#deferring-tasks)
|
57
58
|
+ [Timing](#timing)
|
58
59
|
+ [Rescheduling Existing Tasks](#rescheduling-existing-tasks)
|
59
60
|
+ [Retries](#retries)
|
60
61
|
+ [Cancelling](#cancelling)
|
62
|
+
* [Testing with Procrastinator](#testing-with-procrastinator)
|
63
|
+
+ [RSpec Matchers](#rspec-matchers)
|
61
64
|
* [Running Tasks](#running-tasks)
|
62
|
-
+ [In Testing](#in-testing)
|
63
|
-
- [RSpec Matchers](#rspec-matchers)
|
64
|
-
+ [In Production](#in-production)
|
65
65
|
* [Similar Tools](#similar-tools)
|
66
66
|
+ [Linux etc: Cron and At](#linux-etc--cron-and-at)
|
67
67
|
+ [Gem: Resque](#gem--resque)
|
@@ -140,12 +140,12 @@ Task Handlers have attributes that are set after the Handler is created. The att
|
|
140
140
|
the tasks from referencing unknown variables at whatever time they are run - if they're missing, you'll get
|
141
141
|
a `MalformedTaskError`.
|
142
142
|
|
143
|
-
| Attribute
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
143
|
+
| Attribute | Required | Description |
|
144
|
+
|--------------|----------|-------------------------------------------------------------------------------------------------------------------|
|
145
|
+
| `:container` | Yes | Container declared in `#setup` from the currently running instance |
|
146
|
+
| `:logger` | Yes | Logger object for the Queue |
|
147
|
+
| `:scheduler` | Yes | A scheduler object that you can use to schedule new tasks (eg. with `#defer`) |
|
148
|
+
| `:data` | No | Data provided to `#defer`. Calls to `#defer` will error if they do not provide data when expected and vice-versa. |
|
149
149
|
|
150
150
|
### Errors & Logging
|
151
151
|
|
@@ -184,10 +184,10 @@ end
|
|
184
184
|
|
185
185
|
Some events are always logged by default:
|
186
186
|
|
187
|
-
|event
|
188
|
-
|
189
|
-
|Task completed
|
190
|
-
|Task cailure
|
187
|
+
| event | level |
|
188
|
+
|----------------|-------|
|
189
|
+
| Task completed | INFO |
|
190
|
+
| Task cailure | ERROR |
|
191
191
|
|
192
192
|
## Configuration
|
193
193
|
|
@@ -220,12 +220,12 @@ config.define_queue :greeting, SendWelcomeEmail, store: 'procrastinator.csv', ti
|
|
220
220
|
|
221
221
|
Description of keyword options:
|
222
222
|
|
223
|
-
| Option | Description
|
224
|
-
|
225
|
-
| `:store` | Storage IO object for tasks. See [Task Store](#task-store)
|
226
|
-
| `:timeout` | Max duration (seconds) before tasks are failed for taking too long
|
223
|
+
| Option | Description |
|
224
|
+
|------------------|-------------------------------------------------------------------------------------|
|
225
|
+
| `:store` | Storage IO object for tasks. See [Task Store](#task-store) |
|
226
|
+
| `:timeout` | Max duration (seconds) before tasks are failed for taking too long |
|
227
227
|
| `:max_attempts` | Once a task has been attempted `max_attempts` times, it will be permanently failed. |
|
228
|
-
| `:update_period` | Delay (seconds) between reloads of all tasks from the task store
|
228
|
+
| `:update_period` | Delay (seconds) between reloads of all tasks from the task store |
|
229
229
|
|
230
230
|
### Task Store
|
231
231
|
|
@@ -275,8 +275,8 @@ _Warning_: Task stores shared between queues **must** be thread-safe if using th
|
|
275
275
|
These are the data fields for each individual scheduled task. When using the built-in task store, these are the field
|
276
276
|
names. If you have a database, use this to inform your table schema.
|
277
277
|
|
278
|
-
|
|
279
|
-
|
278
|
+
| Hash Key | Type | Description |
|
279
|
+
|-------------------|----------|-------------------------------------------------------------------------|
|
280
280
|
| `:id` | integer | Unique identifier for this exact task |
|
281
281
|
| `:queue` | symbol | Name of the queue the task is inside |
|
282
282
|
| `:run_at` | datetime | Time to attempt running the task next. Updated for retries¹ |
|
@@ -285,7 +285,7 @@ names. If you have a database, use this to inform your table schema.
|
|
285
285
|
| `:attempts` | integer | Number of times the task has tried to run |
|
286
286
|
| `:last_fail_at` | datetime | Time of the most recent failure |
|
287
287
|
| `:last_error` | string | Error message + backtrace of the most recent failure. May be very long. |
|
288
|
-
| `:data` | JSON | Data to be provided to the task handler, serialized² to JSON.
|
288
|
+
| `:data` | JSON | Data to be provided to the task handler, serialized² to JSON. |
|
289
289
|
|
290
290
|
¹ `nil` indicates that it is permanently failed and will never run, either due to expiry or too many attempts.
|
291
291
|
|
@@ -350,6 +350,23 @@ class LunchTask
|
|
350
350
|
end
|
351
351
|
```
|
352
352
|
|
353
|
+
### Process Priority
|
354
|
+
|
355
|
+
The whole Procrastinator process may be made less urgent by providing an `adjust_priority` value (aka nice level
|
356
|
+
adjustment).
|
357
|
+
|
358
|
+
If not provided, Procrastinator will assume a priority adjustment of 1.
|
359
|
+
|
360
|
+
```ruby
|
361
|
+
Procrastinator.setup do |config|
|
362
|
+
config.adjust_priority 10 # higher numbers are less urgent. This increases the default priority by 10
|
363
|
+
|
364
|
+
# .. other setup stuff ...
|
365
|
+
end
|
366
|
+
```
|
367
|
+
|
368
|
+
If you want a negative priority nice value, then use system tools like `renice` at your own discretion.
|
369
|
+
|
353
370
|
## Deferring Tasks
|
354
371
|
|
355
372
|
To add tasks to a queue, call `#defer` on the scheduler returned by `Procrastinator.setup`:
|
@@ -532,7 +549,8 @@ end
|
|
532
549
|
```
|
533
550
|
|
534
551
|
> **Note:** There can be a distinction between process full title (`/proc/*/cmdline`) vs the shorter name
|
535
|
-
> (`/proc/*/comm`). Some tools like `ps` and `top` display the process title, while others like `pstree` show the
|
552
|
+
> (`/proc/*/comm`). Some tools like `ps` and `top` display the process title, while others like `pstree` show the
|
553
|
+
> process name.
|
536
554
|
>
|
537
555
|
> Procrastinator uses Ruby's `Process.setproctitle`, which only affects the title.
|
538
556
|
|
data/RELEASE_NOTES.md
CHANGED
@@ -1,6 +1,36 @@
|
|
1
1
|
# Release Notes
|
2
2
|
|
3
|
-
##
|
3
|
+
## [Unreleased]
|
4
|
+
|
5
|
+
### Major Changes
|
6
|
+
|
7
|
+
* none
|
8
|
+
|
9
|
+
### Minor Changes
|
10
|
+
|
11
|
+
* none
|
12
|
+
|
13
|
+
### Bugfixes
|
14
|
+
|
15
|
+
* none
|
16
|
+
|
17
|
+
## [2.1.0] (2025-08-16)
|
18
|
+
|
19
|
+
### Major Changes
|
20
|
+
|
21
|
+
* none
|
22
|
+
|
23
|
+
### Minor Changes
|
24
|
+
|
25
|
+
* Added process priority nice value handling
|
26
|
+
* Improved MultiIO implementation to avoid clobbering class methods
|
27
|
+
* Updated minimum ruby to 3.3
|
28
|
+
|
29
|
+
### Bugfixes
|
30
|
+
|
31
|
+
* Compatible with Ruby 3.4.5
|
32
|
+
|
33
|
+
## [2.0.0] (2023-06-17)
|
4
34
|
|
5
35
|
### Major Changes
|
6
36
|
|
@@ -14,7 +44,7 @@
|
|
14
44
|
|
15
45
|
* none
|
16
46
|
|
17
|
-
## 1.2.0 (2023-06-17)
|
47
|
+
## [1.2.0] (2023-06-17)
|
18
48
|
|
19
49
|
### Major Changes
|
20
50
|
|
@@ -28,7 +58,7 @@
|
|
28
58
|
|
29
59
|
* When logging is disabled, it points to `File::NULL` instead of a dead-end StringIO
|
30
60
|
|
31
|
-
## 1.1.0 (2022-10-16)
|
61
|
+
## [1.1.0] (2022-10-16)
|
32
62
|
|
33
63
|
### Major Changes
|
34
64
|
|
@@ -44,7 +74,7 @@
|
|
44
74
|
* Fixed have_task handling of nested matchers like be_within
|
45
75
|
* Improved have_task handling of string queue names vs symbols
|
46
76
|
|
47
|
-
## 1.0.1 (2022-09-20)
|
77
|
+
## [1.0.1] (2022-09-20)
|
48
78
|
|
49
79
|
### Major Changes
|
50
80
|
|
@@ -58,7 +88,7 @@
|
|
58
88
|
|
59
89
|
* Fixed integration error in rescheduling tasks
|
60
90
|
|
61
|
-
## 1.0.0 (2022-09-18)
|
91
|
+
## [1.0.0] (2022-09-18)
|
62
92
|
|
63
93
|
### Major Changes
|
64
94
|
|
@@ -23,7 +23,9 @@ module Procrastinator
|
|
23
23
|
# @!attribute [r] :log_shift_size
|
24
24
|
# @return [Integer] Filesize before rotating to a new logfile (see Ruby Logger for details)
|
25
25
|
class Config
|
26
|
-
attr_reader :queues, :log_dir, :log_level, :log_shift_age, :log_shift_size, :container
|
26
|
+
attr_reader :queues, :log_dir, :log_level, :log_shift_age, :log_shift_size, :container, :priority
|
27
|
+
|
28
|
+
alias nice priority
|
27
29
|
|
28
30
|
# Default directory to keep logs in.
|
29
31
|
DEFAULT_LOG_DIRECTORY = Pathname.new('log').freeze
|
@@ -45,6 +47,9 @@ module Procrastinator
|
|
45
47
|
msg].join("\t") << "\n"
|
46
48
|
end
|
47
49
|
|
50
|
+
# Default priority 'nice' level.
|
51
|
+
DEFAULT_PRIORITY = 1
|
52
|
+
|
48
53
|
def initialize
|
49
54
|
@queues = []
|
50
55
|
@container = nil
|
@@ -52,6 +57,7 @@ module Procrastinator
|
|
52
57
|
@log_level = Logger::INFO
|
53
58
|
@log_shift_age = DEFAULT_LOG_SHIFT_AGE
|
54
59
|
@log_shift_size = DEFAULT_LOG_SHIFT_SIZE
|
60
|
+
@priority = DEFAULT_PRIORITY
|
55
61
|
|
56
62
|
with_store(csv: TaskStore::SimpleCommaStore::DEFAULT_FILE) do
|
57
63
|
if block_given?
|
@@ -109,7 +115,9 @@ module Procrastinator
|
|
109
115
|
|
110
116
|
properties[:store] = interpret_store(properties[:store]) if properties.key? :store
|
111
117
|
|
112
|
-
|
118
|
+
args = {name: name, task_class: task_class, store: @default_store}.merge(properties)
|
119
|
+
|
120
|
+
@queues << Queue.new(**args)
|
113
121
|
end
|
114
122
|
|
115
123
|
# Sets details of logging behaviour
|
@@ -124,6 +132,19 @@ module Procrastinator
|
|
124
132
|
@log_shift_age = shift_age
|
125
133
|
@log_shift_size = shift_size
|
126
134
|
end
|
135
|
+
|
136
|
+
# Sets the desired process 'nice' priority value adjustment. This value is added to whatever runtime priority
|
137
|
+
# Procrastinator starts with.
|
138
|
+
#
|
139
|
+
# Higher numbers are more nice to other processes (lower priority); think of it as the position in line.
|
140
|
+
# You can be more nice, but never less nice.
|
141
|
+
#
|
142
|
+
# @param new_priority [Number] the new priority.
|
143
|
+
def adjust_priority(new_priority)
|
144
|
+
@priority = new_priority
|
145
|
+
end
|
146
|
+
|
147
|
+
alias adjust_nice adjust_priority
|
127
148
|
end
|
128
149
|
|
129
150
|
include DSL
|
data/lib/procrastinator/queue.rb
CHANGED
@@ -94,7 +94,9 @@ module Procrastinator
|
|
94
94
|
raise AmbiguousTaskFilterError, "too many (#{ tasks.size }) tasks match #{ identifier }. Found: #{ tasks }"
|
95
95
|
end
|
96
96
|
|
97
|
-
|
97
|
+
args = tasks.first.merge(queue: self)
|
98
|
+
|
99
|
+
TaskMetaData.new(**args)
|
98
100
|
end
|
99
101
|
|
100
102
|
# Creates a task on the queue, saved using the Task Store strategy.
|
@@ -34,7 +34,7 @@ module Procrastinator
|
|
34
34
|
|
35
35
|
# Alters an existing task to run at a new time, expire at a new time, or both.
|
36
36
|
#
|
37
|
-
# Call #to on the result and pass in the new :run_at and/or :expire_at.
|
37
|
+
# Call UpdateProxy#to on the result and pass in the new :run_at and/or :expire_at.
|
38
38
|
#
|
39
39
|
# Example:
|
40
40
|
#
|
@@ -92,6 +92,11 @@ module Procrastinator
|
|
92
92
|
@identifier = identifier.merge(queue: queue.name.to_sym)
|
93
93
|
end
|
94
94
|
|
95
|
+
# Updates the task found by the identifier to run at and/or expire at the new provided time.
|
96
|
+
#
|
97
|
+
# @raise [NoSuchTaskError] when no task matches the identifier.
|
98
|
+
# @raise [AmbiguousTaskFilterError] when many tasks match the identifier, meaning you need to be more specific.
|
99
|
+
# @see Scheduler#reschedule
|
95
100
|
def to(run_at: nil, expire_at: nil)
|
96
101
|
task = @queue.fetch_task(@identifier)
|
97
102
|
|
@@ -227,12 +232,14 @@ module Procrastinator
|
|
227
232
|
@streams = stream
|
228
233
|
end
|
229
234
|
|
230
|
-
(
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
235
|
+
def method_missing(method_name, *args)
|
236
|
+
able_streams(method_name).collect do |stream|
|
237
|
+
stream.send(method_name, *args)
|
238
|
+
end.last # forces consistent return result type for callers (but may lose some info)
|
239
|
+
end
|
240
|
+
|
241
|
+
def respond_to_missing?(method_name, include_private)
|
242
|
+
@streams.any? { |stream| stream.respond_to?(method_name, include_private) }
|
236
243
|
end
|
237
244
|
|
238
245
|
private
|
@@ -314,6 +321,7 @@ module Procrastinator
|
|
314
321
|
|
315
322
|
manage_pid pid_path
|
316
323
|
rename_process pid_path
|
324
|
+
update_priority
|
317
325
|
rescue StandardError => e
|
318
326
|
@logger&.fatal ([e.message] + e.backtrace).join("\n")
|
319
327
|
raise e
|
@@ -368,6 +376,16 @@ module Procrastinator
|
|
368
376
|
Process.setproctitle name
|
369
377
|
end
|
370
378
|
|
379
|
+
def update_priority
|
380
|
+
new_priority = @config.priority
|
381
|
+
current_priority = Process.getpriority(Process::PRIO_PROCESS, 0)
|
382
|
+
|
383
|
+
raise ArgumentError, 'Process priority cannot be negative. Use system tools if you need a reduced nice value.' if new_priority.negative?
|
384
|
+
|
385
|
+
# second arg "integer" arg of zero means self
|
386
|
+
Process.setpriority(Process::PRIO_PROCESS, 0, current_priority + new_priority)
|
387
|
+
end
|
388
|
+
|
371
389
|
include ThreadedWorking
|
372
390
|
end
|
373
391
|
|
@@ -34,6 +34,50 @@ module Procrastinator
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
+
# Testing mock Task class that requires a data packet
|
38
|
+
class MockTaskWithData
|
39
|
+
attr_accessor :container, :logger, :scheduler, :data
|
40
|
+
|
41
|
+
# Records that the mock task was run.
|
42
|
+
def run
|
43
|
+
@run = true
|
44
|
+
end
|
45
|
+
|
46
|
+
# @return [Boolean] Whether the task was run
|
47
|
+
def run?
|
48
|
+
@run
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Testing mock Thread class
|
53
|
+
class MockThread
|
54
|
+
attr_reader :status
|
55
|
+
|
56
|
+
def initialize(name: nil, status: nil)
|
57
|
+
@name = name
|
58
|
+
@status = status
|
59
|
+
end
|
60
|
+
|
61
|
+
def join(_timeout)
|
62
|
+
end
|
63
|
+
|
64
|
+
def kill
|
65
|
+
end
|
66
|
+
|
67
|
+
def alive?
|
68
|
+
true
|
69
|
+
end
|
70
|
+
|
71
|
+
def thread_variable_get(var_name)
|
72
|
+
case var_name
|
73
|
+
when :name
|
74
|
+
@name
|
75
|
+
else
|
76
|
+
raise 'test error: undefined thread var'
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
37
81
|
# Data-accepting MockTask
|
38
82
|
#
|
39
83
|
# @see MockTask
|
data/procrastinator.gemspec
CHANGED
@@ -23,5 +23,8 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
24
24
|
spec.require_paths = ['lib']
|
25
25
|
|
26
|
-
spec.required_ruby_version = '>= 3.
|
26
|
+
spec.required_ruby_version = '>= 3.3'
|
27
|
+
|
28
|
+
# TODO: remove when CSV persister is split to separate gem; used to be part of STDLIB but now is separate
|
29
|
+
spec.add_dependency 'csv', '>= 3.3'
|
27
30
|
end
|
metadata
CHANGED
@@ -1,15 +1,28 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: procrastinator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robin Miller
|
8
|
-
autorequire:
|
9
8
|
bindir: exe
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
12
|
-
dependencies:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: csv
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - ">="
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '3.3'
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - ">="
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '3.3'
|
13
26
|
description: A flexible pure Ruby job queue. Tasks are reschedulable after failures.
|
14
27
|
email:
|
15
28
|
- robin@tenjin.ca
|
@@ -50,7 +63,6 @@ licenses:
|
|
50
63
|
- MIT
|
51
64
|
metadata:
|
52
65
|
rubygems_mfa_required: 'true'
|
53
|
-
post_install_message:
|
54
66
|
rdoc_options: []
|
55
67
|
require_paths:
|
56
68
|
- lib
|
@@ -58,15 +70,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
58
70
|
requirements:
|
59
71
|
- - ">="
|
60
72
|
- !ruby/object:Gem::Version
|
61
|
-
version: '3.
|
73
|
+
version: '3.3'
|
62
74
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
63
75
|
requirements:
|
64
76
|
- - ">="
|
65
77
|
- !ruby/object:Gem::Version
|
66
78
|
version: '0'
|
67
79
|
requirements: []
|
68
|
-
rubygems_version: 3.
|
69
|
-
signing_key:
|
80
|
+
rubygems_version: 3.6.9
|
70
81
|
specification_version: 4
|
71
82
|
summary: For apps to put off work until later
|
72
83
|
test_files: []
|