email_assessor 0.7.0 → 0.8.0

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: 8ec3fdbcb7339d1a5d0854cc8fde6b45bc0440e8154807870a36332023be74bc
4
- data.tar.gz: f5ccda7423d991a5e1512a96c39f38bb423bcdf6c86d51ca6f7f487ddfc3571a
3
+ metadata.gz: 8cb395329e3cd39283e977bf23e76aace70bf54f8a4e735fb853452e9e2cd6a9
4
+ data.tar.gz: b5584869b5c06e03a9be43827685a59b69a1d3b0f516b3670649557097ddc642
5
5
  SHA512:
6
- metadata.gz: c083397bac2fa9a19441e9bb2697344522b54ef7abc0bf206d6f015a185ac365aecbdc1adaa4d50f1a1d393f7c541c821ec1807eda036608dec10188fe3bee4c
7
- data.tar.gz: 92c2b3199bba839ac4e993caa677c20c033cc27b84fcd87cf3c0f85492c0d650bdb2725776882d4eed78616eeffdf08cfc764402aecdb41d34cd81f8caf0314b
6
+ metadata.gz: 22b2a38efbcb45ced0186a9a00a159747aa6c7d66c21c942b02d7862014f99e2a5b3c985d91cab0377ceee2fa91bfbf404e683bfb7b503ad57da4c4fc1bdeadf
7
+ data.tar.gz: 38fc56dd35d5c2dbc31f95cd491398ed102bdc494e6674c5e552e775ba57c538f750c5ae5823ba36ca89d2031b3cffd295fc552047e8793d8ca82a2394f6ec68
@@ -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.0"
3
+ VERSION = "0.8.0"
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