Did you know that Rails has inbuilt a strong email handling library called (ahem) TMail? I just so happen to maintain this now (Minero Aoki wrote it), but it gives you a great way to validate email addresses…

Update

Rails 3.0 uses Mail now instead of TMail, here is another simple way to validate an email.

Original Post:

If you have ever tried to handle the validation of email addresses in your Rails app, you have probably ended up trying to use something like this to validate the address:

validates_format_of :email,
:with => /^([^\s]+)((?:[-a-z0-9]\.)[a-z]{2,})$/i

Now, this works… in most cases, but there are a few specifics that won’t.

So some of you out there decide that you want a REAL email validation process and so you go for this regular expression instead:

EmailAddress = begin
qtext = ‘[^\\x0d\\x22\\x5c\\x80-\\xff]’
dtext = ‘[^\\x0d\\x5b-\\x5d\\x80-\\xff]’
atom = ‘[^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-’ +
‘\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+’
quoted_pair = ‘\\x5c[\\x00-\\x7f]’
domain_literal = “\\x5b(?:#{dtext}|#{quoted_pair})*\\x5d”
quoted_string = “\\x22(?:#{qtext}|#{quoted_pair})*\\x22”
domain_ref = atom
sub_domain = “(?:#{domain_ref}|#{domain_literal})”
word = “(?:#{atom}|#{quoted_string})”
domain = “#{sub_domain}(?:\\x2e#{sub_domain})*”
local_part = “#{word}(?:\\x2e#{word})*”
addr_spec = “#{local_part}\\x40#{domain}”
pattern = /\A#{addr_spec}\z/
end

And then your validates_format_of becomes:

validates_format_of :email,
:with => EmailAddress

Which is neat, but you have to stuff that Regex away somewhere. When I used this, (by the way) I would make a file in /lib called ‘rfc822.rb’ and then put this in it:

#

  1. RFC822 Email Address Regex
  2. -———————————-
  3. Originally written by Cal Henderson
  4. c.f. http://iamcal.com/publish/articles/php/parsing_email/
    #
  5. Translated to Ruby by Tim Fletcher, with changes suggested by Dan Kubb.
    #
  6. Licensed under a Creative Commons Attribution-ShareAlike 2.5 License
  7. http://creativecommons.org/licenses/by-sa/2.5/
  8. module RFC822
    EmailAddress = begin
    qtext = ‘[^\\x0d\\x22\\x5c\\x80-\\xff]’
    dtext = ‘[^\\x0d\\x5b-\\x5d\\x80-\\xff]’
    atom = ‘[^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-’ +
    ‘\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+’
    quoted_pair = ‘\\x5c[\\x00-\\x7f]’
    domain_literal = “\\x5b(?:#{dtext}|#{quoted_pair})*\\x5d”
    quoted_string = “\\x22(?:#{qtext}|#{quoted_pair})*\\x22”
    domain_ref = atom
    sub_domain = “(?:#{domain_ref}|#{domain_literal})”
    word = “(?:#{atom}|#{quoted_string})”
    domain = “#{sub_domain}(?:\\x2e#{sub_domain})*”
    local_part = “#{word}(?:\\x2e#{word})*”
    addr_spec = “#{local_part}\\x40#{domain}”
    pattern = /\A#{addr_spec}\z/
    end
    end

And then in my model:

include RFC822
validates_format_of :email,
:with => EmailAddress

Which is getting better. But the problem with that now is that you need to maintain that email address regular expression… which is quite… umm… “complex” I think is a good word :)

Enter TMail!

TMail has an “Address” class. It will throw an invalid address exception if given an address it can’t handle (and it has about 2,000 test cases of email addresses it can handle, so you are pretty safe.)

Plus, as the maintainer, whenever I find a valid email that TMail can not handle, I fix it, and then you benefit at the next gem install tmail… so it all turns out good :)

To use it, dump this in your user model somewhere (assuming your user model has the attribute ‘email_address’)

def valid_email?
TMail::Address.parse(email)
rescue
errors.add_to_base(“Must be a valid email”)
end

Then your validates_format_of becomes:

validate :valid_email?

Which is even nicer.. as in… less code == nicer :)

Next tip => How to validate the address fully before accepting it…

blogLater

Mikel

  1. jay11 Says:

    I have read your blog it is very helpful for me. I want to say thanks to you. I have bookmark your site for future updates. More info about AFL Tipping

  2. jay11 Says:

    I know your expertise on this. I must say we should have an online discussion on this. Writing only comments will close the discussion straight away! And will restrict the benefits from this information.
    joueraucasino.tv

  3. Enrico Teotti Says:

    nice tip, thanks!

  4. Tim Says:

    Totally useless as a validation tool! If it accepts email address without the @, then whats the point.

  5. Riley James Says:

    Cheers mate, just used this for my freelancing website http://dragonflylist.com

    Legend.

  6. Daniel K. Lima Says:

    @Tony

    def valid_email_address?
    TMail::Address.parse(email_address)
    rescue
    errors.add(:email_address, “Must be a valid email address” )
    end

  7. Daniel K. Lima Says:

    @Tony

    def valid_email_address?
    TMail::Address.parse(email_address)
    rescue
    errors.add(:email_address, “Must be a valid email address” )
    end

  8. Seth Says:

    I’m trying to use this method but it’s failing miserably on some bad email addresses I use to check.

    It’s taking “notrealemail.com” as an address and returning it as valid.

    Your regexp needs some work.

  9. Leonid Shevtsov Says:

    Your EmailAddress matches “vistoropskaya@yandex/ru”.

    I didn’t look too deeply into the regexp, but I think the “atom” part allows the forward slash to be used, which, AFAIK, is not allowed at least in domain names.

  10. yecdlais Says:

    You could use the RFC regex, I do on one ruby script where I don’t want to include TMail, but if you are using other mail functions of TMail, the free verification stuff in TMail works and works well.

  11. valid email Says:

    For those of you interested in validating email addresses using the Microsoft .NET platform, I strongly suggest to take a look at EmailVerify.NET, a managed component (written in C#) that implements a finite state machine to check email syntax against RFC 2821 and RFC 2822 (no regular expressions, to avoid ReDoS issues) and can connect to the responsible mail exchanger(s) to see if the email address point to a mailbox or not.

  12. omer Says:

    Your tips are good but e-mail validates needs minimum @ and . for usage. I improved idea ara.t howard email.split(//).size == 2, my suggestion is simple email.split(//)1.split(“.”).size == 2

  13. dual saw Says:

    I’m having what the rudimentary problem of getting the TMail::Address.parse() method to parse the attribute I want it to. Was wondering if you could mercifully give me a quick pointer to get me rolling.

    Here is m

  14. click here Says:

    Thanks for this, I get so many email and half of them are junk and go nowhere. I was hoping to find a solution to checking emails. I’m gonna test this out.

  15. Basingstoke Locksmiths Says:

    Thank you for this, was really impressive to read this article

  16. haree11 Says:

    An interesting dialogue is price comment. I feel that it is best to write more on this matter, it may not be a taboo topic however usually individuals are not enough to talk on such topics. To the next. Cheers. more info

  17. Albart Says:

    Excellent post & remarkable! I have been visiting various blogs for my Thesis writing help. I have found your blog to be quite useful. Keep updating your blog with valuable information.psychic readings

  18. karl baum Says:

    I use this for email validation.

    http://github.com/dancroak/validates_email_format_of

    Exactly what you would want.

  19. dfg345hf Says:

    Good point though… I have gotten so used to the advanced features ygosxhipw ju healthcool ft somethingwhy dj howdoes
    of Postgres that I sort of forget that the other DBs don’t support everything it does.

  20. jay11 Says:

    This is actually the kind of information I have been trying to find. Thank you for writing this information.
    http://www.joueraucasino.ch/

  21. jay11 Says:

    Thanks for providing recent updates regarding the concern, I look forward to read more.
    Job Site

  22. Travis Reeder Says:

    Your parse method allows emails with no @ ??

    ie: abcdefg passes.

    Is that expected?

  23. Dan Kubb Says:

    What advantages does the TMail Address class have over the RFC regexp shown in terms of validation? The format of email addresses hasn’t changed in years, and is unlikely to change for the forseeable future, so for me maintenance of the Regexp isn’t an issue.

    The only thing I can see the regexp missing is that it doesn’t check the domain’s TLD to see if its one of the valid ones issued by ICANN.

  24. Mikel Says:

    Dan, I think the biggest thing is that you don’t have to maintain or keep your own RFC on it.

    TMail handles some nasty email addresses that can trip up other regexs as it uses a parser to figure out what is good and bad.

    You could use the RFC regex, I do on one ruby script where I don’t want to include TMail, but if you are using other mail functions of TMail, the free verification stuff in TMail works and works well.

  25. James Says:

    Can Tmail operate behind Gmail?
    I want to be able to track all the email that the app sends by passing it through Gmail. If I do this, I get all the cool Gmailness that allows me to have conversations.

    Thanks!

  26. Mikel Says:

    @James: Sure, TMail just handles the Raw email. You need to use something like Net::POP3 or Net::IMAP etc to get the email down, and then you can parse it with TMail.

    Let me know how you go with it :)

    Also, you can try the TMail Talk mailing list (go to the tmail.rubyforge.org website to find it). Bunch of hard cord TMail users there.

    Mikel

  27. Dave Nolan Says:

    Hi,

    Nice tip on using TMail, thanks Mikel.

    Note you don’t need to wrap the rescue in a begin/end block. You can just do this:

    def valid_email?
    TMail::Address.parse(email)
    rescue
    errors.add_to_base(“Must be a valid email”)
    end

    Two lines fewer!

    Cheers,
    Dave

  28. Fannar Says:

    What would be the best way to use TMail and also validate that the email address is just A-Za-z0-9+._- ?

    Because TMail accepts accent chairs.

  29. Dave Nolan Says:

    Gah! Sorry, it’s eaten the linebreaks and I don’t know what formatting engine you’re using. Try this:

    def valid_email?

    TMail::Address.parse(email) rescue errors.add(:email, “must be valid”)

    end

  30. Jeremy Green Says:

    I have tmail installed and have followed the instructions above and it seems to be working for detecting some invalid addresses, but not all. For instance it knows that ‘sdf sdf’ is an invalid email address, but ‘sdfsdf’ is passed through as valid.

    Shouldn’t anything with out exactly 1 ‘@’ and at least 1 ‘.’ be rejected as invalid? Am I missing something?

    Thanks.

  31. Jason King Says:

    Problem is that there are many things in the RFC that you specifically don’t want to accept as valid email addresses – such as local addresses (in fact ‘_’ is valid by RFC822 – but certainly one of the most common things you want to exclude as a valid email address), invalid public domains (eg. @._ is valid RFC822 but not something you want to accept as a valid email address), and silly things like ‘"@"@!.+’.

    Better to require valid addresses because of your business process (eg. by requiring the user to validate their email by clicking a link you send to their address) than to attempt to validate them with regex or against the very liberal RFC822.

  32. Mikel Says:

    @Jason – Good points.

    I usually do both. I validate with TMail to make sure it has a chance of arriving at a mailbox and then send it to get the user to click on it.

    The point is though that you can get some down right weird mail addresses out there – especially if you deal with non English email addresses…..

    And I’d like to be able to at least send a note back to the user saying ‘hey, it looks like you mis typed your email address’ when they put a non quoted space in it, than say ‘thanks!’ and attempt to send it and have that fail due to the space.

    Regards

    Mikel

  33. Darrell Hansen Says:

    Excellent point Travis. TMail::Address.parse just doesn’t work they way we want for validating email. Look at this:
    >> email = TMail::Address.parse(“Mikel Lindsaar <mikel@lindsaar.net>”)
    => #<TMail::Address mikel@lindsaar.net>

    >> email = TMail::Address.parse(“Mikel Lindsaar”)
    email = TMail::Address.parse(“Mikel Lindsaar”)
    TMail::SyntaxError: parse error on token $end

    >> email = TMail::Address.parse(“Mikel”)
    email = TMail::Address.parse(“Mikel”)
    => #

  34. Zach Cox Says:

    Yeah like Travis noted, why is ‘abcdefg’ considered a valid email address? I most certainly do not want users registering on my site with an email address of ‘abcdefg’.

  35. Zach Cox Says:

    Also I’ve noticed that TMail approves email addresses like ‘user@domain’

  36. Tony Says:

    I’m relatively new to Rails and am attempting to use TMail to validate email addresses. I’m having what the rudimentary problem of getting the TMail::Address.parse() method to parse the attribute I want it to. Was wondering if you could mercifully give me a quick pointer to get me rolling.

    Here is my model:

    require ‘tmail’

    class Contact < ActiveRecord::Base
    validate :valid_email_address?

    def valid_email_address?
    TMail::Address.parse(email)
    rescue
    errors.add( “Must be a valid email address” )
    end

    end

    I have an attribute called :email_address – however, it doesn’t seem to be getting validated. Any pointers?

    Thanks!

  37. dharin Says:

    Can you show me the way to validate url and show alert pop up for invalid url onclick submit button with ROR?

  38. Karl Says:

    I use this for email validation.

    http://github.com/dancroak/validates_email_format_of

    Exactly what you would want.

  39. Bob Says:

    In my case simple names like are perfectly valid email addresses. I want to be able to accept those. The global mail alias strategy here (using LDAP) will handle those just fine. So for all of those people who think that an email address must have an ‘@’ in it I think they should add their own validation for just that case. Also the RFC allows user@tld as an address and there are a few of those and so those should not be disallowed on principle either.

  40. Mikel Lindsaar Says:

    Thank you Bob, that’s why TMail handles it.

  41. ara.t.howard Says:

    in all the years i’ve been validating emails the only validation which doesn’t annoy people seems to be

    email.split(/@/).size == 2

    validate actual service in a background task

  42. Brian McQuay Says:

    The TMail stuff isn’t valid as an email validation as previous comments already suggest. However, I still like the rfc822 solution you posted.

  43. karl baum Says:

    I use this for email validation.

    http://github.com/dancroak/validates_email_format_of

    Exactly what you would want.

  44. jay11 Says:

    Very interesting blog. Alot of blogs I see these days don’t really provide anything that I’m interested in, but I’m most definately interested in this one. Just thought that I would post and let you know.
    indian loans

  45. mic1122 Says:

    Admiring the time and effort you put into your blog and detailed information you offer!..
    www.joueraukeno.fr

  46. appreciation Says:

    My family all the time say that I am wasting my time here at web, except I know I am getting knowledge every
    day by reading such fastidious content.

  47. srinarax Says:

    The website is looking bit flashy and it catches the visitors eyes. Design is pretty simple and a good user friendly interface.PS3 Emulator

  48. Terrence Liberman Says:

    I have explain so many article of this site in which some of them were very intresting and inspiring.This article has good title with good description. PS3 Emulator

  49. MrEmo Says:

    Thank you for helping people get the information they need. Great stuff as usual. Keep up the great work!Xbox Emulator

  50. aoiyvg Says:

    dd ennwma[url=http://shopnorthface.co.uk]north face vest[/url]
    [url=http://shopnorthface.co.uk/#north-face-vest]north face vest[/url]

  51. lee1122 Says:

    Interesting post. I Have Been wondering about this issue, so thanks for posting. Pretty cool post.It ’s really very nice and Useful post.Thanks
    mario games

  52. MrEmo Says:

    Upon seeing those families having a trail, I envy because I never got a chance to have to. However, I am still happy that the parents are spending much of their time for the kids.Xbox Emulator

  53. asdsda Says:

    Thank you for some other informative website. The place else may just I get that kind of information written in such a perfect method? I have a venture that I am simply now running on, and I’ve been at the glance out for such info.
    Buy My car

  54. dfsdfsdfsdf Says:

    Its a great pleasure reading your post.Its full of information I am looking for and I love to post a comment that “The content of your post is awesome” Great work.
    Welcome to Distribuciones BAF

  55. Richard1122 Says:

    Thanks for your information, it was really very helpfull..
    interiordesignersingapore.net

  56. dfsdfsdfsdf Says:

    Nice post. I was checking constantly this blog and I am impressed! Extremely helpful information specially the last part I care for such info a lot. I was seeking this particular information for a very long time. Thank you and good luck.
    grezed.com

  57. yoga retreats california Says:

    I know this website provides quality based articles and additional data, is there any other web page which offers these stuff in quality?

  58. sean23 Says:

    Great post. bean bags It’s good to see you to verbalize your heart and your clarity on this important issue can be easily detected. chairs Looking forward to read more. furniture this site|this site|beanbagnerd.com sofa

  59. Victor1122 Says:
    I was curious if you ever considered changing the page layout of your blog? Its very well written. electronic cigarette
  60. Victor1122 Says:

    Your blog provided us with valuable information to work with. Each & every tips of your post are awesome. Thanks a lot for sharing. Keep blogging,
    phentermine uk

  61. private jets rental Says:

    GG5DMR Im thankful for the post.

Leave a Reply