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.
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