email_validator 2.2.0 → 2.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|