email_assessor 0.7.1 → 0.8.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 28e8db711b1571f9f105f0e341b8798bf557edcdd21bfa9af93b97a2fc6e7989
4
- data.tar.gz: 697a161767368eed35a1cf12eaca2fc9c129b6e27f4ab48a4bd07362597a678c
3
+ metadata.gz: 8b030fd55305340114adc5f313d216c398b15097ed82d97c1ae19ab29a8bb371
4
+ data.tar.gz: f28a6570204c3af6fa87ddf9ed4954b6ae2f03e53f30d74ccbdaeb1316424c63
5
5
  SHA512:
6
- metadata.gz: ac12ac0944a707a72b3d7f507b016c0d4dad6cd3cd924c1f229178e8dd806cff1e2bdf4eca887566c2271c15ea4a9210f02b2f6e24e26d768c85e47e73104fdb
7
- data.tar.gz: fd148a7343b485d42f320c0e7fcc2265a636f2973faacf3e928d5315c39e8b909c970809c6ebd07b3d929484a2239b33be3c7b2bb38ef0aeb0bd3648e2288db4
6
+ metadata.gz: a6e6cdf6bc76dcd825ab27f721b489b54143d08fb1dc692c9727afa11bbef658fa678135be3bd0803e0e986789aee8a9a74a240ba7b1154c094d876ca154aa07
7
+ data.tar.gz: 0b8c0b930ba5c0e9a26afb30a6f142336782db311a0ea6ca36476c288f717143c443045a54c3357a711c875ede73995be16c8966d948f4d2fd646278691b4e99
@@ -111,11 +111,23 @@ module EmailAssessor
111
111
  end
112
112
 
113
113
  def disposable?
114
- valid? && EmailAssessor.domain_is_disposable?(address.domain)
114
+ domain_in_file?(EmailAssessor::DISPOSABLE_DOMAINS_FILE_NAME)
115
115
  end
116
116
 
117
117
  def blacklisted?
118
- valid? && EmailAssessor.domain_is_blacklisted?(address.domain)
118
+ domain_in_file?(EmailAssessor::BLACKLISTED_DOMAINS_FILE_NAME)
119
+ end
120
+
121
+ def educational?
122
+ domain_in_file?(EmailAssessor::EDUCATIONAL_DOMAINS_FILE_NAME)
123
+ end
124
+
125
+ def fastpass?
126
+ domain_in_file?(EmailAssessor::FASTPASS_DOMAINS_FILE_NAME)
127
+ end
128
+
129
+ def domain_in_file?(filename)
130
+ valid? && EmailAssessor.domain_in_file?(address.domain, filename)
119
131
  end
120
132
 
121
133
  def valid_mx?
@@ -5,31 +5,74 @@ require "active_model/validations"
5
5
 
6
6
  class EmailValidator < ActiveModel::EachValidator
7
7
  def default_options
8
- { regex: true, disposable: false, mx: false }
8
+ { regex: true, disposable: false, mx: false, fastpass: true }
9
9
  end
10
10
 
11
11
  def validate_each(record, attribute, value)
12
12
  return unless value.present?
13
- options = default_options.merge(self.options)
13
+
14
+ options = default_options.merge!(self.options)
14
15
 
15
16
  address = EmailAssessor::Address.new(value)
16
17
 
17
18
  error(record, attribute) && return unless address.valid?
18
19
 
20
+ # Skip all domain blocklist checks for fastpass domains.
21
+ # The goal is to skip needless validation for common "good" domains such as Gmail, Yahoo, and Outlook.
22
+ # The fastpass domain list is configurable via vendor/fastpass_domains.txt
23
+ validate_domain(record, attribute, address, options) unless options[:fastpass] && address.fastpass?
24
+
25
+ # Exit early if validate_domain found a validation error
26
+ return if record.errors.key?(attribute)
27
+
28
+ if options[:mx]
29
+ error(record, attribute, error_type(:mx, options)) && return unless address.valid_mx?
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def error(record, attribute, type = options[:message] || :invalid)
36
+ record.errors.add(attribute, type)
37
+ end
38
+
39
+ def error_type(validator, options)
40
+ option_value = options[validator]
41
+
42
+ if option_value.is_a?(String) || option_value.is_a?(Symbol)
43
+ return option_value
44
+ end
45
+
46
+ options[:message] || :invalid
47
+ end
48
+
49
+ def validate_domain(record, attribute, address, options)
19
50
  if options[:disposable]
20
- error(record, attribute) && return if address.disposable?
51
+ error(record, attribute, error_type(:disposable, options)) && return if address.disposable?
21
52
  end
22
53
 
23
54
  if options[:blacklist]
24
- error(record, attribute) && return if address.blacklisted?
55
+ error(record, attribute, error_type(:blacklist, options)) && return if address.blacklisted?
25
56
  end
26
57
 
27
- if options[:mx]
28
- error(record, attribute) && return unless address.valid_mx?
58
+ if options[:educational]
59
+ error(record, attribute, error_type(:educational, options)) && return if address.educational?
29
60
  end
30
- end
31
61
 
32
- def error(record, attribute)
33
- record.errors.add(attribute, options[:message] || :invalid)
62
+ # if options[:domain_not_in]
63
+ # matched_blocklist = options[:domain_not_in].select do |entry|
64
+ # unless entry.key?(:blocklist)
65
+ # fail "domain_not_in entries must be in format { blocklist: \"filename\"[, message: symbol|string] }"
66
+ # end
67
+
68
+ # next unless address.domain_in_blocklist?(entry[:blocklist])
69
+
70
+ # error(record, attribute, entry[:message])
71
+
72
+ # true
73
+ # end
74
+
75
+ # return if matched_blocklist
76
+ # end
34
77
  end
35
78
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module EmailAssessor
3
- VERSION = "0.7.1"
3
+ VERSION = "0.8.1"
4
4
  end
@@ -3,6 +3,8 @@ require "email_assessor/email_validator"
3
3
 
4
4
  module EmailAssessor
5
5
  DISPOSABLE_DOMAINS_FILE_NAME = File.expand_path("../../vendor/disposable_domains.txt", __FILE__)
6
+ FASTPASS_DOMAINS_FILE_NAME = File.expand_path("../../vendor/fastpass_domains.txt", __FILE__)
7
+ EDUCATIONAL_DOMAINS_FILE_NAME = File.expand_path("../../vendor/educational_domains.txt", __FILE__)
6
8
  BLACKLISTED_DOMAINS_FILE_NAME = File.expand_path("vendor/blacklisted_domains.txt")
7
9
 
8
10
  def self.domain_is_disposable?(domain)
@@ -17,10 +17,15 @@ class TestUserDisallowBlacklisted < TestModel
17
17
  validates :email, email: { blacklist: true }
18
18
  end
19
19
 
20
+ class TestUserDisallowEducational < TestModel
21
+ validates :email, email: { educational: :no_educational }
22
+ end
23
+
20
24
  describe EmailAssessor do
21
25
  let(:plain_user) { TestUser.new(email: "") }
22
26
  let(:disposable_user) { TestUserDisallowDisposable.new(email: "foo@gmail.com") }
23
27
  let(:blacklist_user) { TestUserDisallowBlacklisted.new(email: "foo@gmail.com") }
28
+ let(:educational_user) { TestUserDisallowEducational.new(email: "foo@gmail.com") }
24
29
  let(:mx_user) { TestUserMX.new(email: "foo@gmail.com") }
25
30
 
26
31
  let(:blacklisted_domains_file_name) { described_class::BLACKLISTED_DOMAINS_FILE_NAME }
@@ -29,6 +34,9 @@ describe EmailAssessor do
29
34
  let(:disposable_domains_file_name) { described_class::DISPOSABLE_DOMAINS_FILE_NAME }
30
35
  let(:disposable_domain) { File.open(disposable_domains_file_name, &:readline).chomp }
31
36
 
37
+ let(:educational_domains_file_name) { described_class::EDUCATIONAL_DOMAINS_FILE_NAME }
38
+ let(:educational_domain) { File.open(educational_domains_file_name, &:readline).chomp }
39
+
32
40
  describe "basic validation" do
33
41
  subject(:user) { plain_user }
34
42
 
@@ -161,6 +169,31 @@ describe EmailAssessor do
161
169
  end
162
170
  end
163
171
 
172
+ describe "educational domains" do
173
+ subject(:user) { educational_user }
174
+
175
+ it "is valid when email domain is not in the educational blocklist" do
176
+ is_expected.to be_valid
177
+ end
178
+
179
+ it "is invalid when email domain is in the educational blocklist" do
180
+ user.email = "foo@#{educational_domain}"
181
+ is_expected.to be_invalid
182
+ end
183
+
184
+ it "is invalid when email is in the educational blocklist regardless of case" do
185
+ user.email = "foo@#{educational_domain.upcase}"
186
+ is_expected.to be_invalid
187
+ end
188
+
189
+ it "is invalid when email domain is in the educational blocklist regardless of subdomain" do
190
+ user.email = "foo@abc123.#{educational_domain}"
191
+ is_expected.to be_invalid
192
+ expect(user.errors.added?(:email, :no_educational)).to be_truthy
193
+ end
194
+ end
195
+
196
+
164
197
  describe "mx lookup" do
165
198
  subject(:user) { mx_user }
166
199