active_job-performs 0.3.0 → 0.3.2

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: 5741d785f1439dac15d6ec2734d40e5560013f69025977713771309c7f15c4c7
4
- data.tar.gz: c01e5d7ebe8f599024cd5eba79011a959d2e31c8d5141b787b46d55b1150b73b
3
+ metadata.gz: 566acd14649609a2e16b658a93fe276f80609866c61fb37c11e720b581bc0f76
4
+ data.tar.gz: 5174b18e562c53966639bb16e3d729885d99f1ebe01163175a1513dded11cbff
5
5
  SHA512:
6
- metadata.gz: 7ea09dcf8da5a0c7c1f5181ed70e07c3a3028d7d12c5c84cece6c6d59ab6a7921ea7ddc05180fd2b8f551332277a2e032bee0c057398a33b4e7f3a645f451c3f
7
- data.tar.gz: 32899d17334f35819a1aa61ffa56c58a492d1b304a003b1e5d6156a26450087e2ac7083021a312269d2c2ee0519e43ad78d129629c6641036f5ad222130f6c3a
6
+ metadata.gz: f2fd767c6bd1c66d26a046e84dea4cd2ea0bfdb0ec50452699151e90fba9856b14875ab1a98371cf2727423a31871db4af432a965784a9cf61d92b58917ce0d2
7
+ data.tar.gz: cd9628dce5378c14ecae35da1fc83ae4487ea0ece02a5e4a78226572778ca7d370c5975e99ff880fa9bf030f026c244d1d3cf2abac838fb7d1bec373f7327cbf
data/Gemfile CHANGED
@@ -16,4 +16,4 @@ gem "debug"
16
16
  gem "activejob", ">= 7.1"
17
17
  gem "activerecord", ">= 7.1"
18
18
 
19
- gem "sqlite3"
19
+ gem "sqlite3", "~> 1.4"
data/Gemfile.lock CHANGED
@@ -1,22 +1,22 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- active_job-performs (0.3.0)
4
+ active_job-performs (0.3.2)
5
5
  activejob (>= 6.1)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
- activejob (7.1.1)
11
- activesupport (= 7.1.1)
10
+ activejob (7.1.3.4)
11
+ activesupport (= 7.1.3.4)
12
12
  globalid (>= 0.3.6)
13
- activemodel (7.1.1)
14
- activesupport (= 7.1.1)
15
- activerecord (7.1.1)
16
- activemodel (= 7.1.1)
17
- activesupport (= 7.1.1)
13
+ activemodel (7.1.3.4)
14
+ activesupport (= 7.1.3.4)
15
+ activerecord (7.1.3.4)
16
+ activemodel (= 7.1.3.4)
17
+ activesupport (= 7.1.3.4)
18
18
  timeout (>= 0.4.0)
19
- activesupport (7.1.1)
19
+ activesupport (7.1.3.4)
20
20
  base64
21
21
  bigdecimal
22
22
  concurrent-ruby (~> 1.0, >= 1.0.2)
@@ -26,46 +26,43 @@ GEM
26
26
  minitest (>= 5.1)
27
27
  mutex_m
28
28
  tzinfo (~> 2.0)
29
- base64 (0.1.1)
30
- bigdecimal (3.1.4)
31
- concurrent-ruby (1.2.2)
29
+ base64 (0.2.0)
30
+ bigdecimal (3.1.8)
31
+ concurrent-ruby (1.3.3)
32
32
  connection_pool (2.4.1)
33
- debug (1.8.0)
34
- irb (>= 1.5.0)
35
- reline (>= 0.3.1)
36
- drb (2.1.1)
37
- ruby2_keywords
33
+ debug (1.9.2)
34
+ irb (~> 1.10)
35
+ reline (>= 0.3.8)
36
+ drb (2.2.1)
38
37
  globalid (1.2.1)
39
38
  activesupport (>= 6.1)
40
- i18n (1.14.1)
39
+ i18n (1.14.5)
41
40
  concurrent-ruby (~> 1.0)
42
- io-console (0.6.0)
43
- irb (1.8.0)
44
- rdoc (~> 6.5)
45
- reline (>= 0.3.6)
46
- minitest (5.16.3)
41
+ io-console (0.7.2)
42
+ irb (1.13.2)
43
+ rdoc (>= 4.0.0)
44
+ reline (>= 0.4.2)
45
+ minitest (5.24.0)
47
46
  minitest-sprint (1.2.2)
48
47
  path_expander (~> 1.1)
49
- mutex_m (0.1.2)
48
+ mutex_m (0.2.0)
50
49
  path_expander (1.1.1)
51
- psych (5.1.0)
50
+ psych (5.1.2)
52
51
  stringio
53
- rake (13.0.6)
54
- rdoc (6.5.0)
52
+ rake (13.2.1)
53
+ rdoc (6.7.0)
55
54
  psych (>= 4.0.0)
56
- reline (0.3.8)
55
+ reline (0.5.9)
57
56
  io-console (~> 0.5)
58
- ruby2_keywords (0.0.5)
59
- sqlite3 (1.6.4-arm64-darwin)
60
- sqlite3 (1.6.4-x86_64-linux)
61
- stringio (3.0.8)
62
- timeout (0.4.0)
57
+ sqlite3 (1.7.3-arm64-darwin)
58
+ sqlite3 (1.7.3-x86_64-linux)
59
+ stringio (3.1.1)
60
+ timeout (0.4.1)
63
61
  tzinfo (2.0.6)
64
62
  concurrent-ruby (~> 1.0)
65
63
 
66
64
  PLATFORMS
67
- arm64-darwin-20
68
- arm64-darwin-22
65
+ arm64-darwin
69
66
  x86_64-linux
70
67
 
71
68
  DEPENDENCIES
@@ -76,7 +73,7 @@ DEPENDENCIES
76
73
  minitest (~> 5.0)
77
74
  minitest-sprint
78
75
  rake (~> 13.0)
79
- sqlite3
76
+ sqlite3 (~> 1.4)
80
77
 
81
78
  BUNDLED WITH
82
- 2.4.19
79
+ 2.5.14
data/README.md CHANGED
@@ -1,8 +1,80 @@
1
1
  # ActiveJob::Performs
2
2
 
3
- `ActiveJob::Performs` adds the `performs` macro to set up jobs by convention.
3
+ `ActiveJob::Performs` adds a `performs` class method to make the model + job loop vastly more conventional. You use it like this:
4
4
 
5
- ## Usage with `include GlobalID::Identification` objects
5
+ ```ruby
6
+ class Post < ApplicationRecord
7
+ performs :publish
8
+ # Or `performs def publish`!
9
+
10
+ def publish
11
+ # Some logic to publish a post
12
+ end
13
+ end
14
+ ```
15
+
16
+ Then we build a job for the instance method and define a `post.publish_later` instance method, and more:
17
+
18
+ ```ruby
19
+ class Post < ApplicationRecord
20
+ class Job < ApplicationJob; end # We build a general Job class to share configuration between method jobs.
21
+
22
+ # Individual method jobs inherit from the `Post::Job` defined above.
23
+ class PublishJob < Job
24
+ # We generate the required `perform` method passing in the `post` and calling `publish` on it.
25
+ def perform(post, *, **) = post.publish(*, **)
26
+ end
27
+
28
+ # On Rails 7.1, where `ActiveJob.perform_all_later` exists, we also generate
29
+ # a bulk method to enqueue many jobs at once. So you can do this:
30
+ #
31
+ # Post.unpublished.in_batches.each(&:publish_later_bulk)
32
+ def self.publish_later_bulk
33
+ ActiveJob.perform_all_later all.map { PublishJob.new(_1) }
34
+ end
35
+
36
+ # We generate `publish_later` to wrap the job execution forwarding arguments and options.
37
+ def publish_later(*, **) = PublishJob.perform_later(self, *, **)
38
+
39
+ def publish
40
+ # Some logic to publish a post.
41
+ end
42
+ end
43
+ ```
44
+
45
+ ## Benefits
46
+
47
+ 1. Conventional Jobs: they'll now mostly call instance methods like `publish_later` -> `publish`.
48
+ 1. Follows Rails' internal conventions: this borrows from `ActionMailbox::InboundEmail#process_later` calling `process` and `ActionMailer::Base#deliver_later` calling `deliver`.
49
+ 1. Clarity & less guess work: the `_later` methods standardize how you call jobs throughout your app, so you can instantly tell what's happening.
50
+ 1. Less tedium: getting an instance method run in the background is just now a `performs` call with some potential configuration.
51
+ 1. Fewer files to manage: you don't have to dig up something in `app/jobs` just to learn almost nothing from the boilerplate in there.
52
+ 1. Remaining jobs stand out: `app/jobs` is way lighter, so any jobs in there that don't fit the `performs` pattern now stand out way more.
53
+ 1. More consolidated logic: sometimes Job classes house model-level logic, but now it's all the way out in `app/jobs` instead of `app/models`, huh?
54
+
55
+ > [!TIP]
56
+ > On that last point, `performs` does put more logic back within your Active Records, so if you need further encapsulation to prevent them growing too large, consider checking out [active_record-associated_object](https://github.com/kaspth/active_record-associated_object).
57
+
58
+ ### Praise from people
59
+
60
+ Here's what [@claudiob](https://github.com/claudiob) had to say after using `ActiveJob::Performs`:
61
+
62
+ > I’ve been using active_job-performs for the last month and I love it love it love it!!
63
+ >
64
+ > Your thought process behind it is so thorough. I have a bunch of jobs now attached to models and my app/jobs folder… is empty!!
65
+ >
66
+ > This saves me a lot of mental hoops, I don’t have to switch between files anymore, everything is self-contained. Thank you!!!
67
+
68
+ From [@andycroll](https://github.com/andycroll) in a [writeup](https://andycroll.com/ruby/launching-usingrails) about launching [UsingRails](https://usingrails.com):
69
+
70
+ > I’ve also adopted a couple of gems—with exceptional Rails-level taste and author pedigree—that I hadn’t used in anger before, including `active_job-performs` from Kasper […]. Would recommend both.
71
+
72
+ And [@nshki](https://github.com/nshki) after trying it:
73
+
74
+ > Spent some time playing with [@kaspth](https://github.com/kaspth)'s [`ActiveRecord::AssociatedObject`](https://github.com/kaspth/active_record-associated_object) and `ActiveJob::Performs` and wow! The conventions these gems put in place help simplify a codebase drastically. I particularly love `ActiveJob::Performs`—it helped me refactor out all `ApplicationJob` classes I had and keep important context in the right domain model.
75
+
76
+ ## Usage
77
+ ### with `ActiveRecord::Base` & other `GlobalID::Identification` objects
6
78
 
7
79
  `ActiveJob::Performs` works with any object that has `include GlobalID::Identification` and responds to that interface.
8
80
 
@@ -14,7 +86,7 @@ class Post < ActiveRecord::Base
14
86
 
15
87
  # `performs` builds a `Post::PublishJob` and routes configs over to it.
16
88
  performs :publish, queue_as: :important, discard_on: SomeError do
17
- retry_on TimeoutError, wait: :exponentially_longer
89
+ retry_on TimeoutError, wait: :polynomially_longer
18
90
  end
19
91
 
20
92
  def publish
@@ -34,7 +106,7 @@ class Post < ActiveRecord::Base
34
106
  class PublishJob < Job
35
107
  queue_as :important
36
108
  discard_on SomeError
37
- retry_on TimeoutError, wait: :exponentially_longer
109
+ retry_on TimeoutError, wait: :polynomially_longer
38
110
 
39
111
  # We generate `perform` passing in the `post` and calling `publish` on it.
40
112
  def perform(post, *arguments, **options)
@@ -46,6 +118,10 @@ class Post < ActiveRecord::Base
46
118
  # a bulk method to enqueue many jobs at once. So you can do this:
47
119
  #
48
120
  # Post.unpublished.in_batches.each(&:publish_later_bulk)
121
+ #
122
+ # Or pass in a subset of posts as an argument:
123
+ #
124
+ # Post.publish_later_bulk Post.unpublished
49
125
  def self.publish_later_bulk
50
126
  ActiveJob.perform_all_later all.map { PublishJob.new(_1) }
51
127
  end
@@ -61,7 +137,7 @@ class Post < ActiveRecord::Base
61
137
  end
62
138
  ```
63
139
 
64
- We generate the `Post::Job` class above to share configuration between method level jobs. E.g. if you had a retract method that was setup very similar, you could do:
140
+ We generate the `Post::Job` class above to share configuration between method level jobs. E.g. if you had a `retract` method that was setup very similar, you could do:
65
141
 
66
142
  ```ruby
67
143
  class Post < ActiveRecord::Base
@@ -99,6 +175,38 @@ class Post < ActiveRecord::Base
99
175
  end
100
176
  ```
101
177
 
178
+ #### Establishing patterns across your app
179
+
180
+ If there's an Active Record method that you'd like any model to be able to run from a background job, you can set them up in your `ApplicationRecord`:
181
+
182
+ ```ruby
183
+ class ApplicationRecord < ActiveRecord::Base
184
+ self.abstract_class = true
185
+
186
+ # We're passing specific queues for monitoring, but you may not need or want them.
187
+ performs :touch, queue_as: "active_record.touch"
188
+ performs :update, queue_as: "active_record.update"
189
+ performs :destroy, queue_as: "active_record.destroy"
190
+ end
191
+ ```
192
+
193
+ Then a model could now run things like:
194
+
195
+ ```ruby
196
+ record.touch_later
197
+ record.touch_later :reminded_at, time: 5.minutes.from_now # Pass supported arguments to `touch`
198
+
199
+ record.update_later reminded_at: 1.year.ago
200
+
201
+ # Particularly handy to use on a record with many `dependent: :destroy` associations.
202
+ # Plus if anything fails, the transaction will rollback and the job fails, so you can retry it later!
203
+ record.destroy_later
204
+ ```
205
+
206
+ You may not want this for `touch` and `update`, and maybe you'd rather architect your system in such a way that they don't have so many side-effects, but having the option can be handy!
207
+
208
+ Also, I haven't tested all the Active Record methods, so please file an issue if you encounter any.
209
+
102
210
  #### Method suffixes
103
211
 
104
212
  `ActiveJob::Performs` supports Ruby's stylistic method suffixes, i.e. ? and ! respectively.
@@ -137,9 +245,22 @@ Additionally, in case the job is meant to be internal to the object, `performs :
137
245
 
138
246
  E.g. `private performs :some_method` will generate a private `some_method_later` method.
139
247
 
140
- ### Usage with ActiveRecord::AssociatedObject
248
+ #### Overriding the generated instance `_later` method
141
249
 
142
- The [`ActiveRecord::AssociatedObject`](https://github.com/kaspth/active_record-associated_object) gem also implements `GlobalID::Identification`, so you can do this too:
250
+ The instance level `_later` methods, like `publish_later` above, are generated into an included module. So in case you have a condition where you'd like to prevent the enqueue, you can override the method and call `super`:
251
+
252
+ ```ruby
253
+ class Post < ApplicationRecord
254
+ performs def publish
255
+ # …
256
+ end
257
+ def publish_later = some_condition? && super
258
+ end
259
+ ```
260
+
261
+ ### Usage with `ActiveRecord::AssociatedObject`
262
+
263
+ The [`ActiveRecord::AssociatedObject`](https://github.com/kaspth/active_record-associated_object) gem also implements `GlobalID::Identification`, so you use `performs` exactly like you would on Active Records:
143
264
 
144
265
  ```ruby
145
266
  class Post::Publisher < ActiveRecord::AssociatedObject
@@ -159,6 +280,11 @@ class Post::Publisher < ActiveRecord::AssociatedObject
159
280
  end
160
281
  ```
161
282
 
283
+ > [!NOTE]
284
+ > There's one difference with Active Record: you must pass in a set to `_later_bulk` methods. Like so:
285
+ >
286
+ > `Post::Publisher.publish_later_bulk Post::Publisher.first(10)`
287
+
162
288
  ### Passing `wait` to `performs`
163
289
 
164
290
  If there's a job you want to defer, `performs` can set it for each invocation:
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ActiveJob
4
4
  module Performs
5
- VERSION = "0.3.0"
5
+ VERSION = "0.3.2"
6
6
  end
7
7
  end
@@ -46,15 +46,15 @@ module ActiveJob::Performs
46
46
  end
47
47
  RUBY
48
48
 
49
- if ActiveJob.respond_to?(:perform_all_later) && respond_to?(:all)
49
+ if ActiveJob.respond_to?(:perform_all_later)
50
50
  class_eval <<~RUBY, __FILE__, __LINE__ + 1
51
- def self.#{method}_later_bulk#{suffix}
52
- ActiveJob.perform_all_later all.map { #{job}.scoped_by_wait(_1).new(_1) }
51
+ def self.#{method}_later_bulk#{suffix}(set#{" = all" if respond_to?(:all)})
52
+ ActiveJob.perform_all_later set.map { #{job}.scoped_by_wait(_1).new(_1) }
53
53
  end
54
54
  RUBY
55
55
  end
56
56
 
57
- class_eval <<~RUBY, __FILE__, __LINE__ + 1
57
+ performs_later_methods.class_eval <<~RUBY, __FILE__, __LINE__ + 1
58
58
  def #{method}_later#{suffix}(*arguments, **options)
59
59
  #{job}.scoped_by_wait(self).perform_later(self, *arguments, **options)
60
60
  end
@@ -73,6 +73,10 @@ module ActiveJob::Performs
73
73
  configs.each { job_class.public_send(_1, _2) }
74
74
  job_class.class_exec(&block) if block_given?
75
75
  end
76
+
77
+ def performs_later_methods
78
+ @performs_later_methods ||= Module.new.tap { include _1 }
79
+ end
76
80
  end
77
81
 
78
82
  ActiveSupport.on_load(:active_record) { extend ActiveJob::Performs }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_job-performs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kasper Timm Hansen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-11-04 00:00:00.000000000 Z
11
+ date: 2024-12-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activejob
@@ -62,7 +62,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
62
62
  - !ruby/object:Gem::Version
63
63
  version: '0'
64
64
  requirements: []
65
- rubygems_version: 3.4.19
65
+ rubygems_version: 3.5.18
66
66
  signing_key:
67
67
  specification_version: 4
68
68
  summary: ActiveJob::Performs adds the `performs` macro to set up jobs by convention.