marj 1.1.0 → 2.0.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/README.md +86 -9
- data/app/models/marj.rb +10 -21
- data/lib/marj.rb +3 -20
- data/lib/marj_adapter.rb +21 -0
- data/lib/marj_config.rb +13 -0
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c4feb80230bafd2d67bdb0316598f4c46eabd01ca86c35fb8b01e04c865f91a8
|
4
|
+
data.tar.gz: be461234b8359c9f974dd4160d896cc048d0dcc1f4b7d74b57042d62f9950a18
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 31735c1c143a47ab490ed08eca12af6d8fce73b551637ed92e37bcb42e8b2560135ad674dfc832353988f4246c10f9ff2822f624bb10ba256070632dd35c209c
|
7
|
+
data.tar.gz: 7f19dee3c8b0510d27ca10eb8f9d64ecfc2e1174ae1cea71e17abf42f0ce8ef07be4f84e1eff9cf0150e6162bb3a790e066e36f17db131056e30ebbeada1bdc3
|
data/README.md
CHANGED
@@ -5,12 +5,14 @@ Marj is a Minimal ActiveRecord-based Jobs library.
|
|
5
5
|
API docs: https://www.rubydoc.info/github/nicholasdower/marj <br>
|
6
6
|
RubyGems: https://rubygems.org/gems/marj <br>
|
7
7
|
Changelog: https://github.com/nicholasdower/marj/releases <br>
|
8
|
-
Issues: https://github.com/nicholasdower/marj/issues
|
8
|
+
Issues: https://github.com/nicholasdower/marj/issues <br>
|
9
|
+
Development: https://github.com/nicholasdower/marj/blob/master/CONTRIBUTING.md
|
9
10
|
|
10
11
|
For more information on ActiveJob, see:
|
11
12
|
|
12
13
|
- https://edgeguides.rubyonrails.org/active_job_basics.html
|
13
14
|
- https://www.rubydoc.info/gems/activejob
|
15
|
+
- https://github.com/nicholasdower/marj/blob/master/README.md#activejob-cheatsheet
|
14
16
|
|
15
17
|
## Setup
|
16
18
|
|
@@ -22,9 +24,11 @@ Add the following to your Gemfile:
|
|
22
24
|
gem 'marj', '~> 1.0'
|
23
25
|
```
|
24
26
|
|
25
|
-
|
27
|
+
```shell
|
28
|
+
bundle install
|
29
|
+
```
|
26
30
|
|
27
|
-
|
31
|
+
### 2. Create the jobs table
|
28
32
|
|
29
33
|
```ruby
|
30
34
|
class CreateJobs < ActiveRecord::Migration[7.1]
|
@@ -53,6 +57,14 @@ class CreateJobs < ActiveRecord::Migration[7.1]
|
|
53
57
|
end
|
54
58
|
```
|
55
59
|
|
60
|
+
To use a different table name:
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
require 'marj'
|
64
|
+
|
65
|
+
MarjConfig.table_name = 'some_name'
|
66
|
+
```
|
67
|
+
|
56
68
|
### 3. Configure the queue adapter
|
57
69
|
|
58
70
|
```ruby
|
@@ -84,19 +96,84 @@ SomeJob.perform_later('foo')
|
|
84
96
|
record = Marj.first
|
85
97
|
record.execute
|
86
98
|
|
87
|
-
# Run all
|
88
|
-
Marj.
|
99
|
+
# Run all ready jobs:
|
100
|
+
while (record = Marj.ready.first)
|
101
|
+
record.execute
|
102
|
+
end
|
89
103
|
|
90
|
-
# Run all
|
91
|
-
|
104
|
+
# Run all ready jobs in a specific queue:
|
105
|
+
while (record = Marj.where(queue_name: 'foo').ready.first)
|
106
|
+
record.execute
|
107
|
+
end
|
92
108
|
|
93
|
-
# Run jobs as they become
|
109
|
+
# Run jobs as they become ready:
|
94
110
|
loop do
|
95
|
-
Marj.
|
111
|
+
while (record = Marj.ready.first)
|
112
|
+
record.execute
|
113
|
+
end
|
96
114
|
sleep 5.seconds
|
97
115
|
end
|
98
116
|
```
|
99
117
|
|
118
|
+
## Extension Examples
|
119
|
+
|
120
|
+
### Timeouts
|
121
|
+
|
122
|
+
```ruby
|
123
|
+
class ApplicationJob < ActiveJob::Base
|
124
|
+
def self.timeout_after(duration)
|
125
|
+
@timeout = duration
|
126
|
+
end
|
127
|
+
|
128
|
+
around_perform do |job, block|
|
129
|
+
if (timeout = job.class.instance_variable_get(:@timeout))
|
130
|
+
::Timeout.timeout(timeout, StandardError, 'execution expired') { block.call }
|
131
|
+
else
|
132
|
+
block.call
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
```
|
137
|
+
|
138
|
+
### Last Error
|
139
|
+
|
140
|
+
```ruby
|
141
|
+
class AddLastErrorToJobs < ActiveRecord::Migration[7.1]
|
142
|
+
def self.up
|
143
|
+
add_column :jobs, :last_error, :text
|
144
|
+
end
|
145
|
+
|
146
|
+
def self.down
|
147
|
+
remove_column :jobs, :last_error
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
class ApplicationJob < ActiveJob::Base
|
152
|
+
attr_reader :last_error
|
153
|
+
|
154
|
+
def last_error=(error)
|
155
|
+
if error.is_a?(Exception)
|
156
|
+
backtrace = error.backtrace&.map { |line| "\t#{line}" }&.join("\n")
|
157
|
+
error = backtrace ? "#{error.class}: #{error.message}\n#{backtrace}" : "#{error.class}: #{error.message}"
|
158
|
+
end
|
159
|
+
|
160
|
+
@last_error = error&.truncate(10_000, omission: '… (truncated)')
|
161
|
+
end
|
162
|
+
|
163
|
+
def set(options = {})
|
164
|
+
super.tap { self.last_error = options[:error] if options[:error] }
|
165
|
+
end
|
166
|
+
|
167
|
+
def serialize
|
168
|
+
super.merge('last_error' => @last_error)
|
169
|
+
end
|
170
|
+
|
171
|
+
def deserialize(job_data)
|
172
|
+
super.tap { self.last_error = job_data['last_error'] }
|
173
|
+
end
|
174
|
+
end
|
175
|
+
```
|
176
|
+
|
100
177
|
## ActiveJob Cheatsheet
|
101
178
|
|
102
179
|
### Configuring a Queue Adapter
|
data/app/models/marj.rb
CHANGED
@@ -2,17 +2,18 @@
|
|
2
2
|
|
3
3
|
require 'active_job'
|
4
4
|
require 'active_record'
|
5
|
+
require_relative '../../lib/marj_config'
|
5
6
|
|
6
7
|
# Marj is a Minimal ActiveRecord-based Jobs library.
|
7
8
|
#
|
8
9
|
# See https://github.com/nicholasdower/marj
|
9
10
|
class Marj < ActiveRecord::Base
|
10
11
|
# The Marj version.
|
11
|
-
VERSION = '
|
12
|
+
VERSION = '2.0.0'
|
12
13
|
|
13
14
|
# Executes the job associated with this record and returns the result.
|
14
15
|
def execute
|
15
|
-
# Normally we would call ActiveJob::Base#execute which has the following
|
16
|
+
# Normally we would call ActiveJob::Base#execute which has the following implementation:
|
16
17
|
# ActiveJob::Callbacks.run_callbacks(:execute) do
|
17
18
|
# job = deserialize(job_data)
|
18
19
|
# job.perform_now
|
@@ -30,7 +31,11 @@ class Marj < ActiveRecord::Base
|
|
30
31
|
job_data = job_data.to_h { |k, v| [k, %w[enqueued_at scheduled_at].include?(k) ? v&.iso8601 : v] }
|
31
32
|
job.deserialize(job_data)
|
32
33
|
|
33
|
-
|
34
|
+
new_executions = executions + 1
|
35
|
+
job.perform_now.tap do
|
36
|
+
# If no error was raised, the job should either be destroyed (success) or updated (retryable failure).
|
37
|
+
raise "job #{job_id} not destroyed or updated" unless destroyed? || (executions == new_executions && !changed?)
|
38
|
+
end
|
34
39
|
end
|
35
40
|
end
|
36
41
|
|
@@ -38,7 +43,7 @@ class Marj < ActiveRecord::Base
|
|
38
43
|
# past. Jobs are ordered by +priority+ (+null+ last), then +scheduled_at+ (+null+ last), then +enqueued_at+.
|
39
44
|
#
|
40
45
|
# @return [ActiveRecord::Relation]
|
41
|
-
def self.
|
46
|
+
def self.ready
|
42
47
|
where('scheduled_at is null or scheduled_at <= ?', Time.now.utc).order(
|
43
48
|
Arel.sql(<<~SQL.squish)
|
44
49
|
CASE WHEN priority IS NULL THEN 1 ELSE 0 END, priority,
|
@@ -48,23 +53,7 @@ class Marj < ActiveRecord::Base
|
|
48
53
|
)
|
49
54
|
end
|
50
55
|
|
51
|
-
|
52
|
-
#
|
53
|
-
# @param source [Proc] a job source
|
54
|
-
# @return [NilClass]
|
55
|
-
def self.work_off(source = -> { Marj.available.first })
|
56
|
-
while (record = source.call)
|
57
|
-
executions = record.executions
|
58
|
-
begin
|
59
|
-
record.execute
|
60
|
-
rescue Exception
|
61
|
-
# The job should either be discarded or updated. Otherwise, something went wrong.
|
62
|
-
raise unless record.destroyed? || (record.executions == (executions + 1) && !record.changed?)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
self.table_name = 'jobs'
|
56
|
+
self.table_name = MarjConfig.table_name
|
68
57
|
|
69
58
|
# Order by +enqueued_at+ rather than +job_id+ (the default)
|
70
59
|
self.implicit_order_column = 'enqueued_at'
|
data/lib/marj.rb
CHANGED
@@ -1,23 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
# ActiveJob queue adapter for Marj.
|
6
|
-
class MarjAdapter
|
7
|
-
# Enqueue a job for immediate execution.
|
8
|
-
#
|
9
|
-
# @param job [ActiveJob::Base] the job to enqueue
|
10
|
-
# @return [ActiveJob::Base] the enqueued job
|
11
|
-
def enqueue(job)
|
12
|
-
Marj.send(:enqueue, job)
|
13
|
-
end
|
3
|
+
require_relative 'marj_adapter'
|
4
|
+
require_relative 'marj_config'
|
14
5
|
|
15
|
-
|
16
|
-
#
|
17
|
-
# @param job [ActiveJob::Base] the job to enqueue
|
18
|
-
# @param timestamp [Numeric, NilClass] optional number of seconds since Unix epoch at which to execute the job
|
19
|
-
# @return [ActiveJob::Base] the enqueued job
|
20
|
-
def enqueue_at(job, timestamp)
|
21
|
-
Marj.send(:enqueue, job, timestamp ? Time.at(timestamp).utc : nil)
|
22
|
-
end
|
23
|
-
end
|
6
|
+
Kernel.autoload(:Marj, File.expand_path('../app/models/marj.rb', __dir__))
|
data/lib/marj_adapter.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# ActiveJob queue adapter for Marj.
|
4
|
+
class MarjAdapter
|
5
|
+
# Enqueue a job for immediate execution.
|
6
|
+
#
|
7
|
+
# @param job [ActiveJob::Base] the job to enqueue
|
8
|
+
# @return [ActiveJob::Base] the enqueued job
|
9
|
+
def enqueue(job)
|
10
|
+
Marj.send(:enqueue, job)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Enqueue a job for execution at the specified time.
|
14
|
+
#
|
15
|
+
# @param job [ActiveJob::Base] the job to enqueue
|
16
|
+
# @param timestamp [Numeric, NilClass] optional number of seconds since Unix epoch at which to execute the job
|
17
|
+
# @return [ActiveJob::Base] the enqueued job
|
18
|
+
def enqueue_at(job, timestamp)
|
19
|
+
Marj.send(:enqueue, job, timestamp ? Time.at(timestamp).utc : nil)
|
20
|
+
end
|
21
|
+
end
|
data/lib/marj_config.rb
ADDED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: marj
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Dower
|
@@ -48,13 +48,15 @@ files:
|
|
48
48
|
- README.md
|
49
49
|
- app/models/marj.rb
|
50
50
|
- lib/marj.rb
|
51
|
+
- lib/marj_adapter.rb
|
52
|
+
- lib/marj_config.rb
|
51
53
|
homepage: https://github.com/nicholasdower/marj
|
52
54
|
licenses:
|
53
55
|
- MIT
|
54
56
|
metadata:
|
55
57
|
bug_tracker_uri: https://github.com/nicholasdower/marj/issues
|
56
|
-
changelog_uri: https://github.com/nicholasdower/marj/releases/tag/
|
57
|
-
documentation_uri: https://www.rubydoc.info/github/nicholasdower/marj/
|
58
|
+
changelog_uri: https://github.com/nicholasdower/marj/releases/tag/v2.0.0
|
59
|
+
documentation_uri: https://www.rubydoc.info/github/nicholasdower/marj/v2.0.0
|
58
60
|
homepage_uri: https://github.com/nicholasdower/marj
|
59
61
|
rubygems_mfa_required: 'true'
|
60
62
|
source_code_uri: https://github.com/nicholasdower/marj
|