As I’ve written about many times before, though I’m constantly learning and trying to become better with Ruby and Ruby on Rails, there are gaps, holes, what have you, in my understanding of certain fundamental things that, to me, a web developer should know how to do. I decided today that it’s time to at least take the first steps in crossing one of those off the (if I wrote it down, very long) list.
Web sites send emails. It happens all the time. If you haven’t tweaked your twitter account, you’ll get so much email you’ll go running for the settings to turn that stuff off (I know I did). Amazon will send you an email when a author you like (or at least bought a book by) has a new book coming. Your bank/credit card may send you an alert when a new statement is ready. All via email, and all created automatically with code that is written to send these notifications at specific times/occurrences, and like most things that are done with so much frequency in web applications, Rails helpfully has built in functionality to help its users get going on such things.
Action Mailer is the tool that Rails provides all its users to facilitate the creation and sending of emails. It’s also one of those things that I’ve been wary of in the past. I’ve done some reading in a variety of places about how to use Action Mailer, but never really focused on it too deeply. I kept pushing it off. Like Active Record, or even finding a wysiwyg I liked, something about the topics gave me pause, like I wasn’t ready, or it would be too frustrating, or any other number of things deep in my neuroatypical psyche. Either way, I’ve known at some point I was going to have to figure this out, and for reasons I can’t really explain I decided that the time was now.
Learning the Fundamentals of Action Mailer
To get started, I thought it would be good to do some instructional reading on the basics of Action Mailer, and then I can work with how to integrate it into an application with testing. I did not start with the guide linked above but instead with my well-worn copy of The Rails 4 Way. If you work in Rails and don’t have a copy of this book, in any format, you’re depriving yourself. It is an incredibly useful and handy reference tool on a variety of topics, and the book has an entire chapter devoted to Action Mailer.
It is a brief chapter (just 12 pages), but it does provide some pretty good beginning information for the uninitiated like me:
- Action Mailer will attempt to send email via SMTP on whatever host your application is on, by default, of course you can configure numerous other ways to send mail, as always in Rails, but if you have access to your own SMTP server, you’re in business.
- Action Mailer uses a structure similar to the VC (View-Controller) set up of the basic MVC, and as such has a generator
rails generate mailer MailerName, and this will create:
- A ruby file, in
app/mailers/corresponding to the MailerName passed to the generator (named as mailer_name.rb)
- A folder in
app/viewsalso similarly corresponding to passed in MailerName
- A test file in the mailers folder of your test module choice, because, let’s face it, if you haven’t tested it, it might not always work the way you want it to.
- A named file in the preview folder (within the mailers test folder) that can be used (see later) to preview your email content in a test server environment. 3, Rails does a lot of the foundation work for you so that you can focus on the primary aspects (who the email is going to, body and subject, for instance) of the email, but at the same time you can customize/override most anything, including any default email headers if you so desire.
- A ruby file, in
- Information is passed to the email itself with instance variables that you can create but there are also some built-in attributes that are common to email (like images, both attached and inline) that are accessible in the mailer and passed to the corresponding view as well.
- Once you’ve gotten all your information together, Rails makes it easy to initiate the email, and if you want to send multiple versions in the same email (multipart), Rails makes that easy as well.
- Rails also allows (out of the box) the ability to receive and process emails as well, but I’m not getting into that at this time
From the chapter in The Rails Way book, I moved on to the rails guide on the Action Mailer basics. The guide presents much of the same information as the previous reference, but goes into more detail and provides a bit more content in certain areas. The guide also provides enough code to follow to build a basic mailer (and preview it). Using the tinyapp principle, I followed along this example code and was able to successfully create an email within the development environment on my local computer.
So, now, I felt comfortable with the basics of the Action Mailer to create simple email notifications. The next step of course would be to implement mailing into a live application, with tests, and then deploying it. Fortunately(maybe?) for me, I already had a simple idea in mind. I wanted to add functionality to this blog that would automatically email me when a comment was made on an article. At this time, there have been many comments on some articles, but only one was an actual comment. Some spammer or the like has found my blog and every so often picks an article and comments a few hundred times. I can easily check the blog manually, but it’s nicer to know when they start showing up automatically, I think, so the next step would be learning how to test a Mailer within a Rails application.
Testing a Mailer
The chapter on Action Mailer in The Rails Way does have a short section on running tests on Action Mailer. And fortunately for me, The Rails Way suggests RSpec for testing, so the instructions are slanted towards RSpec. The way the testing is explained within the guide is that there’s a very helpful gem, named email_spec that seems pretty current that provides a variety of helpers and matchers to test that the Action Mailer functions the way you wish it to function. Testing that the email gets sent properly is another issue that can be dealt with at a later date. At this point, I’m going to be following the instructions for functions within the idea of creating the CommentsMailer on this blog itself.
The set up steps for implementing testing of Mailers with the gem are pretty simple. You add
gem email_spec to your gem file, under the test group, add a couple of new config statements to incorporate the gems functionality and then you can write the basic tests to make sure your mailer function itself yields the content you want it to yield.
After a few mis-steps (passing a symbol to the mailer instead of the Factory Girl created variable can really mess you up) I am able to get the basics of this test working on the Mailer side, I set the to and subject for the email and they pass well. I set up instance variables for both the the comment and its parent article so I could pass them to the body (the view) of the email. For now I will have a link to the article and the content of the comment in the email. In the future it would be smart if I could delete the comment directly from the email if I saw fit, but that requires an admin login and I’m not sure how to do that automatically from an email.
Using basic erb (example code not really necessary) I was able to get tests to pass that indicated the article title, comment body, and the full url (not relative, important in emails) of the article were included in the email body generated for the mailer.
So there you have it, creating (and testing) emails, while not dead simple, is still pretty easy when you use Ruby on Rails. I was able to use two pretty solid references to not only create the basics of an email but to implement its functionality into my blog and test that the functionality worked. That’s a good start, but it’s not the whole thing, there’s still the matter of triggering (and testing) the trigger of the email and setting it up so that it actually works on a live site, but I’ll save that portion for the next time. I now feel more comfortable (and less scared) with one of those fundamental concepts I’d been avoiding for a while, and that’s always a good thing.