email_assessor 0.7.1 → 0.8.1

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: 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