Skip to content

Commit

Permalink
Document and test a pattern for app-wide methods (#7)
Browse files Browse the repository at this point in the history
* Document and test a pattern for app-wide methods

You can do this on `ApplicationRecord`:

```ruby
class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true

  # We're passing specific queues for monitoring, but you may not need or want them.
  performs :touch,   queue_as: "active_record.touch"
  performs :update,  queue_as: "active_record.update"
  performs :destroy, queue_as: "active_record.destroy"
end
```

* Always UTC time so CI is happy?

* Gotta love times on CI
  • Loading branch information
kaspth committed Dec 17, 2023
1 parent 10e17dd commit 6c29ce4
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 1 deletion.
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,38 @@ class Post < ActiveRecord::Base
end
```

#### Establishing patterns across your app

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

```ruby
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true

# We're passing specific queues for monitoring, but you may not need or want them.
performs :touch, queue_as: "active_record.touch"
performs :update, queue_as: "active_record.update"
performs :destroy, queue_as: "active_record.destroy"
end
```

Then a model could now run things like:

```ruby
record.touch_later
record.touch_later :reminded_at, time: 5.minutes.from_now # Pass supported arguments to `touch`

record.update_later reminded_at: 1.year.ago

# Particularly handy to use on a record with many `dependent: :destroy` associations.
# Plus if anything fails, the transaction will rollback and the job fails, so you can retry it later!
record.destroy_later
```

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!

Also, I haven't tested all the Active Record methods, so please file an issue if you encounter any.

#### Method suffixes

`ActiveJob::Performs` supports Ruby's stylistic method suffixes, i.e. ? and ! respectively.
Expand Down
36 changes: 36 additions & 0 deletions test/active_job/active_record/test_performs.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,42 @@

module ActiveJob::ActiveRecord; end
class ActiveJob::ActiveRecord::TestPerforms < ActiveSupport::TestCase
setup { @invoice = Invoice.create! }

test "touch_later" do
assert_changes -> { @invoice.reload.updated_at } do
assert_performed_with job: ApplicationRecord::TouchJob, args: [@invoice] do
@invoice.touch_later
end
end

time = Time.now.utc.change(usec: 0)
assert_changes -> { @invoice.reload.reminded_at }, to: time do
assert_performed_with job: ApplicationRecord::TouchJob, args: [@invoice, :reminded_at, time: time] do
@invoice.touch_later :reminded_at, time: time
end
end
end

test "update_later" do
time = Time.now.utc.change(usec: 0)
assert_changes -> { @invoice.reload.reminded_at }, to: time do
assert_performed_with job: ApplicationRecord::UpdateJob, args: [@invoice, reminded_at: time] do
@invoice.update_later reminded_at: time
end
end
end

test "destroy_later" do
assert_enqueued_with job: ApplicationRecord::DestroyJob, args: [@invoice] do
@invoice.destroy_later
end
perform_enqueued_jobs
assert_raise(ActiveRecord::RecordNotFound) { @invoice.reload }
end
end

class ActiveJob::ActiveRecord::TestPerformsBulk < ActiveSupport::TestCase
setup do
Invoice.insert_all [{}, {}, {}, {}, {}]
end
Expand Down
10 changes: 9 additions & 1 deletion test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,17 @@ class ApplicationJob < ActiveJob::Base; end
end
end

class Invoice < ActiveRecord::Base
class ApplicationRecord < ActiveRecord::Base
include GlobalID::Identification

self.abstract_class = true

performs :touch, queue_as: "active_record.touch"
performs :update, queue_as: "active_record.update"
performs :destroy, queue_as: "active_record.destroy"
end

class Invoice < ApplicationRecord
performs :deliver_reminder!
def deliver_reminder!
touch :reminded_at
Expand Down

0 comments on commit 6c29ce4

Please sign in to comment.