There’s one thing that everyone loves: getting mail! But there’s one thing that all developers would rather avoid: sending mail. Unfortunately, this paradox perfectly describes the joys and horrors of getting your application to send a single email.
I recently worked on building out a password reset feature for one of our applications. In order for this feature to work, I had to figure out how to get my Rails application to send an email to our user with a password reset token in case they had forgotten their password. I thought that handling the authentication and token aspect of this would be complicated, but it turned out that learning about mailers was the more fun part. I had never actually worked with Rails mailers before, and honestly, I thought that I was in over my head (this also might be partly attributed to the fact that I had just come back from a two-week vacation and felt like I had completely forgotten how to code).
So, I did what any developer would do: I cried and went home. Okay, okay, I’m just kidding! What I actually did was read through the documentation, play around with my application and, in the process, taught myself how to use Rails Action Mailer. I never thought that I’d say this but, getting that feature to work and seeing that email pop up was incredibly exciting. In fact, I don’t think I’ve ever been more excited about sending and receiving an email. But don’t let me tell you how thrilling it was — let’s create our own mailer and experience it together!
Generating Some Mail(ers)
Rails has a wonderful built-in mailing system called Action Mailer, which allows us to send email from our application as long as we have a mailer model. Mailers are actually a lot like controllers: they have their own
app/mailers directory, and each mailer has its own associated view as well. Each mailer also inherits from
ActionMailer::Base, and just like controllers, can be generated really easily.
For our bookstore app, we won’t start off with anything too fancy just yet. Instead, let’s stick with a simple mailer that will be responsible for one little thing: sending an order confirmation email whenever a user successfully places an order (did your mind immediately jump to using a state machine? I hope so!)
To start, we’ll use Rails to generate a mailer:
♥ bin/rails generate mailer Order
Running this command in the terminal generates a few different files for us. We now have an
app/mailers directory, with an
application_mailer.rb file. It also generates three files inside of
layouts/mailer.html.erb, as well as test units for our order mailer (
Depending on how many mailers this application will have, it might not makes sense to generate all of these files. If we decided to manually create our mailer rather than generating it, we’d need to keep one thing in mind: our mailer must be a file inside of the
mailers directory, and it must inherit from
ActionMailer::Base (unless, of course, we wanted to use a mailer from another library, such as the
Devise::Mailer from the
The mailer model has methods defined on it that allows us to actually specify how and where an email is sent. Right now, however, our mailer models look pretty empty! Inside of our generated
ApplicationMailer, the only setup we have is our layout configuration and our
1 2 3 4
order_mailer.rb is completely empty:
Since mailers are so much like controllers, we can approach writing them in a similar way. The first thing we’ll do is write some actions. Just like with controllers, we want our methods to adhere to the single-responsiblity principle, which means that they should be handling only one thing at a time. We’ll start by writing a
confirmation_email method, which will take an
Order object as its parameter.
1 2 3 4 5
Just like in controllers, any instance variables that we define in our method — in this case,
@order — become available for us to use inside of our views. This will be important when we want to render the user’s information via our
@order instance. But…we’re not actually mailing anything right now, are we? Of course not! In order to actually create our message and render our email templates, we need to use the
ActionMailer::Base (hence why every mailer should always inherit from it so that it has access to this very crucial method). If we look at the documentation for this method, we can see that it accepts a headers hash, which is where we can specify the most-used headers in an email message. Some of the options we can pass in include
date, among others. For now, we’ll just pass in a
to option and a
1 2 3 4 5 6 7
If we wanted to get really fancy, we could specify
default values for any of these headers (except for
date) inside of our
OrderMailer class. Alternatively, we could also write our
1 2 3 4
But let’s hold off on all these bells and whistles. Let’s just get this method into our state machine and actually send this bad boy.
Send Me Some Mail
Now for the fun part: sending and receiving our mail! There are two methods we can use to send an email:
deliver_later. The former sends our email inline (in the same request-response cycle), while the latter sends emails in the background by integrating with Active Job.
We already wrote our
confirmation_email method, so now we just need to invoke it. But, we defined it on our mailer class. However, we don’t need to instantiate a new instance of our
OrderMailer class (like we would have to do with a service object, for example). Instead, we can just call our
confirmation_email method on our mailer class directly. Since brevity is the soul of wit, here’s a truncated version of the state machine in our
order.rb file, which is where we’ll invoke this method:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
You’ll remember that our
confirmation_email method takes an
Order as a parameter, which is why we’re passing in
Order object, into the method, before chaining on
deliver_now at the end of it. Now, after our
completed event is called, this email will be sent. But how do we know what our email will say, exactly? Well, we can head over to our email templates to find out.
When we generated our mailer, one of the files that was generated was
app/views/layouts/mailer.html.erb. If we take a look inside of this file, we’ll see that it’s pretty simple; in fact, all it’s going to do for now is
yield to whatever template needs to be rendered. If we wanted to add styles or formatting that would apply to all of our mailers, this is where it would go:
1 2 3 4 5
For things pertaining specifically to our
OrderMailer template, we’ll need to visit the view for that mailer, which will live inside of
app/views/order_mailer/confirmation_email.html.erb. We can again think of how controllers work with their associated views (for example, an
index action corresponds to an
index.html.erb file). Similarly, our
OrderMailer class knows about its own specific view because its name is the same as the mailer’s method (
confirmation_email). This is where we can put the text for our email template; for now, it won’t be anything too special and will just use our
@order instance from the
confirmation_email method we wrote in the
OrderMailer to retrieve and render the order number and user’s email:
1 2 3
Awesome! Now, in development, we can test this out by placing an order, triggering the
send_confirmation_email method in our state machine, and using our
OrderMailer to send an email in a sychronous request to our user’s email address. That’s a lot to do, but we made it happen!
Letter Opener + Instant Delivery
Before we get too email-happy, here’s a thought: how much do you really like email? I don’t know about you, but I would really rather not get an email every single time I test out my mailer in development. Thankfully, there’s a gem that was created to solve precisely this very problem:
This gem intercepts our mailer and allows us to preview an email from within our browser instead of actually sending the email to an email address. One of the great benefits of this — in addition to both saving space in our inbox and not having to set up email delivery in our development environment — is us not having to worry about sending test emails by accident to someone else’s email address!
letter_opener to our application is pretty easy, and the documentation is easy to follow. First, we’ll add the gem to the
:development group in our
gem "letter_opener", group: :development
After we run
bundle install in our terminal, we’ll need to do one last step: setting up our mailer configurations. Basically, all this means is that we need to specifically set up our development environment such that it will use our
letter_opener gem as its delivery method. In fact, that’s pretty much the only line we need to add in our
config.action_mailer.delivery_method = :letter_opener
delivery_method acepts a delivery method object and defaults to
:smtp. Since we want
letter_opener to handle our mail deliveries, we’ll just set our delivery method on Action Mailer to the gem that we want to use.
Now that we’ve set this up, any email that is sent by Action Mailer will be intercepted and open up in a new tab in our browser, rather than actually being sent to an email address. These files will be stored temporarily in
But as lovely and helpful as it is to have all these test emails popping up in our browser, there’s one thing that would be even nicer to have: all of these emails being triggered outside of the request-response cycle. In other words, what we want to do is to run these requests asychronously. Well, what does the documentation say about making this happen?
“Active Job’s default behavior is to execute jobs
:inline. So, you can use
deliver_laternow to send emails, and when you later decide to start sending them from a background job, you’ll only need to set up Active Job to use a queueing backend (Sidekiq, Resque, etc).”
Okay, it sounds like we need to learn a little bit about Active Job and set up a queueing backend to send our emails in a job. But let’s save that for another blog post. Tune in again next week, when I’ll delve into the basics of Active Job and asychronous processes. Until then, have fun opening those emails!
- Rails mailers inherit from
ActionMailer::Base, and work just like controllers, with actions and corresponding views. Check out this fantastic post on sending emails using Action Mailer to dive into the details.
- There are a lot of different header options that you can pass to
ActionMailer::Base. Read more about them over here.
- Curious about how to go about configuring Action Mailer to make the mailing magic happen? The Rails Guides have a great tutorial on how to do that.