Organize Your Rails Mailers (and Observers, Sweepers, etc.)

I’m planning a serious of Rails posts covering some conventions that I’ve come to really to appreciate in the applications I develop. I’m going to make this first one easy on myself — let’s talk about organization!

How Rails Organizes

The way Rails organizes application code was deeply satisfying for me, coming from my home-brew PHP background. The app/ folder with app/controllers, app/models, and app/views really helped me embrace the MVC paradigm. Love it!

But when I start adding email, I get a little twitchy — Rails files the ActionMailer classes under app/models, and the email views under app/views. I get this. I understand this. It still makes me a little twitchy.

Then I start adding observers, which also go in app/models even though they’re really not models. But they’re related to the models, so that’s got to count for something, right? Eh.

And how about sweepers? Do they really even have a home? Their heritage is so confused.

But do you know one of the worst parts of all of this? Having user.rb and user_mailer.rb and user_observer.rb breaks my shell’s tab-completion! Noez!

How I Organize

This is simple — embrace config.load_paths and widen your app/ directory. I like to create app/emails, app/observers, and especially app/sweepers. This really helps me find code faster, because when I’m navigating my folders looking for an observer, the first thing in my mind is “category: observer”, not “something related to models”. So navigating to app/observers is much more intuitive.

How does this happen? Again, simple. do |config|
  %w(observers sweepers emails).each do |dir|
    config.load_paths << "#{RAILS_ROOT}/app/#{dir}"

Then just start moving your code in there. Rails will find it.

We’re Not Done With Mailers

You’ll want to do one more thing to organize your mailers. Without this step, you’ll have only cleaned up your app/models directory, and you’ll still have templates scattered in app/views.

First, create an application mailer (just like the application controller):

class ApplicationMailer < ActionMailer::Base
  self.template_root = File.join(RAILS_ROOT, 'app', 'emails', 'views')

Now make all your mailers inherit from ApplicationMailer, and move your email template into app/emails/views (e.g. your mailer is app/emails/user_mailer.rb and your templates are in app/emails/views/user_mailer/).

And for a bonus? You now have a place to add custom helpers and email setup methods to really streamline email setup for your application.