Skip to content

Commit

Permalink
Fix issue where domain: '' wasn't requiring an empty domain
Browse files Browse the repository at this point in the history
  • Loading branch information
karlwilbur committed Nov 9, 2022
1 parent c402c08 commit 68fcbd2
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 2 deletions.
8 changes: 7 additions & 1 deletion lib/email_validator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ def valid?(value, options = {})
options = parse_options(options)
return true if value.nil? && options[:allow_nil] == true
return false if value.nil?
# quickly fail if domain is required but doesn't match
return false unless options[:domain].nil? || value[/^.*@#{regexp_safe_domain(options)}$/]
!!(value =~ regexp(options))
end

Expand Down Expand Up @@ -134,7 +136,7 @@ def local_part_pattern
end

def domain_part_pattern(options)
return options[:domain].sub(/\./, '\.') if options[:domain].present?
return regexp_safe_domain(options) unless options[:domain].nil?
return fqdn_pattern if options[:require_fqdn]
"#{domain_part_is_correct_length}(?:#{address_literal}|(?:#{host_label_pattern}\\.)*#{tld_label_pattern})"
end
Expand All @@ -150,6 +152,10 @@ def parse_options(options)
options[:require_fqdn] = true if options[:require_fqdn].nil? && options[:mode] == :strict
default_options.merge(options)
end

def regexp_safe_domain(options)
options[:domain].sub(/\./, '\.')
end
end

def validate_each(record, attribute, value)
Expand Down
76 changes: 75 additions & 1 deletion spec/email_validator_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,18 @@ class NonFqdnRfcUser < TestModel
validates :email, :email => { :require_fqdn => false, :mode => :rfc }
end

class RequireFqdnWithEmptyDomainUser < TestModel
validates :email_address, :email => { :require_fqdn => true, :domain => '' }
end

class RequireEmptyDomainStrictUser < TestModel
validates :email_address, :email => { :require_fqdn => true, :domain => '', :mode => :strict }
end

class RequireEmptyDomainRfcUser < TestModel
validates :email_address, :email => { :require_fqdn => true, :domain => '', :mode => :rfc }
end

class DefaultUserWithMessage < TestModel
validates :email_address, :email => { :message => 'is not looking very good!' }
end
Expand Down Expand Up @@ -728,7 +740,7 @@ class DefaultUserWithMessage < TestModel
end

context 'when in `:strict` mode' do
it "#{email.strip} in a model should be valid" do
it "#{email.strip} in a model should not be valid" do
expect(StrictUser.new(:email => email)).not_to be_valid
end

Expand Down Expand Up @@ -765,6 +777,68 @@ class DefaultUserWithMessage < TestModel
end
end

context 'when `require_fqdn` is explicitly enabled with a blank domain' do
let(:opts) { { :require_fqdn => true, :domain => '' } }

context 'when given a email containing any domain' do
let(:email) { 'someuser@somehost' }

context 'when using defaults' do
it 'is not valid in a model' do
expect(RequireFqdnWithEmptyDomainUser.new(:email => email)).not_to be_valid
end

it 'is not using EmailValidator.valid?' do
expect(described_class).not_to be_valid(email, opts)
end

it 'is invalid using EmailValidator.invalid?' do
expect(described_class).to be_invalid(email, opts)
end

it 'does not match the regexp' do
expect(!!(email =~ described_class.regexp(opts))).to be(false)
end
end

context 'when in `:strict` mode' do
it 'is not valid in a model' do
expect(RequireEmptyDomainStrictUser.new(:email => email)).not_to be_valid
end

it 'is not using EmailValidator.valid?' do
expect(described_class).not_to be_valid(email, opts.merge({ :mode => :strict }))
end

it 'is invalid using EmailValidator.invalid?' do
expect(described_class).to be_invalid(email, opts.merge({ :mode => :strict }))
end

it 'does not match the regexp' do
expect(!!(email =~ described_class.regexp(opts.merge({ :mode => :strict })))).to be(false)
end
end

context 'when in `:rfc` mode' do
it 'is not valid in a model' do
expect(RequireEmptyDomainRfcUser.new(:email => email)).not_to be_valid
end

it 'is not using EmailValidator.valid?' do
expect(described_class).not_to be_valid(email, opts.merge({ :mode => :rfc }))
end

it 'is invalid using EmailValidator.invalid?' do
expect(described_class).to be_invalid(email, opts.merge({ :mode => :rfc }))
end

it 'does not match the regexp' do
expect(!!(email =~ described_class.regexp(opts.merge({ :mode => :rfc })))).to be(false)
end
end
end
end

context 'when `require_fqdn` is explicitly disabled' do
let(:opts) { { :require_fqdn => false } }

Expand Down

0 comments on commit 68fcbd2

Please sign in to comment.