Altoros is a big data and Platform-as-a-Service specialist that provides system integration for IaaS/cloud providers, software companies, and information-driven enterprises. Areas of expertise include Cloud Foundry, Hadoop, and NoSQL solutions, as well as Microsoft .NET, Java, Ruby on Rails, and mobile technologies.Written by altoros
This post contains some notes regarding different aspects of work with email in Ruby on Rails and covers such topics as sending and receiving email as well as using address tagging.
Sending email in Rails is being performed with the use of the ActionMailer::Base class. You can check the docs to get more details.
It appears to be convenient to configure ActionMailer in different ways for different environments.
A great tool for this environment is the Letter Opener gem. It intercepts all outgoing email and opens each email in a separate tab of your default browser instead of real sending. Using it allows you not to worry about sending unwanted emails by accident to your real users mailboxes and not to bother you customer and testers during development or debugging some mailer. Here is an example of ActionMailer config for development environment:
It’s rather common to have a 3-environments infrastructure - development, staging and production. Staging environment is usually being used for testing features to work properly (by testers and/or customer) before releasing them to production. In this case it’s important for a tester to be able to verify that some email is being sent successfully and is implemented correctly. At the same time emails should not be sent to real users. In this case Letter Opener is not an option. MailCatcher would be suitable here but again there is a simpler and more convenient option - Mailtrap. You can register a basic account (which is pretty sufficient in most of cases) for free. With this approach email delivery is being actually performed but to mailtrap.io mailbox instead of end user mailboxes. Also as it is expected Mailtrap provides a web interface to manage sent emails. Below is an example of ActionMailer config for staging environment:
In this environment everything is pretty obvious as you should have ActionMailer to be configured for real email delivery to your users. ActionMailer config example for production environment:
By the way if anyone uses Gmail for sending email here is an example of smtp settings for it:
Instead of inheriting your mailers directly from ActionMailer::Base it is convenient to create a parent
ApplicationMailer mailer class where you can configure default mailer properties as for example layout and “from”. Later you create your mailers inherited from it.
Having a layout provides you with obvious advantages of DRYing your email views (for example you can easily add shared header and footer).
So, let’s suppose that your application provides users with a capability to send in-site messages to each other with an option to send an email to the recipient user as well (via a checkbox or something). A possible simplified
create action responding to js format for this could look something like this:
By the way for performance and usability reasons it is better to send emails asynchronously, for example using Sidekiq gem. In this case your code would look like this:
Ok, let’s look at our
And we should have a corresponding view:
And that is all. Basic email should now be sent after invoking the “create” action of MessagesController.
Pretty “From” field
Let’s modify a bit
From field of the email in order to clearly see who sent you a message when you receive such email. And also let’s not show the actual user email address (let’s assume it is for private reasons) but instead we’ll use the email address which we set as the default in the
Assuming a message was sent by John Doe for example Gmail will show an email sent this way as from “John Doe”. Many email clients will show it as from “John Doe <email@example.com>”. I think it looks much better than just from “firstname.lastname@example.org” and besides it allows you to search for emails based on their actual sender.
Suppose we want to send some file attachments along with an email. File uploads are out of scope of this post so let’s just assume that we have following models:
Also you should keep in mind that you can’t send an arbitrary amount of data in attachments as it’s most likely that email size will be limited at the destination mailserver. For example current Gmail total email size limit (including body and attachments) is equal to 25 MB. So we should process attachments somehow taking into account their size. There are plenty of options that you could implement including blocking messages with attachments size overlimit from being sent at all, but to my mind a better solution is to send just as much data as possible and if there are attachments that were not sent then for example explicitly tell about it to the recipient user and invite him to see an original message at your website. Code responsible for this could be as follows:
The algorithm above of course is not the most perfect one and can be improved in many ways depending on your needs. This implementation tries first to create attachments based on first uploaded files assuming that they are the most important and then it tries to add as many files as possible up to the provided limit skipping large files causing overlimit.
content_file.filesize - I usually store filesize in the database for faster access to it and to avoid additional disk operations.
Great, now we can send emails with attachments. Next let’s see how to receive mail in Rails.
Without any doubts it would be very useful to not only send emails but also to receive them and process in the context of your Rails application. The most common solution for this task is to use the Mailman gem. Setting up Mailman is pretty simple. Let’s see how to configure it to fetch email from a Gmail account. First, add it to your
bundle install. Next let’s create a file which we will use to run mailman as a background process.
We will be able to run it almost as a standard UNIX daemon:
Next let’s create the actual Mailman server script. Here is an example of what it could look like:
Here we configured Mailman to get email from a Gmail account via the POP3 protocol once a minute. Within a
Mailman::Application.run block we define rules to determine how to process an email in a way very similar to the Rails router approach - you define routes one by one and the first suitable route’s block will be executed. Pretty simple, isn’t it? Great, now you can both send and receive email in Rails.
Another very important and useful technique anyone should be familiar with is “address tagging” which is sometimes referenced as “sub-addressing” (eg. in RFC 5233). The point of this technique is that you can provide some additional info when you send an email right in an email address after a certain separator (usually a
+ character) this way -
email@example.com. All emails sent to this address will be actually delivered to
One of the most well-known use cases of this technique is to determine sites whose database was stolen (or sold, who knows). So imagine that you have an email address
firstname.lastname@example.org and you want to register at some site
www.awesomesite.com. During registration provide your email as
email@example.com. If after some time period you start receiving magic pills advertising emails with
To header field equal to
firstname.lastname@example.org then it’s pretty obvious where those spammers got your email. Sometimes sites can have email validation rules that will reject your email address containing
+ or some other characters.
Also you should keep in mind that this technique can’t be used in some cases because not all mail servers support it or have it enabled by default (eg. Gmail supports it). So if you’re setting up your own mail server then you better check the docs to be sure.
Rails + Mailman + Address tagging
A very interesting functionality can be achieved combining all the above described techniques. Imagine you have a website where users can send messages to each other and you also automatically send a copy of a message by email (or users can manually choose to send a copy to email) - pretty standard feature. It would be great if a recipient user could reply to your email right in his (or her) email client. Let’s see how we can do it. Further I provide a pseudo code which can lack some details but is sufficient to get the idea.
Let’s suppose we have following models:
MessagesController controller with
create action responsible for sending messages which looks like this:
Our mailer class
MessageMailer could look like this:
Here I set
Reply-to field to contain our default
From email address but with addition of some useful information using address tagging. What it gives is that an user will still receive emails from address
email@example.com, but when he hits “Reply” in his email client than the
To email address will be equal to the one we passed in
Reply-to field. Of course we make an assumption that an user will not change it, but I think that in most of cases he indeed will not.
To my mind it’s better to send some hashed values rather than just plain ids as the system will be more resistant to fraud actions in this case. Also it’s better to use one-time hashes and expire them after a message is received.
Mailman route for catching these emails can look like this:
In Mailman you can create a new reply-message and also send it’s copy by email. This way you’ll implement such a system where users can exchange messages with each other directly from their email clients and in the meantime there will be created messages on your site.
As a bonus you’ll get the capability to collect user’s alternative emails. Some users can have email forwarding enabled in their mailboxes so the actual reply can come from email address that is not present in your database. It allows you to implement users sign in based on alternative emails too apart from the one that an user provided during a registration.
Also you should notice that if you have to put some users in
BCC fields then you will not be able to recognize an user who used an alternative email to reply to your message. It will happen because you will be able only to put author’s uuid to email
Reply-to address and putting recipient’s uuid will not be possible due to there can be a lot of
CC-recipients and it just will not make sense. So when an user will use an alternative email you will just not have it in the database. In this case you’ll have to determine who sent a reply message only based on “From” field of the incoming email.
Ok, at this point I’m stopping. I hope this post was useful for you. Thanks for reading!