I'm queuing up emails using ActionMailer and Resque. I'm not that familiar with either, but I have the basics down. Right now I'm debugging why when enqueueing emails, the email is sent but there is no body.. Mystifying.
When I execute ArtistReminderWorker.perform(8,2) the email is sent with the body. Great success!
When I execute Resque.enqueue(ArtistReminderWorker, 8, 2) the email is sent without the body. Mystery.
I thought these two statements were functionally the same, no?
At first I thought that the Resque worker wasn't able to look up the record, so I moved the database lookup from the worker into the mailer right before the mail command.
Here are my Worker and Mailer classes.
artist_reminder_worker.rb
class ArtistReminderWorker
#queue = :artist_reminders_queue
def self.perform(event_id, user_id)
ArtistReminderMailer.artist_reminder_email(event_id, user_id).deliver
end
end
artist_reminder_mailer.rb
class ArtistReminderMailer < ActionMailer::Base
default from: 'no-reply#example.com'
def artist_reminder_email(event_id, user_id)
#user = User.find(user_id)
#event = Event.find(event_id)
#url = dashboard_url
subject = "You have an upcoming gig at #{#event.venue.name}"
mail(to: #user.email, subject: subject)
end
end
My views are:
views/artist_reminder_mailer/artist_reminder_email.slim views/artist_reminder_mailer/artist_reminder_email.text.slim
Let me know if I can provide any more detail, thanks in advance!
Solved it. It wasn't actually anything I was doing wrong, but rather that I hadn't restarted the worker rake task in a while.
I guess when updating the Worker classes, you have to restart the worker.. Makes sense.
Related
I am using Sorcery Gem to Authenticate user in my Rails application. Everything is working fine. When user register then I am able to send user activation email. However sending email is taking long time so I was thinking to delay the email or send email in backgroud using Sidekiq. I read Sorcery documentation but couldn't find the way to delay the email using Sidekiq.
Please guide me how to delay the email send by Sorcery gem using Sidekiq.
There could be two approach to this:
Call Sidekiq worker inside Mailer
what I understand from the docs is that you can configure it to call any
Mailer. What you could do inside the method is call
MailerJob.perform_late(*arg) instead of calling mail(*args) right away
Assuming you have a code something like this (ref: https://github.com/Sorcery/sorcery/wiki/User-Activation)
# app/mailers/user_mailer.rb
def activation_needed_email(user)
#user = user
#url = activate_user_url(#user.activation_token)
mail(to: user.email, subject: 'Welcome to My Awesome Site')
end
You can change the code to this
# app/mailers/user_mailer.rb
def activation_needed_email(user)
#user = user
#url = activate_user_url(#user.activation_token)
# mail(to: user.email, subject: 'Welcome to My Awesome Site')
MyMailerWorker.perfor_later(to: user.email, subject: 'Welcome to My Awesome Site')
end
Your worker would be defined as per the sidekiq docs(ref: https://github.com/mperham/sidekiq/wiki/Getting-Started)
class MyMailerWorker
include Sidekiq::Worker
def perform(to: nil, subject: nil)
MailerIWantToCall.send_activation_email(to, subject)
end
end
P.S: Option 1 is not a clean approach but works if you are stuck with Sorcery legacy configs
Don't set mail settings for srcoery(A cleaner approach)
Do not setup mail through sorcery at all.You can revert the changes
mentioned for UserMailer on this page
(github.com/Sorcery/sorcery/wiki/User-Activation) and add a callback on User
model based on when do you want to trigger an email and that way you have
control over what to call and with sidekiq or not.
I found the solution. It's quite easy. We just need to add following line in sorcery.rb file and all sorcery mail will be handled by ActiveJob
user.email_delivery_method = :deliver_later
I have a Rails rake task that I'm using to send emails and the only way I can get my Mailer to send to multiple recipients is by collecting them in the Mailer and then listing them as bcc recipients like this:
class SubscribersMailer < ApplicationMailer
def send_daily_updates
#subscribers = Subscriber.all
recipients = #subscribers.collect(&:email).join(",")
#subscribers.each do |subscriber|
mail(to: "***#example.com.com", subject: "Email subject", :bcc => recipients)
end
end
end
And in my task, I'm just calling this:
SubscribersMailer.send_daily_updates.deliver_now
When I run the rake task it successfully sends the email to each address, but this doesn't seem like the ideal way to do this. Sending out mass emails with a bunch of addresses in the bcc field is a good way to get it marked as spam.
What is a better way to loop through the recipients? Should I do this in the rake task or in the Mailer?
I advice you to use only one subscriber in mailer and call it for each subscriber at the rake task. Like
class SubscribersMailer < ApplicationMailer
def send_daily_updates(subscriber)
mail(to: subscriber.email, subject: "Email subject")
end
end
Subscriber.find_each do |subscriber|
SubscribersMailer.send_daily_updates(subscriber).deliver_now
end
This will generate a bunch of emails, but subscribers will see only their email address in received letter. That is more secure (you not expose all of you subscribers email addresses to each subscriber) and get lower risk that it will be marked as spam.
Also I advice you to use some background workers instead of rake tasks (Sidekiq, Rescue, and etc.)
Just wondering what's the best way to go about structuring asynchronous mailers in my Rails app (using Sidekiq)? I have one ActionMailer class with multiple methods/emails...
notifier.rb:
class Notifier < ActionMailer::Base
default from: "\"Company Name\" <notify#domain.com>"
default_url_options[:host] = Rails.env.production? ? 'domain.com' : 'localhost:5000'
def welcome_email(user)
#user = user
mail to: #user.email, subject: "Thanks for signing up!"
end
...
def password_reset(user)
#user = user
#edit_password_reset_url = edit_password_reset_url(user.perishable_token)
mail to: #user.email, subject: "Password Reset"
end
end
Then for example, the password_reset mail is sent in my User model by doing...
user.rb:
def deliver_password_reset_instructions!
reset_perishable_token!
NotifierWorker.perform_async(self)
end
notifier_worker.rb:
class NotifierWorker
include Sidekiq::Worker
sidekiq_options queue: "mail"
def perform(user)
Notifier.password_reset(user).deliver
end
end
So I guess I'm wondering a couple things here...
Is it possible to define many "perform" actions in one single worker? By doing so I could keep things simple (one notifier/mail worker) as I have it and send many different emails through it. Or should I create many workers? One for each mailer (e.g. WelcomeEmailWorker, PasswordResetWorker, etc) and just assign them all to use the same "mail" queue with Sidekiq.
I know it works as it is, but should I break out each of those mail methods (welcome_email, password_reset, etc) into individually mailer classes or is it ok to have them all under one class like Notifier?
Really appreciate any advice here. Thanks!
As discussed here, Sidekiq supports delayed mailer by default, so there is no need to create separate workers:
Notifier.delay.password_reset(user.id)
I am not sure but I think its not a good idea to pass an instance in mailer action if you're using delay, so maybe its better to change the code above to :
Notifier.delay.password_reset(user.id)
I am using Rails 3.0.9 and I have following code to send an email when a comment is posted.
class Mailer < ActionMailer::Base
def comment_notification(comment)
User.active.each do |user|
#user = user
mail(:to => #user.email, :subject => subject)
end
end
end
If there are not active users then User.active is empty and the code inside does not get executed. However the view is rendered and view fails because #user is missing.
The above code is invoked by observer
Mailer.comment_notification(comment).deliver
One way to fix this problem would be to change the code in observer to something like this
User.active.each do |recipient|
Mailer.comment_notification(comment, recipient).deliver
end
Is this the right way to fix this way. I would like my observer to be as thin as possible.
Yes, your observer fix is correct. You should loop through and send emails one by one. The mailer should just send one email at a time. This is a job best left to Delayed Job though. You don't want to waiting around while an email sends.
Here is a tutorial on Delayed Job: http://railscasts.com/episodes/171-delayed-job
Be sure to check the Readme for Delayed Job as well, paying special attention to the "Rails 3 Mailers" section: http://github.com/collectiveidea/delayed_job
When trying to send an email to the user for reseting their password, I keep getting an execution timed out error. Other mailer functions work, so I know that the config settings are correct. The header reads: "Timeout::Error in Password resetsController#create"
Here is the password_resets_controller:
def create
#user = User.find_by_email(params[:email])
if #user
User.deliver_password_reset_instructions(#user.id)
flash[:notice] = "Instructions to reset your password have been emailed to you. " +
"Please check your email."
redirect_to '/'
else
flash[:notice] = "No user was found with that email address"
render :action => :new
end
end
Here is the method inside of User.rb
def self.deliver_password_reset_instructions(user_id)
user = User.find(user_id)
user.reset_perishable_token!
Emailer.deliver_password_reset_instructions(user)
end
Finally, here is the actual method inside of emailer.rb:
default_url_options[:host] = "http://0.0.0.0:3000" #development
def password_reset_instructions(user)
#subject = "Application Password Reset"
#from = 'Notice#myApp.com'
#recipients = user.email
#sent_on = Time.now
#body["edit_password_reset_url"] = edit_password_reset_url(user.perishable_token)
#headers["X-SMTPAPI"] = "{\"category\" : \"Password Recovery\"}"#send grid category header
end
Why is "Password" in the error message referred to causing a timeout::error
Sending email (or other long running processes) from the main controller request thread is not a good idea. The sending of the email can time out for a variety of reasons that are not under your control (e.g. the outbound email delivery server being down) and you don't want your application server and users to suffer due to that.
A better approach is to use a queuing mechanism like Delayed Job (DJ) to queue these email tasks, and have them be processed outside of your controller threads.
See https://github.com/collectiveidea/delayed_job
Integration of this (or another queuing system) into your rails app is fairly simple. And rails 4 is said to have built in queuing services (which I'm yet to use) http://blog.remarkablelabs.com/2012/12/asynchronous-action-mailer-rails-4-countdown-to-2013.
For instance, if you use DJ in your app, the new code will look like below
def self.deliver_password_reset_instructions(user_id)
user = User.find(user_id)
user.reset_perishable_token!
# this is the only line that changes
Emailer.delay.deliver_password_reset_instructions(user)
end
The jobs are stored in the database, and re-tried when errors like time outs happen.
You can read more about DJ on the github page.