Como fazer a validação de E-mail em Ruby on Rails?

Estou a fazer a validação de E-mail em carris com:

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

também faço a validação do HTML5 na interface, mas endereços de E-mail como

[email protected]
[email protected]

ainda são válidos. O que me está a escapar?

Author: Flip, 2016-07-27

14 answers

Uso a constante incorporada no URI na biblioteca ruby normal

validates :email, format: { with: URI::MailTo::EMAIL_REGEXP } 
 28
Author: Joshua Hunter, 2018-04-19 15:53:52

Actualização: acabei de encontrar a valid_ Email2 gem que parece muito boa.

Não use uma expressão regular para a validação do endereço de E-mail. É uma armadilha. Há muito mais formatos de endereço de E-mail válidos do que você vai pensar. No entanto! A gema (é necessária pelo ActionMailer, por isso tem-na) irá processar os endereços de E-mail-com um analisador adequado-para si:

require 'mail'
a = Mail::Address.new('[email protected]')

Isto vai lançar um Mail::Field::ParseError Se for um endereço de E-mail não conforme. (Nós não estamos nos metendo em coisas como a fazer uma pesquisa de endereços MX ou algo assim.)

Se queres a experiência de validador dos carris, podes fazer 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

E depois no teu modelo, podes:

include EmailValidatable
validates :email, email: true
Como o comentário de Iwo Dziechciarow menciona abaixo, Isto passa qualquer coisa que seja um endereço válido "para:". Então algo como Foo Bar <[email protected]> é válido. Este pode ser um problema para você, pode não ser; ele realmente é um endereço válido, afinal de contas.

Se quiser apenas a parte do endereço de it:

a = Mail::Address.new('Foo Bar <[email protected]>')
a.address
=> "[email protected]"

Como Björn Weinbrenne notas abaixo, há uma maneira mais válido RFC2822 endereços que você pode esperar (tenho certeza de que todos os endereços listados não são compatíveis, e pode receber mail de acordo configurações do sistema) - é por isso que eu não recomendo tentar uma regex, mas usando um analisador compatível com.

Se realmente se importa se pode enviar e-mail para um endereço, então a sua melhor aposta-de longe-é realmente enviar uma mensagem com uma verificação link.

 15
Author: Nate, 2018-06-30 17:59:26

Se usar a gema do dispositivo já no seu aplicativo, pode ser oportuno usar

email =~ Devise.email_regexp

...o que também significa que diferentes locais da aplicação usam a mesma validação.

 11
Author: M.G.Palmer, 2017-04-06 09:12:51
 6
Author: thaleshcv, 2016-07-27 11:20:20
@Nate Muito obrigado por juntar esta resposta. Não sabia que a validação de E-mail tinha tantas nuances até ver o teu excerto de código.

Eu notei que a gema do correio atual: mail-2.6.5 não lança um erro para um e-mail de "abc". Exemplos:

>> a = Mail::Address.new('abc')
=> #<Mail::Address:70343701196060 Address: |abc| >
>> a.address # this is weird
=> "abc"
>> a = Mail::Address.new('"Jon Doe" <[email protected]>')
=> #<Mail::Address:70343691638900 Address: |Jon Doe <[email protected]>| >
>> a.address
=> "[email protected]"
>> 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
>>

Ele atira Mail::Field::ParseError erros para "Jon Doe <jon o que é óptimo. Eu acredito que vai verificar para o simples "padrão abc" também.

Em 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]+\.)+[a-z]{2,})\z/i
        record.errors[attribute] << (options[:message] || "is not an email")
      end
    end

  end
end
E depois no teu modelo, tu... can:
include PrettyEmailValidatable
validates :pretty_email, email: true
Então eu uso o acima para a validação do "pretty email" e o https://github.com/balexand/email_validator para validação padrão de E-mail.
 6
Author: tongueroo, 2017-05-19 18:24:56
Podes tentar isto.
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i
 1
Author: vipin cp, 2016-07-27 11:17:02
Se mais alguém estiver muito concentrado, queria algo contra o qual pudesse escrever testes e melhorar mais tarde, se necessário, sem ligar os testes a outro modelo.

Edifício fora de Nate e tongueroo's código (Graças Nate e tongueroo!), isto foi feito em Rails 5, Ruby 2.4.1. Aqui está o que eu coloquei 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]+\.)+[a-z]{2,})\z/i
      add_error(record, attribute)
    end
  end
end
E isto não é nada abrangente, mas aqui está o que eu coloquei 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
Espero que sim. ajuda!
 1
Author: Jack Barry, 2017-09-06 22:32:37
Aqui está a nova forma de fazer a validação por e-mail:
validates :email, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, on: :create }

Ver as valicações dos carris doc.

 1
Author: Cyzanfar, 2017-10-11 21:18:06

A resposta simples é: não uses uma expressão regular. Há demasiados casos extremos e falsos negativos e falsos positivos. Verifique se existe um sinal @ e envie um e-mail para o endereço para validá-lo:

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

 1
Author: M.G.Palmer, 2018-03-27 08:04:54

É melhor seguir a documentação dos carris:

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]+\.)+[a-z]{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
Author: Adriano Tadao, 2018-07-26 20:05:03
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
!("[email protected]" =~ VALID_EMAIL_REGEX).nil?
 1
Author: A H K, 2018-07-30 11:56:23
Experimenta isto.
validates_format_of  :email, :with => /^[\+A-Z0-9\._%-]+@([A-Z0-9-]+\.)+[A-Z]{2,4}$/i
 0
Author: jithya, 2016-07-27 11:52:52

Existe validates_email_ format_of a biblioteca ruby que valida os endereços de E-mail com base no RFC 2822 e no RFC 3696

Utilização:

Adicione ao seu ficheiro Gemfile:

gem 'validates_email_format_of'

Na tua classe de modelos

class Person < ActiveRecord::Base
  validates :email, email_format: { message: 'is not looking good' }
end
 0
Author: Hirurg103, 2018-04-14 13:53:35

O Ruby tem uma expressão regular incorporada para validar e-mails:

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

Fonte: Biblioteca Padrão Ruby: URI:: MailTo

 0
Author: Will, 2018-09-04 18:44:02