Reputation: 38092
I've got a sidekiq job that needs to be run after the commit, but only in some situations and not all, in order to avoid a common race condition.
For example, the below after_commit
will always fire but the code inside will only execute if the flag is true (previously set in the verify method).
class User < ActiveRecord::Base
...
after_commit do |user|
if @enqueue_some_job
SomeJob.new(user).enqueue
@enqueue_some_job = nil
end
end
def verify
@enqueue_some_job = ...
...
save!
end
end
The code is a bit ugly. I'd much rather be able to somehow wrap the callback inline like this:
class User < ActiveRecord::Base
def verify
if ...
run_after_commit do |user|
SomeJob.new(user).enqueue
end
end
...
save!
end
end
Does anything built into Rails exist to support a syntax like this (that doesn't rely on setting a temporary instance variable)? Or do any libraries exist that extend Rails to add a syntax like this?
Upvotes: 4
Views: 2751
Reputation: 125
Assuming that you need to verify a user after create.
after_commit :run_sidekiq_job, on: :create
after_commit :run_sidekiq_job, on: [:create, :update] // if you want on update as well.
This will ensure that your job will run only after a commit to db.
Then define your job that has to be performed.
def run_sidekiq_job
---------------
---------------
end
Hope it helps you :)
Upvotes: -1
Reputation: 38092
Found a solution using a via a concern. The snippet gets reused enough that it is probably a better option to abstract the instance variable and form a reusable pattern. It doesn't handle returns (not sure which are supported via after_commit
since no transaction is present to roll back.
app/models/concerns/callbackable.rb
module Callbackable
extend ActiveSupport::Concern
included do
after_commit do |resource|
if @_execute_after_commit
@_execute_after_commit.each do |callback|
callback.call(resource)
end
@_execute_after_commit = nil
end
end
end
def execute_after_commit(&callback)
if callback
@_execute_after_commit ||= []
@_execute_after_commit << callback
end
end
end
app/models/user.rb
class User < ActiveRecord::Base
include Callbackable
def verify
if ...
execute_after_commit do |user|
SomeJob.new(user).enqueue
end
end
...
save!
end
end
Upvotes: 3
Reputation: 102433
You can use a method name instead of a block when declaring callbacks:
class User < ActiveRecord::Base
after_commit :do_something!
def do_something!
end
end
To set a condition on the callback you can use the if
and unless
options. Note that these are just hash options - not keywords.
You can use a method name or a lambda:
class User < ActiveRecord::Base
after_commit :do_something!, if: -> { self.some_value > 2 }
after_commit :do_something!, unless: :something?
def do_something!
end
def something?
true || false
end
end
Upvotes: 0