email_validator 2.2.2 → 2.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9f5010b8d56592e8febeda1e96b6fa35a27e2dae75dab6277fbade5f35b74b18
4
- data.tar.gz: 50bbb4a9c6abea2db5c2e887ba67a88739afccdb445c56be74c063b10993cef3
3
+ metadata.gz: c6952edf85fc0eafbe159b4a3b812731c81361ee0a15f22927e270a3eb885c9d
4
+ data.tar.gz: 8b5a56e059f652f1822c93cbcec0be9da23842433e9c7e5f97478496ba888ff9
5
5
  SHA512:
6
- metadata.gz: b520404b2777c72ac87eaccaaacb073b340ebca043d104d531d8d78c74fd413d2f0e1c5ef6b7d08529a23d2aad913c7e1d15ab16989ea5777b5f41d37539365b
7
- data.tar.gz: 100eb11eaff2bd530d7a6c554655d93649a45aa6a5e5718a31cfa0c994f8e5341b57964d01c7f256ce4e8e927c3ba8e5610694cd293dda2d3241a2f2c187e1b9
6
+ metadata.gz: 158307d4196989b8d73872fa91c302ee9eab174b25f0686f3d61fe395796fa0b8e6c1c08185a4accf6c9581e23ba66bbebb8e3e8dc46b7ded935ed80a2484b53
7
+ data.tar.gz: 7f9ccf405a9bcf08d809fa58819d9e6d8489871a52020115802f3c755eddc3962b3533928f8f7c40e8122b055ad185c6f72cf1878ad6c920ba1b86872c0362a8
data/CHANGELOG.md CHANGED
@@ -5,6 +5,41 @@ 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.4 (2022-11-09)
9
+
10
+ * [karlwilbur] - Remove Ruby 2.4 from tested versions; add Ruby 3.0 and 3.1 to tested versions
11
+ * [karlwilbur] - Fix issue where `domain: ''` wasn't requiring empty domain
12
+ * [karlwilbur] - Remove checks for double hyphens (fixes [#87](https://github.com/K-and-R/email_validator/issues/87))
13
+ * [dependabot] - Security updates
14
+ - [#89](https://github.com/K-and-R/email_validator/pull/89)
15
+ + Bump `minimist` from `1.2.5` to `1.2.7`
16
+ - [#86](https://github.com/K-and-R/email_validator/pull/86)
17
+ + Bump `node-fetch` from `2.6.1` to `2.6.7`
18
+ + Add `whatwg-url` at `5.0.0`
19
+ + Add `tr46` at `0.0.3`
20
+ + Add `webidl-conversions` at `3.0.0`
21
+ - [#80](https://github.com/K-and-R/email_validator/pull/80)
22
+ + Bump `tar` from `6.0.5` to `6.1.11`
23
+ + Bump `minipass` from `3.1.3` to `3.1.5`
24
+ - [#79](https://github.com/K-and-R/email_validator/pull/79)
25
+ + Bump `path-parse` from `1.0.6` to `1.0.7`
26
+ - [#76](https://github.com/K-and-R/email_validator/pull/76)
27
+ + Bump `lodash` from `4.17.20` to `4.17.21`
28
+ - [#75](https://github.com/K-and-R/email_validator/pull/75)
29
+ + Bump `hosted-git-info` from `2.8.8` to `2.8.9`
30
+ * [msands] - Fix URL in `README.md` [#81](https://github.com/K-and-R/email_validator/pull/81)
31
+ * [kerolloz] - Fix typo in `README.md` [#73](https://github.com/K-and-R/email_validator/pull/73)
32
+
33
+ ## 2.2.3 (2021-04-05)
34
+
35
+ * [karlwilbur] - Fix regexp for numeric domains (fixes [#72](https://github.com/K-and-R/email_validator/issues/72))
36
+ - [delphaber] - Add checks for numeric-only domains in tests (should be considered valid)
37
+ - [karlwilbur] - Fix specs for numeric-only domains labels (should be considered valid)
38
+ - [karlwilbur] - Add checks for numeric-only TLDs in tests (should be considered invalid)
39
+ - [karlwilbur] - Add tests to ensure that `regexp` returns expected value
40
+ * [karlwilbur] - Add checks for double dash in domain (should be considered invalid)
41
+ * [karlwilbur] - Add `EmailValidator::Error` class, raise `EmailValidator::Error` when invalid `mode`
42
+
8
43
  ## 2.2.2 (2020-12-10)
9
44
 
10
45
  * [karlwilbur] - Fix includes for `:rfc` and `:strict` modes from `Gemfile`
data/README.md CHANGED
@@ -14,7 +14,7 @@ Formerly found at: <https://github.com/balexand/email_validator>
14
14
 
15
15
  The default validation provided by this gem (the `:loose` configuration option)
16
16
  is extremely loose. It just checks that there's an `@` with something before and
17
- after it without any whitespace. See [this article by David Gilbertson](https://hackernoon.com/the-100-correct-way-to-validate-email-addresses-7c4818f24643)
17
+ after it without any whitespace. See [this article by David Gilbertson](https://medium.com/hackernoon/the-100-correct-way-to-validate-email-addresses-7c4818f24643)
18
18
  for an explanation of why.
19
19
 
20
20
  We understand that many use cases require an increased level of validation. This
@@ -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'}
@@ -68,7 +68,7 @@ Default configuration can be overridden by setting options in `config/initialize
68
68
 
69
69
  ```ruby
70
70
  if defined?(EmailValidator)
71
- # To completly override the defaults
71
+ # To completely override the defaults
72
72
  EmailValidator.default_options = {
73
73
  allow_nil: false,
74
74
  domain: nil,
@@ -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
@@ -18,6 +27,8 @@ class EmailValidator < ActiveModel::EachValidator
18
27
  options = parse_options(options)
19
28
  return true if value.nil? && options[:allow_nil] == true
20
29
  return false if value.nil?
30
+ # quickly fail if domain is required but doesn't match
31
+ return false unless options[:domain].nil? || value[/^.*@#{regexp_safe_domain(options)}$/]
21
32
  !!(value =~ regexp(options))
22
33
  end
23
34
 
@@ -35,8 +46,11 @@ class EmailValidator < ActiveModel::EachValidator
35
46
  loose_regexp(options)
36
47
  when :rfc
37
48
  rfc_regexp(options)
38
- else
49
+ when :strict
50
+ options[:require_fqdn] = true
39
51
  strict_regexp(options)
52
+ else
53
+ fail EmailValidator::Error, "Validation mode '#{options[:mode]}' is not supported by EmailValidator"
40
54
  end
41
55
  end
42
56
 
@@ -81,31 +95,33 @@ class EmailValidator < ActiveModel::EachValidator
81
95
  end
82
96
 
83
97
  def host_label_pattern
98
+ "#{label_is_correct_length}" \
84
99
  "#{alnum}(?:#{alnumhy}{,61}#{alnum})?"
85
100
  end
86
101
 
87
102
  # splitting this up into separate regex pattern for performance; let's not
88
103
  # try the "contains" pattern unless we have to
89
104
  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
- ')'
105
+ "#{host_label_pattern}\\.#{tld_label_pattern}"
97
106
  end
98
107
 
99
- def domain_label_starts_with_a_letter_pattern
100
- "#{alpha}#{alnumhy}{,61}#{alnum}"
108
+ # While, techincally, TLDs can be numeric-only, this is not allowed by ICANN
109
+ # Ref: ICANN Application Guidebook for new TLDs (June 2012)
110
+ # says the following starting at page 64:
111
+ #
112
+ # > The ASCII label must consist entirely of letters (alphabetic characters a-z)
113
+ #
114
+ # -- https://newgtlds.icann.org/en/applicants/agb/guidebook-full-04jun12-en.pdf
115
+ def tld_label_pattern
116
+ "#{alpha}{1,64}"
101
117
  end
102
118
 
103
- def domain_label_ends_with_a_letter_pattern
104
- "#{alnum}#{alnumhy}{,61}#{alpha}"
119
+ def label_is_correct_length
120
+ '(?=[^.]{1,63}(?:\.|$))'
105
121
  end
106
122
 
107
- def domain_label_contains_a_letter_pattern
108
- "(?:[[:digit:]])(?:[[:digit:]]|-)*#{alpha}#{alnumhy}*#{alnum}"
123
+ def domain_part_is_correct_length
124
+ '(?=.{1,255}$)'
109
125
  end
110
126
 
111
127
  def atom_char
@@ -120,13 +136,13 @@ class EmailValidator < ActiveModel::EachValidator
120
136
  end
121
137
 
122
138
  def domain_part_pattern(options)
123
- return options[:domain].sub(/\./, '\.') if options[:domain].present?
139
+ return regexp_safe_domain(options) unless options[:domain].nil?
124
140
  return fqdn_pattern if options[:require_fqdn]
125
- "(?=.{1,255}$)(?:#{address_literal}|(?:#{host_label_pattern}\\.)*#{domain_label_pattern})"
141
+ "#{domain_part_is_correct_length}(?:#{address_literal}|(?:#{host_label_pattern}\\.)*#{tld_label_pattern})"
126
142
  end
127
143
 
128
144
  def fqdn_pattern
129
- "(?=.{1,255}$)(?:#{host_label_pattern}\\.)*#{domain_label_pattern}\\.#{domain_label_pattern}"
145
+ "(?=.{1,255}$)(?:#{host_label_pattern}\\.)*#{domain_label_pattern}"
130
146
  end
131
147
 
132
148
  private
@@ -136,6 +152,10 @@ class EmailValidator < ActiveModel::EachValidator
136
152
  options[:require_fqdn] = true if options[:require_fqdn].nil? && options[:mode] == :strict
137
153
  default_options.merge(options)
138
154
  end
155
+
156
+ def regexp_safe_domain(options)
157
+ options[:domain].sub(/\./, '\.')
158
+ end
139
159
  end
140
160
 
141
161
  def validate_each(record, attribute, value)
@@ -52,6 +52,18 @@ class NonFqdnRfcUser < TestModel
52
52
  validates :email, :email => { :require_fqdn => false, :mode => :rfc }
53
53
  end
54
54
 
55
+ class RequireFqdnWithEmptyDomainUser < TestModel
56
+ validates :email_address, :email => { :require_fqdn => true, :domain => '' }
57
+ end
58
+
59
+ class RequireEmptyDomainStrictUser < TestModel
60
+ validates :email_address, :email => { :require_fqdn => true, :domain => '', :mode => :strict }
61
+ end
62
+
63
+ class RequireEmptyDomainRfcUser < TestModel
64
+ validates :email_address, :email => { :require_fqdn => true, :domain => '', :mode => :rfc }
65
+ end
66
+
55
67
  class DefaultUserWithMessage < TestModel
56
68
  validates :email_address, :email => { :message => 'is not looking very good!' }
57
69
  end
@@ -146,7 +158,10 @@ RSpec.describe EmailValidator do
146
158
  'john.doe@a2.com',
147
159
  'john.doe@2020.a-z.com',
148
160
  'john.doe@2020.a2z.com',
149
- 'john.doe@2020.12345a6789.com'
161
+ 'john.doe@2020.12345a6789.com',
162
+ 'jonh.doe@163.com',
163
+ 'test@umläut.com', # non-ASCII
164
+ 'test@xn--umlut-ira.com' # ASCII-compatibale encoding of non-ASCII
150
165
  ]).flatten.each do |email|
151
166
  context 'when using defaults' do
152
167
  it "'#{email}' should be valid" do
@@ -289,20 +304,20 @@ RSpec.describe EmailValidator do
289
304
  end
290
305
 
291
306
  context 'when in `:strict` mode' do
292
- it "'#{email}' should not be valid" do
293
- expect(StrictUser.new(:email => email)).not_to be_valid
307
+ it "'#{email}' should be valid" do
308
+ expect(StrictUser.new(:email => email)).to be_valid
294
309
  end
295
310
 
296
- it "'#{email}' should not be valid using EmailValidator.valid?" do
297
- expect(described_class).not_to be_valid(email, :mode => :strict)
311
+ it "'#{email}' should be valid using EmailValidator.valid?" do
312
+ expect(described_class).to be_valid(email, :mode => :strict)
298
313
  end
299
314
 
300
- it "'#{email}' should be invalid using EmailValidator.invalid?" do
301
- expect(described_class).to be_invalid(email, :mode => :strict)
315
+ it "'#{email}' should be not invalid using EmailValidator.invalid?" do
316
+ expect(described_class).not_to be_invalid(email, :mode => :strict)
302
317
  end
303
318
 
304
- it "'#{email}' should not match the regexp" do
305
- expect(!!(email.strip =~ described_class.regexp(:mode => :strict))).to be(false)
319
+ it "'#{email}' should match the regexp" do
320
+ expect(!!(email.strip =~ described_class.regexp(:mode => :strict))).to be(true)
306
321
  end
307
322
  end
308
323
 
@@ -703,7 +718,8 @@ RSpec.describe EmailValidator do
703
718
  ]}).concat([
704
719
  'user..-with-double-dots@example.com',
705
720
  '.user-beginning-with-dot@example.com',
706
- 'user-ending-with-dot.@example.com'
721
+ 'user-ending-with-dot.@example.com',
722
+ 'fully-numeric-tld@example.123'
707
723
  ]).flatten.each do |email|
708
724
  context 'when using defaults' do
709
725
  it "#{email.strip} in a model should be valid" do
@@ -724,7 +740,7 @@ RSpec.describe EmailValidator do
724
740
  end
725
741
 
726
742
  context 'when in `:strict` mode' do
727
- it "#{email.strip} in a model should be valid" do
743
+ it "#{email.strip} in a model should not be valid" do
728
744
  expect(StrictUser.new(:email => email)).not_to be_valid
729
745
  end
730
746
 
@@ -761,6 +777,68 @@ RSpec.describe EmailValidator do
761
777
  end
762
778
  end
763
779
 
780
+ context 'when `require_fqdn` is explicitly enabled with a blank domain' do
781
+ let(:opts) { { :require_fqdn => true, :domain => '' } }
782
+
783
+ context 'when given a email containing any domain' do
784
+ let(:email) { 'someuser@somehost' }
785
+
786
+ context 'when using defaults' do
787
+ it 'is not valid in a model' do
788
+ expect(RequireFqdnWithEmptyDomainUser.new(:email => email)).not_to be_valid
789
+ end
790
+
791
+ it 'is not using EmailValidator.valid?' do
792
+ expect(described_class).not_to be_valid(email, opts)
793
+ end
794
+
795
+ it 'is invalid using EmailValidator.invalid?' do
796
+ expect(described_class).to be_invalid(email, opts)
797
+ end
798
+
799
+ it 'does not match the regexp' do
800
+ expect(!!(email =~ described_class.regexp(opts))).to be(false)
801
+ end
802
+ end
803
+
804
+ context 'when in `:strict` mode' do
805
+ it 'is not valid in a model' do
806
+ expect(RequireEmptyDomainStrictUser.new(:email => email)).not_to be_valid
807
+ end
808
+
809
+ it 'is not using EmailValidator.valid?' do
810
+ expect(described_class).not_to be_valid(email, opts.merge({ :mode => :strict }))
811
+ end
812
+
813
+ it 'is invalid using EmailValidator.invalid?' do
814
+ expect(described_class).to be_invalid(email, opts.merge({ :mode => :strict }))
815
+ end
816
+
817
+ it 'does not match the regexp' do
818
+ expect(!!(email =~ described_class.regexp(opts.merge({ :mode => :strict })))).to be(false)
819
+ end
820
+ end
821
+
822
+ context 'when in `:rfc` mode' do
823
+ it 'is not valid in a model' do
824
+ expect(RequireEmptyDomainRfcUser.new(:email => email)).not_to be_valid
825
+ end
826
+
827
+ it 'is not using EmailValidator.valid?' do
828
+ expect(described_class).not_to be_valid(email, opts.merge({ :mode => :rfc }))
829
+ end
830
+
831
+ it 'is invalid using EmailValidator.invalid?' do
832
+ expect(described_class).to be_invalid(email, opts.merge({ :mode => :rfc }))
833
+ end
834
+
835
+ it 'does not match the regexp' do
836
+ expect(!!(email =~ described_class.regexp(opts.merge({ :mode => :rfc })))).to be(false)
837
+ end
838
+ end
839
+ end
840
+ end
841
+
764
842
  context 'when `require_fqdn` is explicitly disabled' do
765
843
  let(:opts) { { :require_fqdn => false } }
766
844
 
@@ -781,23 +859,24 @@ RSpec.describe EmailValidator do
781
859
  end
782
860
  end
783
861
 
862
+ # Strict mode enables `require_fqdn` anyway
784
863
  context 'when in `:strict` mode' do
785
864
  let(:opts) { { :require_fqdn => false, :mode => :strict } }
786
865
 
787
- it 'is valid' do
788
- expect(NonFqdnStrictUser.new(:email => email)).to be_valid
866
+ it 'is not valid' do
867
+ expect(NonFqdnStrictUser.new(:email => email)).not_to be_valid
789
868
  end
790
869
 
791
- it 'is valid using EmailValidator.valid?' do
792
- expect(described_class).to be_valid(email, opts)
870
+ it 'is not valid using EmailValidator.valid?' do
871
+ expect(described_class).not_to be_valid(email, opts)
793
872
  end
794
873
 
795
- it 'is not invalid using EmailValidator.invalid?' do
796
- expect(described_class).not_to be_invalid(email, opts)
874
+ it 'is invalid using EmailValidator.invalid?' do
875
+ expect(described_class).to be_invalid(email, opts)
797
876
  end
798
877
 
799
878
  it 'matches the regexp' do
800
- expect(!!(email =~ described_class.regexp(opts))).to be(true)
879
+ expect(!!(email =~ described_class.regexp(opts))).to be(false)
801
880
  end
802
881
  end
803
882
 
@@ -1012,4 +1091,36 @@ RSpec.describe EmailValidator do
1012
1091
  end
1013
1092
  end
1014
1093
  end
1094
+
1095
+ context 'with regexp' do
1096
+ it 'returns a regexp when asked' do
1097
+ expect(described_class.regexp).to be_a(Regexp)
1098
+ end
1099
+
1100
+ it 'returns a strict regexp when asked' do
1101
+ expect(described_class.regexp(:mode => :strict)).to be_a(Regexp)
1102
+ end
1103
+
1104
+ it 'returns a RFC regexp when asked' do
1105
+ expect(described_class.regexp(:mode => :rfc)).to be_a(Regexp)
1106
+ end
1107
+
1108
+ it 'has different regexp for strict and loose' do
1109
+ expect(described_class.regexp(:mode => :strict)).not_to eq(described_class.regexp(:mode => :loose))
1110
+ end
1111
+
1112
+ it 'has different regexp for RFC and loose' do
1113
+ expect(described_class.regexp(:mode => :rfc)).not_to eq(described_class.regexp(:mode => :loose))
1114
+ end
1115
+
1116
+ it 'has different regexp for RFC and strict' do
1117
+ expect(described_class.regexp(:mode => :rfc)).not_to eq(described_class.regexp(:mode => :strict))
1118
+ end
1119
+ end
1120
+
1121
+ context 'with invalid `:mode`' do
1122
+ it 'raises an error' do
1123
+ expect { described_class.regexp(:mode => :invalid) }.to raise_error(EmailValidator::Error)
1124
+ end
1125
+ end
1015
1126
  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.4
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: 2022-11-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activemodel
@@ -61,7 +61,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
61
61
  - !ruby/object:Gem::Version
62
62
  version: '0'
63
63
  requirements: []
64
- rubygems_version: 3.1.4
64
+ rubygems_version: 3.2.15
65
65
  signing_key:
66
66
  specification_version: 4
67
67
  summary: An email validator for Rails 3+.