dnsaur 0.1.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.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +37 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +85 -0
- data/Guardfile +7 -0
- data/LICENSE +22 -0
- data/README.md +140 -0
- data/Rakefile +1 -0
- data/dnsaur.gemspec +22 -0
- data/lib/dnsaur/dnsaur.rb +61 -0
- data/lib/dnsaur/email_manipulation.rb +97 -0
- data/lib/dnsaur/sift_3_distance.rb +48 -0
- data/lib/dnsaur/version.rb +3 -0
- data/lib/dnsaur.rb +1 -0
- data/spec/lib/dnsaur/dnsaur_spec.rb +105 -0
- data/spec/lib/dnsaur/email_manipulation_spec.rb +123 -0
- data/spec/lib/dnsaur/sift_3_distance_spec.rb +34 -0
- data/spec/spec_helper.rb +22 -0
- data/tasks/debug.rake +4 -0
- data/tasks/rspec.rake +4 -0
- metadata +70 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: f66e48837e8ecf6f1054428ec24ded5bb6dac963
|
|
4
|
+
data.tar.gz: 60c6ead262c3ec8520a55c178fb4113978a1482a
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: aff9730e949bc99f3ce852f46a5b2183bb7cccc41def6e738da726734f3d9103fdcdf66e8c54a059d315cd3043bd5ef4ca60ade445710e87a13b590319aed295
|
|
7
|
+
data.tar.gz: 067d14eb95347c9eff2bae485b7cf3e8beddca05964cf614a493b2b613706067b51fd136aca82ca670082010885ea6367d0f44c3af03f5c6ad9ca632947be072
|
data/.coveralls.yml
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
repo_token: 8NC95fsJvtAh0zfZgqPvvDAjJqs4ZGXs9
|
data/.gitignore
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
*.gem
|
|
2
|
+
*.rbc
|
|
3
|
+
/.config
|
|
4
|
+
/coverage/
|
|
5
|
+
/InstalledFiles
|
|
6
|
+
/pkg/
|
|
7
|
+
/spec/reports/
|
|
8
|
+
/test/tmp/
|
|
9
|
+
/test/version_tmp/
|
|
10
|
+
/tmp/
|
|
11
|
+
|
|
12
|
+
## Specific to RubyMotion:
|
|
13
|
+
.dat*
|
|
14
|
+
.repl_history
|
|
15
|
+
build/
|
|
16
|
+
|
|
17
|
+
## Documentation cache and generated files:
|
|
18
|
+
/.yardoc/
|
|
19
|
+
/_yardoc/
|
|
20
|
+
/doc/
|
|
21
|
+
/rdoc/
|
|
22
|
+
|
|
23
|
+
## Environment normalisation:
|
|
24
|
+
/.bundle/
|
|
25
|
+
/lib/bundler/man/
|
|
26
|
+
|
|
27
|
+
# for a library or gem, you might want to ignore these files since the code is
|
|
28
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
29
|
+
# Gemfile.lock
|
|
30
|
+
# .ruby-version
|
|
31
|
+
# .ruby-gemset
|
|
32
|
+
|
|
33
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
|
34
|
+
.rvmrc
|
|
35
|
+
|
|
36
|
+
# OSX Operating files
|
|
37
|
+
.DS_Store
|
data/.rspec
ADDED
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.1.3
|
data/Gemfile
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
source 'https://rubygems.org'
|
|
2
|
+
|
|
3
|
+
group :development, :test do
|
|
4
|
+
gem "codeclimate-test-reporter", group: :test, require: nil
|
|
5
|
+
gem 'coveralls', require: false
|
|
6
|
+
gem 'guard-rspec', require: false
|
|
7
|
+
gem 'pry'
|
|
8
|
+
gem 'rake'
|
|
9
|
+
gem 'rspec', '~> 3.1.0'
|
|
10
|
+
gem 'terminal-notifier-guard'
|
|
11
|
+
end
|
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
GEM
|
|
2
|
+
remote: https://rubygems.org/
|
|
3
|
+
specs:
|
|
4
|
+
celluloid (0.16.0)
|
|
5
|
+
timers (~> 4.0.0)
|
|
6
|
+
codeclimate-test-reporter (0.4.1)
|
|
7
|
+
simplecov (>= 0.7.1, < 1.0.0)
|
|
8
|
+
coderay (1.1.0)
|
|
9
|
+
coveralls (0.7.1)
|
|
10
|
+
multi_json (~> 1.3)
|
|
11
|
+
rest-client
|
|
12
|
+
simplecov (>= 0.7)
|
|
13
|
+
term-ansicolor
|
|
14
|
+
thor
|
|
15
|
+
diff-lcs (1.2.5)
|
|
16
|
+
docile (1.1.5)
|
|
17
|
+
ffi (1.9.6)
|
|
18
|
+
formatador (0.2.5)
|
|
19
|
+
guard (2.7.0)
|
|
20
|
+
formatador (>= 0.2.4)
|
|
21
|
+
listen (~> 2.7)
|
|
22
|
+
lumberjack (~> 1.0)
|
|
23
|
+
pry (>= 0.9.12)
|
|
24
|
+
thor (>= 0.18.1)
|
|
25
|
+
guard-rspec (4.3.1)
|
|
26
|
+
guard (~> 2.1)
|
|
27
|
+
rspec (>= 2.14, < 4.0)
|
|
28
|
+
hitimes (1.2.2)
|
|
29
|
+
listen (2.7.11)
|
|
30
|
+
celluloid (>= 0.15.2)
|
|
31
|
+
rb-fsevent (>= 0.9.3)
|
|
32
|
+
rb-inotify (>= 0.9)
|
|
33
|
+
lumberjack (1.0.9)
|
|
34
|
+
method_source (0.8.2)
|
|
35
|
+
mime-types (2.4.3)
|
|
36
|
+
multi_json (1.10.1)
|
|
37
|
+
netrc (0.8.0)
|
|
38
|
+
pry (0.10.1)
|
|
39
|
+
coderay (~> 1.1.0)
|
|
40
|
+
method_source (~> 0.8.1)
|
|
41
|
+
slop (~> 3.4)
|
|
42
|
+
rake (10.1.0)
|
|
43
|
+
rb-fsevent (0.9.4)
|
|
44
|
+
rb-inotify (0.9.5)
|
|
45
|
+
ffi (>= 0.5.0)
|
|
46
|
+
rest-client (1.7.2)
|
|
47
|
+
mime-types (>= 1.16, < 3.0)
|
|
48
|
+
netrc (~> 0.7)
|
|
49
|
+
rspec (3.1.0)
|
|
50
|
+
rspec-core (~> 3.1.0)
|
|
51
|
+
rspec-expectations (~> 3.1.0)
|
|
52
|
+
rspec-mocks (~> 3.1.0)
|
|
53
|
+
rspec-core (3.1.7)
|
|
54
|
+
rspec-support (~> 3.1.0)
|
|
55
|
+
rspec-expectations (3.1.2)
|
|
56
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
57
|
+
rspec-support (~> 3.1.0)
|
|
58
|
+
rspec-mocks (3.1.3)
|
|
59
|
+
rspec-support (~> 3.1.0)
|
|
60
|
+
rspec-support (3.1.2)
|
|
61
|
+
simplecov (0.9.1)
|
|
62
|
+
docile (~> 1.1.0)
|
|
63
|
+
multi_json (~> 1.0)
|
|
64
|
+
simplecov-html (~> 0.8.0)
|
|
65
|
+
simplecov-html (0.8.0)
|
|
66
|
+
slop (3.6.0)
|
|
67
|
+
term-ansicolor (1.3.0)
|
|
68
|
+
tins (~> 1.0)
|
|
69
|
+
terminal-notifier-guard (1.6.1)
|
|
70
|
+
thor (0.19.1)
|
|
71
|
+
timers (4.0.1)
|
|
72
|
+
hitimes
|
|
73
|
+
tins (1.3.3)
|
|
74
|
+
|
|
75
|
+
PLATFORMS
|
|
76
|
+
ruby
|
|
77
|
+
|
|
78
|
+
DEPENDENCIES
|
|
79
|
+
codeclimate-test-reporter
|
|
80
|
+
coveralls
|
|
81
|
+
guard-rspec
|
|
82
|
+
pry
|
|
83
|
+
rake
|
|
84
|
+
rspec (~> 3.1.0)
|
|
85
|
+
terminal-notifier-guard
|
data/Guardfile
ADDED
data/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2014 Patrick Vice
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
data/README.md
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#Dnsaur
|
|
2
|
+
[](https://codeclimate.com/github/patvice/dnsaur) [](https://coveralls.io/r/patvice/dnsaur?branch=master) [](https://circleci.com/gh/patvice/dnsaur)
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
Dnsaur is a simple DNS checker / email corrector for ruby. This gem does three things; when a user misspells a domain, it suggests the right spelling, it provide simple reverse DNS helper methods to help verifiy these emails, and splits emails into three parts(top level domain, domain, address).
|
|
6
|
+
|
|
7
|
+
The email suggestion part of the gem is based off a small javascript library called
|
|
8
|
+
[mailcheck.js](https://github.com/mailcheck/mailcheck). If you are looking for a more front end
|
|
9
|
+
solution for correcting email input, I suggest checking this out.
|
|
10
|
+
|
|
11
|
+
## How the suggest works
|
|
12
|
+
|
|
13
|
+
When your user types in "user@hotnail.con", Dnsaur will suggest "user@hotmail.com". It can also
|
|
14
|
+
suggest top level domains, where a user types "user@hotmail.cmo" and would suggest ".com"
|
|
15
|
+
|
|
16
|
+
It does this by comparing a list of popular default domains and defualt top level domains suplied to
|
|
17
|
+
the gem.
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
Dnsaur can be used ether as an instance or as class methods. When creating an instance you need to
|
|
21
|
+
supply it with email,
|
|
22
|
+
```ruby
|
|
23
|
+
email = "test@example.com"
|
|
24
|
+
dns = Dnsaur.new email
|
|
25
|
+
=> #<Dnsaur:0x007fb8ea17d590 @original_email="test@example.com", ...
|
|
26
|
+
```
|
|
27
|
+
custom domains and/or custom top level domains are optional.
|
|
28
|
+
For more information on custom domain, see the `Domains`section below.
|
|
29
|
+
|
|
30
|
+
### Email Suggestion/Correction
|
|
31
|
+
|
|
32
|
+
In the suggest method, you can pass an email and it will return a suggested email in a hash
|
|
33
|
+
example:
|
|
34
|
+
```ruby
|
|
35
|
+
# Class Methods
|
|
36
|
+
email = "test@hotnail.con"
|
|
37
|
+
Dnsaur.suggest email
|
|
38
|
+
=>
|
|
39
|
+
{
|
|
40
|
+
address: 'test', // the address; part before the @ sign
|
|
41
|
+
domain: 'hotmail.com', // the suggested domain
|
|
42
|
+
top_level_domain: 'com', // the suggested top level domain
|
|
43
|
+
full: 'test@hotmail.com' // the full suggested email
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
If there is no match, or it is an exact match to a defualt domain, it will return false.
|
|
47
|
+
|
|
48
|
+
### Reverse DNS lookup Helpers
|
|
49
|
+
The DNS lookup for this gem uses `Resolv` from the ruby stdlib, but I supplied class/instanced helper methods
|
|
50
|
+
for ease of use. If you have an instances of the class, it
|
|
51
|
+
|
|
52
|
+
```ruby
|
|
53
|
+
#Class Methods
|
|
54
|
+
email = "test@example.com"
|
|
55
|
+
Dnsaur.valid_dns? email
|
|
56
|
+
=> true || false
|
|
57
|
+
|
|
58
|
+
#Instant Methods
|
|
59
|
+
email = "test@hotnail.com"
|
|
60
|
+
dns = Dnsaur.new email
|
|
61
|
+
dns.valid_original_dns?
|
|
62
|
+
=> true || false
|
|
63
|
+
|
|
64
|
+
dns.valid_suggested_email?
|
|
65
|
+
=> true || false
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Spliting and Vaildating Emails
|
|
69
|
+
|
|
70
|
+
The split_email method is used to split the email into three parts
|
|
71
|
+
```ruby
|
|
72
|
+
email = "test@exmaple.com"
|
|
73
|
+
Dnsaur.split_email email
|
|
74
|
+
{
|
|
75
|
+
top_level_domain: 'com', // the top level domain
|
|
76
|
+
domain: 'example.com', // the domain
|
|
77
|
+
address: 'test' // the address; part before the @ sign
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Domains
|
|
82
|
+
Just like [mailcheck.js](https://github.com/mailcheck/mailcheck), you can and should give Dnsaur
|
|
83
|
+
a custom list of domains/top level domains that your user will most likely use to sign up.
|
|
84
|
+
|
|
85
|
+
This can be done in two different way, ether by giving a completly new list and passing it
|
|
86
|
+
into and instance:
|
|
87
|
+
```ruby
|
|
88
|
+
email = "test@example.com"
|
|
89
|
+
domains = ['customdomain.com', 'anotherdomain.net']
|
|
90
|
+
top_level_domains = ['com.au', 'ru']
|
|
91
|
+
|
|
92
|
+
Dnsaur.default_domains = domains
|
|
93
|
+
Dnsaur.default_top_level_domains = top_level_domains
|
|
94
|
+
|
|
95
|
+
Dnsaur.default_domains
|
|
96
|
+
=> ["customdomain.com", "anotherdomain.net"]
|
|
97
|
+
Dnsaur.default_top_level_domains
|
|
98
|
+
=> ["com.au", "ru"]
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Or if you want to just add values to the default domains provided,
|
|
102
|
+
you can pushing new values into the class constants:
|
|
103
|
+
```ruby
|
|
104
|
+
Dnsaur.default_domains.push('customdomain.com', 'anotherdomain.net')
|
|
105
|
+
Dnsaur.default_top_level_domains.push('com.au', 'ru')
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`default_domains` and `default_top_level_domains` are class members, with one change, all other instances of your class will have your custom list of domains
|
|
109
|
+
|
|
110
|
+
## Installation
|
|
111
|
+
|
|
112
|
+
Add this line to your application's Gemfile:
|
|
113
|
+
|
|
114
|
+
gem 'dnsaur'
|
|
115
|
+
|
|
116
|
+
And then execute:
|
|
117
|
+
|
|
118
|
+
$ bundle
|
|
119
|
+
|
|
120
|
+
Or install it yourself as:
|
|
121
|
+
|
|
122
|
+
$ gem install dnsaur
|
|
123
|
+
|
|
124
|
+
## Still left Todo
|
|
125
|
+
|
|
126
|
+
There is still alot of polish that I would like to do to the code in this gem, and I would
|
|
127
|
+
also like to add some more functionality for validating emails:
|
|
128
|
+
|
|
129
|
+
- More test, and better test for all specs
|
|
130
|
+
- RFC822 regexp-based to be able to validate emails. [link](http://ex-parrot.com/~pdw/Mail-RFC822-Address.html)
|
|
131
|
+
- Using STMP VRFY command as well as RCTP TO: for better DNS validation
|
|
132
|
+
- Improve suggest methods readlity, and usablity
|
|
133
|
+
|
|
134
|
+
## Contributing
|
|
135
|
+
|
|
136
|
+
1. Fork it ( https://github.com/[my-github-username]/dnsaur/fork )
|
|
137
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
138
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
139
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
140
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Dir.glob('tasks/**/*.rake').each(&method(:import))
|
data/dnsaur.gemspec
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
$:.push File.expand_path('../lib', __FILE__)
|
|
2
|
+
require 'dnsaur'
|
|
3
|
+
|
|
4
|
+
Gem::Specification.new do |s|
|
|
5
|
+
s.name = 'dnsaur'
|
|
6
|
+
s.version = Dnsaur::VERSION
|
|
7
|
+
s.date = '2014-10-25'
|
|
8
|
+
s.authors = ['Patrick Vice']
|
|
9
|
+
s.email = ['patrickgvice@gmail.com']
|
|
10
|
+
s.summary = 'A simple dns checker/ mistyped email suggester'
|
|
11
|
+
s.description = <<-EOF
|
|
12
|
+
Dnsaur is a simple DNS checker / email corrector for ruby. This gem does three
|
|
13
|
+
things; when a user misspells a domain, it suggests the right spelling, it
|
|
14
|
+
provide simple reverse DNS helper methods to help verifiy these emails, and
|
|
15
|
+
splits emails into three parts(top level domain, domain, address).
|
|
16
|
+
EOF
|
|
17
|
+
s.homepage = 'https://github.com/pavice/dnsaur'
|
|
18
|
+
s.license = 'MIT'
|
|
19
|
+
|
|
20
|
+
s.files = `git ls-files`.split("\n")
|
|
21
|
+
s.require_paths = ["lib"]
|
|
22
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
require "dnsaur/version"
|
|
2
|
+
require "dnsaur/sift_3_distance"
|
|
3
|
+
require "dnsaur/email_manipulation"
|
|
4
|
+
|
|
5
|
+
class Dnsaur
|
|
6
|
+
include EmailManipulation
|
|
7
|
+
|
|
8
|
+
ARG_ERROR = "An email can not be empty or invalid"
|
|
9
|
+
attr_reader :original_email
|
|
10
|
+
|
|
11
|
+
def initialize email
|
|
12
|
+
if Dnsaur.valid_email? email
|
|
13
|
+
@original_email = email
|
|
14
|
+
else
|
|
15
|
+
raise ArgumentError, ARG_ERROR
|
|
16
|
+
end
|
|
17
|
+
@suggested_email = suggest @original_email
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.open(*args)
|
|
21
|
+
dnsaur = new(*args)
|
|
22
|
+
return dnsaur unless block_given?
|
|
23
|
+
begin
|
|
24
|
+
yield dnsaur
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def original_email
|
|
29
|
+
@original_email
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def original_email= email
|
|
33
|
+
raise ArgumentError, ARG_ERROR unless Dnsaur.valid_email? email
|
|
34
|
+
@original_email = email
|
|
35
|
+
@suggested_email = suggest email
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def suggested_email
|
|
39
|
+
return false unless @suggested_email
|
|
40
|
+
@suggested_email[:full] if @suggested_email
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def valid_original_dns?
|
|
44
|
+
Dnsaur.valid_dns? @original_email
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def valid_suggested_dns?
|
|
48
|
+
return false unless @suggested_email
|
|
49
|
+
Dnsaur.valid_dns? @suggested_email[:full]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def self.valid_dns? email
|
|
53
|
+
email_parts = Dnsaur.split_email email
|
|
54
|
+
mx = Resolv::DNS.open.getresources(email_parts[:domain], Resolv::DNS::Resource::IN::MX)
|
|
55
|
+
mx.size > 0 ? true : false
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def self.valid_email? email
|
|
59
|
+
!!(Dnsaur.split_email email)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
require 'pry'
|
|
2
|
+
|
|
3
|
+
module EmailManipulation
|
|
4
|
+
include Sift3Distance
|
|
5
|
+
|
|
6
|
+
DOMAIN_THRESHOLD = 4
|
|
7
|
+
TOP_LEVEL_THRESHOLD = 3
|
|
8
|
+
DEFAULT_ERROR = "default domains list can't be nil or empty"
|
|
9
|
+
TOP_LEVEL_ERROR = "default top level domains can't be nil or empty"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@@default_domains = ["yahoo.com", "google.com", "hotmail.com", "gmail.com", "me.com", "aol.com", "mac.com",
|
|
13
|
+
"live.com", "comcast.net", "googlemail.com", "msn.com", "hotmail.co.uk", "yahoo.co.uk",
|
|
14
|
+
"facebook.com", "verizon.net", "sbcglobal.net", "att.net", "gmx.com", "mail.com",
|
|
15
|
+
"outlook.com", "icloud.com"]
|
|
16
|
+
|
|
17
|
+
@@default_top_level_domains = ["co.jp", "co.uk", "com", "net", "org", "info", "edu", "gov", "mil", "ca"]
|
|
18
|
+
|
|
19
|
+
def default_domains
|
|
20
|
+
@@default_domains
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def default_domains= domains
|
|
24
|
+
raise ArgumentError, DEFAULT_ERROR if domains.nil? || domains.empty?
|
|
25
|
+
@@default_domains = domains
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def default_top_level_domains
|
|
29
|
+
@@default_top_level_domains
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def default_top_level_domains= top_level_domains
|
|
33
|
+
raise ArgumentError, TOP_LEVEL_ERROR if top_level_domains.nil? || top_level_domains.empty?
|
|
34
|
+
@@default_top_level_domains = top_level_domains
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def suggest email
|
|
38
|
+
email_parts = self.split_email(email)
|
|
39
|
+
closest_domain = self.find_closest_domain(email_parts[:domain], @@default_domains, DOMAIN_THRESHOLD)
|
|
40
|
+
|
|
41
|
+
if closest_domain
|
|
42
|
+
if closest_domain != email_parts[:domain]
|
|
43
|
+
return { address: email_parts[:address], domain: closest_domain, full: email_parts[:address] + "@" + closest_domain }
|
|
44
|
+
end
|
|
45
|
+
else
|
|
46
|
+
closest_top_level_domain = self.find_closest_domain(email_parts[:top_level_domain], @@default_top_level_domains, TOP_LEVEL_THRESHOLD)
|
|
47
|
+
|
|
48
|
+
if closest_top_level_domain && closest_top_level_domain != email_parts[:top_level_domain]
|
|
49
|
+
closest_domain = email_parts[:domain].split('.', 2).first + '.' + closest_top_level_domain
|
|
50
|
+
|
|
51
|
+
return { address: email_parts[:address], domain: closest_domain, full: email_parts[:address] + "@" + closest_domain }
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
return false
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def split_email email
|
|
59
|
+
parts = email.downcase.split('@')
|
|
60
|
+
|
|
61
|
+
return false if parts.length < 2 || parts.include?('')
|
|
62
|
+
|
|
63
|
+
address = parts.first
|
|
64
|
+
domain = parts.last
|
|
65
|
+
top_level_domain = domain.split('.', 2).last
|
|
66
|
+
|
|
67
|
+
return {top_level_domain: top_level_domain, domain: domain, address: address}
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def find_closest_domain domain, domains, threshold
|
|
71
|
+
min_distance = 99
|
|
72
|
+
closest_domain = nil
|
|
73
|
+
|
|
74
|
+
return false if !domain || !domains
|
|
75
|
+
|
|
76
|
+
domains.each do |d|
|
|
77
|
+
if domain == d
|
|
78
|
+
return
|
|
79
|
+
end
|
|
80
|
+
dist = self.sift_3_distance(domain, d)
|
|
81
|
+
if (dist < min_distance)
|
|
82
|
+
min_distance = dist
|
|
83
|
+
closest_domain = d
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
if min_distance <= threshold && !closest_domain.nil?
|
|
88
|
+
return closest_domain
|
|
89
|
+
else
|
|
90
|
+
return false
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def self.included(base)
|
|
95
|
+
base.extend(EmailManipulation)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module Sift3Distance
|
|
2
|
+
|
|
3
|
+
def sift_3_distance s1, s2
|
|
4
|
+
#My version of the the sift3 algorithm re-written in ruby
|
|
5
|
+
# sift3: http://siderite.blogspot.com/2007/04/super-fast-and-accurate-string-distance.html
|
|
6
|
+
if s1.nil? || s1.length == 0
|
|
7
|
+
if s2.nil? || s2.length == 0
|
|
8
|
+
return 0;
|
|
9
|
+
else
|
|
10
|
+
return s2.length
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
return s1.length if s2.nil? || s2.length == 0
|
|
15
|
+
|
|
16
|
+
c = 0
|
|
17
|
+
offset1 = 0
|
|
18
|
+
offset2 = 0
|
|
19
|
+
lcs = 0
|
|
20
|
+
max_offset = 5
|
|
21
|
+
|
|
22
|
+
begin
|
|
23
|
+
if s1[c + offset1] == s2[c + offset2]
|
|
24
|
+
lcs+= 1
|
|
25
|
+
else
|
|
26
|
+
offset1 = 0
|
|
27
|
+
offset2 = 0
|
|
28
|
+
max_offset.times do |i|
|
|
29
|
+
if (c+i < s1.length) && (s1[c+i] == s2[c])
|
|
30
|
+
offset1 = i
|
|
31
|
+
break
|
|
32
|
+
end
|
|
33
|
+
if (c+i < s2.length) && (s1[c] == s2[c+i])
|
|
34
|
+
offset2 = i
|
|
35
|
+
break
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
c+= 1
|
|
40
|
+
end while c+offset1 < s1.length && c+offset2 < s2.length
|
|
41
|
+
|
|
42
|
+
(s1.length + s2.length)/2 - lcs
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def self.included(base)
|
|
46
|
+
base.extend(Sift3Distance)
|
|
47
|
+
end
|
|
48
|
+
end
|
data/lib/dnsaur.rb
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require "dnsaur/dnsaur"
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Dnsaur do
|
|
4
|
+
|
|
5
|
+
describe "::initialize" do
|
|
6
|
+
it "when the email is invalid it raises an error" do
|
|
7
|
+
email = ""
|
|
8
|
+
expect{ Dnsaur.new email }.to raise_error(ArgumentError)
|
|
9
|
+
end
|
|
10
|
+
it "suggests an email when it being initialized" do
|
|
11
|
+
email = "test@hotnail.con"
|
|
12
|
+
dns = Dnsaur.new email
|
|
13
|
+
expect(dns.suggested_email).to be_truthy
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
context "class methods" do
|
|
18
|
+
|
|
19
|
+
describe "::valid_dns?" do
|
|
20
|
+
it "returns true when there reverse DNS lookup returns MX" do
|
|
21
|
+
email = "test@example.com"
|
|
22
|
+
Resolv::DNS.stub_chain(:open, :getresources).and_return ['true', 'true']
|
|
23
|
+
expect(Dnsaur.valid_dns? email).to be true
|
|
24
|
+
end
|
|
25
|
+
it "returns false when there is no MX for that domain" do
|
|
26
|
+
email = "test@example.com"
|
|
27
|
+
Resolv::DNS.stub_chain(:open, :getresources).and_return []
|
|
28
|
+
expect(Dnsaur.valid_dns? email).to be false
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
describe "::valid_email?" do
|
|
33
|
+
it "returns true when the email format is valid" do
|
|
34
|
+
email = "test@example.com"
|
|
35
|
+
expect(Dnsaur.valid_email? email).to be true
|
|
36
|
+
end
|
|
37
|
+
it "returns false when the email format is not valid" do
|
|
38
|
+
email = "test@"
|
|
39
|
+
expect(Dnsaur.valid_email? email).to be false
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
context 'instance methods' do
|
|
45
|
+
before do
|
|
46
|
+
@dnsaur = Dnsaur.new "test@hotnail.com"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
describe "#original_email" do
|
|
50
|
+
it "returns an email given when a new instance is created" do
|
|
51
|
+
expect(@dnsaur.original_email).to eq("test@hotnail.com")
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
describe "#original_email=" do
|
|
56
|
+
it "sets a new email for that object" do
|
|
57
|
+
email = "test@example.com"
|
|
58
|
+
@dnsaur.original_email = email
|
|
59
|
+
expect(@dnsaur.original_email).to eq(email)
|
|
60
|
+
end
|
|
61
|
+
it "sets a new suggestion when an email is changed" do
|
|
62
|
+
email = "test@example.com"
|
|
63
|
+
@dnsaur.original_email = email
|
|
64
|
+
expect(@dnsaur.suggested_email).to be_falsey
|
|
65
|
+
end
|
|
66
|
+
it "raises an error when trying to change to an invalid email" do
|
|
67
|
+
email = "test@"
|
|
68
|
+
expect{@dnsaur.original_email= email}.to raise_error(ArgumentError)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
describe "#suggested_email" do
|
|
73
|
+
it "returns a suggested email" do
|
|
74
|
+
expect(@dnsaur.suggested_email).to eq('test@hotmail.com')
|
|
75
|
+
end
|
|
76
|
+
it "it returns false if there is not suggest" do
|
|
77
|
+
email = "test@example.com"
|
|
78
|
+
@dnsaur.original_email = email
|
|
79
|
+
expect(@dnsaur.suggested_email).to be_falsey
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
describe "#valid_original_dns?" do
|
|
84
|
+
it "returns true when reverse DNS lookup returns MX" do
|
|
85
|
+
Resolv::DNS.stub_chain(:open, :getresources).and_return ['true', 'true']
|
|
86
|
+
expect(@dnsaur.valid_original_dns?).to be true
|
|
87
|
+
end
|
|
88
|
+
it "returns fail when there is no MX for that domain" do
|
|
89
|
+
Resolv::DNS.stub_chain(:open, :getresources).and_return []
|
|
90
|
+
expect(@dnsaur.valid_original_dns?).to be false
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
describe "#valid_suggested_dns?" do
|
|
95
|
+
it "returns true when there reverse DNS lookup returns MX" do
|
|
96
|
+
Resolv::DNS.stub_chain(:open, :getresources).and_return ['true', 'true']
|
|
97
|
+
expect(@dnsaur.valid_suggested_dns?).to be true
|
|
98
|
+
end
|
|
99
|
+
it "returns false when there is no MX for that domain" do
|
|
100
|
+
Resolv::DNS.stub_chain(:open, :getresources).and_return []
|
|
101
|
+
expect(@dnsaur.valid_suggested_dns?).to be false
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe EmailManipulation do
|
|
4
|
+
let(:domains) {["yahoo.com", "google.com", "hotmail.com", "gmail.com", "me.com", "aol.com", "mac.com",
|
|
5
|
+
"live.com", "comcast.net", "googlemail.com", "msn.com", "hotmail.co.uk", "yahoo.co.uk",
|
|
6
|
+
"facebook.com", "verizon.net", "sbcglobal.net", "att.net", "gmx.com", "mail.com",
|
|
7
|
+
"outlook.com", "icloud.com"] }
|
|
8
|
+
|
|
9
|
+
let(:top_level_domains){ ["co.jp", "co.uk", "com", "net", "org", "info", "edu", "gov", "mil", "ca"] }
|
|
10
|
+
|
|
11
|
+
class DummyEmailManipulation
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
before(:each) do
|
|
15
|
+
@email_manipulation = DummyEmailManipulation.new
|
|
16
|
+
@email_manipulation.extend(EmailManipulation)
|
|
17
|
+
end
|
|
18
|
+
after(:each) do
|
|
19
|
+
@email_manipulation.default_domains = domains
|
|
20
|
+
@email_manipulation.default_top_level_domains = top_level_domains
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
describe "#default_domains" do
|
|
24
|
+
it "returns a list of domains" do
|
|
25
|
+
expect(@email_manipulation.default_domains).to be_truthy
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
describe "#default_domains=" do
|
|
30
|
+
it "assigns a new set of domains" do
|
|
31
|
+
domain_list = ['example.com', 'fake.org']
|
|
32
|
+
@email_manipulation.default_domains= domain_list
|
|
33
|
+
expect(@email_manipulation.default_domains).to eq(domain_list)
|
|
34
|
+
end
|
|
35
|
+
it "raise an error when passed an empty array" do
|
|
36
|
+
domain_list = []
|
|
37
|
+
expect{@email_manipulation.default_domains= domain_list}.to raise_error
|
|
38
|
+
end
|
|
39
|
+
it "raises an error when passed nil" do
|
|
40
|
+
domain_list = nil
|
|
41
|
+
expect{@email_manipulation.default_domains= domain_list}.to raise_error
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
describe "#default_top_level_domains" do
|
|
46
|
+
it "returns a list of top_level_domains" do
|
|
47
|
+
expect(@email_manipulation.default_top_level_domains).to be_truthy
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
describe "#default_top_level_domains=" do
|
|
52
|
+
it "assigns a new set of domains" do
|
|
53
|
+
tldomain_list = ['example.com', 'fake.org']
|
|
54
|
+
@email_manipulation.default_top_level_domains= tldomain_list
|
|
55
|
+
expect(@email_manipulation.default_top_level_domains).to eq(tldomain_list)
|
|
56
|
+
end
|
|
57
|
+
it "raise an error when passed an empty array" do
|
|
58
|
+
tldomain_list = []
|
|
59
|
+
expect{@email_manipulation.default_top_level_domains= tldomain_list}.to raise_error
|
|
60
|
+
end
|
|
61
|
+
it "raises an error when passed nil" do
|
|
62
|
+
tldomain_list = nil
|
|
63
|
+
expect{@email_manipulation.default_top_level_domains= tldomain_list}.to raise_error
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
describe "#suggest" do
|
|
68
|
+
it "returns a corrected top level domain" do
|
|
69
|
+
email = "test@example.con"
|
|
70
|
+
suggest_return = {address: "test", domain: "example.com", full: "test@example.com"}
|
|
71
|
+
expect(@email_manipulation.suggest email).to eq(suggest_return)
|
|
72
|
+
end
|
|
73
|
+
it "returns a corrected domain" do
|
|
74
|
+
email = "test@hotnail.con"
|
|
75
|
+
suggest_return = {address: "test", domain: "hotmail.com", full: "test@hotmail.com"}
|
|
76
|
+
expect(@email_manipulation.suggest email).to eq(suggest_return)
|
|
77
|
+
end
|
|
78
|
+
it "returns false when a email doesn't match default domains" do
|
|
79
|
+
email = "test@example.com"
|
|
80
|
+
expect(@email_manipulation.suggest email).to be false
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
describe "#split_email" do
|
|
85
|
+
it "splits an email into three parts" do
|
|
86
|
+
email = 'test@example.com'
|
|
87
|
+
split_return = {top_level_domain: 'com',domain: 'example.com', address: 'test'}
|
|
88
|
+
expect(@email_manipulation.split_email email).to eq(split_return)
|
|
89
|
+
end
|
|
90
|
+
it "returns the same top level domain/domain when givin an domain without a top level domain" do
|
|
91
|
+
#this is actually a valid email, according
|
|
92
|
+
email = 'test@example'
|
|
93
|
+
split_return = {:top_level_domain=>"example", :domain=>"example", :address=>"test"}
|
|
94
|
+
expect(@email_manipulation.split_email email).to eq(split_return)
|
|
95
|
+
end
|
|
96
|
+
it "returns false if the email doesn't have a domain" do
|
|
97
|
+
email = 'test@'
|
|
98
|
+
expect(@email_manipulation.split_email email).to be false
|
|
99
|
+
end
|
|
100
|
+
it "returns false if there is no @ symbol in the string" do
|
|
101
|
+
email = 'test'
|
|
102
|
+
expect(@email_manipulation.split_email email).to be false
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
describe "#find_closest_domain" do
|
|
107
|
+
it "returns false when passed nil attributes" do
|
|
108
|
+
expect(@email_manipulation.find_closest_domain nil, nil, 4).to be false
|
|
109
|
+
end
|
|
110
|
+
it "returns false when there is no match" do
|
|
111
|
+
domain = "example.com"
|
|
112
|
+
expect(@email_manipulation.find_closest_domain domain, domains, 4).to be false
|
|
113
|
+
end
|
|
114
|
+
it "returns nil when there is an exact match" do
|
|
115
|
+
domain = "hotmail.com"
|
|
116
|
+
expect(@email_manipulation.find_closest_domain domain, domains, 4).to be nil
|
|
117
|
+
end
|
|
118
|
+
it "passes a suggestion when there is a differece less then the threshold" do
|
|
119
|
+
domain = "hotmail.c"
|
|
120
|
+
expect(@email_manipulation.find_closest_domain domain, domains, 4).to eq('hotmail.com')
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Sift3Distance do
|
|
4
|
+
class DummySift3Distance
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
before(:each) do
|
|
8
|
+
@sift_3_distance = DummySift3Distance.new
|
|
9
|
+
@sift_3_distance.extend(Sift3Distance)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
describe "#sift_3_distance" do
|
|
13
|
+
it "returns the different of two strings" do
|
|
14
|
+
s1 = "example"
|
|
15
|
+
s2 = "eample"
|
|
16
|
+
expect(@sift_3_distance.sift_3_distance s1, s2).to eq 1
|
|
17
|
+
end
|
|
18
|
+
it "returns zero when both arguments is nil or empty" do
|
|
19
|
+
s1 = nil
|
|
20
|
+
s2 = ""
|
|
21
|
+
expect(@sift_3_distance.sift_3_distance s1, s2).to eq 0
|
|
22
|
+
end
|
|
23
|
+
it "returns the length of the first string if the second sring is nil or empty" do
|
|
24
|
+
s1 = "example"
|
|
25
|
+
s2 = nil
|
|
26
|
+
expect(@sift_3_distance.sift_3_distance s1, s2).to eq s1.length
|
|
27
|
+
end
|
|
28
|
+
it "returns the length of the second string if the first string is nil or empty" do
|
|
29
|
+
s1 = nil
|
|
30
|
+
s2 = "example"
|
|
31
|
+
expect(@sift_3_distance.sift_3_distance s1, s2).to eq s2.length
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require "codeclimate-test-reporter"
|
|
2
|
+
CodeClimate::TestReporter.start
|
|
3
|
+
|
|
4
|
+
require 'coveralls'
|
|
5
|
+
Coveralls.wear!
|
|
6
|
+
|
|
7
|
+
require 'bundler/setup'
|
|
8
|
+
Bundler.setup
|
|
9
|
+
|
|
10
|
+
require 'dnsaur'
|
|
11
|
+
|
|
12
|
+
Dir[File.dirname(__FILE__) + "/support/**/*.rb"].each {|f| require f}
|
|
13
|
+
|
|
14
|
+
RSpec.configure do |config|
|
|
15
|
+
config.expect_with :rspec do |expectations|
|
|
16
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
config.mock_with :rspec do |mocks|
|
|
20
|
+
mocks.verify_partial_doubles = true
|
|
21
|
+
end
|
|
22
|
+
end
|
data/tasks/debug.rake
ADDED
data/tasks/rspec.rake
ADDED
metadata
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: dnsaur
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Patrick Vice
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2014-10-25 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: |2
|
|
14
|
+
Dnsaur is a simple DNS checker / email corrector for ruby. This gem does three
|
|
15
|
+
things; when a user misspells a domain, it suggests the right spelling, it
|
|
16
|
+
provide simple reverse DNS helper methods to help verifiy these emails, and
|
|
17
|
+
splits emails into three parts(top level domain, domain, address).
|
|
18
|
+
email:
|
|
19
|
+
- patrickgvice@gmail.com
|
|
20
|
+
executables: []
|
|
21
|
+
extensions: []
|
|
22
|
+
extra_rdoc_files: []
|
|
23
|
+
files:
|
|
24
|
+
- ".coveralls.yml"
|
|
25
|
+
- ".gitignore"
|
|
26
|
+
- ".rspec"
|
|
27
|
+
- ".ruby-version"
|
|
28
|
+
- Gemfile
|
|
29
|
+
- Gemfile.lock
|
|
30
|
+
- Guardfile
|
|
31
|
+
- LICENSE
|
|
32
|
+
- README.md
|
|
33
|
+
- Rakefile
|
|
34
|
+
- dnsaur.gemspec
|
|
35
|
+
- lib/dnsaur.rb
|
|
36
|
+
- lib/dnsaur/dnsaur.rb
|
|
37
|
+
- lib/dnsaur/email_manipulation.rb
|
|
38
|
+
- lib/dnsaur/sift_3_distance.rb
|
|
39
|
+
- lib/dnsaur/version.rb
|
|
40
|
+
- spec/lib/dnsaur/dnsaur_spec.rb
|
|
41
|
+
- spec/lib/dnsaur/email_manipulation_spec.rb
|
|
42
|
+
- spec/lib/dnsaur/sift_3_distance_spec.rb
|
|
43
|
+
- spec/spec_helper.rb
|
|
44
|
+
- tasks/debug.rake
|
|
45
|
+
- tasks/rspec.rake
|
|
46
|
+
homepage: https://github.com/pavice/dnsaur
|
|
47
|
+
licenses:
|
|
48
|
+
- MIT
|
|
49
|
+
metadata: {}
|
|
50
|
+
post_install_message:
|
|
51
|
+
rdoc_options: []
|
|
52
|
+
require_paths:
|
|
53
|
+
- lib
|
|
54
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
55
|
+
requirements:
|
|
56
|
+
- - ">="
|
|
57
|
+
- !ruby/object:Gem::Version
|
|
58
|
+
version: '0'
|
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
60
|
+
requirements:
|
|
61
|
+
- - ">="
|
|
62
|
+
- !ruby/object:Gem::Version
|
|
63
|
+
version: '0'
|
|
64
|
+
requirements: []
|
|
65
|
+
rubyforge_project:
|
|
66
|
+
rubygems_version: 2.2.2
|
|
67
|
+
signing_key:
|
|
68
|
+
specification_version: 4
|
|
69
|
+
summary: A simple dns checker/ mistyped email suggester
|
|
70
|
+
test_files: []
|