captured sparks

Receiving emails with Ruby on Rails

I spent the weekend getting a little application to play nicely with emails being sent to it. As receiving emails with Rails is something that is not written about as much as sending emails, I thought I would share my solution.

This tutorial will show you how to receive emails into your application, store them in the database and forward the contents to multiple recipients.

The first problem of course is getting the emails into your application. I have root access to my server, so my solution uses Postfix. If you don’t have root access, you can use POP and there are a number of examples out there. On with the instructions…

As I am hosting a number of domains on the same box, I set up my main.cf file as follows:

virtual_alias_domains = mydomain.com
virtual_alias_maps = hash:/etc/postfix/virtual

I then set up my virtual file (which you may need to create) with a single line:

@mydomain.com rails_mailer

This will intercept any emails sent to mydomain.com and pass them to the rails_mailer alias, which is created in your alias file. Mine is located in /etc/aliases: yours might be in /etc/postfix/aliases. Add to the bottom of your aliases file:

rails_mailer: "|curl -F mail='<-' http://mydomain.com/emails"

This uses curl to post the mail as form data to the given url. The email contents will be stored inside the mail parameter. The pipe character at the start indicates that this is a shell command and not a directory.

To apply all these changes:

sudo postmap /etc/postfix/virtual
sudo newaliases
sudo /etc/init.d/postfix reload

To check that this is working, change into your /var/log directory and use:

tail -f mail.info

Then send an email to any address @mydomain.com and you should see it received in your mail log and passed to your Rails app. If you look in your app’s logs, you should see it spitting out a 500 error as there is no code to handle the email…yet.

On to the Ruby. Create an Emails controller and define a create method:

def create
  if params[:mail]
    Notifier.receive(params[:mail])
  end
end

This receives the contents of params[:mail] and passes it to the receive method of our notifier model.

To deal with Rails’ cross-site scripting protection, you need to add to the top of your controller:

skip_before_filter :verify_authenticity_token

Next, generate a mailer called notifier. In notifier.rb, define a method called receive(email). Using this method name automatically invokes the power of TMail. My receive method is as follows. You will need to change this to make sure you pull the information you need from the email:

def receive(email)
  event_name = email.to
  event_name = event_name.to_s.sub('@mydomain.com', '')
  event = Event.find_by_tidy_name(event_name)
  body = email.body
  address = Array.new
  event.attendees.each do |attendee|
    address << attendee.email.to_s
  end
  from = email.from.to_s
  new_email = event.emails.new
  new_email.from = email.friendly_from
  new_email.body = body
  new_email.save
  Notifier.deliver_event_email(event, from, address, body)
end

Before running through this code, I should explain that when people use the app, they receive a dedicated email address in format myeventname@mydomain.com.

This code does two things. Firstly, it extracts the following data from the email: the name of the event, the body of the email and who the email is from. It then constructs a new email that belongs to the event and saves to the database. It then invokes another method in our notifier.rb to forward the email body to the individuals who are attending the event.

def event_email(event, from, address, body)
  setup_email(from)
  @bcc = address
  @subject += "#{event.name}"
  @body[:body] = body
end

In terms of the application that I wrote this for, I hope to launch it within the next week. If you’ve got any questions or run into any problems, leave a comment and I’ll try and help.

You can talk to me, or trackback from your own site. Or, if you're looking for more things to read? Why not check out the Archive.

  1. Thanks very much for going to the effort of posting this – saved me scrabbling around to figure out how the postfix stuff is configured.

  2. Nitin R said at 12:10 pm on March 9, 2009

    I sincerely am grateful to you for sharing this clean documentation on receiving emails into Rails app..

    I have doubt understanding the differences in usage as I am using a Windows based Development PC..
    Where should I make the changes which you made in
    -main.cf file?
    -@mydomain.com rails_mailer
    -/etc/postfix/aliases

    [Also is there a Postfix installation for Windows or I need to use some different Mail Handler??]

    I am yet to incorporate the above solution in my Rails App, but this solution is exactly what my App was lacking intending to do..

    Thanks,
    Nitin R

  3. Is there a way to do the same thing, but for smtp outgoing emails as well with postfix?

  4. Robin Fisher said at 9:46 am on March 17, 2012

    Hi Tripp,

    I haven’t sent emails directly from one of my servers for a while.

    My preferred method is to pass this off to an external service. I’m currently using Amazon Simple Email Service but I hear good things about Postmark.

  5. Hi,

    Many thanks for this very useful guide!

    I had a question; you mentioned that for every user that signs up on your website, you automatically create an anonymous email like 12345@mydomain.com. How do do this?

    If you could share code/guidelines for doing this, that would be awesome as I am relatively new to rails.

    Thank you,

    Faisal

  6. Robin Fisher said at 9:21 am on April 16, 2012

    Hi Faisal,

    I don’t actually create the email address. For each event created, it would be given a downcased name e.g. robinsbirthday. Emails sent to robinsbirthday@mydomain.com would be routed into the application and then I strip out the part before the @ sign and find the relevant event e.g. Event.find_by_downcased_name

    Robin

Talk to me