email_address 0.1.1 → 0.1.2
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 +4 -4
- data/README.md +15 -0
- data/email_address.gemspec +1 -1
- data/lib/email_address/active_record_validator.rb +5 -1
- data/lib/email_address/address.rb +24 -16
- data/lib/email_address/config.rb +16 -0
- data/lib/email_address/exchanger.rb +9 -4
- data/lib/email_address/host.rb +2 -1
- data/lib/email_address/local.rb +10 -8
- data/lib/email_address/version.rb +1 -1
- data/test/activerecord/test_ar.rb +1 -0
- data/test/email_address/test_address.rb +10 -0
- data/test/test_helper.rb +2 -2
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b6a59891931ddfd551f8ead37a376795d22e70eb
|
4
|
+
data.tar.gz: 8b71384418e8b1a4eb505faa5e95e021f0b30b7d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 62af3189a51354059a4d05e03c294e12a3e76839bb4cafd0697231fae638cbe5959324034cbdeea4d83f8a10303e6f7e6bad4c7aa0d2bd90bd7ef3bbafc0c930
|
7
|
+
data.tar.gz: 34d7af8021c044f4cc4e1e00c4d2aa0e0066881178f7300c6322ccad52dd7787448b0929efb1148cfc582e6b073d9ae318fe515937bab9085a94e6ddfa98ab3b
|
data/README.md
CHANGED
@@ -365,6 +365,21 @@ configuration options:
|
|
365
365
|
EmailAddress::Config.provider(:github,
|
366
366
|
host_match: %w(github.com), local_format: :standard)
|
367
367
|
|
368
|
+
#### Override Error Messaegs
|
369
|
+
|
370
|
+
You can override the default error messages as follows:
|
371
|
+
|
372
|
+
EmailAddress::Config.error_messages(
|
373
|
+
invalid_address: "Invalid Email Address",
|
374
|
+
invalid_mailbox: "Invalid Recipient/Mailbox",
|
375
|
+
invalid_host: "Invalid Host/Domain Name",
|
376
|
+
exceeds_size: "Address too long",
|
377
|
+
not_allowed: "Address is not allowed",
|
378
|
+
incomplete_domain: "Domain name is incomplete")
|
379
|
+
|
380
|
+
Full translation support would be ideal though.
|
381
|
+
|
382
|
+
|
368
383
|
### Available Configuration Settings
|
369
384
|
|
370
385
|
* dns_lookup: Enables DNS lookup for validation by
|
data/email_address.gemspec
CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.add_development_dependency "rake"
|
22
22
|
spec.add_development_dependency "minitest", "~> 5.8.3"
|
23
23
|
spec.add_development_dependency "bundler", "~> 1.3"
|
24
|
-
spec.add_development_dependency "activerecord", "~> 5.0.
|
24
|
+
spec.add_development_dependency "activerecord", "~> 5.0.1" if RUBY_PLATFORM != 'java'
|
25
25
|
spec.add_development_dependency "activerecord", "~> 4.2.5" if RUBY_PLATFORM == 'java'
|
26
26
|
spec.add_development_dependency "sqlite3" if RUBY_PLATFORM != 'java'
|
27
27
|
spec.add_development_dependency "activerecord-jdbcsqlite3-adapter" if RUBY_PLATFORM == 'java'
|
@@ -34,7 +34,11 @@ module EmailAddress
|
|
34
34
|
def validate_email(r,f)
|
35
35
|
return if r[f].nil?
|
36
36
|
e = EmailAddress.new(r[f])
|
37
|
-
|
37
|
+
unless e.valid?
|
38
|
+
r.errors[f] << (@opt[:message] ||
|
39
|
+
EmailAddress::Config.error_messages[:invalid_address] ||
|
40
|
+
"Invalid Email Address")
|
41
|
+
end
|
38
42
|
end
|
39
43
|
|
40
44
|
end
|
@@ -6,12 +6,14 @@ module EmailAddress
|
|
6
6
|
# (EmailAddress::Local) and Host (Email::AddressHost) parts.
|
7
7
|
class Address
|
8
8
|
include Comparable
|
9
|
-
attr_accessor :original, :local, :host, :config
|
9
|
+
attr_accessor :original, :local, :host, :config
|
10
10
|
|
11
11
|
CONVENTIONAL_REGEX = /\A#{::EmailAddress::Local::CONVENTIONAL_MAILBOX_WITHIN}
|
12
12
|
@#{::EmailAddress::Host::DNS_HOST_REGEX}\z/x
|
13
13
|
STANDARD_REGEX = /\A#{::EmailAddress::Local::STANDARD_LOCAL_WITHIN}
|
14
14
|
@#{::EmailAddress::Host::DNS_HOST_REGEX}\z/x
|
15
|
+
RELAXED_REGEX = /\A#{::EmailAddress::Local::RELAXED_MAILBOX_WITHIN}
|
16
|
+
@#{::EmailAddress::Host::DNS_HOST_REGEX}\z/x
|
15
17
|
|
16
18
|
# Given an email address of the form "local@hostname", this sets up the
|
17
19
|
# instance, and initializes the address to the "normalized" format of the
|
@@ -109,7 +111,9 @@ module EmailAddress
|
|
109
111
|
# spaves and comments and tags, and any extraneous part of the address
|
110
112
|
# not considered a unique account by the provider.
|
111
113
|
def canonical
|
112
|
-
|
114
|
+
c = self.local.canonical
|
115
|
+
c += "@" + self.host.canonical if self.host.canonical && self.host.canonical > " "
|
116
|
+
c
|
113
117
|
end
|
114
118
|
|
115
119
|
# True if the given address is already in it's canonical form.
|
@@ -119,9 +123,13 @@ module EmailAddress
|
|
119
123
|
|
120
124
|
# Returns the redacted form of the address
|
121
125
|
# This format is defined by this libaray, and may change as usage increases.
|
122
|
-
|
126
|
+
# Takes either :sha1 (default) or :md5 as the argument
|
127
|
+
def redact(digest=:sha1)
|
128
|
+
raise "Unknown Digest type: #{digest}" unless %i(sha1 md5).include?(digest)
|
123
129
|
return self.to_s if self.local.redacted?
|
124
|
-
%Q({#{
|
130
|
+
r = %Q({#{send(digest)}})
|
131
|
+
r += "@" + self.host.to_s if self.host.to_s && self.host.to_s > " "
|
132
|
+
r
|
125
133
|
end
|
126
134
|
|
127
135
|
# True if the address is already in the redacted state.
|
@@ -147,7 +155,7 @@ module EmailAddress
|
|
147
155
|
# This returns the SHA1 digest (in a hex string) of the canonical email
|
148
156
|
# address. See #md5 for more background.
|
149
157
|
def sha1
|
150
|
-
Digest::SHA1.hexdigest(canonical + @config[:sha1_secret])
|
158
|
+
Digest::SHA1.hexdigest((canonical||"") + (@config[:sha1_secret]||""))
|
151
159
|
end
|
152
160
|
|
153
161
|
#---------------------------------------------------------------------------
|
@@ -204,35 +212,35 @@ module EmailAddress
|
|
204
212
|
# Returns true if this address is considered valid according to the format
|
205
213
|
# configured for its provider, It test the normalized form.
|
206
214
|
def valid?(options={})
|
207
|
-
|
215
|
+
@error = nil
|
208
216
|
unless self.local.valid?
|
209
|
-
|
210
|
-
return false
|
217
|
+
return set_error :invalid_mailbox
|
211
218
|
end
|
212
219
|
unless self.host.valid?
|
213
|
-
|
214
|
-
return false
|
220
|
+
return set_error :invalid_host
|
215
221
|
end
|
216
222
|
if @config[:address_size] && !@config[:address_size].include?(self.to_s.size)
|
217
|
-
|
218
|
-
return false
|
223
|
+
return set_error :exceeds_size
|
219
224
|
end
|
220
225
|
if @config[:address_validation].is_a?(Proc)
|
221
226
|
unless @config[:address_validation].call(self.to_s)
|
222
|
-
|
223
|
-
return false
|
227
|
+
return set_error :not_allowed
|
224
228
|
end
|
225
229
|
else
|
226
230
|
return false unless self.local.valid?
|
227
231
|
return false unless self.host.valid?
|
228
232
|
end
|
229
233
|
if !@config[:address_local] && !self.hostname.include?(".")
|
230
|
-
|
231
|
-
return false
|
234
|
+
return set_error :incomplete_domain
|
232
235
|
end
|
233
236
|
true
|
234
237
|
end
|
235
238
|
|
239
|
+
def set_error(err)
|
240
|
+
@error = EmailAddress::Config.error_messages[err] || err
|
241
|
+
false
|
242
|
+
end
|
243
|
+
|
236
244
|
def error
|
237
245
|
self.valid? ? nil : @error
|
238
246
|
end
|
data/lib/email_address/config.rb
CHANGED
@@ -84,6 +84,7 @@ module EmailAddress
|
|
84
84
|
# The value is an array of match tokens.
|
85
85
|
# * host_match: %w(.org example.com hotmail. user*@ sub.*.com)
|
86
86
|
# * exchanger_match: %w(google.com 127.0.0.1 10.9.8.0/24 ::1/64)
|
87
|
+
#
|
87
88
|
|
88
89
|
class Config
|
89
90
|
@config = {
|
@@ -132,6 +133,15 @@ module EmailAddress
|
|
132
133
|
},
|
133
134
|
}
|
134
135
|
|
136
|
+
@errors = {
|
137
|
+
invalid_address: "Invalid Email Address",
|
138
|
+
invalid_mailbox: "Invalid Recipient/Mailbox",
|
139
|
+
invalid_host: "Invalid Host/Domain Name",
|
140
|
+
exceeds_size: "Address too long",
|
141
|
+
not_allowed: "Address is not allowed",
|
142
|
+
incomplete_domain: "Domain name is incomplete",
|
143
|
+
}
|
144
|
+
|
135
145
|
# Set multiple default configuration settings
|
136
146
|
def self.configure(config={})
|
137
147
|
@config.merge!(config)
|
@@ -158,6 +168,12 @@ module EmailAddress
|
|
158
168
|
@providers[name]
|
159
169
|
end
|
160
170
|
|
171
|
+
# Customize your own error message text.
|
172
|
+
def self.error_messages(hash=nil)
|
173
|
+
@errors = @errors.merge(hash) if hash
|
174
|
+
@errors
|
175
|
+
end
|
176
|
+
|
161
177
|
def self.all_settings(*configs)
|
162
178
|
config = @config.clone
|
163
179
|
configs.each {|c| config.merge!(c) }
|
@@ -33,7 +33,7 @@ module EmailAddress
|
|
33
33
|
|
34
34
|
# Returns the provider name based on the MX-er host names, or nil if not matched
|
35
35
|
def provider
|
36
|
-
return @provider if @provider
|
36
|
+
return @provider if defined? @provider
|
37
37
|
EmailAddress::Config.providers.each do |provider, config|
|
38
38
|
if config[:exchanger_match] && self.matches?(config[:exchanger_match])
|
39
39
|
return @provider = provider
|
@@ -47,10 +47,15 @@ module EmailAddress
|
|
47
47
|
def mxers
|
48
48
|
@mxers ||= Resolv::DNS.open do |dns|
|
49
49
|
ress = dns.getresources(@host, Resolv::DNS::Resource::IN::MX)
|
50
|
-
ress.map
|
50
|
+
records = ress.map do |r|
|
51
|
+
begin
|
52
|
+
[r.exchange.to_s, IPSocket::getaddress(r.exchange.to_s), r.preference]
|
53
|
+
rescue SocketError # not found, but could also mean network not work or it could mean one record doesn't resolve an address
|
54
|
+
nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
records.compact
|
51
58
|
end
|
52
|
-
rescue SocketError # not found, but could also mean network not work
|
53
|
-
@_dns_a_record ||= []
|
54
59
|
end
|
55
60
|
|
56
61
|
# Returns Array of domain names for the MX'ers, used to determine the Provider
|
data/lib/email_address/host.rb
CHANGED
@@ -30,7 +30,8 @@ module EmailAddress
|
|
30
30
|
# that provider.
|
31
31
|
##############################################################################
|
32
32
|
class Host
|
33
|
-
|
33
|
+
attr_reader :host_name
|
34
|
+
attr_accessor :dns_name, :domain_name, :registration_name,
|
34
35
|
:tld, :tld2, :subdomains, :ip_address, :config, :provider,
|
35
36
|
:comment
|
36
37
|
MAX_HOST_LENGTH = 255
|
data/lib/email_address/local.rb
CHANGED
@@ -65,7 +65,8 @@ module EmailAddress
|
|
65
65
|
# [CFWS]
|
66
66
|
############################################################################
|
67
67
|
class Local
|
68
|
-
|
68
|
+
attr_reader :local
|
69
|
+
attr_accessor :mailbox, :comment, :tag, :config, :original
|
69
70
|
attr_accessor :syntax
|
70
71
|
|
71
72
|
# RFC-2142: MAILBOX NAMES FOR COMMON SERVICES, ROLES AND FUNCTIONS
|
@@ -79,19 +80,20 @@ module EmailAddress
|
|
79
80
|
STANDARD_MAX_SIZE = 64
|
80
81
|
|
81
82
|
# Conventional : word([.-+'_]word)*
|
82
|
-
CONVENTIONAL_MAILBOX_REGEX = /\A [\p{L}\p{N}]+ ( [\.\-\+\'_] [\p{L}\p{N}]+ )* \z/x
|
83
|
-
CONVENTIONAL_MAILBOX_WITHIN = /[\p{L}\p{N}]+ ( [\.\-\+\'_] [\p{L}\p{N}]+ )*/x
|
83
|
+
CONVENTIONAL_MAILBOX_REGEX = /\A [\p{L}\p{N}]+ (?: [\.\-\+\'_] [\p{L}\p{N}]+ )* \z/x
|
84
|
+
CONVENTIONAL_MAILBOX_WITHIN = /[\p{L}\p{N}]+ (?: [\.\-\+\'_] [\p{L}\p{N}]+ )*/x
|
84
85
|
|
85
86
|
# Relaxed: same characters, relaxed order
|
86
|
-
|
87
|
+
RELAXED_MAILBOX_WITHIN = /[\p{L}\p{N}]+ (?: [\.\-\+\'_]+ [\p{L}\p{N}]+ )*/x
|
88
|
+
RELAXED_MAILBOX_REGEX = /\A [\p{L}\p{N}]+ (?: [\.\-\+\'_]+ [\p{L}\p{N}]+ )* \z/x
|
87
89
|
|
88
90
|
# RFC5322 Token: token."token".token (dot-separated tokens)
|
89
91
|
# Quoted Token can also have: SPACE \" \\ ( ) , : ; < > @ [ \ ] .
|
90
92
|
STANDARD_LOCAL_WITHIN = /
|
91
|
-
( [\p{L}\p{N}\!\#\$\%\&\'\*\+\-\/\=\?\^\_\`\{\|\}\~\(\)]+
|
92
|
-
| \" ( \\[\" \\] | [\x20 \! \x23-\x5B \x5D-\x7E \p{L} \p{N}] )+ \" )
|
93
|
-
( \. ( [\p{L}\p{N}\!\#\$\%\&\'\*\+\-\/\=\?\^\_\`\{\|\}\~\(\)]+
|
94
|
-
| \" ( \\[\" \\] | [\x20 \! \x23-\x5B \x5D-\x7E \p{L} \p{N}] )+ \" ) )* /x
|
93
|
+
(?: [\p{L}\p{N}\!\#\$\%\&\'\*\+\-\/\=\?\^\_\`\{\|\}\~\(\)]+
|
94
|
+
| \" (?: \\[\" \\] | [\x20 \! \x23-\x5B \x5D-\x7E \p{L} \p{N}] )+ \" )
|
95
|
+
(?: \. (?: [\p{L}\p{N}\!\#\$\%\&\'\*\+\-\/\=\?\^\_\`\{\|\}\~\(\)]+
|
96
|
+
| \" (?: \\[\" \\] | [\x20 \! \x23-\x5B \x5D-\x7E \p{L} \p{N}] )+ \" ) )* /x
|
95
97
|
STANDARD_LOCAL_REGEX = /\A #{STANDARD_LOCAL_WITHIN} \z/x
|
96
98
|
|
97
99
|
REDACTED_REGEX = /\A \{ [0-9a-f]{40} \} \z/x # {sha1}
|
@@ -7,6 +7,7 @@ class TestAR < MiniTest::Test
|
|
7
7
|
def test_validation
|
8
8
|
user = User.new(email:"Pat.Jones+ASDF#GMAIL.com")
|
9
9
|
assert_equal false, user.valid?
|
10
|
+
assert user.errors.messages[:email].first
|
10
11
|
user = User.new(email:"Pat.Jones+ASDF@GMAIL.com")
|
11
12
|
assert_equal true, user.valid?
|
12
13
|
end
|
@@ -31,6 +31,7 @@ class TestAddress < Minitest::Test
|
|
31
31
|
assert_equal "user+tag@example.com", a.to_s
|
32
32
|
assert_equal "user@example.com", a.canonical
|
33
33
|
assert_equal "{63a710569261a24b3766275b7000ce8d7b32e2f7}@example.com", a.redact
|
34
|
+
assert_equal "{b58996c504c5638798eb6b511e6f49af}@example.com", a.redact(:md5)
|
34
35
|
assert_equal "b58996c504c5638798eb6b511e6f49af", a.reference
|
35
36
|
end
|
36
37
|
|
@@ -55,6 +56,14 @@ class TestAddress < Minitest::Test
|
|
55
56
|
assert_equal 'user*@gmail*', a.matches?('user*@gmail*')
|
56
57
|
end
|
57
58
|
|
59
|
+
def test_empty_address
|
60
|
+
a = EmailAddress.new("")
|
61
|
+
assert_equal "{da39a3ee5e6b4b0d3255bfef95601890afd80709}", a.redact
|
62
|
+
assert_equal "", a.to_s
|
63
|
+
assert_equal "", a.canonical
|
64
|
+
assert_equal "d41d8cd98f00b204e9800998ecf8427e", a.reference
|
65
|
+
end
|
66
|
+
|
58
67
|
# VALIDATION
|
59
68
|
def test_valid
|
60
69
|
assert EmailAddress.valid?("User+tag@example.com", dns_lookup: :a), "valid 1"
|
@@ -73,6 +82,7 @@ class TestAddress < Minitest::Test
|
|
73
82
|
assert "First.Last+TAG@example.com".match(EmailAddress::Address::STANDARD_REGEX)
|
74
83
|
assert_equal nil, "First.Last+TAGexample.com".match(EmailAddress::Address::STANDARD_REGEX)
|
75
84
|
assert_equal nil, "First#Last+TAGexample.com".match(EmailAddress::Address::CONVENTIONAL_REGEX)
|
85
|
+
assert "aasdf-34-.z@example.com".match(EmailAddress::Address::RELAXED_REGEX)
|
76
86
|
end
|
77
87
|
|
78
88
|
end
|
data/test/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: email_address
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Allen Fair
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-02-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -58,14 +58,14 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 5.0.
|
61
|
+
version: 5.0.1
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: 5.0.
|
68
|
+
version: 5.0.1
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: sqlite3
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -175,7 +175,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
175
175
|
version: '0'
|
176
176
|
requirements: []
|
177
177
|
rubyforge_project:
|
178
|
-
rubygems_version: 2.
|
178
|
+
rubygems_version: 2.6.8
|
179
179
|
signing_key:
|
180
180
|
specification_version: 4
|
181
181
|
summary: This gem provides a ruby language library for working with and validating
|