How to authenticate with Ruby on Rails?

I am doing an email check in Rails with:

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

In addition, I am checking HTML5 in the front end, but email addresses such as

 ..abc@gmail.com .abc@gmail.com 

still valid. What am I missing?

+42
source share
14 answers

I use the constant built-in URI in the ruby ​​standard library

 validates :email, format: { with: URI::MailTo::EMAIL_REGEXP } 
+125
source

Update: I just found a valid_email2 stone that looks very good.

Do not use regex to validate email addresses. It is a trap. There are many more valid email address formats than you think. Anyway! Gem mail (it is required by ActionMailer, so you have one) will analyze email addresses - with the right parser - for you:

 require 'mail' a = Mail::Address.new(' foo@example.com ') 

This will Mail::Field::ParseError if it doesn't match the email address. (We don’t do things like MX address lookups or something like that.)

If you need good experience with the Rails validator, you can do app/models/concerns/email_validatable.rb :

 require 'mail' module EmailValidatable extend ActiveSupport::Concern class EmailValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) begin a = Mail::Address.new(value) rescue Mail::Field::ParseError record.errors[attribute] << (options[:message] || "is not an email") end end end end 

and then in your model you can:

 include EmailValidatable validates :email, email: true 

As Ivo Dzechcharov’s commentary below mentions, he passes everything through which the valid “To:” address passes. So something like Foo Bar < foo.bar@example.com > valid. It may be a problem for you; it cannot be; In the end, this is indeed the correct address.

If you only need the address part:

 a = Mail::Address.new('Foo Bar < foobar@example.com >') a.address => " foobar@example.com " 

As Bjorn Weinbrenne notes below, there are more valid RFC2822 addresses than you might expect (I’m pretty sure that all of the listed addresses meet the requirements and can receive mail depending on the system configuration) - that’s why I don’t recommend trying regular expressions, but I use compatible parser.

If it really matters to you whether you can send an email to the address, then it is best - in fact - to send a message with a confirmation link.

+19
source

If you are already using the Devise gem in your application, it may be convenient to use

 email =~ Devise.email_regexp 

... which also means that different places in the application use the same check.

+13
source

@Nate Thank you so much for putting together this answer. I did not realize that checking e-mail had so many nuances until I looked at your piece of code.

I noticed that the current mail gem: mail-2.6.5 does not give an error for the letter "abc". Examples:

 >> a = Mail::Address.new('abc') => #<Mail::Address:70343701196060 Address: |abc| > >> a.address # this is weird => "abc" >> a = Mail::Address.new('"Jon Doe" < jon@doe.com >') => #<Mail::Address:70343691638900 Address: |Jon Doe < jon@doe.com >| > >> a.address => " jon@doe.com " >> a.display_name => "Jon Doe" >> Mail::Address.new('"Jon Doe <jon') Mail::Field::ParseError: Mail::AddressList can not parse |"Jon Doe <jon| Reason was: Only able to parse up to "Jon Doe <jon from (irb):3:in 'new' from (irb):3 >> 

This throws Mail::Field::ParseError for "Jon Doe <jon , and that’s great. I believe that we’ll check a simple" abc pattern "too.

In app/models/concerns/pretty_email_validatable.rb :

 require 'mail' module PrettyEmailValidatable extend ActiveSupport::Concern class PrettyEmailValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) begin a = Mail::Address.new(value) rescue Mail::Field::ParseError record.errors[attribute] << (options[:message] || "is not an email") end # regexp from http://guides.rubyonrails.org/active_record_validations.html value = a.address unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[az]{2,})\z/i record.errors[attribute] << (options[:message] || "is not an email") end end end end 

and then in your model you can:

 include PrettyEmailValidatable validates :pretty_email, email: true 

So I use the above to check for "pretty email" and https://github.com/balexand/email_validator for standard email checking.

+7
source
+5
source

If someone else is very focused on TDD: I need something that I could write tests against and improve later, if necessary, without tying the tests to another model.

Creating Nate and tongueroo code (thanks Nate and tongueroo !), This was done in Rails 5 , Ruby 2.4.1 . Here is what I threw at app/validators/email_validator.rb :

 require 'mail' class EmailValidator < ActiveModel::EachValidator def add_error(record, attribute) record.errors.add(attribute, (options[:message] || "is not a valid email address")) end def validate_each(record, attribute, value) begin a = Mail::Address.new(value) rescue Mail::Field::ParseError add_error(record, attribute) end # regexp from http://guides.rubyonrails.org/active_record_validations.html value = a.address unless a.nil? unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[az]{2,})\z/i add_error(record, attribute) end end end 

And this is by no means comprehensive, but here is what I threw at spec/validators/email_validator_spec.rb :

 require 'rails_helper' RSpec.describe EmailValidator do subject do Class.new do include ActiveModel::Validations attr_accessor :email validates :email, email: true end.new end context 'when the email address is valid' do let(:email) { Faker::Internet.email } it 'allows the input' do subject.email = email expect(subject).to be_valid end end context 'when the email address is invalid' do let(:invalid_message) { 'is not a valid email address' } it 'invalidates the input' do subject.email = ' not_valid@ ' expect(subject).not_to be_valid end it 'alerts the consumer' do subject.email = 'notvalid' subject.valid? expect(subject.errors[:email]).to include(invalid_message) end end end 

Hope this helps!

+4
source

Below is a new way to check your email:

 validates :email, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[az]{2,})\z/i, on: :create } 

Refer to the Rails valications doc .

+3
source

Simple answer: Do not use regex. Too many edge cases and false negatives and false positives. Check the @ sign and send an email to the address to check it:

https://www.youtube.com/watch?v=xxX81WmXjPg

+2
source
 VALID_EMAIL_REGEX = /\A[\w+\-.] +@ [az\d\-]+(\.[az\d\-]+)*\.[az]+\z/i !(" yourmail@example.com " =~ VALID_EMAIL_REGEX).nil? 
+2
source

You can try this

 VALID_EMAIL_REGEX = /\A[\w+\-.] +@ [az\d\-]+(\.[az]+)*\.[az]+\z/i 
+1
source

try it.

 validates_format_of :email, :with => /^[\+A-Z0-9\._%-] +@ ([A-Z0-9-]+\.)+[AZ]{2,4}$/i 
+1
source

Best to follow the Rails documentation:

https://guides.rubyonrails.org/active_record_validations.html#custom-validators

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

Please keep in mind that at the moment email_validator gem does not have complex validation rules, but only:

 /[^\s]@[^\s]/ 

https://github.com/balexand/email_validator/blob/master/lib/email_validator.rb#L13

Arguments at https://medium.com/hackernoon/the-100-correct-way-to-validate-email-addresses-7c4818f24643

+1
source

Ruby has a built-in regular expression for checking emails:

 validates :email, format: { with: URI::MailTo::EMAIL_REGEXP, message: 'Only valid emails allowed' } 

Source: Ruby Standard Library: URI :: MailTo

0
source

All Articles