email_address 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 225554c1b1abf4ead2900a7209b55ec7babeaefe
4
+ data.tar.gz: 4de853f82e80be5e9e6ce843a16fd2dc3f4a3255
5
+ SHA512:
6
+ metadata.gz: 11b73a88096b1553dac4aa7979a1a7385c568ae8d5c2dca268c88bf1fd2827aa77bb76e3a412a6cc85d1f5d9983f470a1a6b1840df73d428fbfdae76277bfe9a
7
+ data.tar.gz: 97b3f3497d00346404b94dbfde915bbbcf1dae41bcbc6d7fc98a4e0dc0072245b57597ca4939295e6db47fc2700ed935add5f8019519389950288f2501ad7bc6
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in email_address.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Allen Fair
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # EmailAddress
2
+
3
+ The EmailAddress library is an _opinionated_ email address handler and
4
+ validator.
5
+
6
+ So you have an email address input by a user. Do you want to validate
7
+ it, check it for uniqueness, or mine statistics on all your addresses?
8
+ Then the email_address gem is for you!
9
+
10
+ Opininated? Yes. By default, this does not support RFC822 specification
11
+ because it allows addresses that should never exist for real people.
12
+ By limiting email addresses to a subset of standardly used ones, you can
13
+ remove false positives in validation, and help the email community
14
+ evolve into a friendlier place. I like standards as much as the next
15
+ person, but enough is enough!
16
+
17
+ Why my opinion? I've been working with email and such since the late
18
+ 1990's. I would like to see modern practices applied to email addresses.
19
+ These rules really do apply to most (without real statistics, I'll claim
20
+ 99.9+%) usage in the 21st Century. They may not be for everyone, you may
21
+ need strict adherence to these standards for historical reasons, but I
22
+ bet you'll wish you could support this one instead!
23
+
24
+ ## Installation
25
+
26
+ Add this line to your application's Gemfile:
27
+
28
+ gem 'email_address'
29
+
30
+ And then execute:
31
+
32
+ $ bundle
33
+
34
+ Or install it yourself as:
35
+
36
+ $ gem install email_address
37
+
38
+ ## Usage
39
+
40
+ Inspect your email address string by creating an instance of
41
+ EmailAddress:
42
+
43
+ email = EmailAddress.new("user@example.com")
44
+
45
+ You can see if it validates as an opinionated address:
46
+
47
+ email.valid?
48
+
49
+ This runs the following checks you can do yourself:
50
+
51
+ email.valid_format?
52
+ email.valid_domain?
53
+ email.valid_user?
54
+ email.disposable_email?
55
+ email.spam_trap?
56
+
57
+ Of course, the last couple tests can't be published, so you can provide
58
+ a callback to check them yourself if you need.
59
+
60
+
61
+ ## Contributing
62
+
63
+ 1. Fork it
64
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
65
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
66
+ 4. Push to the branch (`git push origin my-new-feature`)
67
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ task :default do
4
+ sh "ruby test/email_address/*"
5
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'email_address/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "email_address"
8
+ spec.version = EmailAddress::VERSION
9
+ spec.authors = ["Allen Fair"]
10
+ spec.email = ["allen.fair@gmail.com"]
11
+ spec.description = %q{The EmailAddress library is an _opinionated_ email address handler and
12
+ validator.}
13
+ spec.summary = %q{EmailAddress checks on validates an acceptable set of email addresses.}
14
+ spec.homepage = "https://github.com/afair/email_address"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files`.split($/)
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.3"
23
+ spec.add_development_dependency "rake"
24
+ spec.add_development_dependency "simpleidn"
25
+ end
@@ -0,0 +1,145 @@
1
+ #require "email_address/version"
2
+ #require "email_address/config"
3
+ #require "email_address/host"
4
+ #require "email_address/address"
5
+ require 'simpleidn'
6
+
7
+ class EmailAddress
8
+ attr_reader :address, :local, :account, :tag, :comment,
9
+ :domain, :subdomains, :domain_name, :base_domain, :top_level_domain
10
+
11
+ def initialize(email)
12
+ self.address = email
13
+ end
14
+
15
+ ##############################################################################
16
+ # Basic email address: local@domain
17
+ # Only supporting FQDN's (Fully Qualified Domain Names)?
18
+ # Length: Up to 254 characters
19
+ ##############################################################################
20
+ def address=(email)
21
+ @address = email.strip
22
+ (local_name, domain_name) = @address.split('@')
23
+ self.domain = domain_name
24
+ self.local = local_name
25
+ @address
26
+ end
27
+
28
+ def to_s
29
+ [local, domain].join('@')
30
+ end
31
+
32
+ ##############################################################################
33
+ # Domain Parsing
34
+ # Parts: subdomains.basedomain.top-level-domain
35
+ # IPv6/IPv6: [128.0.0.1], [IPv6:2001:db8:1ff::a0b:dbd0]
36
+ # Comments: (comment)example.com, example.com(comment)
37
+ # Internationalized: Unicode to Punycode
38
+ # Length: up to 255 characters
39
+ ##############################################################################
40
+ def domain=(host_name)
41
+ host_name ||= ''
42
+ @domain = host_name.strip.downcase
43
+ parse_domain
44
+ @domain
45
+ end
46
+
47
+ def parse_domain
48
+ @subdomains = @base_domain = @domain_name = @top_level_domain = ''
49
+ # Patterns: *.com, *.xx.cc, *.cc
50
+ if @domain =~ /\A(.+)\.(\w{3,10})\z/ || @domain =~ /\A(.+)\.(\w{1,3}\.\w\w)\z/ || @domain =~ /\A(.+)\.(\w\w)\z/
51
+ @top_level_domain = $2;
52
+ sld = $1 # Second level domain
53
+ if sld =~ /\A(.+)\.(.+)\z/ # is subdomain? sub.example [.tld]
54
+ @subdomains = $1
55
+ @base_domain = $2
56
+ else
57
+ @subdomains = ""
58
+ @base_domain = sld
59
+ end
60
+ @domain_name = @base_domain + '.' + @top_level_domain
61
+ end
62
+ end
63
+
64
+ def dns_hostname
65
+ @dns_hostname ||= SimpleIDN.to_ascii(domain)
66
+ end
67
+
68
+ ##############################################################################
69
+ # Parsing id provider-dependent, but RFC allows:
70
+ # A-Z a-z 0-9 . ! # $ % ' * + - / = ? ^ _ { | } ~
71
+ # Quoted: space ( ) , : ; < > @ [ ]
72
+ # Quoted-Backslash-Escaped: \ "
73
+ # Quote local part or dot-separated sub-parts x."y".z
74
+ # (comment)mailbox | mailbox(comment)
75
+ # 8-bit/UTF-8: allowed but mail-system defined
76
+ # RFC 5321 also warns that "a host that expects to receive mail SHOULD avoid defining mailboxes where the Local-part requires (or uses) the Quoted-string form".
77
+ # Postmaster: must always be case-insensitive
78
+ # Case: sensitive, but usually treated as equivalent
79
+ # Local Parts: comment, account tag
80
+ # Length: upt o 64 cgaracters
81
+ ##############################################################################
82
+ def local=(local)
83
+ local ||= ''
84
+ @local = local.strip.downcase
85
+ @account = parse_comment(@local)
86
+ (@account, @tag) = @account.split(tag_separator)
87
+ @tag ||= ''
88
+
89
+ @local
90
+ end
91
+
92
+ def parse_comment(local)
93
+ if local =~ /\A\((.+?)\)(.+)\z/
94
+ (@comment, local) = [$1, $2]
95
+ elsif @local =~ /\A(.+)\((.+?)\)\z/
96
+ (@comment, local) = [$1, $2]
97
+ else
98
+ @comment = '';
99
+ end
100
+ local
101
+ end
102
+
103
+ ##############################################################################
104
+ # Provider-Specific Settings
105
+ ##############################################################################
106
+
107
+ def provider
108
+ # @provider ||= EmailProviders::Default.new
109
+ 'unknown'
110
+ end
111
+
112
+ def tag_separator
113
+ '+'
114
+ end
115
+
116
+ def case_sensitive_local
117
+ false
118
+ end
119
+
120
+ # Returns the unique address as simplified account@hostname
121
+ def unique_address
122
+ "#{account}@#{dns_hostname}".downcase
123
+ end
124
+
125
+ # Letters, numbers, period (no start) 6-30chars
126
+ def user_pattern
127
+ /\A[a-z0-9][\.a-z0-9]{0,29}\z/i
128
+ end
129
+
130
+ ##############################################################################
131
+ # Validations -- Eventually a provider-sepecific check
132
+ ##############################################################################
133
+ def valid?
134
+ return false unless @local =~ user_pattern
135
+ return false unless provider # .valid_domain
136
+ true
137
+ end
138
+
139
+ def valid_format?
140
+ return false unless @local.match(user_pattern)
141
+ return false unless @host.valid_format?
142
+ true
143
+ end
144
+
145
+ end
@@ -0,0 +1,4 @@
1
+ module EmailAddress
2
+ class Address
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module EmailAddress
2
+ class Config
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module EmailAddress
2
+ class Esp
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module EmailAddress
2
+ class Host
3
+ def domain
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,4 @@
1
+ module EmailAddress
2
+ class Local
3
+ end
4
+ end
@@ -0,0 +1,8 @@
1
+ module EmailAddress
2
+ module Providers
3
+ class Default
4
+ def initialize(local, host)
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module EmailAddress
2
+ module Providers
3
+ class Google < EmailAddress::Providers::Default
4
+ def initialize(local, host)
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,3 @@
1
+ module EmailAddress
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,102 @@
1
+ module EmailAddress
2
+
3
+ # EmailAddress::Address - Inspects a Email Address.
4
+ #
5
+ # * hostname - Everything to the rigth of the @
6
+ # * mailbox - Everything to the left of the @
7
+ #
8
+ class Address
9
+ attr_reader :address, :mailbox, :host, :account, :tags
10
+
11
+ def initialize(mailbox, host_object)
12
+ self.mailbox = mailbox
13
+ @host = host_object
14
+ end
15
+
16
+ def mailbox=(mailbox)
17
+ @mailbox = mailbox.strip.downcase
18
+ (@account, @tags) = @mailbox.split(tag_separator)
19
+ @mailbox
20
+ end
21
+
22
+ def address=(address)
23
+ @address = address.strip
24
+ (mailbox_name, host_name) = @address.split(/\@/)
25
+ return unless host_part
26
+
27
+ @mailbox_name = mailbox_name
28
+ @host = EmailAddress::Host.new(host_part)
29
+ @mailbox = host.provider_mailbox(mailbox_part, @host)
30
+ @address
31
+ end
32
+
33
+ def provider
34
+ 'unknown'
35
+ end
36
+
37
+ def tag_separator
38
+ '+'
39
+ end
40
+
41
+ def case_sensitive_mailbox
42
+ false
43
+ end
44
+
45
+ # Letters, numbers, period (no start) 6-30chars
46
+ def user_pattern
47
+ /\A[a-z0-9][\.a-z0-9]{5,29}\z/i
48
+ end
49
+
50
+ # Returns the unique address as simplified account@hostname
51
+ def unique_address
52
+ "#{account}@#{dns_hostname}"
53
+ end
54
+
55
+ def valid?
56
+ return false unless @mailbox.valid?
57
+ return false unless @host.valid?
58
+ true
59
+ end
60
+
61
+ def valid_format?
62
+ return false unless @mailbox.match(user_pattern)
63
+ return false unless @host.valid_format?
64
+ true
65
+ end
66
+
67
+ ############################################################################
68
+ # Host Deletation: domain parts
69
+ ############################################################################
70
+
71
+ # Returns the fully-qualified host name (everything to the right of the @).
72
+ def hostname
73
+ host.host
74
+ end
75
+
76
+ # Returns the host without any subdomains (domain.tld(
77
+ def domain_name
78
+ host.domain_name
79
+ end
80
+
81
+ # Returns the Top-Level-Domain parts (after domain): com, co.jp
82
+ def tld
83
+ host.tld
84
+ end
85
+
86
+ # Returns the registration name without subdomains or TLD.
87
+ def base_domain
88
+ host.base_domain
89
+ end
90
+
91
+ # Returns any given subdomains of the domain name
92
+ def subdomains
93
+ host.subdomains
94
+ end
95
+
96
+ # Returns an ASCII name for DNS lookup, Punycode for Unicode domains.
97
+ def dns_hostname
98
+ host.dns_hostname
99
+ end
100
+
101
+ end
102
+ end
@@ -0,0 +1,36 @@
1
+ module EmailAddress
2
+ class Config
3
+
4
+ # EmailAddress::Config.add_provider(:google, domain_names:["gmail.com", "googlemail.com", "google.com"])
5
+ def self.add_provider(provider, matches={})
6
+ @pmatch ||= []
7
+ @pmatch << matches
8
+ end
9
+
10
+ # EmailAddress::Config.config do .... end
11
+ def self.setup(&block)
12
+ @config = Config::DSL.new(&block)
13
+ @config.instance_eval(&block)
14
+ end
15
+
16
+ def self.get
17
+ @config
18
+ end
19
+
20
+ class DSL
21
+ attr_accessor :provider_matching_rules
22
+ def add_provider(provider, matches={})
23
+ puts provider, matches
24
+ @provider_matching_rules ||= []
25
+ @provider_matching_rules << {provider:provider, matches:matches}
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+
32
+ #EmailAddress::Config.setup do
33
+ # add_provider :google, domain_names:["gmail.com", "googlemail.com", "google.com"]
34
+ #end
35
+
36
+ #puts EmailAddress::Config.provider_matching_rules
@@ -0,0 +1,17 @@
1
+ module EmailAddress
2
+
3
+ # EmailAddress::Address - Inspects a Email Address.
4
+ #
5
+ # Format: mailbox@hostname
6
+ #
7
+ class Factory
8
+ attr_reader :mailbox, :host
9
+
10
+ def address(address)
11
+ (@mailbox, host) = address.strip.split(/\@/)
12
+ return unless host
13
+ @host = EmailAddress::Host.new(host)
14
+ @mailbox = @host.provider_address(@mailbox)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,87 @@
1
+ # EmailAddress::Address
2
+ # EmailAddress::Host
3
+ # EmailAddress::MailExchanger
4
+ # EmailAddress::Config
5
+ # EmailAddress::EspMapping
6
+ # EmailAddress::Esp::Base, Yahoo, Msn, ...
7
+
8
+ require 'simpleidn'
9
+
10
+ module EmailAddress
11
+
12
+ # EmailAddress::Host handles mail host properties of an email address
13
+ # The host is typically the data to the right of the @ in the address
14
+ # and consists of:
15
+ #
16
+ # * hostname - full name of DNS host with the MX record.
17
+ # * domain_name - generally, the name and TLD without subdomain
18
+ # * base_domain - the identity name of the domain name, without the tld
19
+ # * tld (top-level-domain), like .com, .co.jp, .com.xx, etc.
20
+ # * subdomain - optional name of server/service under the domain
21
+ # * esp (email service provider) a name of the provider: yahoo, msn, etc.
22
+ # * dns_hostname - Converted hostname Unicode to Punycode
23
+
24
+ class Host
25
+ attr_reader :host, :domain_name, :tld, :base_domain, :subdomains, :dns_hostname
26
+
27
+ def initialize(host)
28
+ host.gsub!(/\A.*@/, '')
29
+ host.downcase!
30
+ self.host = host
31
+ end
32
+
33
+ def address(mailbox)
34
+ # Determine EmailAddress::Provider::Xxxx
35
+ EmailAddress::Address.new(mailbox, self)
36
+ end
37
+
38
+ def host=(host)
39
+ @host = host
40
+ # Patterns: *.com, *.xx.cc, *.cc
41
+ if @host =~ /(.+)\.(\w{3,10})\z/ || @host =~ /(.+)\.(\w{1,3}\.\w\w)\z/ || @host =~ /(.+)\.(\w\w)\z/
42
+ @tld = $2;
43
+ sld = $1 # Second level domain
44
+ if @sld =~ /(.+)\.(.+)$/ # is subdomain?
45
+ @subdomains = $1
46
+ @base_domain = $2
47
+ else
48
+ @subdomains = ""
49
+ @base_domain = sld
50
+ end
51
+ @domain_name = @base_domain + '.' + @tld
52
+ @dns_hostname = SimpleIDN.to_ascii(@host)
53
+ @host
54
+ end
55
+ end
56
+
57
+ # Resets the host to the domain name, dropping any subdomain
58
+ def drop_subdomain!
59
+ self.hostname = domain_name
60
+ end
61
+
62
+ def valid?
63
+ return false unless valid_format?
64
+ return false unless valid_mx?
65
+ true
66
+ end
67
+
68
+ def valid_format?
69
+ Host.valid_format?(@host)
70
+ end
71
+
72
+ def self.valid_format?(host)
73
+ return false unless host.match(/\A([0-9a-z\-]{1,63}\.)+[a-z0-9\-]{2,15}\z/)
74
+ return false unless host.length <= 253
75
+ true
76
+ end
77
+
78
+ def valid_mx?
79
+ Host.valid_mx?(@host)
80
+ end
81
+
82
+ def self.valid_mx?(host)
83
+ true
84
+ end
85
+
86
+ end
87
+ end
@@ -0,0 +1,60 @@
1
+ require 'resolv'
2
+ require 'netaddr'
3
+ require 'socket'
4
+
5
+ class MailExchanger
6
+ cattr_accessor :domains, :lookups
7
+
8
+ def self.valid_mx?(domain)
9
+ dns_a_record_exists?(domain) || mxers(domain).size > 0
10
+ end
11
+
12
+ def self.dns_a_record_exists?(domain)
13
+ @dns_a_record ||= {}
14
+ @dns_a_record[domain] = false
15
+ if Socket.gethostbyname(domain)
16
+ return @dns_a_record[domain] = true
17
+ end
18
+ rescue SocketError # not found
19
+ @dns_a_record[domain] = false
20
+ end
21
+
22
+ # Returns DNS A record results for the domain as: [[domain, ip, 0],...]
23
+ def self.domain_hosts(domain)
24
+ @domain_hosts ||= {}
25
+ @domain_hosts[domain] = []
26
+ res = TCPSocket.gethostbyname(domain)
27
+ res = res.slice(3, res.size)
28
+ res.each { |r| @domain_hosts[domain] << [domain, r, 0] }
29
+ @domain_hosts[domain]
30
+
31
+ rescue SocketError
32
+ return []
33
+ end
34
+
35
+ # Returns: [["mta7.am0.yahoodns.net", "66.94.237.139", 1], ["mta5.am0.yahoodns.net", "67.195.168.230", 1], ["mta6.am0.yahoodns.net", "98.139.54.60", 1]]
36
+ # If not found, returns []
37
+ def self.mxers(domain)
38
+ @domains ||= {}
39
+ return @domains[domain] if @domains.key?(domain)
40
+ @lookups = @lookups ? @lookups + 1 : 1
41
+ mx = nil
42
+ mxs = Resolv::DNS.open do |dns|
43
+ ress = dns.getresources domain, Resolv::DNS::Resource::IN::MX
44
+ ress.map { |r| [r.exchange.to_s, IPSocket::getaddress(r.exchange.to_s), r.preference] }
45
+ end
46
+ @domains[domain] = mxs
47
+ end
48
+
49
+ # Returns an array of MX IP address (String) for the given email domain
50
+ def self.mx_ips(domain)
51
+ mxers(domain).map {|m| m[1] }
52
+ end
53
+
54
+ # Given a cidr (ip/bits) and ip address, returns true on match. Caches cidr object.
55
+ def self.in_cidr?(cidr, ip)
56
+ @cidrs ||= {}
57
+ @cidrs[cidr] ||= NetAddr::CIDR.create(cidr)
58
+ @cidrs[cidr].matches?(ip)
59
+ end
60
+ end
@@ -0,0 +1,44 @@
1
+ module EmailAddress
2
+
3
+ # EmailAddress::Mailbox - Left side of the @
4
+ #
5
+ # * mailbox - Everything to the left of the @
6
+ # * account - part of the mailbox typically sent to a user
7
+ # * tags - Address tags appended to the account for tracking
8
+ #
9
+ class Mailbox
10
+ attr_reader :mailbox, :account, :tags, :provider
11
+
12
+ def initialize(mailbox, mail_provider=nil)
13
+ @provider = mail_provider
14
+ @mailbox = mailbox
15
+ end
16
+
17
+ def to_s
18
+ @mailbox
19
+ end
20
+
21
+ def mailbox=(mailbox)
22
+ @mailbox = mailbox.strip.downcase
23
+ (@account, @tags) = @mailbox.split(@provider.tag_separator)
24
+ @mailbox
25
+ end
26
+
27
+ def valid?
28
+ return false unless provider.valid?(mailbox)
29
+ true
30
+ end
31
+
32
+ def valid_format?
33
+ return false unless provider.valid_format?(mailbox)
34
+ #return false unless @mailbox =~ /\A\w[\w\.\-\+\']*\z/
35
+ true
36
+ end
37
+
38
+ # Returns true if the email account is a standard reserved address
39
+ def reserved?
40
+ %Q(postmaster abuse).include?(account)
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,55 @@
1
+ module EmailAddress
2
+ module Providers
3
+ class Default
4
+ def initialize(address)
5
+ @mailbox = mailbox
6
+ end
7
+
8
+ def account(mailbox)
9
+ mailbox
10
+ end
11
+
12
+ def provider
13
+ 'default'
14
+ end
15
+
16
+ def tag_separator
17
+ '+'
18
+ end
19
+
20
+ def case_sensitive_mailbox
21
+ false
22
+ end
23
+
24
+ def max_domain_length
25
+ 253
26
+ end
27
+
28
+ def max_email_length
29
+ 254
30
+ end
31
+
32
+ # Letters, numbers, period (no start) 6-30chars
33
+ def max_mailbox_length
34
+ 64
35
+ end
36
+
37
+ # Letters, numbers, period (no start) 6-30chars
38
+ def user_pattern
39
+ /\A[a-z0-9][\.\'a-z0-9]{5,29}\z/i
40
+ end
41
+
42
+ def valid?
43
+ return false unless valid_format?
44
+ end
45
+
46
+ def valid_format?
47
+ return false if mailbox.length > max_mailbox_length
48
+ return false if address.length > max_email_length
49
+ return false unless mailbox.to_s.match(user_pattern)
50
+ true
51
+ end
52
+
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,27 @@
1
+ module EmailAddress
2
+ module Providers
3
+ class Google < Default
4
+ def account
5
+ account.gsub(/\./. '')
6
+ end
7
+
8
+ def provider
9
+ 'google'
10
+ end
11
+
12
+ def tag_separator
13
+ '+'
14
+ end
15
+
16
+ def case_sensitive_mailbox
17
+ false
18
+ end
19
+
20
+ # Letters, numbers, period (no start) 6-30chars
21
+ def user_pattern
22
+ /\A[a-z0-9][\.a-z0-9]{5,29}\z/i
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,3 @@
1
+ module EmailAddress
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,52 @@
1
+ # encoding: UTF-8
2
+ require_relative 'test_helper'
3
+
4
+ class TestEmailAddress < MiniTest::Unit::TestCase
5
+ def test_address
6
+ a = EmailAddress.new('user@example.com')
7
+ assert_equal a.local, 'user'
8
+ assert_equal a.tag, ''
9
+ assert_equal a.comment, ''
10
+ assert_equal a.domain, 'example.com'
11
+ assert_equal a.subdomains, ''
12
+ assert_equal a.base_domain, 'example'
13
+ assert_equal a.dns_hostname, 'example.com'
14
+ assert_equal a.top_level_domain, 'com'
15
+ assert_equal a.to_s, 'user@example.com'
16
+ end
17
+
18
+ def test_foreign_address
19
+ a = EmailAddress.new("user@sub.example.co.jp")
20
+ assert_equal a.domain, "sub.example.co.jp"
21
+ assert_equal a.subdomains, "sub"
22
+ assert_equal a.domain_name, "example.co.jp"
23
+ assert_equal a.base_domain, "example"
24
+ assert_equal a.top_level_domain, "co.jp"
25
+ end
26
+
27
+ def test_address_tag
28
+ a = EmailAddress.new('user+etc@example.com')
29
+ assert_equal a.account, 'user'
30
+ assert_equal a.tag, 'etc'
31
+ assert_equal a.unique_address, 'user@example.com'
32
+ end
33
+
34
+ def test_address_comment
35
+ a = EmailAddress.new('(comment)user@example.com')
36
+ assert_equal a.comment, 'comment'
37
+ assert_equal a.account, 'user'
38
+ assert_equal a.unique_address, 'user@example.com'
39
+ end
40
+
41
+ def test_user_validation
42
+ a = EmailAddress.new("user@example.co.jp")
43
+ assert a.valid? == true
44
+ end
45
+
46
+ def test_unicode_domain
47
+ a = EmailAddress.new("User@København.eu")
48
+ assert_equal a.dns_hostname, 'xn--kbenhavn-54a.eu'
49
+ assert_equal a.unique_address, 'user@xn--kbenhavn-54a.eu'
50
+ end
51
+
52
+ end
@@ -0,0 +1,16 @@
1
+ #encoding: utf-8
2
+ require_relative '../test_helper'
3
+
4
+ class TestAddress < MiniTest::Unit::TestCase
5
+ def test_address
6
+ a = EmailAddress.new("User+tag@example.com")
7
+ assert_equal "user", a.account
8
+ assert_equal "user+tag", a.local
9
+ assert_equal "user@example.com", a.unique_address
10
+ end
11
+
12
+ def test_unicode_user
13
+ a = EmailAddress.new("å@example.com")
14
+ assert_equal false, a.valid_format?
15
+ end
16
+ end
@@ -0,0 +1,13 @@
1
+ #encoding: utf-8
2
+ require_relative '../test_helper'
3
+ #require 'minitest/autorun'
4
+
5
+ class TestConfig < MiniTest::Unit::TestCase
6
+ def test_config
7
+ EmailAddress::Config.setup do
8
+ add_provider :google, domain_names: %w(gmail.com googlemail.com google.com)
9
+ end
10
+ assert_equal EmailAddress::Config.get.provider_matching_rules.first[:provider], :google
11
+ end
12
+
13
+ end
@@ -0,0 +1,29 @@
1
+ # encoding: UTF-8
2
+ require_relative '../test_helper'
3
+
4
+
5
+ class TestHost < MiniTest::Unit::TestCase
6
+ def test_host
7
+ a = EmailAddress::Host.new("example.com")
8
+ assert_equal "example.com", a.host
9
+ assert_equal "example.com", a.domain_name
10
+ assert_equal "example", a.base_domain
11
+ assert_equal ".com", a.tld
12
+ assert_equal "", a.subdomains
13
+ end
14
+
15
+ def test_foreign_host
16
+ a = EmailAddress::Host.new("yahoo.co.jp")
17
+ assert_equal "yahoo.co.jp", a.host
18
+ assert_equal "yahoo.co.jp", a.domain_name
19
+ assert_equal "yahoo", a.base_domain
20
+ assert_equal "co.jp", a.tld
21
+ assert_equal "", a.subdomains
22
+ end
23
+
24
+ def test_unicode_host
25
+ a = EmailAddress::Host.new("å.com")
26
+ assert_equal "xn--5ca.com", a.dns_hostname
27
+ end
28
+
29
+ end
@@ -0,0 +1,9 @@
1
+ #encoding: utf-8
2
+ require_relative '../test_helper'
3
+
4
+ class TestMailExchanger < MiniTest::Unit::TestCase
5
+ def test_exchanger
6
+ a = EmailAddress::MailExchanger.new("example.com")
7
+ # assert_equal "user@example.com", a.unique_address
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ require 'rubygems'
2
+ require 'minitest/autorun'
3
+ require 'minitest/pride'
4
+
5
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
6
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
7
+ require 'email_address'
8
+
9
+ # Go!
metadata ADDED
@@ -0,0 +1,125 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: email_address
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Allen Fair
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-01-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: simpleidn
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: |-
56
+ The EmailAddress library is an _opinionated_ email address handler and
57
+ validator.
58
+ email:
59
+ - allen.fair@gmail.com
60
+ executables: []
61
+ extensions: []
62
+ extra_rdoc_files: []
63
+ files:
64
+ - ".gitignore"
65
+ - Gemfile
66
+ - LICENSE.txt
67
+ - README.md
68
+ - Rakefile
69
+ - email_address.gemspec
70
+ - lib/email_address.rb
71
+ - lib/email_address/address.rb
72
+ - lib/email_address/config.rb
73
+ - lib/email_address/esp.rb
74
+ - lib/email_address/host.rb
75
+ - lib/email_address/local.rb
76
+ - lib/email_address/providers/default.rb
77
+ - lib/email_address/providers/google.rb
78
+ - lib/email_address/version.rb
79
+ - lib/email_providers/address.rb
80
+ - lib/email_providers/config.rb
81
+ - lib/email_providers/factory.rb
82
+ - lib/email_providers/host.rb
83
+ - lib/email_providers/mail_exchanger.rb
84
+ - lib/email_providers/mailbox.rb
85
+ - lib/email_providers/providers/default.rb
86
+ - lib/email_providers/providers/google.rb
87
+ - lib/email_providers/version.rb
88
+ - test/email_address.rb
89
+ - test/email_address/address.rb
90
+ - test/email_address/config.rb
91
+ - test/email_address/host.rb
92
+ - test/email_address/mail_exchanger.rb
93
+ - test/test_helper.rb
94
+ homepage: https://github.com/afair/email_address
95
+ licenses:
96
+ - MIT
97
+ metadata: {}
98
+ post_install_message:
99
+ rdoc_options: []
100
+ require_paths:
101
+ - lib
102
+ required_ruby_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ required_rubygems_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ requirements: []
113
+ rubyforge_project:
114
+ rubygems_version: 2.2.0.rc.1
115
+ signing_key:
116
+ specification_version: 4
117
+ summary: EmailAddress checks on validates an acceptable set of email addresses.
118
+ test_files:
119
+ - test/email_address.rb
120
+ - test/email_address/address.rb
121
+ - test/email_address/config.rb
122
+ - test/email_address/host.rb
123
+ - test/email_address/mail_exchanger.rb
124
+ - test/test_helper.rb
125
+ has_rdoc: