valid_email2 2.3.1 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gem 'activemodel', '~> 3.2.22'
3
+ gem 'activemodel', '~> 3.2.22.5'
4
4
 
5
5
  gemspec path: '../'
@@ -1,5 +1,5 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gem 'activemodel', '~> 4.2.9'
3
+ gem 'activemodel', '~> 4.2.10'
4
4
 
5
5
  gemspec path: '../'
@@ -1,5 +1,5 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gem 'activemodel', '~> 5.1.2'
3
+ gem 'activemodel', '~> 5.2.1'
4
4
 
5
5
  gemspec path: '../'
data/lib/valid_email2.rb CHANGED
@@ -1,17 +1,30 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "valid_email2/email_validator"
2
4
 
3
5
  module ValidEmail2
6
+ BLACKLIST_FILE = "config/blacklisted_email_domains.yml"
7
+ WHITELIST_FILE = "config/whitelisted_email_domains.yml"
8
+
4
9
  def self.disposable_emails
5
- @@disposable_emails ||= YAML.load_file(File.expand_path("../../vendor/disposable_emails.yml",__FILE__))
10
+ @disposable_emails ||= YAML.load_file(
11
+ File.expand_path('../config/disposable_email_domains.yml', __dir__)
12
+ )
6
13
  end
7
14
 
8
15
  def self.blacklist
9
- blacklist_file = "vendor/blacklist.yml"
10
- @@blacklist ||= File.exists?(blacklist_file) ? YAML.load_file(File.expand_path(blacklist_file)) : []
16
+ @blacklist ||= if File.exist?(BLACKLIST_FILE)
17
+ YAML.load_file(File.expand_path(BLACKLIST_FILE))
18
+ else
19
+ []
20
+ end
11
21
  end
12
22
 
13
23
  def self.whitelist
14
- whitelist_file = "vendor/whitelist.yml"
15
- @@whitelist ||= File.exists?(whitelist_file) ? YAML.load_file(File.expand_path(whitelist_file)) : []
24
+ @whitelist ||= if File.exist?(WHITELIST_FILE)
25
+ YAML.load_file(File.expand_path(WHITELIST_FILE))
26
+ else
27
+ []
28
+ end
16
29
  end
17
30
  end
@@ -45,7 +45,11 @@ module ValidEmail2
45
45
  end
46
46
 
47
47
  def disposable?
48
- valid? && domain_is_in?(ValidEmail2.disposable_emails)
48
+ valid? &&
49
+ (
50
+ domain_is_in?(ValidEmail2.disposable_emails) ||
51
+ mx_server_is_in?(ValidEmail2.disposable_emails)
52
+ )
49
53
  end
50
54
 
51
55
  def whitelisted?
@@ -59,10 +63,7 @@ module ValidEmail2
59
63
  def valid_mx?
60
64
  return false unless valid?
61
65
 
62
- Resolv::DNS.open do |dns|
63
- return dns.getresources(address.domain, Resolv::DNS::Resource::IN::MX).size > 0 ||
64
- dns.getresources(address.domain, Resolv::DNS::Resource::IN::A).size > 0
65
- end
66
+ mx_servers.any?
66
67
  end
67
68
 
68
69
  private
@@ -74,10 +75,29 @@ module ValidEmail2
74
75
  }
75
76
  end
76
77
 
77
- def address_contain_emoticons? email_str
78
- return false if email_str.nil?
78
+ def mx_server_is_in?(domain_list)
79
+ mx_servers.any? { |mx_server|
80
+ return false unless mx_server.respond_to?(:exchange)
81
+ mx_server = mx_server.exchange.to_s
82
+
83
+ domain_list.any? { |domain|
84
+ mx_server.end_with?(domain) && mx_server =~ /\A(?:.+\.)*?#{domain}\z/
85
+ }
86
+ }
87
+ end
88
+
89
+ def address_contain_emoticons?(email)
90
+ return false if email.nil?
91
+
92
+ email.each_char.any? { |char| char.bytesize > 1 }
93
+ end
79
94
 
80
- email_str.each_char.any? { |char| char.bytesize > 1 }
95
+ def mx_servers
96
+ @mx_servers ||= Resolv::DNS.open do |dns|
97
+ mx_servers = dns.getresources(address.domain, Resolv::DNS::Resource::IN::MX)
98
+ (mx_servers.any? && mx_servers) ||
99
+ dns.getresources(address.domain, Resolv::DNS::Resource::IN::A)
100
+ end
81
101
  end
82
102
  end
83
103
  end
@@ -1,3 +1,3 @@
1
1
  module ValidEmail2
2
- VERSION = "2.3.1"
2
+ VERSION = "3.0.0"
3
3
  end
@@ -10,7 +10,7 @@ whitelisted_emails = %w(
10
10
  hush.ai hush.com hushmail.me naver.com qq.com example.com
11
11
  )
12
12
 
13
- existing_emails = YAML.load_file("vendor/disposable_emails.yml")
13
+ existing_emails = YAML.load_file("config/disposable_email_domains.yml")
14
14
 
15
15
  url = "https://raw.githubusercontent.com/FGRibreau/mailchecker/master/list.json"
16
16
  resp = Net::HTTP.get_response(URI.parse(url))
@@ -19,4 +19,4 @@ remote_emails = JSON.parse(resp.body).flatten - whitelisted_emails
19
19
 
20
20
  result_emails = (existing_emails + remote_emails).map(&:strip).uniq.sort
21
21
 
22
- File.open("vendor/disposable_emails.yml", "w") {|f| f.write result_emails.to_yaml }
22
+ File.open("config/disposable_email_domains.yml", "w") {|f| f.write result_emails.to_yaml }
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "spec_helper"
2
4
 
3
5
  class TestUser < TestModel
@@ -5,7 +7,7 @@ class TestUser < TestModel
5
7
  end
6
8
 
7
9
  class TestUserSubaddressing < TestModel
8
- validates :email, 'valid_email_2/email': {disallow_subaddressing: true}
10
+ validates :email, 'valid_email_2/email': { disallow_subaddressing: true }
9
11
  end
10
12
 
11
13
  class TestUserMX < TestModel
@@ -24,99 +26,127 @@ class TestUserDisallowBlacklisted < TestModel
24
26
  validates :email, 'valid_email_2/email': { blacklist: true }
25
27
  end
26
28
 
29
+ class TestUserMessage < TestModel
30
+ validates :email, 'valid_email_2/email': { message: "custom message" }
31
+ end
32
+
27
33
  describe ValidEmail2 do
28
34
  describe "basic validation" do
29
35
  subject(:user) { TestUser.new(email: "") }
30
36
 
31
- it "should not be valid email is valid" do
37
+ it "is valid" do
32
38
  user = TestUser.new(email: "foo@bar123.com")
33
39
  expect(user.valid?).to be_truthy
34
40
  end
35
41
 
36
- it "should be valid when email is empty" do
42
+ it "is invalid if email is empty" do
37
43
  expect(user.valid?).to be_truthy
38
44
  end
39
45
 
40
- it "should not be valid when domain is missing" do
46
+ it "is invalid if domain is missing" do
41
47
  user = TestUser.new(email: "foo@.com")
42
48
  expect(user.valid?).to be_falsey
43
49
  end
44
50
 
45
- it "should be invalid when domain includes invalid characters" do
46
- %w(+ _ !).each do |invalid_character|
51
+ it "is invalid if email contains invalid characters" do
52
+ %w[+ _ !].each do |invalid_character|
47
53
  user = TestUser.new(email: "foo@google#{invalid_character}yahoo.com")
48
54
  expect(user.valid?).to be_falsey
49
55
  end
50
56
  end
51
57
 
52
- it "should be invalid when email is malformed" do
58
+ it "is invalid if address is malformed" do
53
59
  user = TestUser.new(email: "foo@bar")
54
60
  expect(user.valid?).to be_falsey
55
61
  end
56
62
 
57
- it "should be invalid if Mail::AddressListsParser raises exception" do
63
+ it "is invalid if Mail::AddressListsParser raises exception" do
58
64
  user = TestUser.new(email: "foo@gmail.com")
59
65
  expect(Mail::Address).to receive(:new).and_raise(Mail::Field::ParseError.new(nil, nil, nil))
60
66
  expect(user.valid?).to be_falsey
61
67
  end
62
68
 
63
- it "shouldn't be valid if the domain constains consecutives dots" do
69
+ it "is invalid if the domain contains consecutive dots" do
64
70
  user = TestUser.new(email: "foo@bar..com")
65
71
  expect(user.valid?).to be_falsey
66
72
  end
73
+
74
+ it "is invalid if the domain contains emoticons" do
75
+ user = TestUser.new(email: "foo🙈@gmail.com")
76
+ expect(user.valid?).to be_falsy
77
+ end
67
78
  end
68
79
 
69
- describe "disposable emails" do
70
- it "should be valid when the domain is not in the list of disposable email providers" do
80
+ describe "with disposable validation" do
81
+ it "is valid if it's not a disposable email" do
71
82
  user = TestUserDisallowDisposable.new(email: "foo@gmail.com")
72
83
  expect(user.valid?).to be_truthy
73
84
  end
74
85
 
75
- it "should be invalid when domain is in the list of disposable email providers" do
86
+ it "is invalid if it's a disposable email" do
76
87
  user = TestUserDisallowDisposable.new(email: "foo@#{ValidEmail2.disposable_emails.first}")
77
88
  expect(user.valid?).to be_falsey
78
89
  end
79
90
 
80
- it "should be invalid when domain is a subdomain of a disposable domain" do
91
+ it "is invalid if the domain is a subdomain of a disposable domain" do
81
92
  user = TestUserDisallowDisposable.new(email: "foo@bar.#{ValidEmail2.disposable_emails.first}")
82
93
  expect(user.valid?).to be_falsey
83
94
  end
84
95
 
85
- it "should allow example.com that is common in lists of disposable email providers" do
96
+ it "allows example.com" do
86
97
  user = TestUserDisallowDisposable.new(email: "foo@example.com")
87
98
  expect(user.valid?).to be_truthy
88
99
  end
89
100
 
101
+ context "with domain that is not disposable but it's mx server is disposable" do
102
+ let(:domain) { "sogetthis.com" }
103
+
104
+ around do |example|
105
+ ValidEmail2.disposable_emails.delete(domain)
106
+ example.run
107
+ ValidEmail2.disposable_emails << domain
108
+ end
109
+
110
+ it "is invalid" do
111
+ user = TestUserDisallowDisposable.new(email: "foo@sogetthis.com")
112
+ expect(user.valid?).to be_falsey
113
+ end
114
+ end
115
+
90
116
  describe "with whitelisted emails" do
91
- it "should be invalid when the domain is in the list of disposable and there is no whitelist" do
92
- user = TestUserDisallowDisposableWithWhitelist.new(email: "foo@#{ValidEmail2.disposable_emails.first}")
117
+ let(:whitelist_domain) { ValidEmail2.disposable_emails.first }
118
+ let(:whitelist_file_path) { "config/whitelisted_email_domains.yml" }
119
+
120
+ after do
121
+ FileUtils.rm(whitelist_file_path, force: true)
122
+ end
123
+
124
+ it "is invalid if the domain is disposable and not in the whitelist" do
125
+ user = TestUserDisallowDisposableWithWhitelist.new(email: "foo@#{whitelist_domain}")
93
126
  expect(user.valid?).to be_falsey
94
127
  end
95
128
 
96
- it "should be valid when the domain is in the list of disposable but it is in the whitelist" do
97
- whitelist_domain = ValidEmail2.disposable_emails.first
98
- whitelist_file_path = "vendor/whitelist.yml"
99
- File.open(whitelist_file_path, "w") {|f| f.write whitelist_domain.to_yaml }
129
+ it "is valid if the domain is disposable but in the whitelist" do
130
+ File.open(whitelist_file_path, "w") { |f| f.write [whitelist_domain].to_yaml }
100
131
  user = TestUserDisallowDisposableWithWhitelist.new(email: "foo@#{whitelist_domain}")
101
132
  expect(user.valid?).to be_falsey
102
- File.delete(whitelist_file_path)
103
133
  end
104
134
  end
105
135
  end
106
136
 
107
- describe "blacklisted emails" do
108
- it "should be valid when email is not in the blacklist" do
137
+ describe "with blacklist validation" do
138
+ it "is valid if the domain is not blacklisted" do
109
139
  user = TestUserDisallowBlacklisted.new(email: "foo@gmail.com")
110
140
  expect(user.valid?).to be_truthy
111
141
  end
112
142
 
113
- it "should be invalid when email is in the blacklist" do
143
+ it "is invalid if the domain is blacklisted" do
114
144
  user = TestUserDisallowBlacklisted.new(email: "foo@#{ValidEmail2.blacklist.first}")
115
145
  expect(user.valid?).to be_falsey
116
146
  end
117
147
  end
118
148
 
119
- describe "mx lookup" do
149
+ describe "with mx validation" do
120
150
  it "should be valid if mx records are found" do
121
151
  user = TestUserMX.new(email: "foo@gmail.com")
122
152
  expect(user.valid?).to be_truthy
@@ -133,58 +163,35 @@ describe ValidEmail2 do
133
163
  end
134
164
  end
135
165
 
136
- describe "emoticons emails" do
137
- it "should be invalid if email contains emoticon" do
138
- email = ValidEmail2::Address.new("foo🙈@gmail.com")
139
- expect(email.valid?).to be_falsy
166
+ describe "with subaddress validation" do
167
+ it "is valid when address does not contain subaddress" do
168
+ user = TestUserSubaddressing.new(email: "foo@gmail.com")
169
+ expect(user.valid?).to be_truthy
140
170
  end
141
- end
142
171
 
143
- describe "subaddressed emails" do
144
-
145
- describe "::Address::DEFAULT_RECIPIENT_DELIMITER" do
146
- it "should be recipient delimiter ('+')" do
147
- expect(ValidEmail2::Address::DEFAULT_RECIPIENT_DELIMITER).to eq('+')
148
- end
172
+ it "is invalid when address cotains subaddress" do
173
+ user = TestUserSubaddressing.new(email: "foo+1@gmail.com")
174
+ expect(user.valid?).to be_falsey
149
175
  end
176
+ end
150
177
 
151
- describe "::Address#subaddressed?" do
152
- it "should be true when address local part contains a recipient delimiter ('+')" do
153
- email = ValidEmail2::Address.new("foo+1@gmail.com")
154
- expect(email.subaddressed?).to be_truthy
155
- end
156
-
157
- it "should be false when address local part contains a recipient delimiter ('+')" do
158
- email = ValidEmail2::Address.new("foo@gmail.com")
159
- expect(email.subaddressed?).to be_falsey
160
- end
178
+ describe "with custom error message" do
179
+ it "supports settings a custom error message" do
180
+ user = TestUserMessage.new(email: "fakeemail")
181
+ user.valid?
182
+ expect(user.errors.full_messages).to include("Email custom message")
161
183
  end
184
+ end
162
185
 
163
- describe "user validation" do
164
- context "subaddressing is allowed (default)" do
165
- it "should be valid when address local part does not contain a recipient delimiter ('+')" do
166
- user = TestUser.new(email: "foo@gmail.com")
167
- expect(user.valid?).to be_truthy
168
- end
169
-
170
- it "should be valid when address local part contains a recipient delimiter ('+')" do
171
- user = TestUser.new(email: "foo+1@gmail.com")
172
- expect(user.valid?).to be_truthy
173
- end
174
- end
175
-
176
- context "subaddressing is forbidden" do
177
- it "should be valid when address local part does not contain a recipient delimiter ('+')" do
178
- user = TestUserSubaddressing.new(email: "foo@gmail.com")
179
- expect(user.valid?).to be_truthy
180
- end
181
-
182
- it "should be invalid when address local part contains a recipient delimiter ('+')" do
183
- user = TestUserSubaddressing.new(email: "foo+1@gmail.com")
184
- expect(user.valid?).to be_falsey
185
- end
186
- end
186
+ describe "#subaddressed?" do
187
+ it "should be true when address local part contains a recipient delimiter ('+')" do
188
+ email = ValidEmail2::Address.new("foo+1@gmail.com")
189
+ expect(email.subaddressed?).to be_truthy
187
190
  end
188
191
 
192
+ it "should be false when address local part contains a recipient delimiter ('+')" do
193
+ email = ValidEmail2::Address.new("foo@gmail.com")
194
+ expect(email.subaddressed?).to be_falsey
195
+ end
189
196
  end
190
197
  end
data/valid_email2.gemspec CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.required_ruby_version = ">= 1.9.3"
22
22
 
23
- spec.add_development_dependency "bundler", "~> 1.3"
23
+ spec.add_development_dependency "bundler", "~> 2.0"
24
24
  spec.add_development_dependency "rake", "~> 11.3.0"
25
25
  spec.add_development_dependency "rspec", "~> 3.5.0"
26
26
  spec.add_runtime_dependency "mail", "~> 2.5"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: valid_email2
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.1
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Micke Lisinge
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-10-16 00:00:00.000000000 Z
11
+ date: 2019-02-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.3'
19
+ version: '2.0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.3'
26
+ version: '2.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -95,6 +95,8 @@ files:
95
95
  - LICENSE.txt
96
96
  - README.md
97
97
  - Rakefile
98
+ - config/blacklisted_email_domains.yml
99
+ - config/disposable_email_domains.yml
98
100
  - gemfiles/activemodel3.gemfile
99
101
  - gemfiles/activemodel4.gemfile
100
102
  - gemfiles/activemodel5.gemfile
@@ -106,8 +108,6 @@ files:
106
108
  - spec/spec_helper.rb
107
109
  - spec/valid_email2_spec.rb
108
110
  - valid_email2.gemspec
109
- - vendor/blacklist.yml
110
- - vendor/disposable_emails.yml
111
111
  homepage: https://github.com/micke/valid_email2
112
112
  licenses:
113
113
  - MIT
data/vendor/blacklist.yml DELETED
@@ -1 +0,0 @@
1
- - blacklisted-test.com