name_checker 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/.gitignore +21 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE +22 -0
  5. data/README.md +66 -0
  6. data/Rakefile +2 -0
  7. data/lib/name_checker.rb +27 -0
  8. data/lib/name_checker/availability.rb +39 -0
  9. data/lib/name_checker/configuration.rb +30 -0
  10. data/lib/name_checker/facebook_checker.rb +37 -0
  11. data/lib/name_checker/logging.rb +26 -0
  12. data/lib/name_checker/net_checker.rb +15 -0
  13. data/lib/name_checker/railties.rb +7 -0
  14. data/lib/name_checker/robo_whois_checker.rb +61 -0
  15. data/lib/name_checker/twitter_checker.rb +67 -0
  16. data/lib/name_checker/version.rb +3 -0
  17. data/name_checker.gemspec +25 -0
  18. data/spec/fixtures/vcr_cassettes/facebook/available.yml +44 -0
  19. data/spec/fixtures/vcr_cassettes/facebook/unavailable.yml +42 -0
  20. data/spec/fixtures/vcr_cassettes/facebook/weird_chars.yml +42 -0
  21. data/spec/fixtures/vcr_cassettes/robo_whois/available.yml +48 -0
  22. data/spec/fixtures/vcr_cassettes/robo_whois/credits.yml +48 -0
  23. data/spec/fixtures/vcr_cassettes/robo_whois/unavailable.yml +48 -0
  24. data/spec/fixtures/vcr_cassettes/twitter/available.yml +55 -0
  25. data/spec/fixtures/vcr_cassettes/twitter/rate_limit.yml +61 -0
  26. data/spec/fixtures/vcr_cassettes/twitter/suspended.yml +55 -0
  27. data/spec/fixtures/vcr_cassettes/twitter/unavailable.yml +61 -0
  28. data/spec/name_checker/availability_spec.rb +39 -0
  29. data/spec/name_checker/facebook_checker_spec.rb +54 -0
  30. data/spec/name_checker/net_checker_spec.rb +26 -0
  31. data/spec/name_checker/robo_whois_checker_spec.rb +59 -0
  32. data/spec/name_checker/twitter_checker_spec.rb +72 -0
  33. data/spec/name_checker/whois_checker.rb +10 -0
  34. data/spec/name_checker/whois_checker_spec.rb +33 -0
  35. data/spec/name_checker_spec.rb +24 -0
  36. data/spec/spec_helper.rb +27 -0
  37. data/spec/support/helpers.rb +12 -0
  38. metadata +149 -0
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+
19
+ *.swp
20
+ *.swo
21
+ .DS_Store
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in name_checker.gemspec
4
+ gemspec
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
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -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,7 @@
1
+ module NameChecker
2
+ class Railties < ::Rails::Railtie
3
+ initializer 'Rails logger' do
4
+ NameChecker::Logging.logger = Rails.logger
5
+ end
6
+ end
7
+ 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