email_validator 2.2.0 → 2.2.1
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 +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +1 -1
- data/lib/email_validator.rb +53 -9
- data/spec/email_validator_spec.rb +135 -5
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: faae1443109b7d1c95aca6addd9d2be7c3cfaf58a771e003d80a1ea75dd0993f
|
4
|
+
data.tar.gz: c1094ac3f9d4887193eb2145c62f46cefe1d4ed0a734a6d7add1cf68222de1dd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e99e6549ebfa01f6fc43b0a8f8bfba80ce0037700f2672be89bb0936b71f929bde127d667fc634a38bc6ea9e26d08a61393c5f261db3581362f8497001305790
|
7
|
+
data.tar.gz: 7e74593c1bb75c71d8864c1db31d9a415ec6fbc4d56a7f190363224f3906b884f0bd39af9208969112f12978b733ae09271e3a94b42ed1fc84b88553cd71df9d
|
data/CHANGELOG.md
CHANGED
@@ -5,6 +5,13 @@ 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.1 (2020-12-10)
|
9
|
+
|
10
|
+
* [karlwilbur] - Modify regexp to:
|
11
|
+
- allow numeric-only hosts [#68]
|
12
|
+
- allow mailbox-only addresses in `:rfc` mode
|
13
|
+
- enforce the 255-char domain limit (not in `:loose` mode unless using `:domain`)
|
14
|
+
|
8
15
|
## 2.2.0 (2020-12-09)
|
9
16
|
|
10
17
|
* [karlwilbur] - Rename `:strict` -> `:rfc`; `:moderate` -> `:strict`
|
data/README.md
CHANGED
@@ -45,7 +45,7 @@ Add the following to your model:
|
|
45
45
|
validates :my_email_attribute, email: true
|
46
46
|
```
|
47
47
|
|
48
|
-
You may wish to allow domains without a
|
48
|
+
You may wish to allow domains without a FQDN, like `user@somehost`. While this
|
49
49
|
is technically a valid address, it is uncommon to consider such address valid.
|
50
50
|
We will consider them valid by default with the `:loose` checking. Disallowed
|
51
51
|
by setting `require_fqdn: true` or by enabling `:strict` checking:
|
data/lib/email_validator.rb
CHANGED
@@ -30,15 +30,31 @@ class EmailValidator < ActiveModel::EachValidator
|
|
30
30
|
# https://tools.ietf.org/html/rfc5321 : 4.1.2. Command Argument Syntax
|
31
31
|
def regexp(options = {})
|
32
32
|
options = parse_options(options)
|
33
|
-
|
34
|
-
|
35
|
-
|
33
|
+
case options[:mode]
|
34
|
+
when :loose
|
35
|
+
loose_regexp(options)
|
36
|
+
when :rfc
|
37
|
+
rfc_regexp(options)
|
38
|
+
else
|
39
|
+
strict_regexp(options)
|
36
40
|
end
|
37
|
-
/\A(?>#{local_part_pattern})@#{domain_pattern(options)}\z/i
|
38
41
|
end
|
39
42
|
|
40
43
|
protected
|
41
44
|
|
45
|
+
def loose_regexp(options = {})
|
46
|
+
return /\A[^\s]+@[^\s]+\z/ if options[:domain].nil?
|
47
|
+
/\A[^\s]+@#{domain_part_pattern(options)}\z/
|
48
|
+
end
|
49
|
+
|
50
|
+
def strict_regexp(options = {})
|
51
|
+
/\A(?>#{local_part_pattern})@#{domain_part_pattern(options)}\z/i
|
52
|
+
end
|
53
|
+
|
54
|
+
def rfc_regexp(options = {})
|
55
|
+
/\A(?>#{local_part_pattern})(?:@#{domain_part_pattern(options)})?\z/i
|
56
|
+
end
|
57
|
+
|
42
58
|
def alpha
|
43
59
|
'[[:alpha:]]'
|
44
60
|
end
|
@@ -64,8 +80,32 @@ class EmailValidator < ActiveModel::EachValidator
|
|
64
80
|
"\\[(?:#{ipv4}|#{ipv6})\\]"
|
65
81
|
end
|
66
82
|
|
67
|
-
def
|
68
|
-
"#{
|
83
|
+
def host_label_pattern
|
84
|
+
"#{alnum}(?:#{alnumhy}{,61}#{alnum})?"
|
85
|
+
end
|
86
|
+
|
87
|
+
# splitting this up into separate regex pattern for performance; let's not
|
88
|
+
# try the "contains" pattern unless we have to
|
89
|
+
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
|
+
')'
|
97
|
+
end
|
98
|
+
|
99
|
+
def domain_label_starts_with_a_letter_pattern
|
100
|
+
"#{alpha}#{alnumhy}{,61}#{alnum}"
|
101
|
+
end
|
102
|
+
|
103
|
+
def domain_label_ends_with_a_letter_pattern
|
104
|
+
"#{alnum}#{alnumhy}{,61}#{alpha}"
|
105
|
+
end
|
106
|
+
|
107
|
+
def domain_label_contains_a_letter_pattern
|
108
|
+
"(?:[[:digit:]])(?:[[:digit:]]|-)*#{alpha}#{alnumhy}*#{alnum}"
|
69
109
|
end
|
70
110
|
|
71
111
|
def atom_char
|
@@ -79,10 +119,14 @@ class EmailValidator < ActiveModel::EachValidator
|
|
79
119
|
"#{atom_char}(?:\\.?#{atom_char}){,63}"
|
80
120
|
end
|
81
121
|
|
82
|
-
def
|
122
|
+
def domain_part_pattern(options)
|
83
123
|
return options[:domain].sub(/\./, '\.') if options[:domain].present?
|
84
|
-
return
|
85
|
-
"(?:#{address_literal}|(?:#{
|
124
|
+
return fqdn_pattern if options[:require_fqdn]
|
125
|
+
"(?=.{1,255}$)(?:#{address_literal}|(?:#{host_label_pattern}\\.)*#{domain_label_pattern})"
|
126
|
+
end
|
127
|
+
|
128
|
+
def fqdn_pattern
|
129
|
+
"(?=.{1,255}$)(?:#{host_label_pattern}\\.)*#{domain_label_pattern}\\.#{domain_label_pattern}"
|
86
130
|
end
|
87
131
|
|
88
132
|
private
|
@@ -140,7 +140,13 @@ RSpec.describe EmailValidator do
|
|
140
140
|
'someuser@somehost.somedomain',
|
141
141
|
'mixed-1234-in-{+^}-local@sld.dev',
|
142
142
|
'partially."quoted"@sld.com',
|
143
|
-
'areallylongnameaasdfasdfasdfasdf@asdfasdfasdfasdfasdf.ab.cd.ef.gh.co.ca'
|
143
|
+
'areallylongnameaasdfasdfasdfasdf@asdfasdfasdfasdfasdf.ab.cd.ef.gh.co.ca',
|
144
|
+
'john.doe@2020.example.com',
|
145
|
+
'john.doe@2a.com',
|
146
|
+
'john.doe@a2.com',
|
147
|
+
'john.doe@2020.a-z.com',
|
148
|
+
'john.doe@2020.a2z.com',
|
149
|
+
'john.doe@2020.12345a6789.com'
|
144
150
|
]).flatten.each do |email|
|
145
151
|
context 'when using defaults' do
|
146
152
|
it "'#{email}' should be valid" do
|
@@ -260,6 +266,132 @@ RSpec.describe EmailValidator do
|
|
260
266
|
end
|
261
267
|
end
|
262
268
|
|
269
|
+
context 'when given the numeric domain' do
|
270
|
+
[
|
271
|
+
'only-numbers-in-domain-label@sub.123.custom'
|
272
|
+
].each do |email|
|
273
|
+
context 'when using defaults' do
|
274
|
+
it "'#{email}' should be valid" do
|
275
|
+
expect(DefaultUser.new(:email => email)).to be_valid
|
276
|
+
end
|
277
|
+
|
278
|
+
it "'#{email}' should be valid using EmailValidator.valid?" do
|
279
|
+
expect(described_class).to be_valid(email)
|
280
|
+
end
|
281
|
+
|
282
|
+
it "'#{email}' should not be invalid using EmailValidator.invalid?" do
|
283
|
+
expect(described_class).not_to be_invalid(email)
|
284
|
+
end
|
285
|
+
|
286
|
+
it "'#{email}' should match the regexp" do
|
287
|
+
expect(!!(email.strip =~ described_class.regexp)).to be(true)
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
context 'when in `:strict` mode' do
|
292
|
+
it "'#{email}' should not be valid" do
|
293
|
+
expect(StrictUser.new(:email => email)).not_to be_valid
|
294
|
+
end
|
295
|
+
|
296
|
+
it "'#{email}' should not be valid using EmailValidator.valid?" do
|
297
|
+
expect(described_class).not_to be_valid(email, :mode => :strict)
|
298
|
+
end
|
299
|
+
|
300
|
+
it "'#{email}' should be invalid using EmailValidator.invalid?" do
|
301
|
+
expect(described_class).to be_invalid(email, :mode => :strict)
|
302
|
+
end
|
303
|
+
|
304
|
+
it "'#{email}' should not match the regexp" do
|
305
|
+
expect(!!(email.strip =~ described_class.regexp(:mode => :strict))).to be(false)
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
context 'when in `:rfc` mode' do
|
310
|
+
it "'#{email}' should be valid" do
|
311
|
+
expect(RfcUser.new(:email => email)).to be_valid
|
312
|
+
end
|
313
|
+
|
314
|
+
it "'#{email}' should be valid using EmailValidator.valid?" do
|
315
|
+
expect(described_class).to be_valid(email, :mode => :rfc)
|
316
|
+
end
|
317
|
+
|
318
|
+
it "'#{email}' should not be invalid using EmailValidator.invalid?" do
|
319
|
+
expect(described_class).not_to be_invalid(email, :mode => :rfc)
|
320
|
+
end
|
321
|
+
|
322
|
+
it "'#{email}' should match the regexp" do
|
323
|
+
expect(!!(email.strip =~ described_class.regexp(:mode => :rfc))).to be(true)
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
context 'when given the valid mailbox-only email' do
|
330
|
+
valid_includable.map { |k, v| [
|
331
|
+
"user-#{v}-#{k}-name"
|
332
|
+
]}.concat(valid_beginable.map { |k, v| [
|
333
|
+
"#{v}-start-with-#{k}-user"
|
334
|
+
]}).concat(valid_endable.map { |k, v| [
|
335
|
+
"end-with-#{k}-#{v}"
|
336
|
+
]}).concat([
|
337
|
+
'user'
|
338
|
+
]).flatten.each do |email|
|
339
|
+
context 'when using defaults' do
|
340
|
+
it "'#{email}' should not be valid" do
|
341
|
+
expect(DefaultUser.new(:email => email)).not_to be_valid
|
342
|
+
end
|
343
|
+
|
344
|
+
it "'#{email}' should not be valid using EmailValidator.valid?" do
|
345
|
+
expect(described_class).not_to be_valid(email)
|
346
|
+
end
|
347
|
+
|
348
|
+
it "'#{email}' should be invalid using EmailValidator.invalid?" do
|
349
|
+
expect(described_class).to be_invalid(email)
|
350
|
+
end
|
351
|
+
|
352
|
+
it "'#{email}' should not match the regexp" do
|
353
|
+
expect(!!(email.strip =~ described_class.regexp)).to be(false)
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
context 'when in `:strict` mode' do
|
358
|
+
it "'#{email}' should not be valid" do
|
359
|
+
expect(StrictUser.new(:email => email)).not_to be_valid
|
360
|
+
end
|
361
|
+
|
362
|
+
it "'#{email}' should not be valid using EmailValidator.valid?" do
|
363
|
+
expect(described_class).not_to be_valid(email, :mode => :strict)
|
364
|
+
end
|
365
|
+
|
366
|
+
it "'#{email}' should be invalid using EmailValidator.invalid?" do
|
367
|
+
expect(described_class).to be_invalid(email, :mode => :strict)
|
368
|
+
end
|
369
|
+
|
370
|
+
it "'#{email}' should not match the regexp" do
|
371
|
+
expect(!!(email.strip =~ described_class.regexp(:mode => :strict))).to be(false)
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
context 'when in `:rfc` mode' do
|
376
|
+
it "'#{email}' should be valid" do
|
377
|
+
expect(RfcUser.new(:email => email)).to be_valid
|
378
|
+
end
|
379
|
+
|
380
|
+
it "'#{email}' should be valid using EmailValidator.valid?" do
|
381
|
+
expect(described_class).to be_valid(email, :mode => :rfc)
|
382
|
+
end
|
383
|
+
|
384
|
+
it "'#{email}' should not be invalid using EmailValidator.invalid?" do
|
385
|
+
expect(described_class).not_to be_invalid(email, :mode => :rfc)
|
386
|
+
end
|
387
|
+
|
388
|
+
it "'#{email}' should match the regexp" do
|
389
|
+
expect(!!(email.strip =~ described_class.regexp(:mode => :rfc))).to be(true)
|
390
|
+
end
|
391
|
+
end
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
263
395
|
context 'when given the valid IP address email' do
|
264
396
|
[
|
265
397
|
'bracketed-IP@[127.0.0.1]',
|
@@ -334,8 +466,6 @@ RSpec.describe EmailValidator do
|
|
334
466
|
'test@example.com@example.com',
|
335
467
|
'missing-sld@.com',
|
336
468
|
'missing-tld@sld.',
|
337
|
-
'only-numbers-in-domain-label@sub.123.com',
|
338
|
-
'only-numbers-in-domain-label@123.example.com',
|
339
469
|
'unbracketed-IPv6@abcd:ef01:1234:5678:9abc:def0:1234:5678',
|
340
470
|
'unbracketed-and-labled-IPv6@IPv6:abcd:ef01:1234:5678:9abc:def0:1234:5678',
|
341
471
|
'bracketed-and-unlabeled-IPv6@[abcd:ef01:1234:5678:9abc:def0:1234:5678]',
|
@@ -347,6 +477,7 @@ RSpec.describe EmailValidator do
|
|
347
477
|
'domain-beginning-with-dash@-example.com',
|
348
478
|
'domain-ending-with-dash@example-.com',
|
349
479
|
'the-local-part-is-invalid-if-it-is-longer-than-sixty-four-characters@sld.dev',
|
480
|
+
"domain-too-long@t#{".#{'o' * 63}" * 5}.long",
|
350
481
|
"user@example.com<script>alert('hello')</script>"
|
351
482
|
]).flatten.each do |email|
|
352
483
|
context 'when using defaults' do
|
@@ -504,8 +635,7 @@ RSpec.describe EmailValidator do
|
|
504
635
|
'@bar.com',
|
505
636
|
'test@',
|
506
637
|
'@missing-local.dev',
|
507
|
-
' '
|
508
|
-
'missing-at-sign.dev'
|
638
|
+
' '
|
509
639
|
].each do |email|
|
510
640
|
context 'when using defaults' do
|
511
641
|
it "'#{email}' should not be valid" do
|
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.
|
4
|
+
version: 2.2.1
|
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-
|
12
|
+
date: 2020-12-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activemodel
|