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
Related
When using Rails Action Mailer, you have the option to use deliver_now to send the email immediately, or deliver_later to send through asynchronously using Active Job. If Active Job is not specified an adapter, it will use an in-process thread pool which would not persist if the server were to stop. Alternatively, I can create a Job to manage my e-mails, which I can then call with perform_now or perform_later, which as I understand is more or less the exact same thing as deliver_now and deliver_later.
My question is, if I specify an adapter, let's say Sidekiq, and then have a database to store my jobs, why would I create a job to handle my e-mails? Is there any additional benefit, or is it an unnecessary step? On a slightly different note, if I did want to create a job for the process, would my email method need to have deliver_now or simply nothing at all? I presume if the e-mail were to say deliver_later, would it knock back the email to the end of the queue and force it wait again until it is sent?
To illustrate, if I have no Job set up to handle my emails, I could simply have:
class UserMailer < ActionMailer::Base
def send_email(user)
mail(to: user.email, subject: "My Subject")
end
end
To call, I would use:
UserMailer.send_email(my_user).deliver_later.
However, if I had wanted to add a job, and both options were called with UserJob.perform_later(user), would my setup be:
class UserJob < ActiveJob::Base
def perform(user)
UserMailer.send_email(user).deliver_now
end
end
Or
def perform(user)
UserMailer.send_email(user)
end
Lastly, I don't think this makes sense, but what would happen if I used:
def perform(user)
UserMailer.send_email(user).deliver_later
end
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.
Note: Using Rails 3.1 and current delayed_job gem.
I have a User model that calls after_create :mail_confirmation.
The mail_confirmation method looks like the following, per the delayed_job instructions:
def mail_confirmation
UserMailer.delay.registration_confirmation(self)
end
The UserMailer is:
class UserMailer < ActionMailer::Base
default from: "test#mysite.com"
def registration_confirmation(user)
#user = user
mail(:to => "#{user.full_name} <#{user.email}>", :subject => "Test registration email")
end
end
The job is queued, and the output from rake jobs:work makes it seem as if it completed successfully:
[Worker(host:mymac.local pid:73694)] Starting job worker
[Worker(host:mymac.local pid:73694)] Class#registration_confirmation completed after 1.3659
[Worker(host:mymac.local pid:73694)] 1 jobs processed at 0.7288 j/s, 0 failed ...
There is no error but the email is never sent. It works fine if I remove delayed from the method call in the User model and go with the standard deliver method:
def mail_confirmation
UserMailer.registration_confirmation(self).deliver
end
How can I find out what is happening when the job is processed? Any idea how to fix it?
Update It appears that it is related to this:
NoMethodError with delayed_job (collectiveidea gem)
Yeah i had this same issue. #Clay is correct, there is an issue at the moment: https://github.com/collectiveidea/delayed_job/issues/323
I resolved this problem by reverting back to the previous version of delayed_job.
gem 'delayed_job', '2.1.2'
I'm having the same issues here. I discovered that for some reason the delay method called on Mailer classes is being handled by the method Delayed::MessageSending#delay instead of Delayed::DelayMail#delay which instantiates the right performable (which is PerformableMailer instead of PerformableMethod). It doesn't crash the job because PerformableMethod just calls the method without the deliver.
Take a look at:
delayted_job/lib/delayed/message_sending.rb
delayted_job/lib/delayed/performable_mailer.rb
I'm trying to implement an ActionMailer function that will send out a newsletter to a specific user. I want to make sure that the newsletter is only sent to subscribed users. I tried implementing it like so:
class UserMailer < ActionMailer::Base
def newsletter(user)
return unless user.subscribed # This still renders my mailer view
mail(:to => user.email, :subject => "Newsletter")
end
end
The problem is that the return unless user.subscribed line still appears to be rendering the mailer view and is still sent by the calling code (from a cron job):
task :cron => :environment do
User.where(:subscribed => true).each do |user|
UserMailer.newsletter(user).deliver
end
end
Note that I do have that subscription logic in my cron job as well for performance reasons (shouldn't have to iterate over ALL users, only those that are subscribed). However, it feels like the UserMailer class is the right place for this logic to exist (otherwise any other location that calls the newsletter method will need to check the subscribed flag as well.
The Mailer, IMHO, is the wrong place for this logic. The mailer should do nothing but format and send messages. The logic to decide whether or not to send should be within the calling block of code. It's not the right way, but something as simple as:
UserMailer.newsletter(user).deliver if user.subscribed?
Alternately, as you mentioned, you shouldn't have to iterate over all users, just the subscribed. So with a scope in the User model called subscribed:
User.subscribed.each do |user|
UserMailer.newsletter(user).deliver
end
This way you don't need to test on a per-user basis; only the subscribed users are included, and the logic is in the calling block, not in the mailer.
Is there a gem to do this, and if not, what is the best approach. I'm assuming i'd store the emails in a newsletter database, and would want a form that emails everyone at once. thanks!
No gem for this that I know of.
Actually, due to processing issues, the best way would be to use external bulk email service provider via provider's API.
However, building your own newsletter system is no different from building your regular MVC. Only addition are mailers and mailer views.
(1) So you create model that deals with registration data (:name, :email, :etc...) and model that deals with newsletter itself.
(2) Controller that deals with it (CRUD) + (:send). Send takes each recipient, sends data to mailer which creates email and then sends it.
def send
#newsletter = Newsletter.find(:params['id'])
#recipients = Recipient.all
#recipients.each do |recipient|
Newsletter.newsletter_email(recipient, #newsletter).deliver
end
end
(3) Mailer builds an email, makes some changes in each email if you want to do something like that and returns it to controller action send for delivery.
class Newsletter < ActionMailer::Base
default :from => "my_email#example.com", :content_type => "multipart/mixed"
def newsletter_email(recipient, newsletter)
# these are instance variables for newsletter view
#newsletter = newsletter
#recipient = recipient
mail(:to => recipient.email, :subject => newsletter.subject)
end
end
(4) Ofc, you need mailer views which are just like regular views. Well in multipart there is:
newsletter_email.html.erb (or haml)
newsletter_email.txt.erb
When you want to send both html and text only emails. Instance variables are defined in mailer ofc.
And... that is it. Well you could use delayed job to send them since sending more than several hundred emails can take a while. Several ms times n can be a lot of time.
Have fun.
Please check the maktoub gem there is a blog post over it.
No gem that I know of too and building on #Krule's answer, here's a screencast of setting up mailers in Rails.
How to create, preview and send email from your rails app
I was looking for something similar when I found this. I think with a bit of customization, it can easily be used to create newsletter emails too.
Save money! Spend somewhere else.