email_validator 2.2.2 → 2.2.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9f5010b8d56592e8febeda1e96b6fa35a27e2dae75dab6277fbade5f35b74b18
4
- data.tar.gz: 50bbb4a9c6abea2db5c2e887ba67a88739afccdb445c56be74c063b10993cef3
3
+ metadata.gz: 46bd723714c6aa3a844e584e986e5989920b6d3f212a44d800183d118c561cb7
4
+ data.tar.gz: bb58e5bf862c4be796e3ce59727dcf3fd2e28267e7c6077014f01d25dc841580
5
5
  SHA512:
6
- metadata.gz: b520404b2777c72ac87eaccaaacb073b340ebca043d104d531d8d78c74fd413d2f0e1c5ef6b7d08529a23d2aad913c7e1d15ab16989ea5777b5f41d37539365b
7
- data.tar.gz: 100eb11eaff2bd530d7a6c554655d93649a45aa6a5e5718a31cfa0c994f8e5341b57964d01c7f256ce4e8e927c3ba8e5610694cd293dda2d3241a2f2c187e1b9
6
+ metadata.gz: 2ece91a44bd82bd79c5484a17729996731787148b6813af0c4e41804715242a9cbfb4c0d661b08e0c904753dd59f23560d11aed7bf9044986802445e36f69438
7
+ data.tar.gz: 1b91bf21ce112f86f9b97138573d20fe14b67f075935136c3b19a620f155d50c5d5cf5ff9566e4c101429b8d7ce80ccb80709a2a15eeb07f4caeaac34f2b86b0
data/CHANGELOG.md CHANGED
@@ -5,6 +5,16 @@ This file is used to list changes made in `email_validator`.
5
5
  All notable changes to this project will be documented in this file.
6
6
  This project adheres to [Semantic Versioning](http://semver.org/).
7
7
 
8
+ ## 2.2.3 (2021-04-05)
9
+
10
+ * [karlwilbur] - Fix regexp for numeric domains (fixes [#72](https://github.com/K-and-R/email_validator/issues/72))
11
+ - [delphaber] - Add checks for numeric-only domains in tests (should be considered valid)
12
+ - [karlwilbur] - Fix specs for numeric-only domains labels (should be considered valid)
13
+ - [karlwilbur] - Add checks for numeric-only TLDs in tests (should be considered invalid)
14
+ - [karlwilbur] - Add tests to ensure that `regexp` returns expected value
15
+ - [karlwilbur] - Add checks for double dash in domain (should be considered invalid)
16
+ - [karlwilbur] - Add `EmailValidator::Error` class, raise `EmailValidator::Error` when invalid `mode`
17
+
8
18
  ## 2.2.2 (2020-12-10)
9
19
 
10
20
  * [karlwilbur] - Fix includes for `:rfc` and `:strict` modes from `Gemfile`
data/README.md CHANGED
@@ -54,9 +54,9 @@ by setting `require_fqdn: true` or by enabling `:strict` checking:
54
54
  validates :my_email_attribute, email: {mode: :strict, require_fqdn: true}
55
55
  ```
56
56
 
57
- You can also limit to a single domain (e.g: this might help if, for example, you
58
- have separate `User` and `AdminUser` models and want to ensure that `AdminUser`
59
- emails are on a specific domain):
57
+ You can also limit to a single domain (e.g: you have separate `User` and
58
+ `AdminUser` models and want to ensure that `AdminUser` emails are on a specific
59
+ domain):
60
60
 
61
61
  ```ruby
62
62
  validates :my_email_attribute, email: {domain: 'example.com'}
@@ -166,6 +166,10 @@ EmailValidator.valid?('narf@somehost') # boolean false
166
166
  EmailValidator.invalid?('narf@somehost', require_fqdn: false) # boolean true
167
167
  ```
168
168
 
169
+ _NB: Enabling strict mode (`mode: :strict`) enables `require_fqdn`
170
+ (`require_fqdn: true`), overridding any `require_fqdn: false` while
171
+ `mode: :strict` is set._
172
+
169
173
  ### Requiring a specific domain
170
174
 
171
175
  ```ruby
@@ -1,4 +1,6 @@
1
1
  # Based on work from http://thelucid.com/2010/01/08/sexy-validation-in-edge-rails-rails-3/
2
+
3
+ # EmailValidator class
2
4
  class EmailValidator < ActiveModel::EachValidator
3
5
  # rubocop:disable Style/ClassVars
4
6
  @@default_options = {
@@ -9,6 +11,13 @@ class EmailValidator < ActiveModel::EachValidator
9
11
  }
10
12
  # rubocop:enable Style/ClassVars
11
13
 
14
+ # EmailValidator::Error class
15
+ class Error < StandardError
16
+ def initialize(msg = 'EmailValidator error')
17
+ super
18
+ end
19
+ end
20
+
12
21
  class << self
13
22
  def default_options
14
23
  @@default_options
@@ -35,8 +44,11 @@ class EmailValidator < ActiveModel::EachValidator
35
44
  loose_regexp(options)
36
45
  when :rfc
37
46
  rfc_regexp(options)
38
- else
47
+ when :strict
48
+ options[:require_fqdn] = true
39
49
  strict_regexp(options)
50
+ else
51
+ fail EmailValidator::Error, "Validation mode '#{options[:mode]}' is not supported by EmailValidator"
40
52
  end
41
53
  end
42
54
 
@@ -81,31 +93,38 @@ class EmailValidator < ActiveModel::EachValidator
81
93
  end
82
94
 
83
95
  def host_label_pattern
96
+ "#{label_is_correct_length}" \
97
+ "#{label_contains_no_more_than_one_consecutive_hyphen}" \
84
98
  "#{alnum}(?:#{alnumhy}{,61}#{alnum})?"
85
99
  end
86
100
 
87
101
  # splitting this up into separate regex pattern for performance; let's not
88
102
  # try the "contains" pattern unless we have to
89
103
  def domain_label_pattern
90
- '(?=[^.]{1,63}(?:\.|$))' \
91
- '(?:' \
92
- "#{alpha}" \
93
- "|#{domain_label_starts_with_a_letter_pattern}" \
94
- "|#{domain_label_ends_with_a_letter_pattern}" \
95
- "|#{domain_label_contains_a_letter_pattern}" \
96
- ')'
104
+ "#{host_label_pattern}\\.#{tld_label_pattern}"
105
+ end
106
+
107
+ # While, techincally, TLDs can be numeric-only, this is not allowed by ICANN
108
+ # Ref: ICANN Application Guidebook for new TLDs (June 2012)
109
+ # says the following starting at page 64:
110
+ #
111
+ # > The ASCII label must consist entirely of letters (alphabetic characters a-z)
112
+ #
113
+ # -- https://newgtlds.icann.org/en/applicants/agb/guidebook-full-04jun12-en.pdf
114
+ def tld_label_pattern
115
+ "#{alpha}{1,64}"
97
116
  end
98
117
 
99
- def domain_label_starts_with_a_letter_pattern
100
- "#{alpha}#{alnumhy}{,61}#{alnum}"
118
+ def label_is_correct_length
119
+ '(?=[^.]{1,63}(?:\.|$))'
101
120
  end
102
121
 
103
- def domain_label_ends_with_a_letter_pattern
104
- "#{alnum}#{alnumhy}{,61}#{alpha}"
122
+ def domain_part_is_correct_length
123
+ '(?=.{1,255}$)'
105
124
  end
106
125
 
107
- def domain_label_contains_a_letter_pattern
108
- "(?:[[:digit:]])(?:[[:digit:]]|-)*#{alpha}#{alnumhy}*#{alnum}"
126
+ def label_contains_no_more_than_one_consecutive_hyphen
127
+ '(?!.*?--.*$)'
109
128
  end
110
129
 
111
130
  def atom_char
@@ -122,11 +141,11 @@ class EmailValidator < ActiveModel::EachValidator
122
141
  def domain_part_pattern(options)
123
142
  return options[:domain].sub(/\./, '\.') if options[:domain].present?
124
143
  return fqdn_pattern if options[:require_fqdn]
125
- "(?=.{1,255}$)(?:#{address_literal}|(?:#{host_label_pattern}\\.)*#{domain_label_pattern})"
144
+ "#{domain_part_is_correct_length}(?:#{address_literal}|(?:#{host_label_pattern}\\.)*#{tld_label_pattern})"
126
145
  end
127
146
 
128
147
  def fqdn_pattern
129
- "(?=.{1,255}$)(?:#{host_label_pattern}\\.)*#{domain_label_pattern}\\.#{domain_label_pattern}"
148
+ "(?=.{1,255}$)(?:#{host_label_pattern}\\.)*#{domain_label_pattern}"
130
149
  end
131
150
 
132
151
  private
@@ -146,7 +146,8 @@ RSpec.describe EmailValidator do
146
146
  'john.doe@a2.com',
147
147
  'john.doe@2020.a-z.com',
148
148
  'john.doe@2020.a2z.com',
149
- 'john.doe@2020.12345a6789.com'
149
+ 'john.doe@2020.12345a6789.com',
150
+ 'jonh.doe@163.com'
150
151
  ]).flatten.each do |email|
151
152
  context 'when using defaults' do
152
153
  it "'#{email}' should be valid" do
@@ -289,20 +290,20 @@ RSpec.describe EmailValidator do
289
290
  end
290
291
 
291
292
  context 'when in `:strict` mode' do
292
- it "'#{email}' should not be valid" do
293
- expect(StrictUser.new(:email => email)).not_to be_valid
293
+ it "'#{email}' should be valid" do
294
+ expect(StrictUser.new(:email => email)).to be_valid
294
295
  end
295
296
 
296
- it "'#{email}' should not be valid using EmailValidator.valid?" do
297
- expect(described_class).not_to be_valid(email, :mode => :strict)
297
+ it "'#{email}' should be valid using EmailValidator.valid?" do
298
+ expect(described_class).to be_valid(email, :mode => :strict)
298
299
  end
299
300
 
300
- it "'#{email}' should be invalid using EmailValidator.invalid?" do
301
- expect(described_class).to be_invalid(email, :mode => :strict)
301
+ it "'#{email}' should be not invalid using EmailValidator.invalid?" do
302
+ expect(described_class).not_to be_invalid(email, :mode => :strict)
302
303
  end
303
304
 
304
- it "'#{email}' should not match the regexp" do
305
- expect(!!(email.strip =~ described_class.regexp(:mode => :strict))).to be(false)
305
+ it "'#{email}' should match the regexp" do
306
+ expect(!!(email.strip =~ described_class.regexp(:mode => :strict))).to be(true)
306
307
  end
307
308
  end
308
309
 
@@ -476,6 +477,7 @@ RSpec.describe EmailValidator do
476
477
  'host-beginning-with-dot@.example.com',
477
478
  'domain-beginning-with-dash@-example.com',
478
479
  'domain-ending-with-dash@example-.com',
480
+ 'domain-contains-double-dash@foo--example.com',
479
481
  'the-local-part-is-invalid-if-it-is-longer-than-sixty-four-characters@sld.dev',
480
482
  "domain-too-long@t#{".#{'o' * 63}" * 5}.long",
481
483
  "user@example.com<script>alert('hello')</script>"
@@ -703,7 +705,8 @@ RSpec.describe EmailValidator do
703
705
  ]}).concat([
704
706
  'user..-with-double-dots@example.com',
705
707
  '.user-beginning-with-dot@example.com',
706
- 'user-ending-with-dot.@example.com'
708
+ 'user-ending-with-dot.@example.com',
709
+ 'fully-numeric-tld@example.123'
707
710
  ]).flatten.each do |email|
708
711
  context 'when using defaults' do
709
712
  it "#{email.strip} in a model should be valid" do
@@ -781,23 +784,24 @@ RSpec.describe EmailValidator do
781
784
  end
782
785
  end
783
786
 
787
+ # Strict mode enables `require_fqdn` anyway
784
788
  context 'when in `:strict` mode' do
785
789
  let(:opts) { { :require_fqdn => false, :mode => :strict } }
786
790
 
787
- it 'is valid' do
788
- expect(NonFqdnStrictUser.new(:email => email)).to be_valid
791
+ it 'is not valid' do
792
+ expect(NonFqdnStrictUser.new(:email => email)).not_to be_valid
789
793
  end
790
794
 
791
- it 'is valid using EmailValidator.valid?' do
792
- expect(described_class).to be_valid(email, opts)
795
+ it 'is not valid using EmailValidator.valid?' do
796
+ expect(described_class).not_to be_valid(email, opts)
793
797
  end
794
798
 
795
- it 'is not invalid using EmailValidator.invalid?' do
796
- expect(described_class).not_to be_invalid(email, opts)
799
+ it 'is invalid using EmailValidator.invalid?' do
800
+ expect(described_class).to be_invalid(email, opts)
797
801
  end
798
802
 
799
803
  it 'matches the regexp' do
800
- expect(!!(email =~ described_class.regexp(opts))).to be(true)
804
+ expect(!!(email =~ described_class.regexp(opts))).to be(false)
801
805
  end
802
806
  end
803
807
 
@@ -1012,4 +1016,36 @@ RSpec.describe EmailValidator do
1012
1016
  end
1013
1017
  end
1014
1018
  end
1019
+
1020
+ context 'with regexp' do
1021
+ it 'returns a regexp when asked' do
1022
+ expect(described_class.regexp).to be_a(Regexp)
1023
+ end
1024
+
1025
+ it 'returns a strict regexp when asked' do
1026
+ expect(described_class.regexp(:mode => :strict)).to be_a(Regexp)
1027
+ end
1028
+
1029
+ it 'returns a RFC regexp when asked' do
1030
+ expect(described_class.regexp(:mode => :rfc)).to be_a(Regexp)
1031
+ end
1032
+
1033
+ it 'has different regexp for strict and loose' do
1034
+ expect(described_class.regexp(:mode => :strict)).not_to eq(described_class.regexp(:mode => :loose))
1035
+ end
1036
+
1037
+ it 'has different regexp for RFC and loose' do
1038
+ expect(described_class.regexp(:mode => :rfc)).not_to eq(described_class.regexp(:mode => :loose))
1039
+ end
1040
+
1041
+ it 'has different regexp for RFC and strict' do
1042
+ expect(described_class.regexp(:mode => :rfc)).not_to eq(described_class.regexp(:mode => :strict))
1043
+ end
1044
+ end
1045
+
1046
+ context 'with invalid `:mode`' do
1047
+ it 'raises an error' do
1048
+ expect { described_class.regexp(:mode => :invalid) }.to raise_error(EmailValidator::Error)
1049
+ end
1050
+ end
1015
1051
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: email_validator
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.2
4
+ version: 2.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Alexander
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-12-11 00:00:00.000000000 Z
12
+ date: 2021-04-05 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activemodel