Skip to content

Receiving emails in Rails using Gmail & IMAP, while staying efficient and RESTful.

For a recent project I had a need to receive emails (actually MMSs, but that’ll be the subject of a future post) in a Rails application. My requirements for the solution were:

  1. Shouldn’t require root access: I’m deploying this app to a shared hosting service on which I don’t have root.
  2. Shouldn’t require firing up the Rails stack for every incoming email: A number of the solutions I came across use this method. Seems awfully inefficient, especially if you expect to receive lots of messages. Since I’ve already got a running rails application, why not just create a new service/controller in it?
  3. Can be scheduled to run automatically: Pretty obvious. I want to be able to retrieve messages periodically.

There is already a lot of information out there on how to achieve parts of these requirements, including the Rails wiki, Rails Tips and Craig’s writeup. My solution is really just piecing together bits of each. Here’s a high level overview of the components in my solution :

High level overview of components and desired interaction

High level overview of components and desired interaction

Mail Poller:

This is the component responsible for :

  • Connecting to the email provider (Gmail)
  • Downloading new emails
  • Posting contents of new emails to the Rails app.

I started by creating a new ruby script in RAILS_ROOT/script/ called mail_poller. This is based heavily on the script by John at RailsTips

#!/usr/bin/env ruby
require 'net/imap'
require 'net/http'
require 'rubygems'
require 'logger'

config = YAML.load(File.read(File.join(File.dirname(__FILE__), '..', 'config', 'mail.yml')))
log = Logger.new(STDOUT)

begin
  imap = Net::IMAP.new(config['host'], config['port'], true)
  imap.login(config['username'], config['password'])

  # select inbox as our mailbox to process
  imap.select('Inbox')
  # get all emails that are in inbox that have not been deleted
  imap.uid_search(["NOT", "DELETED"]).each do |uid|
    # fetches the straight up source of the email
    source   = imap.uid_fetch(uid, 'RFC822').first.attr['RFC822']
    # Post the email to the Rails app's restful service
    res = Net::HTTP.post_form(URI.parse(config['service_url']), {'email'=> source})
    case res
      when Net::HTTPSuccess, Net::HTTPRedirection
        # OK
      else
        res.error!
    end
    # Delete the email
    imap.uid_store(uid, "+FLAGS", [:Deleted])
  end

  # expunge removes the deleted emails
  imap.expunge
  imap.logout

  # NoResponseError and ByResponseError happen often when imap'ing
  rescue Net::IMAP::NoResponseError => e
    # Log if you'd like
  rescue Net::IMAP::ByeResponseError => e
    # Log if you'd like
  rescue => e
    log.warn e
end

This script relies on a RAILS_ROOT/config/mail.yml script for configuration of mail server, username, password etc. Here’s a sample of that config file. It is pretty ugly, and needs to be refactored to be environment aware. Perhaps I’ll update this post once I’ve refactored it. The username & password in the service_url are explained in the “Mail Controller” section below.

host: 'imap.gmail.com'
port: '993'
username: 'your.address@gmail.com'
password: 'your password'
service_url: 'http://admin:pass@localhost/admin/incoming'

I like this solution over a postfix/sendmail/qmail/et all solution because it keeps the logic of fetching mail ‘close’ to my application. What does that mean? If I needed to deploy my application to a new host, I wouldn’t have to reconfigure an MTA on that host. It also has the added benefit of not requiring sudo/root privs.

Mail Poller Scheduler:

Since this is running on *nix systems, cron is a well vetted, ubiquitous solution. Creating a cron job to run our mail poller every minute is easy. Add the following to your crontab entry (crontab -e). Replace the full path to ruby with what is correct for your system (a ‘which ruby’ command should give you the  path you need). Replace RAILS_ROOT with the full path to your rails application.

* * * * * /opt/local/bin/ruby RAILS_ROOT/script/mail_poller >> RAILS_ROOT/log/mail_poller.log

This will ask cron to invoke the mail_poller script every minute, and redirect STDOUT to the ‘mail_poller.log’ log file.

Mail Controller:

This is the last piece in the puzzle. It is invoked via a POST when the poller retrieves a new email. In my case, the controller is namespaced to an /admin/ area which is password protected, thus the need for the username & password in the service_url above.

Lets start of with my routes.rb

  ...
  map.namespace :admin do |admin|
    admin.resource :incoming,  :o nly => [:create]
  end
  ...

This creates the folowing route :

admin_incoming POST   /admin/incoming                  {:controller=>"admin/incomings", :action=>"create"}

My controller looks like this :

class Admin::IncomingsController < ApplicationController
  before_filter :admin_required
  skip_before_filter :verify_authenticity_token  

  # Being invoked as a POST from the mail_poller
  def create
    mail = TMail::Mail.parse(params[:email])
    # Do stuff with the mail object
    ...
  end
end

I’m using TMail to parse the email (which is also used by ActionMailer’s receive method). Once you’ve got the mail object parsed, you can process away at will. There are plenty of examples out there on how to walk through the email looking for what you want.

Acknowledgments:

As mentioned above, this solution is heavily based on the following resources. Many thanks to the folks behind that information

Summary:

I really like the simplicity & separation of concerns this solution provides. The poller is pretty ‘dumb’, i.e. it doesn’t have any application logic (does nothing with the contents of the email), and it doesn’t have any timer/sleep logic. The application logic is relegated to the already running rails application. The timer logic is relegated to the old stalwart, cron.

I’d love to hear your thoughts on this approach. Post a comment below and let me know what you think.

10 Comments

  1. Hey, thanks for the post.

    While not directly related, I’m playing around with a Twitter bot and was wondering how I would go about hooking it up to a Rails application, and the way you post the email to a new controller is perfect for adapting for tweets.

    Thanks :)

    Posted on 11-Feb-09 at 12:06 am | Permalink
  2. @ Tim

    Thanks for the feeedback! Glad (part of) the post was useful to at least one person! :)

    Posted on 11-Feb-09 at 7:29 am | Permalink
  3. This looks really nice. I was thinking of using this plugin:

    http://github.com/entp/astrotrain/tree/master

    But perhaps this solution is lightweight enough. It’s nice that you don’t need to have another process running all the time to worry about, I guess.

    Posted on 11-Feb-09 at 2:33 pm | Permalink
  4. @Trevor

    I wasn’t aware of that project, thanks! I’ll follow it to see how it evolves.

    Posted on 11-Feb-09 at 2:58 pm | Permalink
  5. You may also be interested in the Fetcher library that I helped write.

    I just moved the code to GitHub tonight. http://github.com/look/fetcher/tree/master

    It downloads email via POP3 or IMAP and lets you process it. It can run as a daemon or via cron. I mostly use it for http://events.fanchatter.com

    Cheers!

    Posted on 15-Feb-09 at 10:15 pm | Permalink
  6. @Luke,
    That is a really neat looking project. I’m going to try it out as part of a refactor I’m doing. The less code I have to maintain, the better! :)

    Thanks!

    Posted on 16-Feb-09 at 9:40 am | Permalink
  7. broadiainduby

    Hello, I can’t understand how to add your blog in my rss reader
    ————————
    internet signature:

    Posted on 26-Apr-09 at 3:43 pm | Permalink
  8. Acerhoorurb

    Hello, I can’t understand how to add your blog in my rss reader
    ————————
    signature:

    Posted on 28-Apr-09 at 11:07 am | Permalink
  9. @broadiainduby and @acerhoorurb, at the top right corner of every page is a link that says “Subscribe to our feeds..”, which is a link to http://blog.yetisoftware.com/feed/. Clicking that should take you to a feedburner page that lets you add the feed to your RSS reader of choice.

    Posted on 28-Apr-09 at 10:20 pm | Permalink
  10. Ian

    Thanks for this post, very useful and saved me a lot of time.

    Posted on 03-Feb-10 at 6:54 am | Permalink

2 Trackbacks/Pingbacks

  1. [...] post: Receiving emails in Rails regulating Gmail & IMAP, whilst staying fit as well as RESTful. Tags: application, humor, iphone, programming, rails, routes, ruby, Server Hosting, thoughts, [...]

  2. [...] Receiving emails in Rails using Gmail & IMAP, while staying efficient and RESTful For a recent project I had a need to receive emails (actually MMSs, but that’ll be the subject of a future post) in a Rails application. My requirements for the solution were: Shouldn’t require root access. Shouldn’t require firing up the Rails stack for every incoming email. Can be scheduled to run automatically. [...]

Post a Comment

Your email is never published nor shared. Required fields are marked *
*
*

Spam Protection by WP-SpamFree

  • patterson kelley heat exchanger kelley
  • beet goat cheese beet
  • 14 carrot cafe seattle carrot
  • findley roofers acworth ga roofers
  • pinhole camera lenses pinhole
  • masons patent nov30th 1858 masons
  • discrete mathematics combinations mathematics
  • frontera roasted tomato salsa frontera
  • workstations accountability form accountability
  • bernstein international monuments bernstein
  • openbsd frequently asked questions frequently
  • scary kids scaring kids holding scary
  • virtua girl hd torrent sandra virtua
  • union county readers workshop nc workshop
  • country smokehouse imlay city smokehouse
  • illegals coming into us desert illegals
  • mareike thum mareike
  • florist kenner la 70065 kenner
  • belmar school belmar
  • pj's coffee shop franchises for sale franchises
  • silke sauter silke
  • slander and the work place cases slander
  • ssh putty downlad chiark putty
  • hoyle games closes down hoyle
  • tag heuer frame heuer
  • weeds showtime season 3 showtime
  • interesting travel kansas interesting
  • creating a merge document in word document
  • provera injuries to baby from provera provera
  • gene shaw villanova baseball gene
  • ingrid mccallum santa rosa beach florida mccallum
  • wyndam hotel el paso tx wyndam
  • goddard alliens in your neighborhood goddard
  • stitching ott light stitching
  • gambler gunnison gambler
  • rosemarie poliet rosemarie
  • gothic clubs in fayetteville gothic
  • alternatives to mellow light iv mellow
  • free novel eckhardt tolle on patience tolle
  • ohne dich lyric ohne
  • hotels chelmsford massachusetts chelmsford
  • lloyds tsb united kingdom berkshire lloyds
  • servo to encoder connectors encoder
  • kubota 4330 service manual kubota
  • olivet luthern fargo nd olivet
  • aaj kala joda mp3 kala
  • laptop competion nz competion
  • lanny bip sokol lanny
  • hrw atlas mundial anguilla mundial
  • twin discs 514 discs
  • ultimate linx communications linx
  • albrechts volk delfshaven volk
  • shire of waroona wa shire
  • integra usa integra
  • trainee mind trainee
  • laker girls laker
  • dia de los muertos alter decorations muertos
  • l c h resourses inc resourses
  • joist hanger installation joist
  • used easton havoc bat havoc
  • gehl skidsteer wheel skidsteer
  • 02 ranger shocks bilstein ranger
  • king power pot puller review puller
  • buckingham little league elijah roche buckingham
  • foxfire texas resort foxfire
  • extracting first and last names extracting
  • cellulitis antibiotic treatment cellulitis
  • mali and sheryl malcolm marshall barbados mali
  • ethos development ethos
  • political cartoons about the dust bowl cartoons
  • buy hungarian paprika top quality paprika
  • grandparents raising grandchildren blogs grandchildren
  • selenium related diseases selenium
  • sparrows pictures sparrows
  • anybody to chat anybody
  • virginia basketball referee rulebook referee
  • arizona admiralty lawyer rating admiralty
  • petroleum engineering opportunity belgium opportunity
  • gareth eames tiverton rhode island eames
  • crutches assistance crutches
  • gladstone models meadowlakes gladstone
  • c-west super aero louver hood 350z aero
  • vallejo ca times herald vallejo
  • pleasantville ohio local news pleasantville
  • treating pins and needles pins
  • thermo generator history thermo
  • horner family in idaho horner
  • famous scientist of japan scientist
  • nottinghamshire history and archaeology cambridge county archaeology
  • mechwarrior home mechwarrior
  • incomplete dominance in plants dominance
  • pharmacist michigan suny buffalo pharmacist
  • audiology licensure audiology
  • predator prey relationships in arctic predator
  • bubble buddy game buddy
  • celtics and basketball celtics
  • modify software modify
  • two plus two bob seger system seger
  • brain injury prolonged pt prolonged
  • madeline bozzelli madeline
  • cassandra soul calibur iv screens calibur
  • pentax w60 camera best price pentax
  • advance aluminum castings corporation castings
  • gustavo velazquez localizaci n de facilidades velazquez
  • gena wild previews gena
  • urea formaldehyde thermoset formaldehyde
  • anarchist cookbook free online anarchist
  • haight ashbury 60 s haight
  • dunkin donuts westboro donuts
  • how to investigate kidnapping investigate
  • ghost videos caught on tape caught
  • is charles capps still living capps
  • jurassic park hd torrent jurassic
  • powerpuff girls wiki powerpuff
  • crestwood idylwood crestwood
  • ernie halter played mp3 ernie
  • villeroy boch savoy boch
  • renters bureau renters
  • romana poepping krawciw romana
  • bluecross blue shield insurance bluecross
  • andorra scottsdale andorra
  • can oxycodone affect blood sugar oxycodone
  • sanford's antiques san anselmo california anselmo
  • cartoon piranha fish piranha
  • nortel japan telephone fax numbers nortel
  • lactic acid bacteria in yoghurt production lactic
  • baptistry sediment filter sediment
  • distribuidora de electronica sinavi electronica
  • paul brandt fly brandt
  • italian restaurant cedar place beechwood ohio beechwood
  • prey and predators of the taiga predators
  • tina lotter tina
  • free monthly horoscopes a true psychic horoscopes
  • antidote for motrin antidote
  • pearl rhinestone wedding jewelry rhinestone
  • jennifer barkley massachusetts barkley
  • 1876 1910 romance 1876
  • fly pets compartment under cockpit $75 compartment
  • mdma dental erosion mdma
  • quorum wall sconce quorum
  • business investing stocks and bonds small investing
  • genesis severna park md severna
  • retractable awning support pole awning
  • unanwsered prayers garth brooks garth
  • bayside yankees 2008 vinny ferraro vinny
  • eliot's deli stoughton ma stoughton
  • falling bra straps people looking straps
  • clear cache xbox 360 cache
  • lisa evers ohio evers
  • boxed edition the good earth oriental boxed
  • erin christensen old christensen
  • medecin marseille rue de lodi marseille
  • super bowl partys in alantic city partys
  • starcraft christmas starcraft
  • fawn ridge estates fawn
  • tumwater family clinci tumwater
  • snowmobiles sherton wyoming area snowmobiles
  • lips of an angel akon lips
  • shrimp shooters recipe shooters
  • loretta lynn family info loretta
  • horizonview chillicothe ohio chillicothe
  • gooseneck hauling jobs texas hauling
  • who pandemic 2008 pandemic
  • captain claw cheat codes claw
  • symptoms immediately preceding labor immediately
  • tracfone motorola gsm c139 4 tracfone
  • darby creek excavation circleville ohio circleville
  • bathroom remodeling schiller park schiller
  • 2009 all star nationals u16 female nationals
  • free royalty free music sounds royalty
  • planned parenthood texas brownsville parenthood
  • transfer mange on clothes mange
  • crave tube clips crave
  • darien lake amusment park amusment
  • truro 11 november remembrance service truro
  • unusual sunflower seeds garden sunflower
  • testicular muscle injured testicular
  • you gotta walk that lonesome valley lonesome
  • file extension ebk details extension
  • lowes infield camping lowes
  • star wars galaxies spaceship textures imperial spaceship
  • free cavity search pics cavity
  • glass insert for exterior doors exterior
  • bullmastiff size bullmastiff
  • devo jocko homo devo
  • shelby car keychains shelby
  • burn to dvd burn
  • 2020 pic 2020
  • reinhart auctioneers reinhart
  • traditional roma stories roma
  • maximus photos fegie maximus
  • welcome to molly meadows com welcome
  • kunst wahnsinn kunst
  • sending magazines to deployed soldiers deployed
  • grants specific for cancer survivors opportunities survivors
  • canadian shield special events shield
  • rugs runners cape cod rugs
  • turn soldering gun into spot welder soldering
  • tunnel to heaven animation tunnel