email_veracity 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/LICENSE +19 -0
- data/README.md +75 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/email_veracity.gemspec +75 -0
- data/lib/email_veracity/address.rb +37 -0
- data/lib/email_veracity/config.rb +71 -0
- data/lib/email_veracity/domain.rb +69 -0
- data/lib/email_veracity/resolver.rb +45 -0
- data/lib/email_veracity/server.rb +18 -0
- data/lib/email_veracity/utils.rb +11 -0
- data/lib/email_veracity/validatability.rb +31 -0
- data/lib/email_veracity.rb +19 -0
- data/test/helper.rb +51 -0
- data/test/test_address.rb +54 -0
- data/test/test_config.rb +101 -0
- data/test/test_domain.rb +94 -0
- data/test/test_resolver.rb +33 -0
- data/test/test_server.rb +29 -0
- data/test/test_utils.rb +12 -0
- data/test/test_validatability.rb +35 -0
- metadata +103 -0
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2008 Carsten Nielsen
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
Email Veracity
|
2
|
+
==============
|
3
|
+
|
4
|
+
A straight-forward Ruby library for checking the real-world validity of email
|
5
|
+
addresses.
|
6
|
+
|
7
|
+
|
8
|
+
### It Can
|
9
|
+
|
10
|
+
* Validate email addresses for proper form against a pattern.
|
11
|
+
* Accept and reject addresses based whitelists and blacklists of domains.
|
12
|
+
* Check an address' domain for MX and/or A records.
|
13
|
+
* Be configured for a variety of use-cases, to be as discerning or as
|
14
|
+
indiscriminate as you would like.
|
15
|
+
|
16
|
+
|
17
|
+
### It Can Not
|
18
|
+
|
19
|
+
* Validate all possible permutations of addresses to the RFC 2822
|
20
|
+
specification.
|
21
|
+
|
22
|
+
|
23
|
+
A Note About The ActiveRecord Plugin
|
24
|
+
------------------------------------
|
25
|
+
|
26
|
+
**It's dead!** Why? Determining the validity of an email address based on a lookup of its domain is a good idea, but basing it off of one single test done inline with a request is not. A name server might be down, shit happens, and you might be snubbing a totally legitimate person from using your product — not good.
|
27
|
+
|
28
|
+
I feel a better solution is to check the pattern of the email address and perhaps check it against the blacklist on creation. The other tests should be done in the background over the period of many days and be added to a log. A report can then be performed and you can statistically determine the addresses that are most likely false and take appropriate action from that point.
|
29
|
+
|
30
|
+
|
31
|
+
Using The Gem
|
32
|
+
-------------
|
33
|
+
|
34
|
+
In your project, you must require `email_veracity` after that you can try it
|
35
|
+
out, consider the following examples:
|
36
|
+
|
37
|
+
|
38
|
+
require 'email_veracity'
|
39
|
+
|
40
|
+
address = EmailVeracity::Address.new('heycarsten@gmail.com')
|
41
|
+
|
42
|
+
address.valid?
|
43
|
+
# => true
|
44
|
+
|
45
|
+
address.domain.to_s
|
46
|
+
# => 'gmail.com'
|
47
|
+
|
48
|
+
address.domain.address_servers.map { |s| s.to_s }
|
49
|
+
# => ["64.233.171.83", "64.233.161.83", "209.85.171.83"]
|
50
|
+
|
51
|
+
address = EmailVeracity::Address.new('fakey@crazy-z3d9df-domain.com')
|
52
|
+
|
53
|
+
address.valid?
|
54
|
+
# => false
|
55
|
+
|
56
|
+
address.errors
|
57
|
+
# => [:no_address_servers]
|
58
|
+
|
59
|
+
|
60
|
+
As you can see, playing with the core library is pretty fun. The basic building
|
61
|
+
blocks are:
|
62
|
+
|
63
|
+
#### Address
|
64
|
+
|
65
|
+
Responsible for parsing full email addresses and checking for pattern-based
|
66
|
+
validity.
|
67
|
+
|
68
|
+
#### Domain
|
69
|
+
|
70
|
+
Contains methods to query the domain for information.
|
71
|
+
|
72
|
+
#### Resolver
|
73
|
+
|
74
|
+
Abstracts Resolv::DNS into a super-simple single public method, this is where
|
75
|
+
the timeout error is raised.
|
data/Rakefile
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = 'email_veracity'
|
8
|
+
gem.summary = 'A simple library for checking the real-world validity of email addresses.'
|
9
|
+
gem.description = 'Email Veracity abstracts an email address into a series of objects which makes it easy to see if an address is invalid, and if so, why.'
|
10
|
+
gem.email = 'heycarsten@gmail.com'
|
11
|
+
gem.homepage = 'http://github.com/heycarsten/email-veracity'
|
12
|
+
gem.authors = 'Carsten Nielsen'
|
13
|
+
gem.add_development_dependency 'mocha', '>= 0.9.8'
|
14
|
+
gem.add_development_dependency 'shoulda', '>= 2.10.2'
|
15
|
+
end
|
16
|
+
Jeweler::GemcutterTasks.new
|
17
|
+
rescue LoadError
|
18
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'rake/testtask'
|
22
|
+
Rake::TestTask.new(:test) do |test|
|
23
|
+
test.libs << 'lib' << 'test'
|
24
|
+
test.pattern = 'test/**/test_*.rb'
|
25
|
+
test.verbose = true
|
26
|
+
end
|
27
|
+
|
28
|
+
begin
|
29
|
+
require 'rcov/rcovtask'
|
30
|
+
Rcov::RcovTask.new do |test|
|
31
|
+
test.libs << 'test'
|
32
|
+
test.pattern = 'test/**/test_*.rb'
|
33
|
+
test.verbose = true
|
34
|
+
end
|
35
|
+
rescue LoadError
|
36
|
+
task :rcov do
|
37
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
task :test => :check_dependencies
|
42
|
+
|
43
|
+
task :default => :test
|
44
|
+
|
45
|
+
require 'rake/rdoctask'
|
46
|
+
Rake::RDocTask.new do |rdoc|
|
47
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
48
|
+
|
49
|
+
rdoc.rdoc_dir = 'rdoc'
|
50
|
+
rdoc.title = "email-veracity #{version}"
|
51
|
+
rdoc.rdoc_files.include('README*')
|
52
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
53
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.4.0
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{email_veracity}
|
8
|
+
s.version = "0.4.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Carsten Nielsen"]
|
12
|
+
s.date = %q{2009-12-12}
|
13
|
+
s.description = %q{Email Veracity abstracts an email address into a series of objects which makes it easy to see if an address is invalid, and if so, why.}
|
14
|
+
s.email = %q{heycarsten@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.md"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".gitignore",
|
21
|
+
"LICENSE",
|
22
|
+
"README.md",
|
23
|
+
"Rakefile",
|
24
|
+
"VERSION",
|
25
|
+
"email_veracity.gemspec",
|
26
|
+
"lib/email_veracity.rb",
|
27
|
+
"lib/email_veracity/address.rb",
|
28
|
+
"lib/email_veracity/config.rb",
|
29
|
+
"lib/email_veracity/domain.rb",
|
30
|
+
"lib/email_veracity/resolver.rb",
|
31
|
+
"lib/email_veracity/server.rb",
|
32
|
+
"lib/email_veracity/utils.rb",
|
33
|
+
"lib/email_veracity/validatability.rb",
|
34
|
+
"test/helper.rb",
|
35
|
+
"test/test_address.rb",
|
36
|
+
"test/test_config.rb",
|
37
|
+
"test/test_domain.rb",
|
38
|
+
"test/test_resolver.rb",
|
39
|
+
"test/test_server.rb",
|
40
|
+
"test/test_utils.rb",
|
41
|
+
"test/test_validatability.rb"
|
42
|
+
]
|
43
|
+
s.homepage = %q{http://github.com/heycarsten/email-veracity}
|
44
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
45
|
+
s.require_paths = ["lib"]
|
46
|
+
s.rubygems_version = %q{1.3.5}
|
47
|
+
s.summary = %q{A simple library for checking the real-world validity of email addresses.}
|
48
|
+
s.test_files = [
|
49
|
+
"test/helper.rb",
|
50
|
+
"test/test_address.rb",
|
51
|
+
"test/test_config.rb",
|
52
|
+
"test/test_domain.rb",
|
53
|
+
"test/test_resolver.rb",
|
54
|
+
"test/test_server.rb",
|
55
|
+
"test/test_utils.rb",
|
56
|
+
"test/test_validatability.rb"
|
57
|
+
]
|
58
|
+
|
59
|
+
if s.respond_to? :specification_version then
|
60
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
61
|
+
s.specification_version = 3
|
62
|
+
|
63
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
64
|
+
s.add_development_dependency(%q<mocha>, [">= 0.9.8"])
|
65
|
+
s.add_development_dependency(%q<shoulda>, [">= 2.10.2"])
|
66
|
+
else
|
67
|
+
s.add_dependency(%q<mocha>, [">= 0.9.8"])
|
68
|
+
s.add_dependency(%q<shoulda>, [">= 2.10.2"])
|
69
|
+
end
|
70
|
+
else
|
71
|
+
s.add_dependency(%q<mocha>, [">= 0.9.8"])
|
72
|
+
s.add_dependency(%q<shoulda>, [">= 2.10.2"])
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module EmailVeracity
|
2
|
+
class Address
|
3
|
+
|
4
|
+
include Validatability
|
5
|
+
|
6
|
+
attr_reader :domain
|
7
|
+
|
8
|
+
def initialize(email = '')
|
9
|
+
self.email_address = email
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
email_address
|
14
|
+
end
|
15
|
+
|
16
|
+
def email_address
|
17
|
+
@email_address.to_s.strip
|
18
|
+
end
|
19
|
+
|
20
|
+
def email_address=(new_email_address)
|
21
|
+
@email_address = new_email_address.to_s
|
22
|
+
@domain = Domain.new(@email_address.split('@')[1] || '')
|
23
|
+
end
|
24
|
+
|
25
|
+
protected
|
26
|
+
|
27
|
+
def validate!
|
28
|
+
add_error(:malformed) if !pattern_valid?
|
29
|
+
add_errors(domain.errors)
|
30
|
+
end
|
31
|
+
|
32
|
+
def pattern_valid?
|
33
|
+
@email_address =~ Config[:valid_pattern]
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module EmailVeracity
|
2
|
+
module Config
|
3
|
+
|
4
|
+
DEFAULT_OPTIONS = {
|
5
|
+
:whitelist => %w[ aol.com gmail.com hotmail.com mac.com msn.com
|
6
|
+
rogers.com sympatico.ca yahoo.com telus.com sprint.com sprint.ca ],
|
7
|
+
:blacklist => %w[ dodgeit.com mintemail.com mintemail.uni.cc
|
8
|
+
1mintemail.mooo.com spammotel.com trashmail.net ],
|
9
|
+
:valid_pattern => %r{\A
|
10
|
+
(
|
11
|
+
(
|
12
|
+
[a-z0-1]{1}
|
13
|
+
|
|
14
|
+
[\w]+[\w\+_\-\.]+
|
15
|
+
[\+_\-\.]{0}
|
16
|
+
)
|
17
|
+
@
|
18
|
+
(
|
19
|
+
(?:
|
20
|
+
[-a-z0-9]+\.
|
21
|
+
)+
|
22
|
+
[a-z]{2,}
|
23
|
+
)
|
24
|
+
){1}
|
25
|
+
\Z}xi,
|
26
|
+
:must_include => [], # :a, :mx
|
27
|
+
:timeout => 2,
|
28
|
+
:enforce_blacklist => false,
|
29
|
+
:enforce_whitelist => true }
|
30
|
+
@options = DEFAULT_OPTIONS.clone
|
31
|
+
|
32
|
+
def [](key)
|
33
|
+
@options[key.to_sym]
|
34
|
+
end
|
35
|
+
|
36
|
+
def []=(key, value)
|
37
|
+
@options[key.to_sym] = value
|
38
|
+
end
|
39
|
+
|
40
|
+
def options
|
41
|
+
@options
|
42
|
+
end
|
43
|
+
|
44
|
+
def options=(options)
|
45
|
+
unless options.is_a?(Hash)
|
46
|
+
raise ArgumentError, "options must be a Hash not #{options.class}"
|
47
|
+
end
|
48
|
+
@options = options
|
49
|
+
end
|
50
|
+
|
51
|
+
def update(options)
|
52
|
+
unless options.is_a?(Hash)
|
53
|
+
raise ArgumentError, "options must be a Hash not #{options.class}"
|
54
|
+
end
|
55
|
+
@options.update(options)
|
56
|
+
end
|
57
|
+
|
58
|
+
def enforced_record?(record)
|
59
|
+
return unless @options[:must_include].is_a?(Array)
|
60
|
+
@options[:must_include].any? { |i| i.to_s == record.to_s }
|
61
|
+
end
|
62
|
+
|
63
|
+
def revert!
|
64
|
+
@options = DEFAULT_OPTIONS.clone
|
65
|
+
end
|
66
|
+
|
67
|
+
module_function :[], :[]=, :options, :options=, :update, :enforced_record?,
|
68
|
+
:revert!
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module EmailVeracity
|
2
|
+
class Domain
|
3
|
+
|
4
|
+
include Validatability
|
5
|
+
|
6
|
+
def self.whitelisted?(name)
|
7
|
+
Config[:whitelist].include?(name.downcase.strip)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.blacklisted?(name)
|
11
|
+
Config[:blacklist].include?(name.downcase.strip)
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(name = '')
|
15
|
+
@name = name
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
name
|
20
|
+
end
|
21
|
+
|
22
|
+
def name
|
23
|
+
@name.to_s.downcase.strip
|
24
|
+
end
|
25
|
+
|
26
|
+
def whitelisted?
|
27
|
+
Domain.whitelisted?(name)
|
28
|
+
end
|
29
|
+
|
30
|
+
def blacklisted?
|
31
|
+
Domain.blacklisted?(name)
|
32
|
+
end
|
33
|
+
|
34
|
+
def address_servers
|
35
|
+
@address_servers ||= servers_in(:a)
|
36
|
+
end
|
37
|
+
|
38
|
+
def exchange_servers
|
39
|
+
@exchange_servers ||= servers_in(:mx)
|
40
|
+
end
|
41
|
+
|
42
|
+
def servers
|
43
|
+
address_servers + exchange_servers
|
44
|
+
end
|
45
|
+
|
46
|
+
protected
|
47
|
+
|
48
|
+
def validate!
|
49
|
+
return if whitelisted?
|
50
|
+
add_error(:blacklisted) if blacklisted? &&
|
51
|
+
Config[:enforce_blacklist]
|
52
|
+
add_error(:no_records) if servers.empty? &&
|
53
|
+
!Config.enforced_record?(:a) &&
|
54
|
+
!Config.enforced_record?(:mx)
|
55
|
+
add_error(:no_address_servers) if address_servers.empty? &&
|
56
|
+
Config.enforced_record?(:a)
|
57
|
+
add_error(:no_exchange_servers) if exchange_servers.empty? &&
|
58
|
+
Config.enforced_record?(:mx)
|
59
|
+
end
|
60
|
+
|
61
|
+
def servers_in(record)
|
62
|
+
return [] if Utils.blank?(name)
|
63
|
+
Resolver.get_servers_for(name, record)
|
64
|
+
rescue DomainResourcesTimeoutError
|
65
|
+
add_error :timed_out
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module EmailVeracity
|
2
|
+
module Resolver
|
3
|
+
|
4
|
+
RECORD_NAMES_TO_RESOLVE_MAP = {
|
5
|
+
:a => {
|
6
|
+
:method => 'address',
|
7
|
+
:type => AddressServer,
|
8
|
+
:constant => Resolv::DNS::Resource::IN::A },
|
9
|
+
:mx => {
|
10
|
+
:method => 'exchange',
|
11
|
+
:type => ExchangeServer,
|
12
|
+
:constant => Resolv::DNS::Resource::IN::MX } }
|
13
|
+
|
14
|
+
def get_servers_for(domain_name, record = :a)
|
15
|
+
Timeout::timeout(Config[:timeout]) do
|
16
|
+
get_resources_for(domain_name, record).map do |server_name|
|
17
|
+
type = RECORD_NAMES_TO_RESOLVE_MAP[record.to_sym][:type]
|
18
|
+
type.new(server_name)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
rescue Timeout::Error
|
22
|
+
raise DomainResourcesTimeoutError,
|
23
|
+
"Timed out while try to resolve #{domain_name}"
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
def get_resources_for(domain_name, record = :a)
|
29
|
+
Resolv::DNS.open do |server|
|
30
|
+
record_map = RECORD_NAMES_TO_RESOLVE_MAP[record]
|
31
|
+
resources = server.getresources(domain_name, record_map[:constant])
|
32
|
+
resources_to_servers(resources, record_map[:method])
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def resources_to_servers(resources, resolve_method)
|
37
|
+
resources.inject([]) do |array, resource|
|
38
|
+
array << resource.method(resolve_method).call.to_s.strip
|
39
|
+
end.reject { |i| Utils.blank?(i) }
|
40
|
+
end
|
41
|
+
|
42
|
+
module_function :get_servers_for, :get_resources_for, :resources_to_servers
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module EmailVeracity
|
2
|
+
module Validatability
|
3
|
+
|
4
|
+
def valid?
|
5
|
+
self.errors.empty?
|
6
|
+
end
|
7
|
+
|
8
|
+
def errors
|
9
|
+
self.clear_errors!
|
10
|
+
self.validate!
|
11
|
+
@errors
|
12
|
+
end
|
13
|
+
|
14
|
+
protected
|
15
|
+
|
16
|
+
def validate!
|
17
|
+
# Adds errors to the object.
|
18
|
+
end
|
19
|
+
|
20
|
+
def clear_errors!
|
21
|
+
@errors = []
|
22
|
+
end
|
23
|
+
|
24
|
+
def add_error(*new_errors)
|
25
|
+
@errors ||= []
|
26
|
+
@errors.concat(new_errors.flatten)
|
27
|
+
end
|
28
|
+
alias_method :add_errors, :add_error
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'resolv'
|
2
|
+
require 'timeout'
|
3
|
+
|
4
|
+
require 'email_veracity/utils'
|
5
|
+
require 'email_veracity/validatability'
|
6
|
+
require 'email_veracity/config'
|
7
|
+
require 'email_veracity/server'
|
8
|
+
require 'email_veracity/resolver'
|
9
|
+
require 'email_veracity/domain'
|
10
|
+
require 'email_veracity/address'
|
11
|
+
|
12
|
+
|
13
|
+
module EmailVeracity
|
14
|
+
|
15
|
+
class Error < StandardError; end
|
16
|
+
class MalformedEmailAddressError < Error; end
|
17
|
+
class DomainResourcesTimeoutError < Error; end
|
18
|
+
|
19
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'shoulda'
|
4
|
+
require 'mocha'
|
5
|
+
require 'redgreen'
|
6
|
+
|
7
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
8
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
9
|
+
|
10
|
+
require 'email_veracity'
|
11
|
+
|
12
|
+
|
13
|
+
class ClassWithValidationMock
|
14
|
+
|
15
|
+
include EmailVeracity::Validatability
|
16
|
+
|
17
|
+
attr_accessor :give_error
|
18
|
+
attr_accessor :give_errors
|
19
|
+
attr_accessor :give_array_of_errors
|
20
|
+
|
21
|
+
def validate!
|
22
|
+
add_error(:one) if give_error
|
23
|
+
add_errors(:two, :three) if give_errors
|
24
|
+
add_errors([:four, :five]) if give_array_of_errors
|
25
|
+
add_errors []
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
class Test::Unit::TestCase
|
32
|
+
|
33
|
+
def assert_empty(array, message = nil)
|
34
|
+
unless array.is_a?(Array)
|
35
|
+
raise ArgumentError, 'First argument must be an Array'
|
36
|
+
end
|
37
|
+
message = [message, "Expected #{array.inspect} to be empty."].
|
38
|
+
flatten.join("\n")
|
39
|
+
assert_block(message) { array.empty? }
|
40
|
+
end
|
41
|
+
|
42
|
+
def assert_not_empty(array, message = nil)
|
43
|
+
unless array.is_a?(Array)
|
44
|
+
raise ArgumentError, 'First argument must be an Array'
|
45
|
+
end
|
46
|
+
message = [message, "Expected #{array.inspect} to contain items."].
|
47
|
+
flatten.join("\n")
|
48
|
+
assert_block(message) { !array.empty? }
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
|
4
|
+
class TestAddress < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_initialize
|
7
|
+
assert_instance_of EmailVeracity::Address,
|
8
|
+
EmailVeracity::Address.new('heycarsten@gmail.com'),
|
9
|
+
'Should create a new Address object.'
|
10
|
+
assert_instance_of Array,
|
11
|
+
EmailVeracity::Address.new('heycarsten@gmail.com').errors,
|
12
|
+
'#errors should be an array.'
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
class DefaultConfigurationAddressValidationsTest < Test::Unit::TestCase
|
19
|
+
|
20
|
+
def test_a_nil_address_argument
|
21
|
+
address = new_address(nil)
|
22
|
+
assert !address.valid?, 'Should be invalid.'
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_a_well_formed_address_with_a_whitelisted_domain
|
26
|
+
address = new_address('heycarsten@gmail.com')
|
27
|
+
assert address.valid?, "Should be valid. @errors: #{address.errors.inspect}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_a_well_formed_address_with_a_blacklisted_domain
|
31
|
+
address = new_address('heycarsten@dodgeit.com')
|
32
|
+
address.stubs(:domain).returns(stub(:errors => [:blacklisted]))
|
33
|
+
assert !address.valid?, "Should not be valid. @errors: #{address.errors.inspect}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_a_well_formed_address_that_does_not_exist
|
37
|
+
address = new_address('heycarsten@i-surely-do-not-exist.nil')
|
38
|
+
address.stubs(:domain).returns(stub(:errors => [:no_records]))
|
39
|
+
assert !address.valid?, 'Should not be valid.'
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_a_well_formed_address_that_exists
|
43
|
+
address = new_address('itsme@heycarsten.com')
|
44
|
+
address.stubs(:domain).returns(stub(:errors => []))
|
45
|
+
assert address.valid?, "Should be valid. @errors: #{address.errors.inspect}"
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def new_address(address = '')
|
51
|
+
EmailVeracity::Address.new(address)
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
data/test/test_config.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestConfig < Test::Unit::TestCase
|
4
|
+
|
5
|
+
VALID_EMAIL_ADDRESS_EXAMPLES = %w[
|
6
|
+
goto@rubyfringe.ca
|
7
|
+
went.to@futureruby.com
|
8
|
+
heyd00d+stuff@gmail.com
|
9
|
+
carsten_nielsen@gmail.com
|
10
|
+
carsten-nielsen@gmail.com
|
11
|
+
goodoldemail@address.ca
|
12
|
+
old-skool@mail.mysite.on.ca
|
13
|
+
heycarsten@del.icio.us
|
14
|
+
nex3@haml.town
|
15
|
+
1234@aplace.com
|
16
|
+
carsten2@happyland.net
|
17
|
+
sweetCandy4@me-and-you.ca
|
18
|
+
simple@example.com
|
19
|
+
neat@b.eat
|
20
|
+
i@shouldwork.com
|
21
|
+
1@shouldworktoo.com ]
|
22
|
+
INVALID_EMAIL_ADDRESS_EXAMPLES = %w[
|
23
|
+
@failure.net
|
24
|
+
craptastic@
|
25
|
+
!!!!!@gmail.com
|
26
|
+
oh-noez@f4iL/\/\@il.net
|
27
|
+
someone@somewhere
|
28
|
+
this!fails@comtown.com
|
29
|
+
$oWrong@fail.net
|
30
|
+
charles\ babbage@gmail.com
|
31
|
+
,@crap.com
|
32
|
+
dis%20blos@dot.com
|
33
|
+
&^%$#$%@yojimbo.nil
|
34
|
+
"greetings\ friend"@comtastic.dk
|
35
|
+
this,fails@ice-t.com
|
36
|
+
ungültige@adresse.de
|
37
|
+
failure@10.0.0.1
|
38
|
+
douche@@bag.net
|
39
|
+
.@fail.net
|
40
|
+
-@fail.org
|
41
|
+
_@fail.org
|
42
|
+
+_-@fail.die
|
43
|
+
+___--@crashburn.net ]
|
44
|
+
|
45
|
+
context 'Default email pattern' do
|
46
|
+
should 'match valid addresses' do
|
47
|
+
VALID_EMAIL_ADDRESS_EXAMPLES.each do |example|
|
48
|
+
assert_match EmailVeracity::Config[:valid_pattern], example
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
should 'not match invalid addresses' do
|
53
|
+
INVALID_EMAIL_ADDRESS_EXAMPLES.each do |example|
|
54
|
+
assert_no_match EmailVeracity::Config[:valid_pattern], example
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_default_whitelist_domains
|
60
|
+
assert_instance_of Array, EmailVeracity::Config[:whitelist]
|
61
|
+
assert_not_empty EmailVeracity::Config[:whitelist],
|
62
|
+
'Should have more than one item.'
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_default_blacklist_domains
|
66
|
+
assert_instance_of Array, EmailVeracity::Config[:blacklist]
|
67
|
+
assert_not_empty EmailVeracity::Config[:blacklist],
|
68
|
+
'Should have more than one item.'
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_must_include_default_setting
|
72
|
+
assert_instance_of Array, EmailVeracity::Config[:must_include]
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_enforced_record_with_symbols
|
76
|
+
assert !EmailVeracity::Config.enforced_record?(:a),
|
77
|
+
'Should not check for A records by default'
|
78
|
+
assert !EmailVeracity::Config.enforced_record?(:mx),
|
79
|
+
'Should not check for MX records be default'
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_enforce_lookup_with_strings
|
83
|
+
assert !EmailVeracity::Config.enforced_record?('a'),
|
84
|
+
'Should not check for A records by default'
|
85
|
+
assert !EmailVeracity::Config.enforced_record?('mx'),
|
86
|
+
'Should not check for MX records be default'
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_changing_and_reverting_configuration
|
90
|
+
EmailVeracity::Config.update(:lookup => false, :timeout => 3)
|
91
|
+
assert_equal false, EmailVeracity::Config[:lookup],
|
92
|
+
'Should change configuration.'
|
93
|
+
assert_equal 3, EmailVeracity::Config[:timeout]
|
94
|
+
'Should change configuration.'
|
95
|
+
EmailVeracity::Config.revert!
|
96
|
+
assert_equal EmailVeracity::Config::DEFAULT_OPTIONS,
|
97
|
+
EmailVeracity::Config.options,
|
98
|
+
'Should revert configuration'
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
data/test/test_domain.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestDomain < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def test_blacklisted_domain
|
6
|
+
assert EmailVeracity::Domain.blacklisted?('dodgeit.com'),
|
7
|
+
'Should match a blacklisted domain.'
|
8
|
+
assert EmailVeracity::Domain.blacklisted?('DoDgEiT.cOm'),
|
9
|
+
'Should match a blacklisted domain regardless of case.'
|
10
|
+
assert EmailVeracity::Domain.blacklisted?(" dodgeit.com \r\n "),
|
11
|
+
'Should match a blacklisted domain regardless of whitespace.'
|
12
|
+
assert !EmailVeracity::Domain.blacklisted?('iamnotblacklisted.com'),
|
13
|
+
'Should not match a non-blacklisted domain.'
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_whitelisted_domain
|
17
|
+
assert EmailVeracity::Domain.whitelisted?('gmail.com'),
|
18
|
+
'Should match a whitelisted domain.'
|
19
|
+
assert EmailVeracity::Domain.whitelisted?('GmAiL.cOm'),
|
20
|
+
'Should match a whitelisted domain regardless of case.'
|
21
|
+
assert EmailVeracity::Domain.whitelisted?(" gmail.com \r\n "),
|
22
|
+
'Should match a whitelisted domain regardless of whitespace.'
|
23
|
+
assert !EmailVeracity::Domain.whitelisted?('iamnotwhitelisted.com'),
|
24
|
+
'Should not match a non-whitelisted domain.'
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_initializing_a_new_domain_with_whitespace
|
28
|
+
domain = new_domain(' heycarsten.com ')
|
29
|
+
assert_equal 'heycarsten.com', domain.name, 'Should strip whitespace.'
|
30
|
+
assert_respond_to domain, :to_s, 'Should have a to_s method.'
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_a_valid_domain_for_address_servers
|
34
|
+
domain_name = 'gmail.com'
|
35
|
+
domain = new_domain(domain_name)
|
36
|
+
EmailVeracity::Resolver.expects(:get_servers_for).
|
37
|
+
with(domain_name, :a).returns(["mail.#{domain_name}"])
|
38
|
+
assert_not_empty domain.address_servers, 'Should contain address servers.'
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_a_valid_domain_for_exchange_servers
|
42
|
+
domain_name = 'gmail.com'
|
43
|
+
domain = new_domain(domain_name)
|
44
|
+
EmailVeracity::Resolver.expects(:get_servers_for).
|
45
|
+
with(domain_name, :mx).returns(["mail.#{domain_name}"])
|
46
|
+
assert_not_empty domain.exchange_servers, 'Should contain mail servers.'
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_an_invalid_domain_for_address_servers
|
50
|
+
domain = new_domain('i-surely-do-not.exist')
|
51
|
+
domain.expects(:servers_in).with(:a).returns([])
|
52
|
+
assert_empty domain.address_servers, 'Should not contain address servers.'
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_an_invalid_domain_for_exchange_servers
|
56
|
+
domain = new_domain('i-surely-do-not.exist')
|
57
|
+
domain.expects(:servers_in).with(:mx).returns([])
|
58
|
+
assert_empty domain.exchange_servers, 'Should not contain exchange servers.'
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_a_blank_domain_for_servers
|
62
|
+
domain = new_domain('')
|
63
|
+
assert_empty domain.exchange_servers, 'Should not contain exchange servers.'
|
64
|
+
assert_empty domain.address_servers, 'Should not contain address servers.'
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_for_errors_on_a_valid_domain
|
68
|
+
domain = new_domain('yahoo.com')
|
69
|
+
assert_empty domain.errors, 'Should not have errors.'
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'A domain with no address records or exchange records' do
|
73
|
+
setup do
|
74
|
+
@domain = new_domain('nothingtoseehere.org')
|
75
|
+
@domain.stubs(:address_servers).returns([])
|
76
|
+
@domain.stubs(:exchange_servers).returns([])
|
77
|
+
end
|
78
|
+
|
79
|
+
should 'not pass validation' do
|
80
|
+
assert !@domain.valid?
|
81
|
+
end
|
82
|
+
|
83
|
+
should 'indicate the appropriate error' do
|
84
|
+
assert @domain.errors.include?(:no_records)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def new_domain(name = 'heycarsten.com')
|
91
|
+
EmailVeracity::Domain.new(name)
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestResolver < Test::Unit::TestCase
|
4
|
+
|
5
|
+
DOMAIN_NAMES = %w[
|
6
|
+
viarails.net heycarsten.com yahoo.com gmail.com savvica.com
|
7
|
+
okayfail.com github.com google.com rogers.com amd.com adobe.com
|
8
|
+
unspace.ca xerox.com webkit.org cooltown.net aiderss.com delicious.com ]
|
9
|
+
|
10
|
+
def test_consecutive_queries
|
11
|
+
DOMAIN_NAMES.each do |domain_name|
|
12
|
+
EmailVeracity::Resolver.stubs(:get_resources_for).
|
13
|
+
with(domain_name, kind_of(Symbol)).returns(["mail.#{domain_name}"])
|
14
|
+
end
|
15
|
+
assert_nothing_raised do
|
16
|
+
DOMAIN_NAMES.each do |domain|
|
17
|
+
assert_instance_of Array, EmailVeracity::Resolver.get_servers_for(domain),
|
18
|
+
'Should return an array of servers'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_timing_out_while_resolving_a_domain
|
24
|
+
domain = 'okayfail.com'
|
25
|
+
timeout_exception = Timeout::Error.new('The connection has timed out')
|
26
|
+
EmailVeracity::Resolver.stubs(:get_resources_for).
|
27
|
+
with(domain, kind_of(Symbol)).raises(timeout_exception)
|
28
|
+
assert_raise EmailVeracity::DomainResourcesTimeoutError, 'Should time out' do
|
29
|
+
EmailVeracity::Resolver.get_servers_for(domain)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
data/test/test_server.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestServer < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def test_creating_a_new_blank_server_object
|
6
|
+
new_server = EmailVeracity::Server.new
|
7
|
+
assert_equal '', new_server.to_s,
|
8
|
+
'Should yield a blank string on call to to_s.'
|
9
|
+
assert_equal '', new_server.name,
|
10
|
+
'Should yield a blank string on call to name.'
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_creating_a_new_server_object_with_a_name_and_type
|
14
|
+
name = 'cooltown.ca'
|
15
|
+
new_server = EmailVeracity::Server.new(name)
|
16
|
+
assert_equal name, new_server.name,
|
17
|
+
'Should yield the provided name on call to name.'
|
18
|
+
assert_equal name, new_server.to_s,
|
19
|
+
'Should yield the provided name on call to to_s.'
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_creating_a_new_blank_server_object_and_setting_its_name_after_initialization
|
23
|
+
new_server = EmailVeracity::Server.new('igvita.com')
|
24
|
+
assert_raise NoMethodError, 'Should fail miserably.' do
|
25
|
+
new_server.name = name
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
data/test/test_utils.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestUtils < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def test_blank
|
6
|
+
assert EmailVeracity::Utils.blank?([]), '[] should be blank.'
|
7
|
+
assert EmailVeracity::Utils.blank?(''), '"" should be blank.'
|
8
|
+
assert EmailVeracity::Utils.blank?(Hash.new), '{} should be blank.'
|
9
|
+
assert EmailVeracity::Utils.blank?(nil), 'nil should be blank.'
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestValidatability < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def test_includes_proper_methods
|
6
|
+
%w[ valid? validate! clear_errors! add_error errors ].each do |method_name|
|
7
|
+
assert_respond_to ClassWithValidationMock.new, method_name
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_add_error
|
12
|
+
mock = ClassWithValidationMock.new
|
13
|
+
mock.give_error = true
|
14
|
+
assert_equal 1, mock.errors.size, 'Should set one error.'
|
15
|
+
mock.give_errors = true
|
16
|
+
assert_equal [:one, :two, :three], mock.errors,
|
17
|
+
'Should push tow new errors for a total of three.'
|
18
|
+
mock.give_array_of_errors = true
|
19
|
+
assert_equal [:one, :two, :three, :four, :five], mock.errors,
|
20
|
+
'Should concat the array leaving two new errors for a total of five.'
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_valid?
|
24
|
+
mock = ClassWithValidationMock.new
|
25
|
+
assert mock.valid?, 'Should be valid by default.'
|
26
|
+
mock.give_error = true
|
27
|
+
assert !mock.valid?, 'Should not be valid if errors are set.'
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_errors
|
31
|
+
assert ClassWithValidationMock.new.errors.empty?,
|
32
|
+
'Should be empty by default.'
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
metadata
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: email_veracity
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.4.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Carsten Nielsen
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-12-12 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: mocha
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.9.8
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: shoulda
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 2.10.2
|
34
|
+
version:
|
35
|
+
description: Email Veracity abstracts an email address into a series of objects which makes it easy to see if an address is invalid, and if so, why.
|
36
|
+
email: heycarsten@gmail.com
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files:
|
42
|
+
- LICENSE
|
43
|
+
- README.md
|
44
|
+
files:
|
45
|
+
- .gitignore
|
46
|
+
- LICENSE
|
47
|
+
- README.md
|
48
|
+
- Rakefile
|
49
|
+
- VERSION
|
50
|
+
- email_veracity.gemspec
|
51
|
+
- lib/email_veracity.rb
|
52
|
+
- lib/email_veracity/address.rb
|
53
|
+
- lib/email_veracity/config.rb
|
54
|
+
- lib/email_veracity/domain.rb
|
55
|
+
- lib/email_veracity/resolver.rb
|
56
|
+
- lib/email_veracity/server.rb
|
57
|
+
- lib/email_veracity/utils.rb
|
58
|
+
- lib/email_veracity/validatability.rb
|
59
|
+
- test/helper.rb
|
60
|
+
- test/test_address.rb
|
61
|
+
- test/test_config.rb
|
62
|
+
- test/test_domain.rb
|
63
|
+
- test/test_resolver.rb
|
64
|
+
- test/test_server.rb
|
65
|
+
- test/test_utils.rb
|
66
|
+
- test/test_validatability.rb
|
67
|
+
has_rdoc: true
|
68
|
+
homepage: http://github.com/heycarsten/email-veracity
|
69
|
+
licenses: []
|
70
|
+
|
71
|
+
post_install_message:
|
72
|
+
rdoc_options:
|
73
|
+
- --charset=UTF-8
|
74
|
+
require_paths:
|
75
|
+
- lib
|
76
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: "0"
|
81
|
+
version:
|
82
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: "0"
|
87
|
+
version:
|
88
|
+
requirements: []
|
89
|
+
|
90
|
+
rubyforge_project:
|
91
|
+
rubygems_version: 1.3.5
|
92
|
+
signing_key:
|
93
|
+
specification_version: 3
|
94
|
+
summary: A simple library for checking the real-world validity of email addresses.
|
95
|
+
test_files:
|
96
|
+
- test/helper.rb
|
97
|
+
- test/test_address.rb
|
98
|
+
- test/test_config.rb
|
99
|
+
- test/test_domain.rb
|
100
|
+
- test/test_resolver.rb
|
101
|
+
- test/test_server.rb
|
102
|
+
- test/test_utils.rb
|
103
|
+
- test/test_validatability.rb
|