ActionMailer works with only one smtp account; a single google apps account is limited to 500 (or 2000) mails per day; my app legitimately needs more. Besides, I'd rather notifications come from an appropriately named account rather than a generic "no-reply@example.com" address. Here's what I came up with. Firstly, config/smtp.yml
describes my various accounts - I settled on one per mailer class. Secondly, a patch to ActionMailer::Base
enables switching smtp accounts based on the mailer class.
Here's an example with four mailers: an account mailer, an exception notifier (works with the lovely ExceptionNotifier plugin), a "share this" mailer so your site can be all viral and stuff, and a prize mailer for the good news.
config/smtp.yml
defaults: &defaults address: smtp.gmail.com port: 587 domain: example.com authentication: !ruby/sym plain account_mailer: <<: *defaults user_name: accounts@example.com password: "pw1" prize_mailer: <<: *defaults user_name: winner@example.com password: "pw2" exception_notifier: <<: *defaults user_name: dev_team_obviously_sucks@example.com password: "pw3" share_this_mailer: <<: *defaults user_name: share_this@example.com password: "pw4"
ActionMailer::Base patch
require 'smtp_tls' module ActionMailer class Base cattr_accessor :smtp_config self.smtp_config = YAML::load(File.open("#{RAILS_ROOT}/config/smtp.yml")) def smtp_settings smtp_config[mailer_name].symbolize_keys end end end
I put this in config/initializers/action_mailer.rb
.
And that's it!
No changes required to your mailers or email templates or anything else in your application.
As you can see, the patch merely overrides ActionMailer's smtp_settings class method
and replaces it with an instance method that decides at sending-time which smtp configuration to use.
We needed to do this because sendmail was failing erratically - some users weren't getting any mail from our app at all - presumably due to hypersensitive spam filters somewhere on the chain, maybe related to my not understanding how SPF records are supposed to work.
You could easily fancify this to switch SMTP config based on the time of day,
or based on your user's locale
(so you can use a nicely localised "from" address - Google overrides the "from" address sent by ActionMailer),
or even based on whether the Moon is in Scorpio if you cared.
Just replace the call to mailer_name
with a call to your config-switching method.
I understand that rails 3 is beautifuller in many ways including the way ActionMailer works so this might well be obsolete in a few months except for you suckers working on legacy systems. I hope this helps, let me know one way or the other.
Hi thanks for this awesome solution.
ReplyDeleteIm a bit of a rails noob.
How would I for example use your prize_mailer in below setup
class Notifier < ActionMailer::Base
def password_reset_instructions(user)
subject "Your prize"
from "Me "
recipients user.email
sent_on Time.now
body :edit_password_reset_url => edit_password_reset_url(user.perishable_token)
end
end
Thanks!
This is a slick solution. I started implementing it in a Rails 2.3.5 app I'm working on, but our email needs are pretty complicated and I ended up using an ActionMailer callback plugin I found here => https://github.com/AnthonyCaliendo/action_mailer_callbacks
ReplyDeleteHad our needs been more straight-forward, I would have continued to use your solution. Thank you for posting it as I found it to be simple, but elegant.
This was really helpful for me. I'm building an app on Rails 3.2.7 and I wanted to spread my notifications across different account. I was able to do it with this approach pretty easily. Two things that I had to update in order to make it work:
ReplyDelete1) Remove "require 'smtp_tls'"
2) Make "def smtp_settings" into a class method "def self.smtp_settings"
Thanks!
Hi Justin, I haven't got to Rails 3.2 yet, so thanks for the update!
ReplyDelete