email_address 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +173 -45
- data/Rakefile +18 -0
- data/email_address.gemspec +1 -0
- data/lib/email_address.rb +27 -2
- data/lib/email_address/active_record_validator.rb +42 -0
- data/lib/email_address/address.rb +98 -11
- data/lib/email_address/config.rb +4 -0
- data/lib/email_address/domain_matcher.rb +12 -4
- data/lib/email_address/domain_parser.rb +0 -2
- data/lib/email_address/email_address_type.rb +62 -0
- data/lib/email_address/exchanger.rb +35 -3
- data/lib/email_address/host.rb +31 -4
- data/lib/email_address/local.rb +11 -5
- data/lib/email_address/matcher.rb +119 -0
- data/lib/email_address/validator.rb +18 -12
- data/lib/email_address/version.rb +1 -1
- data/test/email_address/test_address.rb +24 -2
- data/test/email_address/test_domain_matcher.rb +12 -6
- data/test/email_address/test_host.rb +7 -1
- data/test/email_address/test_local.rb +7 -3
- data/test/email_address/test_matcher.rb +44 -0
- data/test/test_email_address.rb +5 -0
- metadata +22 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 77e8fb26631e827d41da7eee015141495316f430
|
4
|
+
data.tar.gz: 0fa66926ca1dbbdc13154aae789850eec7e1eb75
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5d41c4ca234699c636811187718fc7b73fe52ff55256ebbeb3e32e518d3330c016a72c3185f9632be0cb4d612064e09408517f893927ddd23db9518d0c219066
|
7
|
+
data.tar.gz: 93c7fec757b3a635b39aea3bdc6a259ad5c69b0b5369d4ea48e41cde6009b406a8c2eec94a0bc1e42ed837b220a240d971079135e58455a57d0db5ad1e2df3dd
|
data/README.md
CHANGED
@@ -1,31 +1,152 @@
|
|
1
1
|
# Email Address
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
RFC
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
*
|
13
|
-
|
14
|
-
|
15
|
-
*
|
16
|
-
*
|
17
|
-
|
18
|
-
*
|
19
|
-
|
20
|
-
*
|
21
|
-
|
22
|
-
*
|
23
|
-
*
|
24
|
-
*
|
25
|
-
*
|
26
|
-
|
27
|
-
|
28
|
-
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/email_address.svg)](http://rubygems.org/gems/email_address)
|
4
|
+
|
5
|
+
The EmailAddress gem provides a structured datatype for email addresses
|
6
|
+
and pushes for an _opinionated_ model for which RFC patterns should be
|
7
|
+
accepted as a "best practice" and which should not be supported (in the
|
8
|
+
name of sanity).
|
9
|
+
|
10
|
+
This library provides:
|
11
|
+
|
12
|
+
* Email Address Validation
|
13
|
+
* Converting between email address forms
|
14
|
+
* **Original:** From the user or data source
|
15
|
+
* **Normalized:** A standardized format for identification
|
16
|
+
* **Canonical:** A format used to identify a unique user
|
17
|
+
* **Redacted:** A format used to store an email address privately
|
18
|
+
* **Reference:** Digest formats for sharing addresses without exposing
|
19
|
+
them.
|
20
|
+
* Matching addresses to Email/Internet Service Providers. Per-provider
|
21
|
+
rules for:
|
22
|
+
* Validation
|
23
|
+
* Address Tag formats
|
24
|
+
* Canonicalization
|
25
|
+
* Unicode Support
|
26
|
+
|
27
|
+
## Email Addresses: The Good Parts
|
28
|
+
|
29
|
+
Email Addresses are split into two parts: the `local` and `host` part,
|
30
|
+
separated by the `@` symbol, or of the generalized format:
|
31
|
+
|
32
|
+
mailbox+tag@subdomain.domain.tld
|
33
|
+
|
34
|
+
The **Mailbox** usually identifies the user, role account, or application.
|
35
|
+
A **Tag** is any suffix for the mailbox useful for separating and filtering
|
36
|
+
incoming email. It is usually preceded by a '+' or other character. Tags are
|
37
|
+
not always available for a given ESP or MTA.
|
38
|
+
|
39
|
+
Local Parts should consist of lower-case 7-bit ASCII alphanumeric and these characters:
|
40
|
+
`-+'.,` It should start with and end with an alphanumeric character and
|
41
|
+
no more than one special character should appear together.
|
42
|
+
|
43
|
+
Host parts contain a lower-case version of any standard domain name.
|
44
|
+
International Domain Names are allowed, and can be converted to
|
45
|
+
[Punycode](http://en.wikipedia.org/wiki/Punycode),
|
46
|
+
an encoding system of Unicode strings into the 7-bit ASCII character set.
|
47
|
+
Domain names should be configured with MX records in DNS to receive
|
48
|
+
email, though this is sometimes mis-configured and the A record can be
|
49
|
+
used as a backup.
|
50
|
+
|
51
|
+
This is the subset of the RFC Email Address specification that should be
|
52
|
+
used.
|
53
|
+
|
54
|
+
## Email Addresses: The Bad Parts
|
55
|
+
|
56
|
+
Email addresses are defined and redefined in a series of RFC standards.
|
57
|
+
Conforming to the full standards is not recommended for easily
|
58
|
+
identifying and supporting email addresses. Among these specification,
|
59
|
+
we reject are:
|
60
|
+
|
61
|
+
* Case-sensitive local parts: `First.Last@example.com`
|
62
|
+
* Spaces and Special Characters: `"():;<>@[\\]`
|
63
|
+
* Quoting and Escaping Requirements: `"first \"nickname\" last"@example.com`
|
64
|
+
* Comment Parts: `(comment)mailbox@example.com`
|
65
|
+
* IP and IPv6 addresses as hosts: `mailbox@[127.0.0.1]`
|
66
|
+
* Non-ASCII (7-bit) characters in the local part: `Pelé@example.com`
|
67
|
+
* Validation by regular expressions like:
|
68
|
+
```
|
69
|
+
(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*
|
70
|
+
| "(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]
|
71
|
+
| \\[\x01-\x09\x0b\x0c\x0e-\x7f])*")
|
72
|
+
@ (?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?
|
73
|
+
| \[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}
|
74
|
+
(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:
|
75
|
+
(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]
|
76
|
+
| \\[\x01-\x09\x0b\x0c\x0e-\x7f])+)
|
77
|
+
\])
|
78
|
+
```
|
79
|
+
|
80
|
+
## Internationalization
|
81
|
+
|
82
|
+
The industry is moving to support Unicode characters in the local part
|
83
|
+
of the email address. Currently, SMTP supports only 7-bit ASCII, but a
|
84
|
+
new `SMTPUTF8` standard is available, but not yet widely implemented.
|
85
|
+
To work properly, global Email systems must be converted to UTF-8
|
86
|
+
encoded databases and upgraded to the new email standards.
|
87
|
+
|
88
|
+
The problem with i18n email addresses is that support outside of the
|
89
|
+
given locale becomes hard to enter addresses on keyboards for another
|
90
|
+
locale. Because of this, internationalized local parts are not yet
|
91
|
+
supported by default. They are more likely to be erroneous.
|
92
|
+
|
93
|
+
Proper personal identity can still be provided using
|
94
|
+
[MIME Encoded-Words](http://en.wikipedia.org/wiki/MIME#Encoded-Word)
|
95
|
+
in Email headers.
|
96
|
+
|
97
|
+
## Email Addresses Forms
|
98
|
+
|
99
|
+
* The **original** email address is of the format given by the user.
|
100
|
+
* The **Normalized** address has:
|
101
|
+
* Lower-case the local and domain part
|
102
|
+
* Tags are kept as they are important for the user
|
103
|
+
* Remove comments and any "bad parts"
|
104
|
+
* This format is what should be used to identify the account.
|
105
|
+
* The **Canonical** form is used to uniquely identify the mailbox.
|
106
|
+
* Domains stored as punycode for IDN
|
107
|
+
* Address Tags removed
|
108
|
+
* Special characters removed (dots in gmail addresses are not
|
109
|
+
significant)
|
110
|
+
* Lower cased and "bad parts" removed
|
111
|
+
* Useful for locating a user who forgets registering with a tag or
|
112
|
+
with a "Bad part" in the email address.
|
113
|
+
* The **Redacted** format is used to store email address fingerprints
|
114
|
+
instead of the actual addresses:
|
115
|
+
* Format: sha1(canonical_address)@domain
|
116
|
+
* Given an email address, the record can be found
|
117
|
+
* Useful for treating email addresses as sensitive data and
|
118
|
+
complying with requests to remove the address from your database and
|
119
|
+
still maintain the state of the account.
|
120
|
+
* The **Reference** form allows you to publicly share an address without
|
121
|
+
revealing the actual address.
|
122
|
+
* Can be the MD5 or SHA1 of the normalized or canonical address
|
123
|
+
* Useful for "do not email" lists
|
124
|
+
* Useful for cookies that do not reveal the actual account
|
125
|
+
|
126
|
+
## Treating Email Addresses as Sensitive Data
|
127
|
+
|
128
|
+
Like Social Security and Credit Card Numbers, email addresses are
|
129
|
+
becoming more important as a personal identifier on the internet.
|
130
|
+
Increasingly, we should treat email addresses as sensitive data. If your
|
131
|
+
site/database becomes compromised by hackers, these email addresses can
|
132
|
+
be stolen and used to spam your users and to try to gain access to their
|
133
|
+
accounts. You should not be storing passwords in plain text; perhaps you
|
134
|
+
don't need to store email addresses un-encoded either.
|
135
|
+
|
136
|
+
Consider this: upon registration, store the redacted email address for
|
137
|
+
the user, and of course, the salted, encrypted password.
|
138
|
+
When the user logs in, compute the redacted email address from
|
139
|
+
the user-supplied one and look up the record. Store the original address
|
140
|
+
in the session for the user, which goes away when the user logs out.
|
141
|
+
|
142
|
+
Sometimes, users demand you strike their information from the database.
|
143
|
+
Instead of deleting their account, you can "redact" their email
|
144
|
+
address, retaining the state of the account to prevent future
|
145
|
+
access. Given the original email address again, the redacted account can
|
146
|
+
be identified if necessary.
|
147
|
+
|
148
|
+
Because of these use cases, the **redact** method on the email address
|
149
|
+
instance has been provided.
|
29
150
|
|
30
151
|
## Installation
|
31
152
|
|
@@ -49,6 +170,9 @@ EmailAddress:
|
|
49
170
|
email = EmailAddress.new("USER+tag@EXAMPLE.com")
|
50
171
|
email.normalize #=> "user+tag@example.com"
|
51
172
|
email.canonical #=> "user@example.com"
|
173
|
+
email.redact #=> "63a710569261a24b3766275b7000ce8d7b32e2f7@example.com"
|
174
|
+
email.sha1 #=> "63a710569261a24b3766275b7000ce8d7b32e2f7"
|
175
|
+
email.md5 #=> "dea073fb289e438a6d69c5384113454c"
|
52
176
|
|
53
177
|
Email Service Provider (ESP) specific edits can be created to provide
|
54
178
|
validations and canonical manipulations. A few are given out of the box.
|
@@ -56,7 +180,7 @@ Providers can be defined bu email domain match rules, or by match rules
|
|
56
180
|
for the MX host names using domains or CIDR addresses.
|
57
181
|
|
58
182
|
email = EmailAddress.new("First.Last+Tag@Gmail.Com")
|
59
|
-
email.provider #=> :
|
183
|
+
email.provider #=> :google
|
60
184
|
email.canonical #=> "firstlast@gmail.com"
|
61
185
|
|
62
186
|
Storing the canonical address with the request address (don't remove
|
@@ -71,32 +195,36 @@ You can inspect the MX (Mail Exchanger) records
|
|
71
195
|
You can see if it validates as an opinionated address:
|
72
196
|
|
73
197
|
email.valid? # Resonably valid?
|
198
|
+
email.errors #=> [:mx]
|
74
199
|
email.valid_host? # Host name is defined in DNS
|
75
200
|
email.strict? # Strictly valid?
|
76
201
|
|
77
|
-
|
78
|
-
format. This allows you to safely keep a record of the address and still
|
79
|
-
protect the account's privacy after it has been closed. Given an address
|
80
|
-
for inquiry, it can still look up a closed account.
|
202
|
+
You can compare email addresses:
|
81
203
|
|
82
|
-
|
83
|
-
|
204
|
+
e1 = EmailAddress.new("First.Last@Gmail.com")
|
205
|
+
e1.to_s #=> "first.last@gmail.com"
|
206
|
+
e2 = EmailAddress.new("FirstLast+tag@Gmail.com")
|
207
|
+
e3.to_s #=> "firstlast+tag@gmail.com"
|
208
|
+
e3 = EmailAddress.new(e2.redact)
|
209
|
+
e3.to_s #=> "554d32017ab3a7fcf51c88ffce078689003bc521@gmail.com"
|
84
210
|
|
211
|
+
e1 == e2 #=> false (Matches by normalized address)
|
212
|
+
e1.same_as?(e2) #=> true (Matches as canonical address)
|
213
|
+
e1.same_as?(e3) #=> true (Matches as redacted address)
|
214
|
+
e1 < e2 #=> true (Compares using normalized address)
|
85
215
|
|
86
|
-
##
|
216
|
+
## Host Inspection
|
87
217
|
|
88
|
-
The
|
89
|
-
parts from `(comment)mailbox+tag@subdomain.domain.tld`
|
218
|
+
The `EmailAddress::Host` can be used to inspect the email domain.
|
90
219
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
* Registration Name - host name without subdomain or TLD
|
220
|
+
```ruby
|
221
|
+
e1 = EmailAddress.new("First.Last@Gmail.com")
|
222
|
+
e1.host.name #=> "gmail.com"
|
223
|
+
e1.host.exchanger.mxers #=> [["alt4.gmail-smtp-in.l.google.com", "2a00:1450:400c:c01::1b", 30],...]
|
224
|
+
e1.host.exchanger.mx_ips #=> ["2a00:1450:400c:c01::1b", ...]
|
225
|
+
e1.host.matches?('.com') #=> true
|
226
|
+
e1.host.txt #=> "v=spf1 redirect=_spf.google.com"
|
227
|
+
```
|
100
228
|
|
101
229
|
## Domain Matching
|
102
230
|
|
data/Rakefile
CHANGED
@@ -3,3 +3,21 @@ require "bundler/gem_tasks"
|
|
3
3
|
task :default do
|
4
4
|
sh "ruby test/email_address/*"
|
5
5
|
end
|
6
|
+
require "bundler/gem_tasks"
|
7
|
+
require "bundler/setup"
|
8
|
+
require 'rake/testtask'
|
9
|
+
|
10
|
+
task :default => :test
|
11
|
+
|
12
|
+
desc "Run the Test Suite, toot suite"
|
13
|
+
task :test do
|
14
|
+
sh "find test -name 'test*rb' -exec ruby {} \\;"
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "Open and IRB Console with the gem loaded"
|
18
|
+
task :console do
|
19
|
+
sh "bundle exec irb -Ilib -I . -r email_address"
|
20
|
+
#require 'irb'
|
21
|
+
#ARGV.clear
|
22
|
+
#IRB.start
|
23
|
+
end
|
data/email_address.gemspec
CHANGED
@@ -20,6 +20,7 @@ validator.}
|
|
20
20
|
spec.require_paths = ["lib"]
|
21
21
|
|
22
22
|
spec.add_development_dependency "bundler", "~> 1.3"
|
23
|
+
spec.add_development_dependency "activemodel", "~> 4.2"
|
23
24
|
spec.add_development_dependency "rake"
|
24
25
|
spec.add_dependency "simpleidn"
|
25
26
|
spec.add_dependency "netaddr"
|
data/lib/email_address.rb
CHANGED
@@ -5,13 +5,38 @@ require "email_address/domain_parser"
|
|
5
5
|
require "email_address/exchanger"
|
6
6
|
require "email_address/host"
|
7
7
|
require "email_address/local"
|
8
|
+
require "email_address/matcher"
|
8
9
|
require "email_address/validator"
|
9
10
|
require "email_address/version"
|
11
|
+
require "email_address/active_record_validator" if defined?(ActiveModel)
|
10
12
|
|
11
13
|
module EmailAddress
|
12
14
|
|
13
|
-
|
14
|
-
|
15
|
+
# Creates an instance of this email address.
|
16
|
+
# This is a short-cut to Email::Address::Address.new
|
17
|
+
def self.new(email_address)
|
18
|
+
EmailAddress::Address.new(email_address)
|
15
19
|
end
|
16
20
|
|
21
|
+
# Given an email address, this return true if the email validates, false otherwise
|
22
|
+
def self.valid?(email_address, options={})
|
23
|
+
self.new(email_address).valid?(options)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Shortcut to normalize the given email address
|
27
|
+
def self.normal(email_address)
|
28
|
+
EmailAddress::Address.new(email_address).normalize
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.new_normal(email_address)
|
32
|
+
EmailAddress::Address.new(EmailAddress::Address.new(email_address).normalize)
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.canonical(email_address)
|
36
|
+
EmailAddress::Address.new(email_address).normalize
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.new_canonical(email_address)
|
40
|
+
EmailAddress::Address.new(EmailAddress::Address.new(email_address).canonical)
|
41
|
+
end
|
17
42
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module EmailAddress
|
2
|
+
|
3
|
+
# ActiveRecord validator class for validating an email
|
4
|
+
# address with this library.
|
5
|
+
# Note the initialization happens once per process.
|
6
|
+
#
|
7
|
+
# Usage:
|
8
|
+
# validates_with EmailAddress::ActiveRecordValidator, field: :name
|
9
|
+
#
|
10
|
+
# Options:
|
11
|
+
# field: email,
|
12
|
+
# fields: [:email1, :email2]
|
13
|
+
# Default field:
|
14
|
+
# :email or :email_address (first found)
|
15
|
+
#
|
16
|
+
class ActiveRecordValidator < ActiveModel::Validator
|
17
|
+
|
18
|
+
def initialize(options={})
|
19
|
+
@opt = options
|
20
|
+
end
|
21
|
+
|
22
|
+
def validate(r)
|
23
|
+
if @opt[:fields]
|
24
|
+
@opt[:fields].each {|f| validate_email(r, f) }
|
25
|
+
elsif @opt[:field]
|
26
|
+
validate_email(r, opt[:field])
|
27
|
+
elsif r.respond_to? :email
|
28
|
+
validate_email(r, :email)
|
29
|
+
elsif r.respond_to? :email_address
|
30
|
+
validate_email(r, :email_address)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def validate_email(r,f)
|
35
|
+
return if r[f].nil?
|
36
|
+
e = EmailAddress.new(r[f])
|
37
|
+
r.errors[f] << "Email Address Not Valid" unless e.valid?
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -3,72 +3,159 @@ require 'digest/md5'
|
|
3
3
|
|
4
4
|
module EmailAddress
|
5
5
|
class Address
|
6
|
+
include Comparable
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
@
|
15
|
-
@local = EmailAddress::Local.new(local, @host.provider)
|
8
|
+
# Given an email address of the form "local@hostname", this sets up the
|
9
|
+
# instance, and initializes the address to the "normalized" format of the
|
10
|
+
# address. The original string is available in the #original method.
|
11
|
+
def initialize(email_address)
|
12
|
+
@original = email_address
|
13
|
+
(local, host) = email_address.split('@', 2)
|
14
|
+
@host = EmailAddress::Host.new(host)
|
15
|
+
@local = EmailAddress::Local.new(local||@address, @host)
|
16
16
|
end
|
17
17
|
|
18
|
+
# Returns the Email::Address::Host to inspect the host name of the address
|
18
19
|
def host
|
19
20
|
@host
|
20
21
|
end
|
21
22
|
|
23
|
+
# Returns the EmailAddress::local to inspect the data to the left of the @
|
24
|
+
# Use the #left method to access the full string
|
22
25
|
def local
|
23
26
|
@local
|
24
27
|
end
|
25
28
|
|
29
|
+
# Everything to the left of the @ in the address, called the local part.
|
30
|
+
def left
|
31
|
+
local.to_s
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns the mailbox portion of the local port, with no tags. Usually, this
|
35
|
+
# can be considered the user account or role account names. Some systems
|
36
|
+
# employ dynamic email addresses which don't have the same meaning.
|
26
37
|
def mailbox
|
27
38
|
@local.mailbox
|
28
39
|
end
|
29
40
|
|
41
|
+
# Returns the host name, the part to the right of the @ sign.
|
30
42
|
def host_name
|
31
43
|
@host.host_name
|
32
44
|
end
|
45
|
+
alias :right :host_name
|
33
46
|
|
47
|
+
# Returns the tag part of the local address, or nil if not given.
|
34
48
|
def tag
|
35
49
|
@local.tag
|
36
50
|
end
|
37
51
|
|
52
|
+
# Retuns any comments parsed from the local part of the email address.
|
53
|
+
# This is retained for inspection after construction, even if it is
|
54
|
+
# removed from the normalized email address.
|
38
55
|
def comment
|
39
56
|
@local.comment
|
40
57
|
end
|
41
58
|
|
59
|
+
# Returns the ESP (Email Service Provider) or ISP name derived
|
60
|
+
# using the provider configuration rules.
|
42
61
|
def provider
|
43
62
|
@host.provider
|
44
63
|
end
|
45
64
|
|
65
|
+
# Returns the string representation of the normalized email address.
|
46
66
|
def to_s
|
47
67
|
normalize
|
48
68
|
end
|
49
69
|
|
50
|
-
|
70
|
+
# The original email address in the request (unmodified).
|
71
|
+
def original
|
72
|
+
@original
|
73
|
+
end
|
74
|
+
|
75
|
+
# Returns the normailed email address according to the provider
|
76
|
+
# and system normalization rules. Ususally this downcases the address,
|
77
|
+
# removes spaces and comments, but includes any tags.
|
78
|
+
def normal
|
51
79
|
[@local.normalize, @host.normalize].join('@')
|
52
80
|
end
|
81
|
+
alias :normalize :normal
|
53
82
|
|
83
|
+
# Returns the canonical email address according to the provider
|
84
|
+
# uniqueness rules. Usually, this downcases the address, removes
|
85
|
+
# spaves and comments and tags, and any extraneous part of the address
|
86
|
+
# not considered a unique account by the provider.
|
54
87
|
def canonical
|
55
88
|
[@local.canonical, @host.canonical].join('@')
|
56
89
|
end
|
90
|
+
alias :uniq :canonical
|
91
|
+
alias :canonicalize :canonical
|
57
92
|
|
93
|
+
# Returns and MD5 of the canonical address form. Some cross-system systems
|
94
|
+
# use the email address MD5 instead of the actual address to refer to the
|
95
|
+
# same shared user identity without exposing the actual address when it
|
96
|
+
# is not known in common.
|
58
97
|
def md5
|
59
98
|
Digest::MD5.hexdigest(canonical)
|
60
99
|
end
|
61
100
|
|
101
|
+
def canonical_md5
|
102
|
+
Digest::MD5.hexdigest(self.canonical)
|
103
|
+
end
|
104
|
+
|
105
|
+
# This returns the SHA1 digest (in a hex string) of the canonical email
|
106
|
+
# address. See #md5 for more background.
|
62
107
|
def sha1
|
63
108
|
Digest::SHA1.hexdigest(canonical)
|
64
109
|
end
|
65
110
|
|
66
|
-
|
111
|
+
# Equal matches the normalized version of each address. Use the Threequal to check
|
112
|
+
# for match on canonical or redacted versions of addresses
|
113
|
+
def ==(other_email)
|
114
|
+
normalize == other_email.normalize
|
115
|
+
end
|
116
|
+
alias :eql? :==
|
117
|
+
alias :equal? :==
|
118
|
+
|
119
|
+
# Return the <=> or CMP comparison operator result (-1, 0, +1) on the comparison
|
120
|
+
# of this addres with another, using the canonical or redacted forms.
|
121
|
+
def same_as?(other_email)
|
122
|
+
canonical == other_email.canonical ||
|
123
|
+
redact == other_email.canonical || canonical == other_email.redact
|
124
|
+
end
|
125
|
+
alias :include? :same_as?
|
126
|
+
|
127
|
+
# Return the <=> or CMP comparison operator result (-1, 0, +1) on the comparison
|
128
|
+
# of this addres with another, using the normalized form.
|
129
|
+
def <=>(other_email)
|
130
|
+
normalize <=> other_email.normalize
|
131
|
+
end
|
132
|
+
|
133
|
+
# Redact the address for storage. To protect the user's privacy,
|
134
|
+
# use this when you don't want to store a real email, only a fingerprint.
|
135
|
+
# Given the original address, you can match the original with this method.
|
136
|
+
# This returns the SHA1 of the canonical address (no tags, no gmail dots)
|
137
|
+
# at the original host. The host is part of the digest part, but also
|
138
|
+
# retained for verification and domain maintenance.
|
139
|
+
def redact
|
67
140
|
[sha1, @host.canonical].join('@')
|
68
141
|
end
|
69
142
|
|
143
|
+
def redacted?
|
144
|
+
@local.to_s =~ /\A[0-9a-f]{40}\z/ ? true : false
|
145
|
+
end
|
146
|
+
|
147
|
+
# Returns true if this address is considered valid according to the format
|
148
|
+
# configured for its provider, It test the normalized form.
|
70
149
|
def valid?(options={})
|
71
150
|
EmailAddress::Validator.validate(self, options)
|
72
151
|
end
|
152
|
+
|
153
|
+
# Returns an array of error messages generated from the validation process via
|
154
|
+
# the #valid? method.
|
155
|
+
def errors(options={})
|
156
|
+
v = EmailAddress::Validator.new(self, options)
|
157
|
+
v.valid?
|
158
|
+
v.errors
|
159
|
+
end
|
73
160
|
end
|
74
161
|
end
|