Tip #4 - Validating an Email Address with Ruby on Rails
Sun Apr 13 17:27:28 -0700 2008
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:
#
- RFC822 Email Address Regex
-———————————-- Originally written by Cal Henderson
- c.f. http://iamcal.com/publish/articles/php/parsing_email/
# - Translated to Ruby by Tim Fletcher, with changes suggested by Dan Kubb.
# - Licensed under a Creative Commons Attribution-ShareAlike 2.5 License
- http://creativecommons.org/licenses/by-sa/2.5/
- 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

Sun May 11 02:00:35 -0700 2008
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.
Sun May 11 12:01:46 -0700 2008
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.
Sun May 11 19:26:56 -0700 2008
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!
Sun May 11 19:38:21 -0700 2008
@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
Wed May 14 22:18:58 -0700 2008
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.
Wed May 28 18:43:49 -0700 2008
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
Wed May 28 18:56:45 -0700 2008
Gah! Sorry, it’s eaten the linebreaks and I don’t know what formatting engine you’re using. Try this:
def valid_email?
end
Thu Jun 19 10:02:13 -0700 2008
nice tip, thanks!
Thu Jun 26 00:43:10 -0700 2008
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.
Sun Jul 13 06:15:35 -0700 2008
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.
Sun Jul 13 19:41:35 -0700 2008
@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
Sat Jan 03 01:56:09 -0800 2009
I’d like to see a method in the TMail::Address interface that provides validation without depending on exception handling.
Tue Jan 20 18:08:09 -0800 2009
Your parse method allows emails with no @ ??
ie: abcdefg passes.
Is that expected?
Fri Jan 30 23:25:59 -0800 2009
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“)
=> #
Mon Feb 02 10:04:17 -0800 2009
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’.
Mon Feb 02 10:29:25 -0800 2009
Also I’ve noticed that TMail approves email addresses like ‘user@domain’
Tue Feb 24 15:48:16 -0800 2009
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?
end
I have an attribute called :email_address – however, it doesn’t seem to be getting validated. Any pointers?
Thanks!
Sun Mar 01 02:25:04 -0800 2009
Can you show me the way to validate url and show alert pop up for invalid url onclick submit button with ROR?
Wed Jun 10 17:43:08 -0700 2009
Totally useless as a validation tool! If it accepts email address without the @, then whats the point.
Sat Aug 29 20:11:05 -0700 2009
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.
Fri Sep 18 21:23:00 -0700 2009
Thank you Bob, that’s why TMail handles it.
Sat Oct 17 16:31:39 -0700 2009
Интересный пост, спасибо. Интересует только вопрос – будет ли продолжение? :)
Tue Nov 03 00:32:47 -0800 2009
in all the years i’ve been validating emails the only validation which doesn’t annoy people seems to be
email.split(/@/).size == 2validate actual service in a background task
Mon Nov 09 02:17:24 -0800 2009
The TMail stuff isn’t valid as an email validation as previous comments already suggest. However, I still like the rfc822 solution you posted.
Wed Nov 25 20:31:10 -0800 2009
I use this for email validation.
http://github.com/dancroak/validates_email_format_of
Exactly what you would want.
Wed Nov 25 20:31:23 -0800 2009
I use this for email validation.
http://github.com/dancroak/validates_email_format_of
Exactly what you would want.
Wed Nov 25 20:31:36 -0800 2009
I use this for email validation.
http://github.com/dancroak/validates_email_format_of
Exactly what you would want.
Fri Nov 27 04:11:52 -0800 2009
Думаю, эта тема слишком сложная для новичка :)