Rails Send Mail In Thread Not Working - ruby-on-rails

I'm trying to send an email in a separate thread (for budget reason I don't want resque etc at the moment) .
The email is sent when I call the mail function without a thread , but when I wrap it in a thread there is no email sent .
#Thread.new do
puts "hello1"
mail(to: "myemail#etc.etc", subject: "blah blah",body: some_var.text)
puts "hello2"
# end
So this piece of code works , but when I uncomment it the email isn't sent (I do however see "hello1 hello2" printed and I can step into the mail function , so the thread is calling the function and it finishes , but alas no mail is being sent).
I'm on Rails 4.16 , development mode (Webrick running from Rubymine) .

If it's within mailer model like
class MyMailer < ActionMailer::Base
def my_method
mail # ...
end
end
Then it doesn't send the email, because you also need to call .deliver on it, so when you run it within a Thread, you don't return the mail object but thread - the main issue. It could probably run when you would have something like:
class MyMailer < ActionMailer::Base
def my_method
Thread.new do
mail().deliver # parameters to mail method as needed
end
end
end

I tried with the solution privied by #leszek-zalewski, but it was not working for me.
What I did is from the model (where I was triggering the email action) I changed it:
Before (in my NotificationMailer < ActionMailer::Base):
def login_email()
subject = 'New Login'
Thread.new do
mail(to: "XYZ", subject: subject).deliver
end
end
Now (in the admin model):
def after_database_authentication
Thread.new do
NotificationMailer.login_email().deliver
end
end
and I deleted the Thread.new block in the NotificationMailer
and there you go!
I hope it helps.

You can use ActiveJob(stock in rails 4.2, and activejob_backport for rails 4+) with SuckerPunch backend to perform jobs in same process

Related

Sidekiq is queueing mails in the 'default' queue instead of 'mailers' queue

While upgrading my Rails 6.0 application to Rails 7.0.3, I'm facing the following problem :
Sidekiq is enqueueing all my mailer jobs through the default queue instead of the mailers queue like it used to before.
Code related to the same :
in application.rb :
config.active_job.queue_adapter = :sidekiq
My mailer :
class UserMailer < ActionMailer::Base
def staff_welcome(user, password)
#user = user
#password = password
mail(to: user.email, from: "DummyName <#{APP_CONFIG[:notifications_email]}>", reply_to: "#{APP_CONFIG[:outbound_email]}", subject: "Your staff account has been created")
end
And then, inside the controller, i'm calling the mailer as follows :
def create
#user = User.new
#user.attributes = users_params.merge(state: 'active', source: :dummy_source, activity: 1)
set_roles(users_params)
respond_to do |format|
if #user.save
if #user.staff?
UserMailer.staff_welcome(#user, params[:user][:password]).deliver_later
I'm interested in finding out why upon upgrading to Rails 7 is my queue for mailers changing? When i run the server with Rails 6, the mailers get queued via the mailers queue, but with Rails 7, the queue is default.
I understand that if i call the mailer from inside an ActiveJob, and inside the controller call the job instead of the mailer directly, I will be able to use syntax like queue_as :mailers. Is that the correct way to fix this?
I checked the changelog for sidekiq but couldn't find anything related to this? Did they change the default queue when it comes to mailers?
Also, can i set sidekiq_options with the deliver_later function?

Send email after attribute updated Rails

I would like to send an email to a user once the status of their pdform is updated. I already have some stuff written out on how I want this done.
In my pdform.rb model
after_update :sendemail
def sendemail
if status_changed?
end
end
I already have emails being sent out when the user creates a new form, however, I am not sure how to send an email in the model.
The controller has this mailer function that works correctly. How could I send this in this model?
NewPdform.notify_user(current_user, #pdform).deliver
Any help would be greatly appreciated. Still getting the hang of ActiveRecord.
Update:
In my pdforms_controller update method I have added the following variable.
update_user = #pdform.user
I added an attr_accessor in pdform.rb (the model)
attr_accessor :update_user
after_update :sendemail
def sendemail
NewPdform.notify_update(update_user).deliver
end
And in my mailer
def notify_update(user)
#user = user
mail(to: #user.email, subject: "some change occured")
end
I solved my own issue after using my brain more extensively.
In the call to the mailer function instead of passing the parameter of pdform, which is the name of the class anyways, just pass self.
def sendemail
NewPdform.notify_update(self).deliver
end

Best practice on structuring asynchronous mailers (using Sidekiq)

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)

Email body not sending with Action Mailer and Resque

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.

Rails 3 - abandon sending mail within ActionMailer action

I'm wondering how I could conditionally abandon sending mail within the action ActionMailer action itself.
class SomeMailer < ActionMailer::Base
...
def some_emails
some_models = Model.where(:a => 1)
if !some_models.blank?
mail(...)
else
# What to add here?
# render :nothing => true doesn't work
end
end
end
Now invoking this through SomeMailer.some_emails.deliver! returns an
ArgumentError: A sender (Return-Path, Sender or From) required to send a message
Set perform_deliveries to false, like so:
emails = get_email_list_somehow
if emails.present?
mail options.merge(:bcc => emails)
else
self.message.perform_deliveries = false
end
This will quietly not try to send and should stop the error from happening.
In Rails 3.2.9 you can finally conditionally call mail(). Here's the related GitHub thread. Now the OP's code can be reworked like this:
class SomeMailer < ActionMailer::Base
...
def some_emails
some_models = Model.where(:a => 1)
unless some_models.blank?
mail(...)
end
end
end
The strange thing is, that with Rails 3.1.rc4 and WEBrick, it works fine on my local WEBrick webserver. But as soon as I push to Heroku cedar stack, their WEBrick throws the
ArgumentError: A sender (Return-Path, Sender or From)
You have to remove the conditional statements as stated in above answer. That fixes it so that it also works on Heroku, not just your local machine
I had this same problem. There is no real way to do it within the ActionMailer action so I did the following in my cron task:
users.each do |user|
begin
UserMailer.event_second_reminder_group_user_email(user).deliver
puts " - sending reminder email to user #{user.email}"
rescue
end
end
puts "Complete!"
Now if an error is thrown, it doesn't break the app!
Instead put your conditions in the place where you are making the call to SomeMailer.some_emails.deliver!

Resources