active_job-performs 0.3.0 → 0.3.2

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: 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.