marj 1.1.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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