heycarsten-email-veracity 0.3.0
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.
- data/README.rdoc +72 -0
- data/Rakefile +93 -0
- data/VERSION +1 -0
- data/init.rb +1 -0
- data/lib/email_veracity.rb +56 -0
- data/lib/email_veracity/address.rb +41 -0
- data/lib/email_veracity/config.rb +56 -0
- data/lib/email_veracity/core_extensions.rb +27 -0
- data/lib/email_veracity/domain.rb +69 -0
- data/lib/email_veracity/errors.rb +9 -0
- data/lib/email_veracity/resolver.rb +46 -0
- data/lib/email_veracity/server.rb +20 -0
- data/lib/email_veracity/validatability.rb +34 -0
- data/test/mocks/class_with_validation_mock.rb +16 -0
- data/test/test_helper.rb +33 -0
- data/test/unit/address_test.rb +49 -0
- data/test/unit/config_test.rb +105 -0
- data/test/unit/core_extensions_test.rb +28 -0
- data/test/unit/domain_test.rb +73 -0
- data/test/unit/resolver_test.rb +25 -0
- data/test/unit/server_test.rb +30 -0
- data/test/unit/validatability_test.rb +36 -0
- metadata +78 -0
data/README.rdoc
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
= Email Veracity
|
|
2
|
+
|
|
3
|
+
A straight-forward Ruby library for checking the real-world validity of email
|
|
4
|
+
addresses.
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
=== It Can
|
|
8
|
+
|
|
9
|
+
* Validate email addresses for proper form against a pattern.
|
|
10
|
+
* Accept and reject addresses based whitelists and blacklists of domains.
|
|
11
|
+
* Check an address' domain for MX and/or A records.
|
|
12
|
+
* Be configured for a variety of use-cases, to be as discerning or as
|
|
13
|
+
indiscriminate as you would like.
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
=== It Can Not
|
|
17
|
+
|
|
18
|
+
* Validate all possible permutations of addresses to the RFC 2822
|
|
19
|
+
specification.
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
== Using The Gem
|
|
23
|
+
|
|
24
|
+
In your project, you must require +email_veracity+ after that you can try it
|
|
25
|
+
out, consider the following examples:
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
require 'email_veracity'
|
|
29
|
+
|
|
30
|
+
address = EmailVeracity::Address.new('heycarsten@gmail.com')
|
|
31
|
+
|
|
32
|
+
address.valid?
|
|
33
|
+
# => true
|
|
34
|
+
|
|
35
|
+
address.domain.to_s
|
|
36
|
+
# => 'gmail.com'
|
|
37
|
+
|
|
38
|
+
address.domain.address_servers.collect { |s| s.to_s }
|
|
39
|
+
# => ["64.233.171.83", "64.233.161.83", "209.85.171.83"]
|
|
40
|
+
|
|
41
|
+
address = EmailVeracity::Address.new('fakey@crazy-z3d9df-domain.com')
|
|
42
|
+
|
|
43
|
+
address.valid?
|
|
44
|
+
# => false
|
|
45
|
+
|
|
46
|
+
address.errors
|
|
47
|
+
# => [:no_address_servers]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
As you can see, playing with the core library is pretty fun. The basic building
|
|
51
|
+
blocks are:
|
|
52
|
+
|
|
53
|
+
==== Address
|
|
54
|
+
|
|
55
|
+
Responsible for parsing full email addresses and checking for pattern-based
|
|
56
|
+
validity.
|
|
57
|
+
|
|
58
|
+
==== Domain
|
|
59
|
+
|
|
60
|
+
Contains methods to query the domain for information.
|
|
61
|
+
|
|
62
|
+
==== Resolver
|
|
63
|
+
|
|
64
|
+
Abstracts Resolv::DNS into a super-simple single public method, this is where
|
|
65
|
+
the timeout error is raised.
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
== Running The Tests
|
|
69
|
+
|
|
70
|
+
A few of the tests will fail if you don't have a live connection to the
|
|
71
|
+
internet, this is obviously not cool and it's high on my list of TODOs. [See
|
|
72
|
+
TODO]
|
data/Rakefile
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# I used Haml as a template for these tasks: Thank you Nex3!
|
|
2
|
+
require 'rubygems'
|
|
3
|
+
require 'rake'
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
task :default => :test
|
|
7
|
+
|
|
8
|
+
require 'rake/testtask'
|
|
9
|
+
|
|
10
|
+
Rake::TestTask.new do |t|
|
|
11
|
+
t.libs << 'lib'
|
|
12
|
+
t.pattern = 'test/**/*_test.rb'
|
|
13
|
+
t.verbose = true
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Packaging
|
|
19
|
+
require 'rake/gempackagetask'
|
|
20
|
+
load 'email_veracity.gemspec'
|
|
21
|
+
Rake::GemPackageTask.new(EMAIL_VERACITY_GEMSPEC) do |pkg|
|
|
22
|
+
if Rake.application.top_level_tasks.include?('release')
|
|
23
|
+
pkg.need_tar_gz = true
|
|
24
|
+
pkg.need_tar_bz2 = true
|
|
25
|
+
pkg.need_zip = true
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
task :revision_file do
|
|
31
|
+
require 'lib/email_veracity'
|
|
32
|
+
|
|
33
|
+
if EmailVeracity.version[:rev] && !Rake.application.top_level_tasks.include?('release')
|
|
34
|
+
File.open('REVISION', 'w') { |f| f.puts EmailVeracity.version[:rev] }
|
|
35
|
+
elsif Rake.application.top_level_tasks.include?('release')
|
|
36
|
+
File.open('REVISION', 'w') { |f| f.puts '(release)' }
|
|
37
|
+
else
|
|
38
|
+
File.open('REVISION', 'w') { |f| f.puts '(unknown)' }
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
Rake::Task[:package].prerequisites.insert(0, :revision_file)
|
|
42
|
+
|
|
43
|
+
# We also need to get rid of this file after packaging.
|
|
44
|
+
at_exit { File.delete('REVISION') rescue nil }
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
task :install => [:package] do
|
|
48
|
+
sudo = RUBY_PLATFORM =~ /win32/ ? '' : 'sudo'
|
|
49
|
+
`#{sudo} gem install --no-ri pkg/email-veracity-#{File.read('VERSION').strip}`
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
task :release => [:package] do
|
|
54
|
+
name, version = ENV['NAME'], ENV['VERSION']
|
|
55
|
+
raise "Must supply NAME and VERSION for release task." unless name && version
|
|
56
|
+
`rubyforge login`
|
|
57
|
+
`rubyforge add_release email-veracity email-veracity "#{name} (v#{version})" pkg/email-veracity-#{version}.gem`
|
|
58
|
+
`rubyforge add_file email-veracity email-veracity "#{name} (v#{version})" pkg/email-veracity-#{version}.tar.gz`
|
|
59
|
+
`rubyforge add_file email-veracity email-veracity "#{name} (v#{version})" pkg/email-veracity-#{version}.tar.bz2`
|
|
60
|
+
`rubyforge add_file email-veracity email-veracity "#{name} (v#{version})" pkg/email-veracity-#{version}.zip`
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
### Documentation
|
|
66
|
+
require 'rake/rdoctask'
|
|
67
|
+
|
|
68
|
+
Rake::RDocTask.new do |rdoc|
|
|
69
|
+
rdoc.title = 'Email Veracity'
|
|
70
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
|
71
|
+
rdoc.rdoc_files.include(*FileList.new('*') do |list|
|
|
72
|
+
list.exclude(/(^|[^.a-z])[a-z]+/)
|
|
73
|
+
list.exclude('TODO')
|
|
74
|
+
end.to_a)
|
|
75
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
76
|
+
rdoc.rdoc_files.exclude('TODO')
|
|
77
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
78
|
+
rdoc.main = 'README.rdoc'
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
### Coverage
|
|
84
|
+
require 'rcov/rcovtask'
|
|
85
|
+
|
|
86
|
+
Rcov::RcovTask.new do |t|
|
|
87
|
+
t.test_files = FileList['test/**/*_test.rb']
|
|
88
|
+
t.rcov_opts << '-x' << '"^\/"'
|
|
89
|
+
if ENV['NON_NATIVE']
|
|
90
|
+
t.rcov_opts << "--no-rcovrt"
|
|
91
|
+
end
|
|
92
|
+
t.verbose = true
|
|
93
|
+
end
|
data/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.3.0
|
data/init.rb
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'email_veracity'
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
dir = File.dirname(__FILE__)
|
|
2
|
+
$LOAD_PATH << dir unless $LOAD_PATH.include?(dir)
|
|
3
|
+
|
|
4
|
+
require 'resolv'
|
|
5
|
+
require 'timeout'
|
|
6
|
+
require 'email_veracity/core_extensions'
|
|
7
|
+
require 'email_veracity/validatability'
|
|
8
|
+
require 'email_veracity/errors'
|
|
9
|
+
require 'email_veracity/config'
|
|
10
|
+
require 'email_veracity/server'
|
|
11
|
+
require 'email_veracity/resolver'
|
|
12
|
+
require 'email_veracity/domain'
|
|
13
|
+
require 'email_veracity/address'
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
module EmailVeracity
|
|
17
|
+
|
|
18
|
+
def self.version # :nodoc:
|
|
19
|
+
return @@version if defined?(@@version)
|
|
20
|
+
|
|
21
|
+
numbers = File.read(scope('VERSION')).strip.split('.').map { |n| n.to_i }
|
|
22
|
+
|
|
23
|
+
@@version = {
|
|
24
|
+
:major => numbers[0],
|
|
25
|
+
:minor => numbers[1],
|
|
26
|
+
:teeny => numbers[2] }
|
|
27
|
+
|
|
28
|
+
@@version[:string] = [:major, :minor, :teeny].map { |comp| @@version[comp] }.compact.join('.')
|
|
29
|
+
|
|
30
|
+
if File.exists?(scope('REVISION'))
|
|
31
|
+
rev = File.read(scope('REVISION')).strip
|
|
32
|
+
rev = nil if rev !~ /[a-f0-9]+/
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
if rev.nil? && File.exists?(scope('.git/HEAD'))
|
|
36
|
+
rev = File.read(scope('.git/HEAD')).strip
|
|
37
|
+
if rev =~ /^ref: (.*)$/
|
|
38
|
+
rev = File.read(scope(".git/#{$1}")).strip
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
if rev
|
|
43
|
+
@@version[:rev] = rev
|
|
44
|
+
@@version[:string] << "." << rev[0...7]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
@@version
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.scope(file) # :nodoc:
|
|
51
|
+
File.join(File.dirname(__FILE__), '..', file)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
VERSION = version[:string] unless defined?(EmailVeracity::VERSION)
|
|
55
|
+
|
|
56
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module EmailVeracity
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Address
|
|
5
|
+
|
|
6
|
+
include Validatability
|
|
7
|
+
|
|
8
|
+
attr_reader :domain
|
|
9
|
+
|
|
10
|
+
def initialize(email = '')
|
|
11
|
+
self.email_address = email
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def to_s
|
|
15
|
+
email_address
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def email_address
|
|
19
|
+
@email_address.to_s.strip
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def email_address=(new_email_address)
|
|
23
|
+
@email_address = new_email_address
|
|
24
|
+
@domain = Domain.new(@email_address.split('@')[1] || '')
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
protected
|
|
28
|
+
def validate!
|
|
29
|
+
add_error(:malformed) if !pattern_valid?
|
|
30
|
+
return unless Config[:lookup]
|
|
31
|
+
add_errors(domain.errors)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def pattern_valid?
|
|
35
|
+
@email_address =~ Config[:valid_pattern]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
module EmailVeracity
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Config
|
|
5
|
+
|
|
6
|
+
DEFAULT_OPTIONS = {
|
|
7
|
+
:whitelist => %w[ aol.com gmail.com hotmail.com mac.com msn.com
|
|
8
|
+
rogers.com sympatico.ca yahoo.com telus.com sprint.com sprint.ca ],
|
|
9
|
+
:blacklist => %w[ dodgeit.com mintemail.com mintemail.uni.cc
|
|
10
|
+
1mintemail.mooo.com spammotel.com trashmail.net ],
|
|
11
|
+
:valid_pattern => /\A(([\d\w\+_\-\.]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})){1}\Z/i,
|
|
12
|
+
:timeout => 2,
|
|
13
|
+
:lookup => [:a],
|
|
14
|
+
:enforce_blacklist => false,
|
|
15
|
+
:enforce_whitelist => true }
|
|
16
|
+
@options = DEFAULT_OPTIONS.clone
|
|
17
|
+
|
|
18
|
+
def Config.[](key)
|
|
19
|
+
@options[key.to_sym]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def Config.[]=(key, value)
|
|
23
|
+
@options[key.to_sym] = value
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def Config.options
|
|
27
|
+
@options
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def Config.options=(options)
|
|
31
|
+
unless options.is_a?(Hash)
|
|
32
|
+
raise ArgumentError, "options must be a Hash not #{options.class}"
|
|
33
|
+
end
|
|
34
|
+
@options = options
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def Config.update(options)
|
|
38
|
+
unless options.is_a?(Hash)
|
|
39
|
+
raise ArgumentError, "options must be a Hash not #{options.class}"
|
|
40
|
+
end
|
|
41
|
+
@options.update(options)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def Config.enforce_lookup?(record)
|
|
45
|
+
return unless @options[:lookup].is_a?(Array)
|
|
46
|
+
@options[:lookup].any? { |i| i.to_s == record.to_s }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def Config.revert!
|
|
50
|
+
@options = DEFAULT_OPTIONS.clone
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module EmailVeracity
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ::Array
|
|
5
|
+
|
|
6
|
+
def reject_blank_items
|
|
7
|
+
reject { |i| i.to_s.strip.blank? }
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def contains_single_item?
|
|
11
|
+
size == 1
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ::Object
|
|
18
|
+
|
|
19
|
+
# Snaked from Rails.
|
|
20
|
+
def blank?
|
|
21
|
+
respond_to?(:empty?) ? empty? : !self
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
module EmailVeracity
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Domain
|
|
5
|
+
|
|
6
|
+
include Validatability
|
|
7
|
+
|
|
8
|
+
def Domain.whitelisted?(name)
|
|
9
|
+
Config[:whitelist].include?(name.downcase.strip)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def Domain.blacklisted?(name)
|
|
13
|
+
Config[:blacklist].include?(name.downcase.strip)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def initialize(name = '')
|
|
17
|
+
@name = name
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def to_s
|
|
21
|
+
name
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def name
|
|
25
|
+
@name.to_s.downcase.strip
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def whitelisted?
|
|
29
|
+
Domain.whitelisted?(name)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def blacklisted?
|
|
33
|
+
Domain.blacklisted?(name)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def address_servers
|
|
37
|
+
@address_servers ||= servers_in(:a)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def exchange_servers
|
|
41
|
+
@exchange_servers ||= servers_in(:mx)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def servers
|
|
45
|
+
address_servers + exchange_servers
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
protected
|
|
49
|
+
def validate!
|
|
50
|
+
return if whitelisted?
|
|
51
|
+
add_error(:blacklisted) if blacklisted? &&
|
|
52
|
+
Config[:enforce_blacklist]
|
|
53
|
+
add_error(:no_address_servers) if address_servers.empty? &&
|
|
54
|
+
Config.enforce_lookup?(:a)
|
|
55
|
+
add_error(:no_exchange_servers) if exchange_servers.empty? &&
|
|
56
|
+
Config.enforce_lookup?(:mx)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def servers_in(record)
|
|
60
|
+
return [] if name.blank?
|
|
61
|
+
Resolver.get_servers_for(name, record)
|
|
62
|
+
rescue DomainResourcesTimeoutError
|
|
63
|
+
add_error :timed_out
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module EmailVeracity
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Resolver
|
|
5
|
+
|
|
6
|
+
RECORD_NAMES_TO_RESOLVE_MAP = {
|
|
7
|
+
:a => {
|
|
8
|
+
:method => 'address',
|
|
9
|
+
:type => AddressServer,
|
|
10
|
+
:constant => Resolv::DNS::Resource::IN::A },
|
|
11
|
+
:mx => {
|
|
12
|
+
:method => 'exchange',
|
|
13
|
+
:type => ExchangeServer,
|
|
14
|
+
:constant => Resolv::DNS::Resource::IN::MX } }
|
|
15
|
+
|
|
16
|
+
def Resolver.get_servers_for(domain_name, record = :a)
|
|
17
|
+
Timeout::timeout(Config[:timeout]) do
|
|
18
|
+
get_resources_for(domain_name, record).collect do |server_name|
|
|
19
|
+
type = RECORD_NAMES_TO_RESOLVE_MAP[record.to_sym][:type]
|
|
20
|
+
type.new(server_name)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
rescue Timeout::Error
|
|
24
|
+
raise DomainResourcesTimeoutError,
|
|
25
|
+
"Timed out while try to resolve #{domain_name}"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
protected
|
|
29
|
+
def Resolver.get_resources_for(domain_name, record = :a)
|
|
30
|
+
Resolv::DNS.open do |server|
|
|
31
|
+
record_map = RECORD_NAMES_TO_RESOLVE_MAP[record]
|
|
32
|
+
resources = server.getresources(domain_name, record_map[:constant])
|
|
33
|
+
resources_to_servers(resources, record_map[:method])
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def Resolver.resources_to_servers(resources, resolve_method)
|
|
38
|
+
resources.inject([]) do |array, resource|
|
|
39
|
+
array << resource.method(resolve_method).call.to_s.strip
|
|
40
|
+
end.reject_blank_items
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module EmailVeracity
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Server
|
|
5
|
+
|
|
6
|
+
attr_reader :name
|
|
7
|
+
alias_method :to_s, :name
|
|
8
|
+
|
|
9
|
+
def initialize(name = '')
|
|
10
|
+
@name = name
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class AddressServer < Server; end
|
|
17
|
+
class ExchangeServer < Server; end
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module EmailVeracity
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
module Validatability
|
|
5
|
+
|
|
6
|
+
def valid?
|
|
7
|
+
self.errors.empty?
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def errors
|
|
11
|
+
self.clear_errors!
|
|
12
|
+
self.validate!
|
|
13
|
+
@errors
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
protected
|
|
17
|
+
def validate!
|
|
18
|
+
# Adds errors to the object.
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def clear_errors!
|
|
22
|
+
@errors = []
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def add_error(*new_errors)
|
|
26
|
+
@errors ||= []
|
|
27
|
+
@errors.concat(new_errors.flatten)
|
|
28
|
+
end
|
|
29
|
+
alias_method :add_errors, :add_error
|
|
30
|
+
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
class ClassWithValidationMock
|
|
2
|
+
|
|
3
|
+
include EmailVeracity::Validatability
|
|
4
|
+
|
|
5
|
+
attr_accessor :give_error
|
|
6
|
+
attr_accessor :give_errors
|
|
7
|
+
attr_accessor :give_array_of_errors
|
|
8
|
+
|
|
9
|
+
def validate!
|
|
10
|
+
add_error(:one) if give_error
|
|
11
|
+
add_errors(:two, :three) if give_errors
|
|
12
|
+
add_errors([:four, :five]) if give_array_of_errors
|
|
13
|
+
add_errors []
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
end
|
data/test/test_helper.rb
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
require 'test/unit'
|
|
2
|
+
require 'lib/email_veracity'
|
|
3
|
+
Dir.glob('test/mocks/*.rb') { |f| require(f) }
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Test::Unit::TestCase
|
|
7
|
+
|
|
8
|
+
def domain_names
|
|
9
|
+
%w[ viarails.net heycarsten.com yahoo.com gmail.com savvica.com
|
|
10
|
+
learnhub.com github.com google.com rogers.com amd.com adobe.com
|
|
11
|
+
unspace.ca xerox.com webkit.org cooltown.net aiderss.com
|
|
12
|
+
del.icio.us ]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def assert_empty(array, message = nil)
|
|
16
|
+
unless array.is_a?(Array)
|
|
17
|
+
raise ArgumentError, 'First argument must be an Array'
|
|
18
|
+
end
|
|
19
|
+
message = [message, "Expected #{array.inspect} to be empty."].
|
|
20
|
+
flatten.join("\n")
|
|
21
|
+
assert_block(message) { array.empty? }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def assert_not_empty(array, message = nil)
|
|
25
|
+
unless array.is_a?(Array)
|
|
26
|
+
raise ArgumentError, 'First argument must be an Array'
|
|
27
|
+
end
|
|
28
|
+
message = [message, "Expected #{array.inspect} to contain items."].
|
|
29
|
+
flatten.join("\n")
|
|
30
|
+
assert_block(message) { !array.empty? }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class AddressTest < 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
|
+
'Should errors should be an array.'
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class DefaultConfigurationAddressValidationsTest < Test::Unit::TestCase
|
|
19
|
+
|
|
20
|
+
def test_a_well_formed_address_with_a_whitelisted_domain
|
|
21
|
+
address = new_address('heycarsten@gmail.com')
|
|
22
|
+
assert address.valid?,
|
|
23
|
+
"Should be valid. @errors: #{address.errors.inspect}"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def test_a_well_formed_address_with_a_blacklisted_domain
|
|
27
|
+
address = new_address('heycarsten@dodgeit.com')
|
|
28
|
+
assert address.valid?,
|
|
29
|
+
"Should be valid. @errors: #{address.errors.inspect}"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def test_a_well_formed_address_that_does_not_exist
|
|
33
|
+
address = new_address('heycarsten@i-surely-do-not-exist.nil')
|
|
34
|
+
assert !address.valid?,
|
|
35
|
+
'Should not be valid.'
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def test_a_well_formed_address_that_exists
|
|
39
|
+
address = new_address('itsme@heycarsten.com')
|
|
40
|
+
assert address.valid?,
|
|
41
|
+
"Should be valid. @errors: #{address.errors.inspect}"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
def new_address(address = '')
|
|
46
|
+
EmailVeracity::Address.new(address)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
end
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ConfigTest < Test::Unit::TestCase
|
|
5
|
+
|
|
6
|
+
def test_default_whitelist_domains
|
|
7
|
+
assert_instance_of Array, EmailVeracity::Config[:whitelist]
|
|
8
|
+
assert_not_empty EmailVeracity::Config[:whitelist],
|
|
9
|
+
'Should have more than one item.'
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def test_default_blacklist_domains
|
|
13
|
+
assert_instance_of Array, EmailVeracity::Config[:blacklist]
|
|
14
|
+
assert_not_empty EmailVeracity::Config[:blacklist],
|
|
15
|
+
'Should have more than one item.'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def test_lookup_default_setting
|
|
19
|
+
assert_instance_of Array, EmailVeracity::Config[:lookup]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def test_enforce_lookup_with_symbols
|
|
23
|
+
assert EmailVeracity::Config.enforce_lookup?(:a),
|
|
24
|
+
'Should check for A records by default'
|
|
25
|
+
assert !EmailVeracity::Config.enforce_lookup?(:mx),
|
|
26
|
+
'Should not check for MX records be default'
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def test_enforce_lookup_with_strings
|
|
30
|
+
assert EmailVeracity::Config.enforce_lookup?('a'),
|
|
31
|
+
'Should check for A records by default'
|
|
32
|
+
assert !EmailVeracity::Config.enforce_lookup?('mx'),
|
|
33
|
+
'Should not check for MX records be default'
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def test_changing_and_reverting_configuration
|
|
37
|
+
EmailVeracity::Config.update(:lookup => false, :timeout => 3)
|
|
38
|
+
assert_equal false, EmailVeracity::Config[:lookup],
|
|
39
|
+
'Should change configuration.'
|
|
40
|
+
assert_equal 3, EmailVeracity::Config[:timeout]
|
|
41
|
+
'Should change configuration.'
|
|
42
|
+
EmailVeracity::Config.revert!
|
|
43
|
+
assert_equal EmailVeracity::Config::DEFAULT_OPTIONS,
|
|
44
|
+
EmailVeracity::Config.options,
|
|
45
|
+
'Should revert configuration'
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class DefaultValidAddressPatternTest < Test::Unit::TestCase
|
|
52
|
+
|
|
53
|
+
def test_default_valid_address_pattern
|
|
54
|
+
assert_instance_of Regexp, EmailVeracity::Config[:valid_pattern],
|
|
55
|
+
'Should exist as a regular expression.'
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def test_valid_email_addresses
|
|
59
|
+
%w[
|
|
60
|
+
goto@rubyfringe.ca
|
|
61
|
+
stuff+heyd00d@gmail.com
|
|
62
|
+
carsten_nielsen@gmail.com
|
|
63
|
+
carsten-nielsen@gmail.com
|
|
64
|
+
iwenttowestern@ivyleague.ca
|
|
65
|
+
old-skool@mail.mysite.on.ca
|
|
66
|
+
heycarsten@del.icio.us
|
|
67
|
+
nex3@haml.town
|
|
68
|
+
1234@aplace.com
|
|
69
|
+
carsten2@happyland.net
|
|
70
|
+
sweetCandy4@me-and-you.ca
|
|
71
|
+
jesus@god.com
|
|
72
|
+
neat@b.eat
|
|
73
|
+
].each do |address|
|
|
74
|
+
assert_match EmailVeracity::Config[:valid_pattern],
|
|
75
|
+
address,
|
|
76
|
+
"#{address} should be valid."
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def test_invalid_email_addresses
|
|
81
|
+
%w[
|
|
82
|
+
@failure.net
|
|
83
|
+
craptastic@
|
|
84
|
+
!!!!!@gmail.com
|
|
85
|
+
oh-noez@f4iL/\/\@il.net
|
|
86
|
+
someone@somewhere
|
|
87
|
+
this!fails@comtown.com
|
|
88
|
+
$oWrong@fail.net
|
|
89
|
+
charles\ babbage@gmail.com
|
|
90
|
+
,@crap.com
|
|
91
|
+
dis%20blos@dot.com
|
|
92
|
+
&^%$#$%@yojimbo.nil
|
|
93
|
+
"greetings\ friend"@comtastic.dk
|
|
94
|
+
this,fails@ice-t.com
|
|
95
|
+
ungültige@adresse.de
|
|
96
|
+
failure@10.0.0.1
|
|
97
|
+
douche@@bag.net
|
|
98
|
+
].each do |address|
|
|
99
|
+
assert_no_match EmailVeracity::Config[:valid_pattern],
|
|
100
|
+
address,
|
|
101
|
+
"#{address} should be invalid."
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ArrayExtensionsTest < Test::Unit::TestCase
|
|
5
|
+
|
|
6
|
+
def test_reject_blank_items
|
|
7
|
+
array = [[], {}, '', nil, ' ', 'good times!']
|
|
8
|
+
assert_equal ['good times!'], array.reject_blank_items, 'Should reject all blank and empty objects.'
|
|
9
|
+
array = [{:neat => 'o'}, ['cool'], 1, 's']
|
|
10
|
+
assert_equal array, array.reject_blank_items, 'Should not reject any filled or not-blank objects.'
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def test_contains_single_item
|
|
14
|
+
assert [''].contains_single_item?, 'Should contain one item.'
|
|
15
|
+
assert [nil].contains_single_item?, 'Should contain one item.'
|
|
16
|
+
assert [[]].contains_single_item?, 'Should contain one item.'
|
|
17
|
+
assert ![].contains_single_item?, 'Should not contain one item.'
|
|
18
|
+
assert ![nil, false].contains_single_item?, 'Should not contain one item.'
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def test_blank
|
|
22
|
+
assert [].blank?, '[] should be blank.'
|
|
23
|
+
assert ''.blank?, '"" should be blank.'
|
|
24
|
+
assert Hash.new.blank?, '{} should be blank.'
|
|
25
|
+
assert nil.blank?, 'nil should be blank.'
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class DomainTest < Test::Unit::TestCase
|
|
5
|
+
|
|
6
|
+
def test_blacklisted_domain
|
|
7
|
+
assert EmailVeracity::Domain.blacklisted?('dodgeit.com'),
|
|
8
|
+
'Should match a blacklisted domain.'
|
|
9
|
+
assert EmailVeracity::Domain.blacklisted?('DoDgEiT.cOm'),
|
|
10
|
+
'Should match a blacklisted domain regardless of case.'
|
|
11
|
+
assert EmailVeracity::Domain.blacklisted?(" dodgeit.com \r\n "),
|
|
12
|
+
'Should match a blacklisted domain regardless of whitespace.'
|
|
13
|
+
assert !EmailVeracity::Domain.blacklisted?('iamnotblacklisted.com'),
|
|
14
|
+
'Should not match a non-blacklisted domain.'
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def test_whitelisted_domain
|
|
18
|
+
assert EmailVeracity::Domain.whitelisted?('gmail.com'),
|
|
19
|
+
'Should match a whitelisted domain.'
|
|
20
|
+
assert EmailVeracity::Domain.whitelisted?('GmAiL.cOm'),
|
|
21
|
+
'Should match a whitelisted domain regardless of case.'
|
|
22
|
+
assert EmailVeracity::Domain.whitelisted?(" gmail.com \r\n "),
|
|
23
|
+
'Should match a whitelisted domain regardless of whitespace.'
|
|
24
|
+
assert !EmailVeracity::Domain.whitelisted?('iamnotwhitelisted.com'),
|
|
25
|
+
'Should not match a non-whitelisted domain.'
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def test_initializing_a_new_domain_with_whitespace
|
|
29
|
+
domain = new_domain(' heycarsten.com ')
|
|
30
|
+
assert_equal 'heycarsten.com', domain.name, 'Should strip whitespace.'
|
|
31
|
+
assert_respond_to domain, :to_s, 'Should have a to_s method.'
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def test_a_valid_domain_for_servers
|
|
35
|
+
domain = new_domain('gmail.com')
|
|
36
|
+
assert_not_empty domain.exchange_servers, 'Should contain mail servers.'
|
|
37
|
+
assert_not_empty domain.address_servers, 'Should contain address servers.'
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def test_a_valid_domain_with_whitespace_for_servers
|
|
41
|
+
domain = new_domain(' learnhub.com ')
|
|
42
|
+
assert_not_empty domain.exchange_servers, 'Should contain mail servers.'
|
|
43
|
+
assert_not_empty domain.address_servers, 'Should contain address servers.'
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def test_an_invalid_domain_for_servers
|
|
47
|
+
domain = new_domain('i-surely-do-not.exist')
|
|
48
|
+
assert_empty domain.exchange_servers, 'Should not contain exchange servers.'
|
|
49
|
+
assert_empty domain.address_servers, 'Should not contain address servers.'
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def test_a_blank_domain_for_servers
|
|
53
|
+
domain = new_domain('')
|
|
54
|
+
assert_empty domain.exchange_servers, 'Should not contain exchange servers.'
|
|
55
|
+
assert_empty domain.address_servers, 'Should not contain address servers.'
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def test_for_errors_on_a_valid_domain
|
|
59
|
+
domain = new_domain('yahoo.com')
|
|
60
|
+
assert_empty domain.errors, 'Should not have errors.'
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def test_for_errors_on_an_invalid_domain
|
|
64
|
+
domain = new_domain('i-surely-do-not.exist')
|
|
65
|
+
assert_not_empty domain.errors, 'Should have errors.'
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
def new_domain(name = 'heycarsten.com')
|
|
70
|
+
EmailVeracity::Domain.new(name)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ResolverTest < Test::Unit::TestCase
|
|
5
|
+
|
|
6
|
+
def test_consecutive_queries
|
|
7
|
+
EmailVeracity::Config[:timeout] = 60
|
|
8
|
+
assert_nothing_raised do
|
|
9
|
+
domain_names.each do |domain|
|
|
10
|
+
assert_instance_of Array,
|
|
11
|
+
EmailVeracity::Resolver.get_servers_for(domain),
|
|
12
|
+
'Should return an array of servers'
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def test_timing_out_while_resolving_a_domain
|
|
18
|
+
EmailVeracity::Config[:timeout] = 0.001
|
|
19
|
+
assert_raise EmailVeracity::DomainResourcesTimeoutError, 'Should time out' do
|
|
20
|
+
EmailVeracity::Resolver.get_servers_for('learnhub.com')
|
|
21
|
+
end
|
|
22
|
+
EmailVeracity::Config[:timeout] = 2
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ServerTest < Test::Unit::TestCase
|
|
5
|
+
|
|
6
|
+
def test_creating_a_new_blank_server_object
|
|
7
|
+
new_server = EmailVeracity::Server.new
|
|
8
|
+
assert_equal '', new_server.to_s,
|
|
9
|
+
'Should yield a blank string on call to to_s.'
|
|
10
|
+
assert_equal '', new_server.name,
|
|
11
|
+
'Should yield a blank string on call to name.'
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def test_creating_a_new_server_object_with_a_name_and_type
|
|
15
|
+
name = 'cooltown.ca'
|
|
16
|
+
new_server = EmailVeracity::Server.new(name)
|
|
17
|
+
assert_equal name, new_server.name,
|
|
18
|
+
'Should yield the provided name on call to name.'
|
|
19
|
+
assert_equal name, new_server.to_s,
|
|
20
|
+
'Should yield the provided name on call to to_s.'
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def test_creating_a_new_blank_server_object_and_setting_its_name_after_initialization
|
|
24
|
+
new_server = EmailVeracity::Server.new('igvita.com')
|
|
25
|
+
assert_raise NoMethodError, 'Should fail miserably.' do
|
|
26
|
+
new_server.name = name
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ValidatabilityTest < Test::Unit::TestCase
|
|
5
|
+
|
|
6
|
+
def test_includes_proper_methods
|
|
7
|
+
%w[ valid? validate! clear_errors! add_error errors ].each do |method_name|
|
|
8
|
+
assert_respond_to ClassWithValidationMock.new, method_name
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def test_add_error
|
|
13
|
+
mock = ClassWithValidationMock.new
|
|
14
|
+
mock.give_error = true
|
|
15
|
+
assert_equal 1, mock.errors.size, 'Should set one error.'
|
|
16
|
+
mock.give_errors = true
|
|
17
|
+
assert_equal [:one, :two, :three], mock.errors,
|
|
18
|
+
'Should push tow new errors for a total of three.'
|
|
19
|
+
mock.give_array_of_errors = true
|
|
20
|
+
assert_equal [:one, :two, :three, :four, :five], mock.errors,
|
|
21
|
+
'Should concat the array leaving two new errors for a total of five.'
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def test_valid?
|
|
25
|
+
mock = ClassWithValidationMock.new
|
|
26
|
+
assert mock.valid?, 'Should be valid by default.'
|
|
27
|
+
mock.give_error = true
|
|
28
|
+
assert !mock.valid?, 'Should not be valid if errors are set.'
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def test_errors
|
|
32
|
+
assert ClassWithValidationMock.new.errors.empty?,
|
|
33
|
+
'Should be empty by default.'
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: heycarsten-email-veracity
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.3.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Carsten Nielsen
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
|
|
12
|
+
date: 2008-07-06 00:00:00 -07:00
|
|
13
|
+
default_executable:
|
|
14
|
+
dependencies: []
|
|
15
|
+
|
|
16
|
+
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.
|
|
17
|
+
email: heycarsten@gmail.com
|
|
18
|
+
executables: []
|
|
19
|
+
|
|
20
|
+
extensions: []
|
|
21
|
+
|
|
22
|
+
extra_rdoc_files:
|
|
23
|
+
- README.rdoc
|
|
24
|
+
files:
|
|
25
|
+
- lib/email_veracity/address.rb
|
|
26
|
+
- lib/email_veracity/config.rb
|
|
27
|
+
- lib/email_veracity/core_extensions.rb
|
|
28
|
+
- lib/email_veracity/domain.rb
|
|
29
|
+
- lib/email_veracity/errors.rb
|
|
30
|
+
- lib/email_veracity/resolver.rb
|
|
31
|
+
- lib/email_veracity/server.rb
|
|
32
|
+
- lib/email_veracity/validatability.rb
|
|
33
|
+
- lib/email_veracity.rb
|
|
34
|
+
- README.rdoc
|
|
35
|
+
- VERSION
|
|
36
|
+
- Rakefile
|
|
37
|
+
- init.rb
|
|
38
|
+
has_rdoc: true
|
|
39
|
+
homepage: http://github.com/heycarsten/email-veracity
|
|
40
|
+
post_install_message:
|
|
41
|
+
rdoc_options:
|
|
42
|
+
- --title
|
|
43
|
+
- Email Veracity
|
|
44
|
+
- --main
|
|
45
|
+
- README.rdoc
|
|
46
|
+
- --line-numbers
|
|
47
|
+
- --inline-source
|
|
48
|
+
require_paths:
|
|
49
|
+
- lib
|
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: "0"
|
|
55
|
+
version:
|
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: "0"
|
|
61
|
+
version:
|
|
62
|
+
requirements: []
|
|
63
|
+
|
|
64
|
+
rubyforge_project:
|
|
65
|
+
rubygems_version: 1.2.0
|
|
66
|
+
signing_key:
|
|
67
|
+
specification_version: 2
|
|
68
|
+
summary: A simple library for checking the real-world validity of email addresses.
|
|
69
|
+
test_files:
|
|
70
|
+
- test/mocks/class_with_validation_mock.rb
|
|
71
|
+
- test/test_helper.rb
|
|
72
|
+
- test/unit/address_test.rb
|
|
73
|
+
- test/unit/config_test.rb
|
|
74
|
+
- test/unit/core_extensions_test.rb
|
|
75
|
+
- test/unit/domain_test.rb
|
|
76
|
+
- test/unit/resolver_test.rb
|
|
77
|
+
- test/unit/server_test.rb
|
|
78
|
+
- test/unit/validatability_test.rb
|