Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@ source 'https://rubygems.org'

# Specify your gem's dependencies in valid_email2.gemspec
gemspec

group :development do
gem "ruby-lsp-rspec", require: false
end
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,26 @@ To validate create your own custom message:
validates :email, 'valid_email_2/email': { message: "is not a valid email" }
```

To validate create your dedicated validation error-message:
```yaml
# set custom validation messages for each type of check in translations ie en.yml
errors:
messages:
disallow_dotted: "contains a dot"
disallow_subaddressing: "..."
disposable: "..."
disposable_domain: "..."
disposable_with_allow_list: "..."
disposable_domain_with_allow_list: "..."
deny_list: "..."
mx: "..."
strict_mx: "..."
```

```ruby
validates :email, 'valid_email_2/email': { disallow_dotted: true, specific_error_messages: true }
```

To allow multiple addresses separated by comma:
```ruby
validates :email, 'valid_email_2/email': { multiple: true }
Expand Down
34 changes: 22 additions & 12 deletions lib/valid_email2/email_validator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,16 @@
module ValidEmail2
class EmailValidator < ActiveModel::EachValidator
def default_options
{ disposable: false, mx: false, strict_mx: false, disallow_subaddressing: false, multiple: false, dns_timeout: 5, dns_nameserver: nil }
{
disposable: false,
mx: false,
strict_mx: false,
disallow_subaddressing: false,
multiple: false,
dns_timeout: 5,
dns_nameserver: nil,
specific_error_messages: false
}
end

def validate_each(record, attribute, value)
Expand All @@ -19,39 +28,39 @@ def validate_each(record, attribute, value)
error(record, attribute) && return unless addresses.all?(&:valid?)

if options[:disallow_dotted]
error(record, attribute) && return if addresses.any?(&:dotted?)
error(record, attribute, :disallow_dotted) && return if addresses.any?(&:dotted?)
end

if options[:disallow_subaddressing]
error(record, attribute) && return if addresses.any?(&:subaddressed?)
error(record, attribute, :disallow_subaddressing) && return if addresses.any?(&:subaddressed?)
end

if options[:disposable]
error(record, attribute) && return if addresses.any?(&:disposable?)
error(record, attribute, :disposable) && return if addresses.any?(&:disposable?)
end

if options[:disposable_domain]
error(record, attribute) && return if addresses.any?(&:disposable_domain?)
error(record, attribute, :disposable_domain) && return if addresses.any?(&:disposable_domain?)
end

if options[:disposable_with_allow_list]
error(record, attribute) && return if addresses.any? { |address| address.disposable? && !address.allow_listed? }
error(record, attribute, :disposable_with_allow_list) && return if addresses.any? { |address| address.disposable? && !address.allow_listed? }
end

if options[:disposable_domain_with_allow_list]
error(record, attribute) && return if addresses.any? { |address| address.disposable_domain? && !address.allow_listed? }
error(record, attribute, :disposable_domain_with_allow_list) && return if addresses.any? { |address| address.disposable_domain? && !address.allow_listed? }
end

if options[:deny_list]
error(record, attribute) && return if addresses.any?(&:deny_listed?)
error(record, attribute, :deny_list) && return if addresses.any?(&:deny_listed?)
end

if options[:mx]
error(record, attribute) && return unless addresses.all?(&:valid_mx?)
error(record, attribute, :mx) && return unless addresses.all?(&:valid_mx?)
end

if options[:strict_mx]
error(record, attribute) && return unless addresses.all?(&:valid_strict_mx?)
error(record, attribute, :strict_mx) && return unless addresses.all?(&:valid_strict_mx?)
end
end

Expand All @@ -67,10 +76,11 @@ def sanitized_values(input)
email_list.reject(&:empty?)
end

def error(record, attribute)
def error(record, attribute, error_key = :invalid)
message = options[:message].respond_to?(:call) ? options[:message].call : options[:message]
message ||= options[:specific_error_messages] ? error_key : :invalid

record.errors.add(attribute, message || :invalid)
record.errors.add(attribute, message)
end
end
end
2 changes: 2 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
$:.unshift File.expand_path("../lib",__FILE__)
require "bundler/setup"
require "rspec"
require "valid_email2"
require "debug"

Expand Down
29 changes: 29 additions & 0 deletions spec/valid_email2_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ class TestUser < TestModel
validates :email, 'valid_email_2/email': true
end

class TestUserSpecificErrorMessage < TestModel
validates :email, 'valid_email_2/email': { disallow_dotted: true, specific_error_messages: true }
end

class TestUserDotted < TestModel
validates :email, 'valid_email_2/email': { disallow_dotted: true }
end
Expand Down Expand Up @@ -418,6 +422,7 @@ def set_allow_list
it "is invalid when address cotains dots" do
user = TestUserDotted.new(email: "[email protected]")
expect(user.valid?).to be_falsey
expect(user.errors.full_messages).to include("Email is invalid")
end
end

Expand All @@ -433,6 +438,30 @@ def set_allow_list
end
end

context "when detail error messages are enabled" do
describe "with custom error message" do
it "supports settings a custom error message" do
with_translations(:en, { errors: { messages: { disallow_dotted: "dotted message" } } }) do
user = TestUserSpecificErrorMessage.new(email: "[email protected]")
user.valid?

expect(user.errors.full_messages).to include("Email dotted message")
end
end
end
end

def with_translations(locale, translations)
original_backend = I18n.backend

I18n.backend = I18n::Backend::KeyValue.new({}, true)
I18n.backend.store_translations locale, translations

yield
ensure
I18n.backend = original_backend
end

context "when message is present" do
describe "with custom error message" do
it "supports settings a custom error message" do
Expand Down