In Rails 3.0, you can use custom validator in your active_record model.
So I wanted to manage email validations without regexp matching like others do.
I find a new way to make this work thanks to ruby mail gem, a dependency of Rails 3.0
I use this with devise, see my blogpost here : Ruby Rails : How to bypass skip validation in Devise
Edit : Here is a gem https://github.com/hallelujah/valid_email
class User < ActiveRecord::Base validates :email, :presence => true, :email => true end |
Just put a file in app/validators/email_validator.rb
require 'mail' class EmailValidator < ActiveModel::EachValidator def validate_each(record,attribute,value) begin m = Mail::Address.new(value) # We must check that value contains a domain and that value is an email address r = m.domain && m.address == value t = m.__send__(:tree) # We need to dig into treetop # A valid domain must have dot_atom_text elements size > 1 # user@localhost is excluded # treetop must respond to domain # We exclude valid email values like <user@localhost.com> # Hence we use m.__send__(tree).domain r &&= (t.domain.dot_atom_text.elements.size > 1) rescue Exception => e r = false end record.errors[attribute] << (options[:message] || "is invalid") unless r end end |
No regexp !! And beautiful !!
Here is the version without activerecord
require 'rubygems' require 'active_model' require 'active_support/all' require 'active_model/validations' require 'mail' class EmailValidator < ActiveModel::EachValidator def validate_each(record,attribute,value) begin m = Mail::Address.new(value) # We must check that value contains a domain and that value is an email address r = m.domain && m.address == value t = m.__send__(:tree) # We need to dig into treetop # A valid domain must have dot_atom_text elements size > 1 # user@localhost is excluded # treetop must respond to domain # We exclude valid email values like <user@localhost.com> # Hence we use m.__send__(tree).domain r &&= (t.domain.dot_atom_text.elements.size > 1) rescue Exception => e r = false end record.errors[attribute] << (options[:message] || "is invalid") unless r end end class Person include ActiveModel::Validations attr_accessor :name, :email validates :name, :presence => true, :length => { :maximum => 100 } validates :email, :presence => true, :email => true end |

34 comments
2 pings
Skip to comment form ↓
Sohan
July 21, 2010 at 16:31 (UTC 2) Link to this comment
Your post has been linked at the Drink Rails blog as one of the top ruby on rails blogs of the day.
Hallelujah
July 21, 2010 at 18:07 (UTC 2) Link to this comment
Thanks a lot
Steve Root
January 1, 2011 at 16:56 (UTC 2) Link to this comment
Thanks for the post, just the solution I was looking for!
David K
February 3, 2011 at 12:58 (UTC 2) Link to this comment
Thanks for the post. I’m having trouble figuring out why this email: grass_1293770498_per@yahoo.com doesn’t pass validation. Any ideas?
Hallelujah
February 4, 2011 at 01:02 (UTC 2) Link to this comment
Well this email passes the validation …
What’s wrong?
David K
February 7, 2011 at 02:32 (UTC 2) Link to this comment
Never mind, it works now. I am not sure why, but I got an “invalid email” error when I tried before.
King
February 27, 2011 at 15:12 (UTC 2) Link to this comment
You should make a gem out of this. I’d like to be able to simply mention it in my Gemfile and begin using it.
sean
March 19, 2011 at 06:21 (UTC 2) Link to this comment
you created your own folder ‘validators’ right? I’m using rails 3.03 but want to make sure that wasn’t one of the provided skeleton folders. sorry kinda of a newb question, but appreciate your reply.
Hallelujah
March 19, 2011 at 09:25 (UTC 2) Link to this comment
Yes I did.
imohan
June 20, 2011 at 02:24 (UTC 2) Link to this comment
How do we check email with subdomain like sg.domain.com if you use above validation it will true if somebody puts sg.domain where as it is invalid in real business case.
-imohan
Hallelujah
June 20, 2011 at 10:20 (UTC 2) Link to this comment
Do you have an example ?
Because it works for me :
In “real business” nothing prevents you to set up an email on a subdomain : http://bit.ly/jzRLCz
Ramiro Jr. Franco
July 8, 2011 at 18:45 (UTC 2) Link to this comment
Wow, this is actually really cool, if you haven’t already you should think about making a gem out of this.
HP
August 11, 2011 at 23:05 (UTC 2) Link to this comment
Nicely done. I like being able to avoid using regexp and haven’t seen a way to do that before now. I’d making a gem out of this”.have to second the motion of “
haxney
August 12, 2011 at 03:05 (UTC 2) Link to this comment
This method seems to fail for valid email addresses like
“name”
Also, treetop.domain doesn’t exist in my version of Ruby (1.9.2p290), and it seems like this would suffice (sorry, I don’t know how to input code to WordPress):
def is_valid_email?(e)
m = Mail::Address.new(e)
m.domain && m.address
rescue Mail::Field::ParseError => e
false
end
This method also has the advantage of returning the “addressy” part of the address (name plus domain), with any comments stripped.
haxney
August 12, 2011 at 03:06 (UTC 2) Link to this comment
Whoops, forgot an extra “end” at the end of the method.
Adam
August 17, 2011 at 09:40 (UTC 2) Link to this comment
This is great, especially for a newbie like myself. But I’m trying to write a test to prevent duplicate email addresses with different case, and it seems to fail.
Does this validation allow for case insensitivity?
Adam
August 17, 2011 at 09:44 (UTC 2) Link to this comment
ha. sorry. got a little ahead of myself. i see why i was confused. Thanks for the great validator!
James Conroy-Finn
September 2, 2011 at 12:50 (UTC 2) Link to this comment
I’ve refactored the validation and added support for specifying a message or using I18n.
I’ve gisted it for now, but plan to stick it in a gem at some point.
https://gist.github.com/1188367
Hallelujah
September 2, 2011 at 14:01 (UTC 2) Link to this comment
As many want to make it a gem, it seems to be a good idea.
However I wonder how consistent it will be in a future since it relies on the “tree” private method
Hallelujah
September 4, 2011 at 01:58 (UTC 2) Link to this comment
Yep but a validation don’t need to return anything else than true or false.
This piece of code was originally intended to rails application
If you want some more features , it might be useful to create a gem upon
Anyway for the most use cases, we do not type such rfc valid email like below in a text field (as in a signup form)
“John Doe”
This email is valid and the mail gem can extract the user and domain part but iMHO this is not the purpose
James Ferguson
November 4, 2011 at 02:17 (UTC 2) Link to this comment
This validator seems to allow multiple email addresses:
p = Person.new
p.name = “Test”
p.email = “test@example.com, test2@example.com”
p.valid? # => true
Is that intended?
José Valim
November 8, 2011 at 17:23 (UTC 2) Link to this comment
Nice! Have you considered providing a patch to the Mail gem that adds a valid? method to Mail::Address? This way, we could just use the valid? method instead of explicitly checking the dots or worrying about Treetop details.
Hallelujah
November 8, 2011 at 19:37 (UTC 2) Link to this comment
Thanks you all for your feedbacks !!
It seems that since this post has become quiet popular so we should create a project in github.
@jose I don’t know yet if I will patch the mail gem but it is a good Idea
The first step is maybe a gem (for backward compatibility) then pull request a patch to Mail gem.
If you want to follow the project status here is the github repository :
https://github.com/hallelujah/valid_email
Paco Guzmán
November 9, 2011 at 15:41 (UTC 2) Link to this comment
I was using this for a while
https://github.com/cesario/activevalidators/blob/master/lib/active_model/validations/email_validator.rb
Hallelujah
November 10, 2011 at 20:01 (UTC 2) Link to this comment
I know that. Many gems use what I found : email_validator, validate_email etc …
)
But who cares !!! It is opensource and I am happy with it !! (a quote or a WWR vote may help me
Himesh
January 20, 2012 at 15:14 (UTC 2) Link to this comment
This email is getting validated:
cool_123@gmail.commmm
cool_123@gmail.commmm.sahb
Hallelujah
January 20, 2012 at 20:14 (UTC 2) Link to this comment
Yes it is valid even if it does not exist yet.
See new ICANN gTLDs
Terry S
April 17, 2012 at 18:28 (UTC 2) Link to this comment
Fabulous solution! It’s going in my default toolbox.
Thank you!
Asfand Yar Qazi
April 30, 2012 at 15:55 (UTC 2) Link to this comment
Could I point out that the blanket-catching of Exceptions means that errors like out of memory, out of disk space, no method errors or any other exception not being expected would also get caught and ignored – could I suggest replacing with it ‘rescue RuntimeError’ as a matter of best practice? Also as a matter of best practice, shouldn’t the begin/rescue/end be wrapped around just the bit or bits that could raise exceptions rather than everything?
Also, the fact that a private method is being called __send__ means that this solution will break the moment that method is changed or disappears, since it is not part of the public API.
Regards,
Asfand Yar Qazi
Hallelujah
May 2, 2012 at 20:28 (UTC 2) Link to this comment
@asfand
You are totally right : using a private method may break the behaviour in the future.
I will try to provide a patch for the mail gem as suggested by Jose Valim. It will be nicer but I don’t know where to begin.
Jean-Charles Mourey
May 12, 2012 at 11:49 (UTC 2) Link to this comment
I’m working through this Rails tutorial.
To be clever, I thought I’d use your valid_email gem instead of the REGEX method used in the tutorial, but for some reason, the tests fail because the gem passes these addresses as valid:
foo@bar_baz.com
foo@bar+baz.com
Is this normal? I didn’t think _ and + were legal in domain names (although they are both legal in the user name).
Regards,
Jean-Charles Mourey
Hallelujah
May 24, 2012 at 19:16 (UTC 2) Link to this comment
You’re right Jean-Charles !!
I will dig into it deeper. Maybe as Joshua stated before, we should implement validation in the Mail gem directly
Marry Laurenceau
June 6, 2012 at 09:52 (UTC 2) Link to this comment
rational decision, especially pleased there is no need to use regexp. Thanks!
Darron Lupacchino
August 29, 2012 at 10:26 (UTC 2) Link to this comment
Thanks for this wonderful tip!
I actually am searching for this and I am truly grateful that I’ve found this post.
Great job!
Rails : How to skip or remove validations in a Model « La rolls des blogs
October 20, 2010 at 18:25 (UTC 2) Link to this comment
[...] my last post with email validations, I was looking for a solution to integrate it with [...]
Email Validator for Rails 3 Without Regex | Rob Conery
November 11, 2011 at 00:18 (UTC 2) Link to this comment
[...] read about this over here but thought I’d add it here as I want to make sure I remember it. Works like a [...]