How to add two delayed jobs - ruby-on-rails

I am using delayed_jobs gem. Suppose, I have a ArticleController with following code
#articles.each do |ar|
#call send mail method and add to delay
#call createpdf method and add to delay
end
and another class BackgroundJobs.rb
class BackgroundJobs < ActiveRecord::Base
def sendmail(article_id)
#code to send mail
end
def createpdf((article_id))
#code to generate pdf
end
end
How can I add the send mail and createpdf methods to delayed job in articlecontroller code.

First, i would create class methods instead of instance methods in BackgroundJobs:
class BackgroundJobs < ActiveRecord::Base
def self.sendmail(article_id)
#code to send mail
end
def self.createpdf(article_id)
#code to generate pdf
end
end
And then just call them directly in the controller:
#articles.each do |ar|
BackgroundJobs.delay.sendmail(ar.id)
BackgroundJobs.delay.createpdf(ar.id)
end

Related

How to access current user in a mailer file before action

I have a before action in a user mailer file, which is supposed to stop mailers sending if a column on user is set to true or false. However current user is currently unavailable. I understand why, but was wondering if there was a way to do this.
I want to avoid adding the check_if_users_can_receive_mailers at the top of each mailer method.
before_action :check_if_users_can_receive_mailers
#methods that send mailers
private
def check_if_users_can_receive_mailers
current_user.send_mailers?
end
You have to make the current user available as a attribute or class variable. The most straight forward method is something like this:
class MailerBase < ActionMailer::Base
before_action :check_if_users_can_receive_mailers
attr_accessor :user
def initialize(user)
#user = user
end
private
def check_if_users_can_receive_mailers
user.send_mailers?
end
end
class SomeMailerClass < MailerBase
end
In Rails only your controller and views are request aware. Mailers and models and other classes in your application are not and they cannot get the current user since they can't access the session nor the method current_user which is a helper method mixed into your controller (and the view context).
If your mailers need to know about the current user the most logical approach is to pass that information into the mailer:
class UserMailer < ApplicationMailer
def intialize(user)
#user = user
end
end
However a mailer should only have one job - to send emails and it shouldn't be questioning if it should do the job or not. Determining if you should send an email to the user should be done elsewhere. You can place this logic in the controller or even better in a service object:
# app/notifiers/user_notifier.rb
class UserNotifier
def initialize(user, event:)
#user = user
#event = event
end
def notify
if #user.wants_email?
spam_user!
end
send_in_app_notification
end
def self.notify(user, event:)
new(user, event:)
end
private
def spam_user!
# ...
end
def send_in_app_notification
# ...
end
end
class ThingsController
def create
#thing = Thing.new
if #thing.save
UserNotifier.notify(current_user, event: :thing_created)
redirect_to #thing
else
render :new
end
end
end

How can you use a setup method in ActionMailer Previews?

I would like to avoid duplicating the setup for multiple mailer previews. What is the best way to clean this up?
class MyMailerPreview < ActionMailer::Preview
def email1
setup
mailer.email1
end
def email2
setup
mailer.email2
end
def email3
setup
mailer.email3
end
end
Here are two possible solutions I found:
There is something called preview_interceptors that are used when generating mailer previews, you could add your own like this:
config/environments/development.rb
config.action_mailer.preview_interceptors = :my_setup
test/mailers/previews/my_setup.rb
class MySetup
def self.previewing_email(message)
message.subject = "New subject"
end
end
test/mailers/previews/user_mailer_preview.rb
class UserMailerPreview < ActionMailer::Preview
include ActionMailer::Previews
register_preview_interceptor :my_setup
def welcome_email
UserMailer.with(user: User.first).welcome_email
end
end
The message parameter is an instance of ActionMailer::Parameterized::MessageDelivery, I am not sure everything you can do with it, but you can set some attributes on the email itself.
I couldn't find much documentation on preview interceptors, but here is a link to how they are used in Rails.
# Previews can also be intercepted in a similar manner as deliveries can be by registering
# a preview interceptor that has a <tt>previewing_email</tt> method:
#
# class CssInlineStyler
# def self.previewing_email(message)
# # inline CSS styles
# end
# end
#
# config.action_mailer.preview_interceptors :css_inline_styler
#
# Note that interceptors need to be registered both with <tt>register_interceptor</tt>
# and <tt>register_preview_interceptor</tt> if they should operate on both sending and
# previewing emails.
I tried to include Rails before_action in the class, but it wouldn't hook the methods in the previewer, so the second option I found is to build your own before_action like this:
module MySetup
def before_action(*names)
UserMailer.instance_methods(false).each do |method|
alias_method "old_#{method}", method
define_method method do
names.each do |name|
send(name)
end
send("old_#{method}")
end
end
end
end
class UserMailerPreview < ActionMailer::Preview
extend MySetup
def welcome_email
UserMailer.with(user: User.first).welcome_email
end
before_action :setup
private
def setup
puts "Setting up"
end
end
Use an initialize method.
Just override the parent initialize method, call super and then run your setup:
class MyMailerPreview < ActionMailer::Preview
def initialize( params = {} )
super( params )
#email_address = "jules#verne.com"
end
def email1
mailer.email1( #email_address )
end
end
You can view the ActionMailer::Preview.new method here as a reference.
Based on my understanding of what you're asking maybe you could add it into one single method that takes the mailer method as a param
class MyMailerPreview < ActionMailer::Preview
def email_for(emailx) # (Pass the method(email1, etc) as an argument where you're calling it
setup
mailer.send(emailx.to_sym) # Call the method param as a method on the mailer
end
end
Would that work for you?

Sending multiple mails from a single method in ActionMailer

I have a simple mailer
class ApplyMailer < ActionMailer::Base
def inform_teacher
end
def inform_division
end
def inform_everyone
inform_teacher.deliver
inform_division.deliver
end
end
Calling inform_teacher and inform_division everything works well. But when I try to call inform_everyone just one blank email arrives.
Is it possible to combine multiple email method though one method?
Found solution to this:
class ApplyMailer < ActionMailer::Base
def inform_teacher
end
def inform_division
end
def self.inform_everyone
ApplyMailer.inform_teacher.deliver
ApplyMailer.inform_division.deliver
end
end

Call 2 post controller methods on single form submission in rails

I have a use case where I need to create 2 tuples ( one in Invitation and one in Notification table) on single form submission( action is directed to Invitation#create).
How do I call the Notification create method from Invitation create to create a new tuple in Notification table.
PS: No relation between Invitation and Notification.
You wouldn't go to the notifications create action, you would just do it in the Invitation create. The below code will use a shared concern method, explained below it:
class InvitationsController < ApplicationController
include Notifications
#....
def create
invitation = Invitiation.create(invitation_params)
create_notification(invitation)
end
private
def invitation_params
# strong params code
end
end
So if you had a second controller/model, e.g Rsvp:
class RsvpsController < ApplicationController
include Notifications
#....
def create
rsvp = Rsvp.create(rsvp_params)
create_notification(rsvp)
end
private
def rsvp_params
# strong params code
end
end
Then in controllers/concerns directory, you could create a file 'notifications.rb'
module Notifications
extend ActiveSupport::Concern
def create_notification(object)
Notification.create(object_id: object.id, object_type: object_type)
end
end

execute controller method with delayed_job gem

I have a method inside my controller that I need execute inside delayed job method:
This is my order controller action:
class OrdersController < ApplicationController
def create
#code here
order = Order.first
Order.delay(queue: "order", priority: 1, run_at: 2.minutes.from_now).expire_order(order)
end
def template_expired_order(order)
#code here
end
end
This is my order model:
class Order
include Mongoid::Document
include Mongoid::Timestamps::Created
.
.
.
#delayed jobs method
def self.expire_order(order)
#code here
end
end
I want execute the controller method template_expired_order(order) when self.expire_order(order) method is executed or triggered or fired up.
How can I do it?
Thank you very much!
You should not put model-related code in the controller, especially if you want to run it in a background job. It's not clear what template_expired_order does, but I would recommend putting it in the Order model and calling it directly on the order that gets pass into expired_order.
class Order
def expire_order(order)
order.template_expired_order
end
def template_expired_order
# code
end
end

Resources