Sending emails with Mandrill

Just a heads up! Mandrill now requires a paid Mailchimp account to use it. There are plenty of other options that have free tiers work similarly such as Sendgrid, Postmark, MailJet, MailGun, and so on.

Resources

Transcripts

I want to talk about sending emails in your rails application, and how we can do that using smtp and Mandrill's API. Mandrill is a service that allows you to send emails very easily, and you basically just register and connect it to your rails application, and that's as simple as it needs to be. You'll do that over smtp, but there's also an API that allows you to create templates inside of Mandrill, and you can use those to design your application in a way that allows your customers if you're consulting or your marketing people or whoever to go and change the emails at any point in time. You will just give them some variables and the content that they could put in the email if they choose to, and that allows you a lot of flexibility and you don't have to update your rails application every time you want to change an email. So let's dive into this.

I think the first thing we want to do is open up our application and go into the config/environments/development.rb, and the blind that we want to add here at the bottom is the exact same as the one that devise told us to do before. I haven't actually done it, so I'm going to edit it now.

That is the domain url that I type in my browser when I'm doing development. So when you want to send an email with your rails application, you pass in to action mailer the objects that you want to use, and action mailer handles this by defining who is going to receive the email, who's sending the email, what the subject is, and what the view of the email should look like. So the view of the email is either in html or a text template that you render, and that gets sent back to the smtp server and it's handled appropriately. The reason why we have to set this url host option here, is because there is no url that this came in on for sending an email. Because sending an email is a separate transaction, you want to define what's going on here separately so that you can use this in other areas. So for example, if you have a background script that runs every night at midnight, and that needs to send an email out for some reason, there's no request that has come into your website that you can count on, so what you end up doing is you configure the mailer to globally have this feature, and then as that is necessary when an email is being sent, it will use that. You're not really inside of your rails application to the same extent as you are when you're in a controller, the action mailer stuff is significantly separate. It's important to understand the separation of action_mailer and the rest of your rails application, because I've seen a lot of confusion come out of that. Your action mailer is really just a tool to integrate with a remote mail system that you can use in your application to send information out to users. Of course, any time that you modify your config environment, initializers or your Gemfile, you want to make sure you restart your rails server. So we might expect that if we went into our application now and click login, and click "Forgot your password" and you typed in your email address here, when you click: "Send instructions", you get a notice saying that it's sent the email and you would maybe expect that you would receive an email. Well the reason it doesn't actually send one is because you haven't configured your action_mailer to actually connect to an smtp server to send the email across. And if you look at your rails logs, you can see that when it actually sent the email, it rendered it out in the terminal, and this tell you that the email was actually trying to be sent, which is good. However, it didn't correctly send, and it sort of failed silently, and that is something that you need to be aware of, that sometimes your emails will send, sometimes they won't, and you need to be very clear that this is working and this isn't working, or whatever. So you don't want ambiguity there when something is failing. The way that you can change that, is you can go back into the development.rb and you can tell it to raise delivery errors when a mailer can't send

config.action_mailer.raise_delivery_errors = true

In development, we don't really want it to fail when we're doing our first version of testing the emails, so if we've never had it work before, then it's good to see what things are going wrong, at a point where you have your application fully built and functional, and you trust that sending the emails works, then you can go back and set this to false, because in development you probably don't need to send yourself a whole bunch of test emails after you've tested to make sure that it's working. So if you want to do this, it's optional, but you'll need to also restart your rails server if you change this.

One other configuration option that I want to change real quick is if you go into config/initializers/devise.rb, there is a mail_sender option here that is basically the email address where your devise mails come from, and when it says: Please change me, it's probably a good idea to change it. So we're going to my gorails address, and if you are familiar with email formatting for email addresses, you can actually put an email in here, and then you can wrap your email address in <> symbols, so then your name will show up as well as the email correctly getting parsed.

After you've registered with Mandrill, you'll get taken to the homepage which gives you links to it's set up, smpt or the api version of Mandrill. We're going to look at the smtp version, and if we jump in there, we get presented with this, and it basically is saying: Here is where you will send the emails to, you will connect to it over a port 587, your username will be your email address, and your password will be any of the API keys that you create, so we're going to create an API key, and you just click the button, and it just creates an API key for you. so we can take this information now, and set it up inside of our rails application. Before we go back to our rails application, let's copy the API key to the clipboard so we can paste into our configuration as we write it. If we go back to MacVim, we can create a new file config/initializers/mail.rb If you want, you can mail separate the configuration out separately for development and production, and use two different mail servers if you'd like. I'm going to configure my application to use the same Mandrill account for both because I'm generally not going to be sending many in development, and I really want to make sure that the configuration works the same between the two.

These are all specific to smtp connections, so don't worry about them too much unless you're using a different service like SendGrid and it needs different configuration options. The last two options will allow us to send many of the utf-8 characters across, and it won't choke on them. Now we can restart our rails server to initialize all of the smtp configuration, and we can go back to our rails app, and click "Forgot your password", and I'm going to send a "forgot your password" email to [email protected], I've already created this account, as well as [email protected], which is my other email address, so if I send this password, reset instructions, we'll get the same message as before, and immediately you can see that I've got an email, and it has come from "Chris at Gorails", and the email address, so the name that we've displayed here properly, and we can click "Change my password to come back to our application", and follow the new password flow thing. So it has a reset token that it set's, and allows you to set your new password that you can do, and click "Change my password", and you're back into your application, logged in as that account. So if you'd like to customize the content of the email, that devise sends for reset your password, you can go into the app/views/devise/mailer folder, and see the password instructions, and this is where it generates the email content, but in order to get to this file, you need to make sure that you've run rails generate devise:views, and that will generate these files and make them available for you to customize.

Now, imagine that you want to add your own email that gets sent out when a new user is created, you like to see the user's that are signing up, kind of get notifications of that, and then be able to write a personal note to each person. So you want to set up an "after create" email that happens when a new user signs up. So let's go ahead and do that. We're going to generate a mailer called "AdminMailer" because this is an email that only goes out to admins in the site, so that will be you and pretty much nobody else for the moment. So we'll generate this mailer, and then we can open it up in our text editor, so we can go into the app/mailers/admin_mailer.rb, and because this inherits from ActionMailer::Base, this is the exact same class that we configured in our mail.rb, and you can see that here in all of these lines, and that means that we automatically have these settings applied to this mailer, so this mailer will be connected to Mandrill over smtp. So we can change the

Because this is just a notification, and if we reply to the email, we don't need to do anything with it, so we'll leave it at that, and that way when you reply to the email, it just gets ignored, and because we're making a mailer to only ourselves, we can have

This is similar to a respond_to in a controller, where this will say: Ok, set up a mail, and we'll send it out, and this will automatically look in our app/views/admin_mailer folder for the same template named new_user.html.erb, so in here

app/views/admin_mailer/new_user.html.erb

<h1><%=@user.email%></h1>

The email will be very simple but it will be sent out when the user is created. So if we go into our user model, we can go add

This will create the email, and then the deliver will actually send it is just initializing it, and the deliver will actually send it out. Now, in order to test this, I'm going to open up the rails console, and just delete the last user that I created which happens to be "[email protected]". Now, we can jump back into our rails application, and because I've deleted the account I was logged in as, I'm signed out, and this time, if I sign up, and I put in a password, I will be signed in, it will appear that nothing has changed to the user, but my email already has a new email, and you can see the email address of the person who signed up. That is a very quick way of integrating Mandrill with smtp and setting that up, and you have a fully transactional email system, built without any trouble at all. But to change the emails, we actually have to go in here, we have to edit this, we have to pass in new variables, and we have to do a bunch of work, just to make any changes in the emails. That might be ok, but for a lot of customers that you're building a site for, you're going to want to allow them to be able to change how the email works without having to hire you to put in some more time to make a change, and that's what we're going to talk about next with Mandrill API integration.

Mandrill provides a RESTful API that you can integrate with your application, so you can send a request over to Mandrill through an http post, for example, and that will be interpreted by Mandrill to take the data you give it, and send out an email. So you don't actually have to connect to Mandrill through your smtp, you can talk to it just like you would talk to another web sever. So you can make a POST request just like your users are making post requests on your site. Your website will do the same to Mandrill, and Mandrill will send out an email. So we're going to talk a little bit about this, and the way that you want to integrate this with rails, is through the Mandrill API rubygem, and it's hosted on BitBucket. Most of these seem to be hosted on GitHub, but this one just happens to be on BitBucket. There's not a ton of good documentation on it, but it's not very hard to use, so we'll be able to jump into that pretty quickly. Now you want to grab the line for your Gemfile and install that as normal. Run bundle install and restart our rails server.

The admin mailer that we have configured right now, uses action mailer's smtp integration with it, and that works really well. We can also use the same class to define methods to send emails over the API instead of using the smtp mail method that we are right now. So we can create a new method called new_book, and we'll pass in the book inside here, we can call Mandrill's API instead of the mail method. And that would do the same thing as sending an email across. So we actually want to do a few things before we get in too deep with the Mandrill API. And the first thing that we want to do is define a method in here

If you're not familiar with the nil guard, when you call this method, it will execute the code here on the right side and save it on the variable, and then the next time you call it, it will just return the results from the variable rather than executing the code again. So you can kind of cache things really quickly in memory. And we are wanting to cache the Mandrill API client, and the client will have the API key that we have before stored here, so we're actually going to name a constant called MANDRILL_API_KEY, and we'll pass that in. On or initializers/mail.rb, this is the API key that we had before, now we want to put it in two places. So it makes sense for us to delete this, and use a constant in both places, so we can have it in our initializer and our mailer. And we can take this and assign it at the top of the file. Since we set a constant like that, we're also going to want to restart our rails application to have that set globally across our app.

Inside of Mandrill, we can go into our Outbound menu and go down to templates, and here's where you can create these templates that you will use to render emails for us. So similar to the view templates that we have inside our application, like the new_user.html.erb, we can create a template on Mandrill that will store it remotely and then keep track of that and make it customizable. So the name of our template, we can call new_book, and we can add this template, and you can put in the html, and then text over here, as well as configure a little bit about the defaults for the "From Address" and who is it from, and the Subject. We can also override those from our application, and handle how that works. So imagine that we want to do the same notification, but with a book instead. If you're not familiar with MailChimp at all, they have these things called Merge tags, and they're essentially variables that you can put inside of your template that are replace dynamically. So we can name this "BOOK_NAME", and this variable denoted by the | will automatically get replaced by a variable that we pass across. We also want to define the "From Adress", and the name before we move on, because we don't need to specify this every single time in the mailer when we send out these emails. So if we publish this template, we will have it available in our application to talk to. And if we go back into our mailer, down in the *new_book method, we can begin defining access to that, and how we're going to replace the "book name" variable in our template with the content that we have. So we have the book, and if you're forgetting, the models/book.rb has the name column on it. It's not title, it's called name. So this is another case where annotate is really helpful, because you can easily look that up. In admin_mailer.rb, we want to define several variables that we're going to pass along to Mandrill that will determine who this gets sent to and everything. Now these defaults that we defined up here, are going to be ignored, because this is not talking over smtp, so everything in action mailer, is going to get ignored essentially because it's going to be handled by Mandrill itself. So the first thing we want to do, is we want to declare what template we are looking for, and the one that we created, is called new_book. And if you go over to Mandrill, you can see the template slug is called new_book, and they even have a helpful hint here, that you use the template slug to identify the template in the API or via smtp headers. So that is what the "new-book" string comes from, and we can also define a thing called template_content. I'm going to leave this empty because the template content that Mandrill provides is ok, but I'd prefer to use the merge tags instead, and we're going to use those and create that right now. So if we create the message, this is what defines the email itself, so we can say that this email goes to, and we can provide an array of people that it goes to, and you can put in the email, and I'm going to hardcode it as my email. If you want to make it dynamic, then you would pass in a user in here, and then instead of this, you would say: user.email, but because I want a hard-coded, I'm going to do that, because this is an admin notification. You can also add in your name in there if you want, you can leave it blank, it's not required, and that makes it just a little friendlier when you receive the message. The next option is the subject of the email, and just like before, we can say: "New book". The next option is called merge_vers, and this is the array of options that you can pass in that are variables that will be replaced by the string, so we have "book name", and we'll define that here to get replaced in the template. Now this is a little bit different, because when you're sending an email like this with smtp, you do it one by one, so you go talk to the server, and you say: Hey, send this out. And with the API, we can actually define a bunch of these at once. So imagine I want to send an email to "[email protected]", or "[email protected]", I could do that by just adding items into this to: field, and they would get sent. And the merge_vers are interesting because when you create these, you have to provide a recipient, and that will check the list of emails that you're sending to and see if it matches and then it will apply it to that email, so every email that you send out could have the same template, but different content. So it's really interesting that you can do that all in one request, and for this we'll want to have the same matching email address as before, and we can pass in the vars option which will be a bunch of variables that we would like to replace. We can save that and then close our recipient, and then we have a closing tag for the merge_vers, and we've defined all of the things that we need inside of our message variable:

If we save this, we can actually test this out with a book, and let's do that in our terminal, rather than integrating it with the model just yet. So we can grab a book, and we'll just grab the first book, which is: "How to win friends and influence people", and we can say

AdminMailer.new_book(book)

And we don't have to call deliver on this because deliver will initiate the smtp call but because we're doing that with the last line here, we're telling it to execute, we don't need to call .deliver on the end. So we can just simply run this, and we see that we run into a problem called "uninitialized constan Mandril:API", now this is because the Gemfile that we have needs to call an option called require: Mandrill, now they've named the gem "mandrill-api", but the module inside of it, is actually called "Mandrill", we can see that when we call Mandrill:API So by default, the gem will require Mandrill API, but since the name doesn't match you have to add the special option in there. It's one of those things that is not obvious, and because their documentation is a little lacking, it's kind of frustrating to work with a gem like this when it's not very smooth in some places, but generally it works really well, and we can restart our rails console, and run the same command. And we can say

AdminMailer.new_book(book)

And this time it will find the Mandrill API constant and send it over, so we get a NullMail response back because we didn't actually send a real action_mailer request. However, when we check our email, we should get a new email from Mandrill. If we take a look at this email, it's got the right name for the person to send it from, it has the right title, and if we look inside of the email, it's got the right title. So this is all working correctly, and now we can go into Mandrill, and just simply make changes. We want to say "New book:" in here as well, we can publish that, and then if we go back to our terminal, we can run the new book method again, send another email, and we immediately get a new email with the change, so we're able to make changes to the email instantly, and the only thing this depends upon, is that you provide the variables across, so if you want to add a new variable, you'll have to do that, but to change the look or the layout or any of those things in the email, you can do that without having to touch a rails application.