Rails 3 - abandon sending mail within ActionMailer action - ruby-on-rails

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!

Related

Rails Send Mail In Thread Not Working

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

How does one use delayed_job to make an Rails 3.0 ActionMailer run asynchronously? Encountering ArgumentErrors

I'm trying to delay a notification email to be sent to users upon signing up to my app. The emails are sent using an ActionMailer which I call InitMailer. The way I am trying to delay the jobs is using collectiveidea's delayed_job https://github.com/collectiveidea/delayed_job. To do this you can see that i specify handle_asynchronously after defining the method initial_email:
class InitMailer < ActionMailer::Base
default :from => "info#blahblahblah.com"
def initial_email(user)
#user = user
#url = "http://www.blahblahblah.com"
mail(:to => user.email,
:subject => "Welcome to my website!"
)
end
handle_asynchronously :initial_email
end
However, I encounter an argument error in my log file "delayed_job.log":
Class#initial_email failed with ArgumentError: wrong number of arguments (1 for 0) - 5
failed attempts
For your information, the email is sent in a controller using the line:
#user = InitUser.new(params[:init_user])
InitMailer.delay.initial_email(#user)
Additionally, when I set up my code without the delay, the emails were sent out without problem (except for the fact that it slowed down my app waiting for gmail servers)
Where is causing the errors here? How can I get the delayed mail to send properly?
Due to the way that Rails3 implements mailers, there are some unusual workarounds for delayed_jobs. For instance, you have seen that to delay the mailing, you write
ExampleMailer.delay.example(user)
While typically you would have to write handle_asynchronously after the method definition, in the case of mailers this declaration (for some reason) prevents that delayed job from working.
So in this code, drop the declaration entirely:
class InitMailer < ActionMailer::Base
default :from => "info#blahblahblah.com"
def initial_email(user)
#user = user
#url = "http://www.blahblahblah.com"
mail(:to => user.email,
:subject => "Welcome to my website!"
)
end
#No handle_asynchronously needed here
end

Delayed_Job - handle_asynchronously with ActionMailer?

So I'm using Delayed Jobs and I'm trying to figure out how to get all of my mailers to be delayed. Right now, I've put handle_asynchronously on all of my action mailer methods… but I don't think that is going to work.
def first_notification(time)
#time = time
mail :to => time.person.email,
:from => "email#example.com",
:subject => "#{time.person.name} wants to say hi"
end
handle_asynchronously :advisor_first_notification, :priority => 20
The reason I don't think this is going to work is because I call it as such:
UserMailer.first_notification(#time).deliver
So how would it handle the .deliver part of this? Right now I get an exception.
EXCEPTION: #<ArgumentError: wrong number of arguments (1 for 0)>
Which makes me feel that something is getting messed up in the deliver aspect.
I would rather not have a separate job file for each email (as I have a lot of them), so what is the proper way to handle this?
The only other option I can think of is to encapsulate the calls into a method within my models and have them have the handle_asynchronously - that way they can call the entire thing at once.
The mailer is a bit tricky... Instead of using the handle_asynchronously syntax:
UserMailer.delay.first_notification(#time)
The 'trick' is having delay() before the mailer method
Further to Jesse's answer, the collectiveidea's fork of delayed_job indicates that you should definitely not use the deliver method at all with Rails 3 Mailer code:
# without delayed_job
Notifier.signup(#user).deliver
# with delayed_job
Notifier.delay.signup(#user)
I've gotten it to work by doing the following:
class MyMailer < ActionMailer::Base
def send_my_mail_method(*args)
MyMailer.my_mail_method(*args).deliver
end
handle_asynchronously :send_my_mail_method
def my_mail_method(*args)
# mail call ...
end
end
I like this way because it allows me to test that delivery happens interactively, without having to do something stupid like mock the delay call.

Need help with sending email

I have a table of emails. And i need that Each user received email.
SO i made:
script/generate mailer Notifier
Next.
class Notifier < ActionMailer::Base
def newgrants_notification(respondent)
recipients user.email
from "lala#lala.com"
subject "Hi!"
body (:respondent => respondent)
end
end
In app/views/notifier/newgrants_notification.erb
wrote : Hello!
and this my controller where i create question
#question = Question.create(:text => params[:question][:text], :security => rand(888).to_i)
if success = #question.save
respondents = Respondent.find(:all)
respondents.each do |res|
Inquiry.create(:question_id=>#question.id.to_i, :respondent_id=>res.id.to_i)
Notifier.newgrants_notification(respondents).deliver #this is right??
end
what mistakes i did? messages aren't coming ;(
HI
respondents.each do |res|
Inquiry.create(:question_id=>#question.id.to_i, :respondent_id=>res.id.to_i)
Notifier.newgrants_notification(res).deliver
end
When you are sending mail,the mail id is passed as a parameter so respondents is replaced with res.
You are passing through your array of respondents, when you use each, the variable in the pipes (|res|) is the one to use to refer to the singular object in the loop.
Notifier.newgrants_notification(res).deliver
In development mode all email are not send. It's all log only in your log file. So if you test on this environment is normal. Check on your log if you see it :)

how can you filter/block outgoing email addresses with rails 2.x actionmailer?

for non-production rails 2.x environments i want to block/filter any outgoing emails that aren't addressed to people in my organization (e.g. "*#where-i-work.com").
please note, i don't want to block email entirely - i know i can just write them to the logs in test mode - i need emails to internal employees to be delivered.
thanks.
You could try extending the Mail::Message.deliver function in your environment.rb file - something like (not tested - just demo code!):
class Mail::Message
def deliver_with_recipient_filter
self.to = self.to.to_a.delete_if {|to| !(to =~ /.*#where-i-work.com\Z/)} if RAILS_ENV != production
self.deliver_without_recipient_filter unless self.to.blank?
end
alias_method_chain :deliver, :recipient_filter
end
Note that this id for Rails 3 - I think all versions of Rails 2 use TMail instead of Mail, so you'll need to override something else if you're not using Rails 3.
Hope this helps!
based on #Xavier's rails 3 proposal i was able to get it working in rails 2:
class ActionMailer::Base
def deliver_with_recipient_filter!(mail = #mail)
unless 'production' == Rails.env
mail.to = mail.to.to_a.delete_if do |to|
!to.ends_with?('where-i-work.com')
end
end
unless mail.to.blank?
deliver_without_recipient_filter!(mail)
end
end
alias_method_chain 'deliver!'.to_sym, :recipient_filter
end

Resources