Call hooks after the main delayed job completed - ruby-on-rails

I have been using delayed_job_active_record gem in my application, in one of my use-case i have to use one hook to send an other email after the delayed job done. https://github.com/collectiveidea/delayed_job#hooks, how can i override that in my application?.
Currently i am calling in this way
do_maintenance.delay(run_at: time).change_all_parts(batch_no)
do_maintenance is the model

Hooks from this gem provide callbacks to the job, but not to the model. But you can define custom callback in the model. That callback will be called after the delayed method:
class DoMaintenance < ApplicationRecord
extend ActiveModel::Callbacks
define_model_callbacks :change_all_parts, :only => [:after]
after_change_all_parts :notify
def change_all_parts
run_callbacks :change_all_parts do
#your delayed method
end
end
handle_asynchronously :change_all_parts
def notify
#your code
end
end

Related

sidekiq perform_in perform_at is not working

I am using Sidekiq
on my gemfile I have:
#gemfile
gem 'sidekiq', '~> 6.0.7'
Now my model looks like
class Special < ActiveRecord::Base
def set_queue
if send_at.present? # send at is a data time
SpecialQueueClientsJob.perform_at(self.send_at, self)
end
end
end
This is the code to my job and this works perfect when I just call it normally SpecialQueueClientsJob.perform_later(special: self)
class SpecialQueueClientsJob < ApplicationJob
queue_as :default
def perform(special)
...
end
end
https://github.com/mperham/sidekiq
I am getting an error undefined method perform_at' for SpecialQueueClientsJob `
I am looking at https://github.com/mperham/sidekiq/wiki/Scheduled-Jobs
Sidekiq allows you to schedule the time when a job will be executed.
You use perform_in(interval_in_seconds, *args) or
perform_at(timestamp, *args) rather than the standard
perform_async(*args):
MyWorker.perform_in(3.hours, 'mike', 1)
MyWorker.perform_at(3.hours.from_now, 'mike', 1) This is useful for
example if you want to send the user an email 3 hours after they sign
up. Jobs which are scheduled in the past are enqueued for immediate
execution.

How do I create delayed_job jobs with hooks/callbacks?

I am using the most basic version of delayed_job in a Rails app. I have the max time allowed for a delayed_job set at 10 minutes. I would like to get the hooks/callbacks working so I can do something after a job stop executing at the 10 minute mark.
I have this set in my rails app:
config.active_job.queue_adapter = :delayed_job
This is how I normally queue a job:
object.delay.object_action
The hook/callback example is for a named job but the basic, getting started steps are not for a named job. So I don't think I have a named job. Here is the example given to get the callbacks working:
class ParanoidNewsletterJob < NewsletterJob
def enqueue(job)
record_stat 'newsletter_job/enqueue'
end
def perform
emails.each { |e| NewsletterMailer.deliver_text_to_email(text, e) }
end
def before(job)
record_stat 'newsletter_job/start'
end
def after(job)
record_stat 'newsletter_job/after'
end
def success(job)
record_stat 'newsletter_job/success'
end
def error(job, exception)
Airbrake.notify(exception)
end
def failure(job)
page_sysadmin_in_the_middle_of_the_night
end
end
I would love to get the after or error hooks/callbacks to fire.
Where do I put these callbacks in my Rails app to have them fire for the basic delayed_job setup? If I should be using ActiveJob callbacks where do you put those callbacks given delayed_job is being used?
You cannot use object.delay.object_action convenience syntax if you want more advanced features like callbacks. The #delay convenience method will generate a job object that works similar to this:
# something like this is already defined in delayed_job
class MethodCallerJob
def initialize(object, method, *args)
#object = object
#method = method
#args = args
end
def perform
#object.send(#method, *#args)
end
end
# `object.delay.object_action` does the below automatically for you
# instantiates a job with your object and method call
job = MethodCallerJob.new(object, :object_action, [])
Delayed::Job.enqueue(job) # enqueues it for running later
then later, in the job worker, something like the below happens:
job = Delayed::Job.find(job_id) # whatever the id turned out to be
job.invoke_job # does all the things, including calling #perform and run any hooks
job.delete # if it was successful
You have to create what the delayed_job README calls "Custom Jobs", which are just plain POROs that have #perform defined at a minimum. Then you can customize it and add all the extra methods that delayed_job uses for extra features like max_run_time, queue_name, and the ones you want to use: callbacks & hooks.
Sidenote: The above info is for using delayed_job directly. All of the above is possible using ActiveJob as well. You just have to do it the ActiveJob way by reading the documentation & guides on how, just as I've linked you to the delayed_job README, above.
You can create delayed_job hooks/callback by something like this
module Delayed
module Plugins
class TestHooks < Delayed::Plugin
callbacks do |lifecycle|
lifecycle.before(:perform) do |_worker, job|
.....
end
end
end
end
end
And need this plugin to initializer
config/initializers/delayed_job.rb
require_relative 'path_to_test_plugin'
Delayed::Worker.plugins << Delayed::Plugins::TestHooks
Similar to perform there are also hooks for success failure and error.
And similar to 'before' you can also capture the 'after' hooks.

Is there any easy way to trigger same instance method after different custom callbacks in Rails?

Consider my model to be like this:
class SampleProject < ActiveRecord::Base
#consider all these callbacks to be custom defined ones
#I'm using default callbacks just to explain my problem
after_save :simple_testing
after_update :simple_testing
before_save :simple_testing
before_create :simple_testing
after_commit :simple_testing
def simple_testing
#my custom code
end
end
So here the same method is being invoked after all the call backs. So is there any simple way to define that all this callbacks should evoke simple_testing method ?
I don't understand why you would ever want to do this, but remember you can use Ruby code anywhere in your class definition.
callbacks = [:after_save, :after_update, :before_save, :before_create, after_commit]
callbacks.each do |callback|
self.send(callback, :simple_testing)
end
I've not tried this but it should work i think.

Could you replicate RubyOnRails AR events in Scala?

In Ruby, you can create a class (Model) like this:
class User < ActiveRecord::Base
before_save :do_something
def save
# do something will get called here magically
...
end
def do_something
end
end
Now say in scala I want to do the same thing, is this possible?
Basically whenever any method is called in a scala class, do_something will get called before it.
Note: You could add more methods to get called like:
before_save :do_something, do_something_else
Take a look at the callbacks hooks of the Scala ActiveRecord library.
Scala ActiveRecord provides callback hooks for executing actions
before or after the model is saved, deleted, or validated. You can
override callback methods and implement logic, if necessary. Nothing
is done by default.

Rails - Send all emails with delayed_job asynchronously

I'm using delayed_job and I'm very happy with it (especially with the workless extension).
But I'd like to set that ALL mails from my app are sent asynchronously.
Indeed, the solution offered for mailers
# without delayed_job
Notifier.signup(#user).deliver
# with delayed_job
Notifier.delay.signup(#user)
doesn't suit me because:
it is not easily maintainable
mails sent from gems are not sent asynchronously (devise, mailboxer)
I could use this kind of extension https://github.com/mhfs/devise-async but I'd rather figure out a solution for the whole app at once.
Can't I extend ActionMailer to override the .deliver method (like here https://stackoverflow.com/a/4316543/1620081 but it is 4 years old, like pretty much all the doc I found on the topic)?
I'm using Ruby 1.9 and Rails 3.2 with activerecord.
Thanks for support
A simple solution would be to write a utility method on the Notifier object as follows:
class Notifier
def self.deliver(message_type, *args)
self.delay.send(message_type, *args)
end
end
Send the sign up email as follows:
Notifier.deliver(:signup, #user)
The utility method provides a single point where if needed you could replace delayed job with resque or sidekiq solutions.
If you have your ActiveJob and concurrency library set-up is done documentation here.The most simple solution is to override you device send_devise_notification instance methods involved with the transactions mails like shown here
class User < ApplicationRecord
# whatever association you have here
devise :database_authenticatable, :confirmable
after_commit :send_pending_devise_notifications
# whatever methods you have here
protected
def send_devise_notification(notification, *args)
if new_record? || changed?
pending_devise_notifications << [notification, args]
else
render_and_send_devise_message(notification, *args)
end
end
private
def send_pending_devise_notifications
pending_devise_notifications.each do |notification, args|
render_and_send_devise_message(notification, *args)
end
pending_devise_notifications.clear
end
def pending_devise_notifications
#pending_devise_notifications ||= []
end
def render_and_send_devise_message(notification, *args)
message = devise_mailer.send(notification, self, *args)
# Deliver later with Active Job's `deliver_later`
if message.respond_to?(:deliver_later)
message.deliver_later
# Remove once we move to Rails 4.2+ only, as `deliver` is deprecated.
elsif message.respond_to?(:deliver_now)
message.deliver_now
else
message.deliver
end
end
end

Resources