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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f5fe8134484360b68c0cf69356f6a037897c6896be81bc393a9dfc0fb0684055
4
- data.tar.gz: 4b7575b721d05681876846f3f06d47440d366bc875e02cadeae7de4582270114
3
+ metadata.gz: c4feb80230bafd2d67bdb0316598f4c46eabd01ca86c35fb8b01e04c865f91a8
4
+ data.tar.gz: be461234b8359c9f974dd4160d896cc048d0dcc1f4b7d74b57042d62f9950a18
5
5
  SHA512:
6
- metadata.gz: dbfa1cd4cfb90ca4c47b039f0f320e5cc847aa9863b640cd52b445f5074c13aaa0f2679f4ec950001a086f0b981f58f6aa180a250a4790753691b249dc4fa19f
7
- data.tar.gz: d1903bcb55c22522d270d950b316ee99ce05edca0c279cc055cb39a71ecfd389d02acd5d1bcc029e931636e26c4708bfd872a20ab71ecc9328e69928a3b535c2
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
- ### 2. Create the jobs table
27
+ ```shell
28
+ bundle install
29
+ ```
26
30
 
27
- Apply a database migration:
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 available jobs:
88
- Marj.work_off
99
+ # Run all ready jobs:
100
+ while (record = Marj.ready.first)
101
+ record.execute
102
+ end
89
103
 
90
- # Run all available jobs in a specific queue:
91
- Marj.work_off(source = -> { Marj.where(queue_name: 'foo').available.first })
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 available:
109
+ # Run jobs as they become ready:
94
110
  loop do
95
- Marj.work_off
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 = '1.1.0'
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 implemenation:
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
- job.perform_now
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.available
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
- # Executes any available jobs from the specified source.
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
- Kernel.autoload(:Marj, File.expand_path('../app/models/marj.rb', __dir__))
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
- # Enqueue a job for execution at the specified time.
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__))
@@ -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
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Marj configuration.
4
+ class MarjConfig
5
+ @table_name = 'jobs'
6
+
7
+ class << self
8
+ # The name of the database table. Defaults to "jobs".
9
+ #
10
+ # @return [String]
11
+ attr_accessor :table_name
12
+ end
13
+ end
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: 1.1.0
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/v1.1.0
57
- documentation_uri: https://www.rubydoc.info/github/nicholasdower/marj/v1.1.0
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