email_address 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: