What's the state of the art in email validation for Rails?

46,073

Solution 1

With Rails 3.0 you can use a email validation without regexp using the Mail gem.

Here is my implementation (packaged as a gem).

Solution 2

Don't make this harder than it needs to be. Your feature is non-critical; validation's just a basic sanity step to catch typos. I would do it with a simple regex, and not waste the CPU cycles on anything too complicated:

/\A[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]+\z/

That was adapted from http://www.regular-expressions.info/email.html -- which you should read if you really want to know all the tradeoffs. If you want a more correct and much more complicated fully RFC822-compliant regex, that's on that page too. But the thing is this: you don't have to get it totally right.

If the address passes validation, you're going to send an email. If the email fails, you're going to get an error message. At which point you can tell the user "Sorry, your friend didn't receive that, would you like to try again?" or flag it for manual review, or just ignore it, or whatever.

These are the same options you'd have to deal with if the address did pass validation. Because even if your validation is perfect and you acquire absolute proof that the address exists, sending could still fail.

The cost of a false positive on validation is low. The benefit of better validation is also low. Validate generously, and worry about errors when they happen.

Solution 3

I created a gem for email validation in Rails 3. I'm kinda surprised that Rails doesn't include something like this by default.

http://github.com/balexand/email_validator

Solution 4

This project seems to have the most watchers on github at the moment (for email validation in rails):

https://github.com/alexdunae/validates_email_format_of

Solution 5

From the Rails 4 docs:

class EmailValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
      record.errors[attribute] << (options[:message] || "is not an email")
    end
  end
end

class Person < ActiveRecord::Base
  validates :email, presence: true, email: true
end
Share:
46,073
Luke Francl
Author by

Luke Francl

I am a software developer currently living in San Francisco. My speciality is web-based applications, lately using Ruby on Rails, though I have probably written more web applications in Tcl than most people. Check out my CV if you're looking for a software engineer.

Updated on December 29, 2020

Comments

  • Luke Francl
    Luke Francl over 3 years

    What are you using to validate users' email addresses, and why?

    I had been using validates_email_veracity_of which actually queries the MX servers. But that is full of fail for various reasons, mostly related to network traffic and reliability.

    I looked around and I couldn't find anything obvious that a lot of people are using to perform a sanity check on an email address. Is there a maintained, reasonably accurate plugin or gem for this?

    P.S.: Please don't tell me to send an email with a link to see if the email works. I'm developing a "send to a friend" feature, so this isn't practical.

  • Elijah
    Elijah about 13 years
    Err, won't that barf on .museum and the new international TLDs? This regex would prevent many valid email addresses.
  • Jaryl
    Jaryl almost 13 years
    Agreed with Elijah, this is a bad recommendation. Additionally, I'm not sure how you think you can tell the user that his friend didn't receive the email because there is no way to tell if the email succeeded right off the bat.
  • SFEley
    SFEley almost 13 years
    Good point on .museum and such -- when I first posted that answer in 2009 it wasn't an issue. I altered the regex. If you have further improvements, you can edit it too, or make this a community wiki post.
  • SFEley
    SFEley almost 13 years
    Jaryl: There's no way to know for sure if it succeeded or failed, but many failures will send back a bounce message, and you can set up your mail service to tell you about them. You can then make the appropriate notifications to the user or to whomever else ought to know.
  • Peter Nixey
    Peter Nixey over 12 years
    This is exactly the regex I was looking for. At the end of the day as SFEley pointed out, the only thing the validation needs to do is to help the user realise if they forgot to put in an email or entered their name or similar by mistake. If they want to give you an invalid email they will.
  • jasoncrawford
    jasoncrawford about 12 years
    Doesn't seem to work for me in Rails 3.1. Mail::Address.new("john") happily returns me a new Mail::Address object, without raising an exception.
  • jasoncrawford
    jasoncrawford about 12 years
    OK, it will throw an exception in some cases, but not all. @Hallelujah's link seems to have a good approach here.
  • jasoncrawford
    jasoncrawford about 12 years
    Nice, I am using your gem. Thanks.
  • Nerdmaster
    Nerdmaster about 12 years
    FYI, this will still miss some valid email addresses. Not many, but a few. For instance, technically #|@foo.com is a valid email address, as is "Hey I can have spaces if they're quoted"@foo.com. I find it easiest to just ignore anything before the @ and validate just the domain part.
  • Rob Dawson
    Rob Dawson about 12 years
    This is essentially a wrapper around the regex.
  • ZoFreX
    ZoFreX over 11 years
    I agree with the motivation that you should not worry about allowing through some incorrect addresses. Sadly this regex will disallow some correct addresses, which I view as unacceptable. Perhaps something like this would be better? /.+@.+\..+/
  • mjnissim
    mjnissim over 11 years
    Building on the above, put this in your model: validates :email_field, :format => { :with => /\A[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]+\z/, :message => "Email address not valid" }, :allow_blank=>false
  • JD.
    JD. over 11 years
    Thanks for this snippet, Sam. I'm a little surprised there is not a generic "good enough most of the time" validation provided by the Mail gem.
  • pyCthon
    pyCthon over 11 years
    yeah i think the regex as just an example to build upon for your custom needs this works nice for other languages too thanks!
  • Lluís
    Lluís over 11 years
    to allow coma-separated emails: \A[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]+(,[A-Za-z0-9._%‌​+-]+@[A-Za-z0-9.-]+\‌​.[A-Za-z]+)*\z
  • cwd
    cwd over 10 years
    Can you give an example of how to use this with an if or unless statement? Documentation seems sparse.
  • cwd
    cwd over 10 years
    looks like ###@domain.com will validate?
  • balexand
    balexand over 10 years
    @cwd I think the documentation is complete. If you're not familiar with Rails 3+ validations, then check out this Railscast (railscasts.com/episodes/211-validations-in-rails-3) or guides.rubyonrails.org/active_record_validations.html
  • Admin
    Admin about 10 years
    validates_format_of :email, with: /\A[^@]+@[^@]+\z/ combined with a dns domain blacklist, captcha and actual email confirmation
  • Joe Lalgee
    Joe Lalgee about 10 years
    My ultimate email validator: /\A\S+@\S+\.\S+\z/
  • Hallelujah
    Hallelujah almost 10 years
    Guys I would like to revive this gem, I did not have time to maintain it. But it seems people still use it and look for improvements. If you are interested, please write me on the github project : hallelujah/valid_email
  • Mauricio Moraes
    Mauricio Moraes over 9 years
    I'm tempted to use your validation, but I have no idea where you got it from or how you made it. Can you tell us?
  • Dave Sag
    Dave Sag over 9 years
    I got the regular expression from a google search, and wrote the wrapper code and spec tests myself.
  • Mauricio Moraes
    Mauricio Moraes over 9 years
    Its great that you posted the tests as well! But what really got me was the power-quote up there! :)