name_checker 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +21 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +66 -0
- data/Rakefile +2 -0
- data/lib/name_checker.rb +27 -0
- data/lib/name_checker/availability.rb +39 -0
- data/lib/name_checker/configuration.rb +30 -0
- data/lib/name_checker/facebook_checker.rb +37 -0
- data/lib/name_checker/logging.rb +26 -0
- data/lib/name_checker/net_checker.rb +15 -0
- data/lib/name_checker/railties.rb +7 -0
- data/lib/name_checker/robo_whois_checker.rb +61 -0
- data/lib/name_checker/twitter_checker.rb +67 -0
- data/lib/name_checker/version.rb +3 -0
- data/name_checker.gemspec +25 -0
- data/spec/fixtures/vcr_cassettes/facebook/available.yml +44 -0
- data/spec/fixtures/vcr_cassettes/facebook/unavailable.yml +42 -0
- data/spec/fixtures/vcr_cassettes/facebook/weird_chars.yml +42 -0
- data/spec/fixtures/vcr_cassettes/robo_whois/available.yml +48 -0
- data/spec/fixtures/vcr_cassettes/robo_whois/credits.yml +48 -0
- data/spec/fixtures/vcr_cassettes/robo_whois/unavailable.yml +48 -0
- data/spec/fixtures/vcr_cassettes/twitter/available.yml +55 -0
- data/spec/fixtures/vcr_cassettes/twitter/rate_limit.yml +61 -0
- data/spec/fixtures/vcr_cassettes/twitter/suspended.yml +55 -0
- data/spec/fixtures/vcr_cassettes/twitter/unavailable.yml +61 -0
- data/spec/name_checker/availability_spec.rb +39 -0
- data/spec/name_checker/facebook_checker_spec.rb +54 -0
- data/spec/name_checker/net_checker_spec.rb +26 -0
- data/spec/name_checker/robo_whois_checker_spec.rb +59 -0
- data/spec/name_checker/twitter_checker_spec.rb +72 -0
- data/spec/name_checker/whois_checker.rb +10 -0
- data/spec/name_checker/whois_checker_spec.rb +33 -0
- data/spec/name_checker_spec.rb +24 -0
- data/spec/spec_helper.rb +27 -0
- data/spec/support/helpers.rb +12 -0
- metadata +149 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 David Tuite
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
# NameChecker
|
2
|
+
|
3
|
+
NameChecker makes it easy to check the availability of a word across various
|
4
|
+
top-level domains and social networks.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
gem 'name_checker'
|
11
|
+
|
12
|
+
And then execute:
|
13
|
+
|
14
|
+
$ bundle
|
15
|
+
|
16
|
+
Or install it yourself as:
|
17
|
+
|
18
|
+
$ gem install name_checker
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
availability = NameChecker.check("github", "twitter")
|
23
|
+
availability.text
|
24
|
+
#=> "github"
|
25
|
+
availability.available?
|
26
|
+
#=> false
|
27
|
+
|
28
|
+
# NOTE: It is important to include the period at the start
|
29
|
+
# of the TLD when checking hosts.
|
30
|
+
availability = NameChecker.check("availabledomain", ".net")
|
31
|
+
availability.text
|
32
|
+
#=> "availabledomain.net"
|
33
|
+
availability.available?
|
34
|
+
#=> true
|
35
|
+
|
36
|
+
## Checking Hosts
|
37
|
+
|
38
|
+
Domain availability checking can occur either with the [Whois Gem](http://bit.ly/KYquaW)
|
39
|
+
(default) or via the [RoboWhois API](http://bit.ly/KYqveX).
|
40
|
+
|
41
|
+
To use the RoboWhois service, simply configure NameChecker with an API key.
|
42
|
+
|
43
|
+
NameChecker.config do |config|
|
44
|
+
config.robo_whois_api_key = 'jdsfldsjflkj'
|
45
|
+
end
|
46
|
+
|
47
|
+
All domain availability requests will then route through RoboWhois.
|
48
|
+
|
49
|
+
## Contributing
|
50
|
+
|
51
|
+
1. Fork it
|
52
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
53
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
54
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
55
|
+
5. Create new Pull Request
|
56
|
+
|
57
|
+
## Running the Specs
|
58
|
+
|
59
|
+
You will need to add a RoboWhois Api Key to `spec/spec_helper.rb` to be
|
60
|
+
able to run all of the specs. Set it up like this:
|
61
|
+
|
62
|
+
# Add your robo whois api key here:
|
63
|
+
ROBO_WHOIS_API_KEY = 'YOUR_KEY'
|
64
|
+
|
65
|
+
You can still run the specs without an API key. However, all specs related
|
66
|
+
to the RoboWhoisChecker will fail.
|
data/Rakefile
ADDED
data/lib/name_checker.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require "name_checker/version"
|
2
|
+
|
3
|
+
require "httparty"
|
4
|
+
require "whois"
|
5
|
+
require "name_checker/logging"
|
6
|
+
require "name_checker/configuration"
|
7
|
+
require "name_checker/availability"
|
8
|
+
require "name_checker/twitter_checker"
|
9
|
+
require "name_checker/facebook_checker"
|
10
|
+
require "name_checker/robo_whois_checker"
|
11
|
+
require "name_checker/whois_checker"
|
12
|
+
require "name_checker/net_checker"
|
13
|
+
|
14
|
+
module NameChecker
|
15
|
+
def self.check(text, service_name)
|
16
|
+
# NOTE: Symbols are not good for detecting the service name.
|
17
|
+
# Sometimes it might be 'co.uk'.
|
18
|
+
case service_name
|
19
|
+
when 'twitter' then TwitterChecker.check(text)
|
20
|
+
when 'facebook' then FacebookChecker.check(text)
|
21
|
+
# If not Twitter or Facebook then assume that the service
|
22
|
+
# name is that of a TLD (like :com or :co.uk) and pass it to the
|
23
|
+
# default net service checker.
|
24
|
+
else NetChecker.check(text, service_name)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module NameChecker
|
2
|
+
class Availability
|
3
|
+
attr_reader :status, :service
|
4
|
+
|
5
|
+
def initialize(service, status_input)
|
6
|
+
@service = service.to_s
|
7
|
+
@status = parse_status(status_input)
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
"#{service}: #{status}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def available?
|
15
|
+
status == "available"
|
16
|
+
end
|
17
|
+
|
18
|
+
def unavailable?
|
19
|
+
status == "unavailable"
|
20
|
+
end
|
21
|
+
|
22
|
+
def unknown?
|
23
|
+
status == "unknown"
|
24
|
+
end
|
25
|
+
|
26
|
+
def status=(status_input)
|
27
|
+
@status ||= parse_status(status_input)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
def parse_status(status_input)
|
32
|
+
case status_input
|
33
|
+
when true then "available"
|
34
|
+
when false then "unavailable"
|
35
|
+
else "unknown"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# Enable setting and getting of configuration options.
|
2
|
+
#
|
3
|
+
# Example:
|
4
|
+
#
|
5
|
+
# This can now be used under config/initializers/name_checker.rb
|
6
|
+
# NameChecker.configure do |config|
|
7
|
+
# config.robo_whois_api_key = 'dfskljkf'
|
8
|
+
# end
|
9
|
+
|
10
|
+
module NameChecker
|
11
|
+
class Configuration
|
12
|
+
DEFAULT_LOG_LEVEL = 'info'
|
13
|
+
DEFAULT_ROBO_WHOIS_API_KEY = nil
|
14
|
+
|
15
|
+
attr_accessor :robo_whois_api_key, :log_level
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
self.log_level = DEFAULT_LOG_LEVEL
|
19
|
+
self.robo_whois_api_key = DEFAULT_ROBO_WHOIS_API_KEY
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.configuration
|
24
|
+
@configuration ||= Configuration.new
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.configure
|
28
|
+
yield configuration
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module NameChecker
|
2
|
+
class FacebookChecker
|
3
|
+
include HTTParty
|
4
|
+
include Logging
|
5
|
+
MIN_NAME_LENGTH = 5
|
6
|
+
base_uri "https://graph.facebook.com"
|
7
|
+
@service_name = :facebook
|
8
|
+
|
9
|
+
def self.check(name, options = {})
|
10
|
+
# just return false if the name is too short to be valid.
|
11
|
+
if name.length < MIN_NAME_LENGTH
|
12
|
+
return Availability.new(@service_name, false)
|
13
|
+
end
|
14
|
+
|
15
|
+
res = get("/#{name}")
|
16
|
+
status = handle_response(res, name)
|
17
|
+
Availability.new(@service_name, status)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def self.log_warning(name, res)
|
23
|
+
warning = "#{@service_name.upcase}_FAILURE: Handling #{name}. Response: #{res}"
|
24
|
+
Logging.logger.warn(warning)
|
25
|
+
# Nil return must be explicit because the logging will return true.
|
26
|
+
return nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.handle_response(res, name)
|
30
|
+
case res.code
|
31
|
+
when 200 then false
|
32
|
+
when 404 then true
|
33
|
+
else log_warning(name, res)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "logger"
|
2
|
+
|
3
|
+
module Logging
|
4
|
+
def logger
|
5
|
+
Logging.logger
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.logger
|
9
|
+
unless @logger
|
10
|
+
@logger = Logger.new(STDOUT)
|
11
|
+
@logger.level = Logging.infer_level
|
12
|
+
end
|
13
|
+
@logger
|
14
|
+
end
|
15
|
+
|
16
|
+
# infer a suitable Log level from the ENVIRONMENT variable
|
17
|
+
# Can pass in an environment value for testing purposes
|
18
|
+
def self.infer_level(level = nil)
|
19
|
+
level = level || NameChecker.configuration.log_level
|
20
|
+
case level
|
21
|
+
when 'debug' then Logger::DEBUG
|
22
|
+
when 'warn' then Logger::WARN
|
23
|
+
else Logger::INFO # default
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module NameChecker
|
2
|
+
class NetChecker
|
3
|
+
def self.check(host_name, tld)
|
4
|
+
host = host_name + tld
|
5
|
+
|
6
|
+
# Use the RoboWhoisChecker if there is an API key set.
|
7
|
+
if NameChecker.configuration.robo_whois_api_key
|
8
|
+
RoboWhoisChecker.check(host)
|
9
|
+
# Or use the WhoisChecker if there isn't (default).
|
10
|
+
else
|
11
|
+
WhoisChecker.check(host)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module NameChecker
|
2
|
+
class NoAPIKeyError < ArgumentError; end
|
3
|
+
|
4
|
+
class RoboWhoisChecker
|
5
|
+
include HTTParty
|
6
|
+
include Logging
|
7
|
+
base_uri 'http://api.robowhois.com'
|
8
|
+
@service_name = :robo_whois
|
9
|
+
|
10
|
+
def self.check(host, options={})
|
11
|
+
options.merge!(basic_auth: auth_options)
|
12
|
+
|
13
|
+
# NOTE: RoboWhois will return 404 if I append ".json".
|
14
|
+
res = get("/whois/#{host}/availability", options)
|
15
|
+
status = handle_response(res, host)
|
16
|
+
Availability.new(@service_name, status)
|
17
|
+
end
|
18
|
+
|
19
|
+
# NOTE: We can't use the 'basic_auth' method because the
|
20
|
+
# configuration object is not available at the class level.
|
21
|
+
def self.auth_options
|
22
|
+
# raise NoAPIKeyError unless NameChecker.configuration.robo_whois_api_key
|
23
|
+
|
24
|
+
{ password: 'X',
|
25
|
+
username: NameChecker.configuration.robo_whois_api_key }
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.warning_limit
|
29
|
+
50
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
def self.log_warning(name, res)
|
34
|
+
warning = "#{@service_name.upcase}_FAILURE: Handling #{name}. Response: #{res}"
|
35
|
+
Logging.logger.warn(warning)
|
36
|
+
# Nil return must be explicit because the logging will return true.
|
37
|
+
return nil
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.handle_response(res, host)
|
41
|
+
log_remaining_credits(res.headers["X-Creditlimit-Remaining"])
|
42
|
+
case res.code
|
43
|
+
when 200 then res["response"]["available"]
|
44
|
+
else log_warning(host, res)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.log_remaining_credits(remaining_count)
|
49
|
+
# INFO: Some responses might not have the header available.
|
50
|
+
unless remaining_count and remaining_count.to_i < warning_limit
|
51
|
+
return nil
|
52
|
+
end
|
53
|
+
|
54
|
+
warning = "RATELIMIT_WARNING: Service #{@service_name}.
|
55
|
+
Remaining credits: #{remaining_count}"
|
56
|
+
Logging.logger.warn(warning)
|
57
|
+
# Nil return must be explicit because the logging will return true.
|
58
|
+
return nil
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# NOTE: This is rate limited to 150 requests per hour.
|
2
|
+
# TODO: Add OAuth or some other authentication in order
|
3
|
+
# to get my request limit rate increased.
|
4
|
+
|
5
|
+
module NameChecker
|
6
|
+
class TwitterChecker
|
7
|
+
include HTTParty
|
8
|
+
include Logging
|
9
|
+
MAX_NAME_LENGTH = 16
|
10
|
+
base_uri "http://api.twitter.com"
|
11
|
+
@service_name = :twitter
|
12
|
+
|
13
|
+
def self.check(screen_name, options = {})
|
14
|
+
# Just return false if the suggestion exceeds the max allowed SN length.
|
15
|
+
if screen_name.length > MAX_NAME_LENGTH
|
16
|
+
return Availability.new(@service_name, false)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Carry on with the availability checking.
|
20
|
+
options.merge!( query: { screen_name: screen_name } )
|
21
|
+
res = get("/1/users/show.json", options)
|
22
|
+
status = handle_response(res, screen_name)
|
23
|
+
Availability.new(@service_name, status)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.warning_limit
|
27
|
+
20
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
def self.handle_response(res, screen_name)
|
32
|
+
log_rate_limit(res.headers["x-ratelimit-remaining"])
|
33
|
+
|
34
|
+
# Twitter response codes:
|
35
|
+
# INFO: https://dev.twitter.com/docs/error-codes-responses
|
36
|
+
# 403 = Forbidden. Can be for a couple of reasons.
|
37
|
+
case res.code
|
38
|
+
when 200 then false
|
39
|
+
when 403 then parse_403(screen_name, res)
|
40
|
+
# Name is available if the request is not found
|
41
|
+
when 404 then true
|
42
|
+
else log_warning(screen_name, res)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.log_warning(name, res)
|
47
|
+
warning = "#{@service_name.upcase}_FAILURE: Handling #{name}. Response: #{res}"
|
48
|
+
Logging.logger.warn(warning)
|
49
|
+
# Nil return must be explicit because the logging will return true.
|
50
|
+
return nil
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.log_rate_limit(remaining)
|
54
|
+
return nil if remaining.to_i > warning_limit
|
55
|
+
warning = "RATELIMIT_WARNING: Service #{@service_name}. Remaining requests: #{remaining}"
|
56
|
+
Logging.logger.warn(warning)
|
57
|
+
# Nil return must be explicit because the logging will return true.
|
58
|
+
return nil
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.parse_403(name, res)
|
62
|
+
return false if res["error"] =~ /suspended/
|
63
|
+
# Something we hadn't intended happened if we reach this point
|
64
|
+
log_warning(name, res)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|