Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
change http library to typhoeus to better follow https links. move su…
…bmethods to their own classes and use the url to pick which class to use. There are still better abstractions that can be used here, but this solves #12 for now.
- Loading branch information
Showing
27 changed files
with
289 additions
and
4,152 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,4 +3,5 @@ | |
Gemfile.lock | ||
pkg/* | ||
coverage/ | ||
credentials.yml | ||
credentials.yml | ||
tmp/* |
This file was deleted.
Oops, something went wrong.
Binary file not shown.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,118 +1,43 @@ | ||
require 'uncoil/domain_finder' | ||
require 'uncoil/expander' | ||
require 'uncoil/response' | ||
|
||
require 'bitly' | ||
require_relative 'uncoil_submethods' | ||
|
||
# @author Joel Stimson | ||
class Uncoil | ||
ISGD_ROOT_URL = "http://is.gd/forward.php?format=json&shorturl=" | ||
BITLY_DOM_ARRAY = %w[bit.ly, j.mp, bitlypro.com, cs.pn, nyti.ms] | ||
|
||
# Creates a new Uncoil object and will log into Bit.ly if you provide credentials | ||
# | ||
# @option options [String] :bitlyuser A key for your Bit.ly API username | ||
# @option options [String] :bitlykey A key for your Bit.ly API key | ||
# | ||
# @return [Class] the new instance of the Uncoil class | ||
# | ||
# @example Set up a new instance | ||
# Uncoil.new(:bitlyuser => CREDENTIALS['bitlyuser'], :bitlykey => CREDENTIALS['bitlykey']) => "#<Uncoil:0x00000102560d30 @bitly_access=true>" | ||
# | ||
def initialize options = {} | ||
def initialize(options = {}) | ||
Bitly.use_api_version_3 | ||
@bitly_access = false | ||
|
||
# create bitly instance if the auth criteria are all entered by user | ||
|
||
if options.has_key?(:bitlyuser) && options.has_key?(:bitlykey) | ||
@bitly_instance = Bitly.new("#{options[:bitlyuser]}", "#{options[:bitlykey]}") | ||
@bitly_access = true | ||
end | ||
end | ||
|
||
# A class method version of the main expand method. This will not have access to the bit.ly API, but it's faster than having to create an instance and then use it for a one-off request. | ||
# | ||
# @param [String, Array] short_url The single url or array of urls you would like to expand | ||
# | ||
# @example Use the class method for a one-off request | ||
# Uncoil.expand("http://tinyurl.com/736swvl") # => #<Uncoil::Response:0x00000101ed9250 @long_url="http://www.chinadaily.com.cn/usa/business/2011-11/08/content_14057648.htm", @short_url="http://tinyurl.com/736swvl", @error=nil> | ||
# | ||
def self.expand short_url | ||
Uncoil.new.expand(short_url) | ||
def self.expand(urls) | ||
Uncoil.new.expand(urls) | ||
end | ||
|
||
# The main method used for all requests. This method will delegate to submethods based on the domain of the link given. | ||
# | ||
# @param [String, Array] url_arr This can be a single url as a String or an array of Strings that the method will expand in order | ||
# | ||
# @return [Uncoil::Response] Returns a response object with getters for the long and short url | ||
# | ||
def expand url_arr | ||
output_array = Array(url_arr).flatten.map do |short_url| | ||
short_url = clean_url(short_url) | ||
domain = identify_domain(short_url) | ||
|
||
begin | ||
long_url = | ||
if @bitly_access && ( BITLY_DOM_ARRAY.include?(domain) || check_bitly_pro(domain) ) | ||
uncoil_bitly(short_url) | ||
elsif domain == "is.gd" | ||
uncoil_isgd(short_url) | ||
else | ||
uncoil_other(short_url) | ||
end | ||
rescue => exception | ||
long_url = nil | ||
error = exception.message | ||
end | ||
# return a response object for each time through the loop | ||
Response.new(long_url, short_url, error) | ||
end | ||
# here's the return | ||
output_array.length == 1 ? output_array[0] : output_array | ||
def expand(urls) | ||
format_output(expand_all(urls)) | ||
end | ||
|
||
private | ||
|
||
# Contacts the bit.ly API to see if the domain is a bitlypro domain, which are custom domains purchased by 3rd parties but managed by bit.ly | ||
# | ||
# @param [String] url_domain The domain to check against the bit.ly API. | ||
# | ||
def check_bitly_pro url_domain | ||
@bitly_instance.bitly_pro_domain(url_domain) | ||
end | ||
|
||
|
||
# Extracts the domain from the link to help match it with the right sub-method to expand the url | ||
# | ||
# @param [String] short_url A single url to extract a domain from. | ||
# | ||
def identify_domain short_url | ||
clean_url(short_url).split("/")[2].to_s | ||
def expand_all(urls) | ||
Array(urls).flatten.map { |short_url| response_for(short_url) } | ||
end | ||
|
||
|
||
# Standardizes the url by adding a protocol if there isn't one and removing trailing slashes | ||
# | ||
# @param [String] short_url A single url to be cleaned up. | ||
# | ||
def clean_url short_url | ||
short_url = "http://" << short_url unless short_url =~ /^https?:\/\// | ||
short_url.chop! if short_url[-1] == "/" | ||
short_url | ||
def response_for(url) | ||
response = Response.new({:short_url => url}) | ||
begin | ||
response.long_url = Expander.expand(url, @bitly_instance) | ||
rescue => exception | ||
response.long_url, response.error = nil, exception.message | ||
end | ||
response | ||
end | ||
|
||
end | ||
|
||
|
||
class Uncoil::Response | ||
attr_reader :long_url, :short_url, :error | ||
|
||
# Creates a new Response object with attributes for the original and short url, as well as any errors that occured. It is called at the end of 'expand' method. | ||
# | ||
# @param [String] long_url The expanded url that we were looking for. | ||
# @param [String] short_url The original, short url that we used to look up the long url. | ||
# @param [String] error The error output if anything went wrong during the request. And I mean ANYTHING from a code error to an HTTP issue. It catches it all. | ||
# | ||
def initialize(long_url, short_url, error) | ||
@long_url = long_url | ||
@short_url = short_url | ||
@error = error | ||
def format_output(output_array) | ||
output_array.length == 1 ? output_array[0] : output_array | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
require 'open-uri' | ||
|
||
class DomainFinder | ||
def domain_for(short_url) | ||
#TODO: new object here? | ||
match_host_to_provider(host_for(short_url)) | ||
end | ||
|
||
def host_for(url) | ||
URI.parse(url).host | ||
end | ||
|
||
def match_host_to_provider(host) | ||
case | ||
when bitly_domains.include?(host) | ||
:bitly | ||
when isgd_domains.include?(host) | ||
:isgd | ||
else | ||
host | ||
end | ||
end | ||
|
||
private | ||
|
||
def bitly_domains | ||
%w[bit.ly j.mp bitlypro.com cs.pn nyti.ms] | ||
end | ||
|
||
def isgd_domains | ||
'is.gd' | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
require_relative './expanders/default_expander' | ||
require_relative './expanders/bitly_expander' | ||
require_relative './expanders/isgd_expander' | ||
|
||
module Expander | ||
class << self | ||
def expand(short_url, bitly_instance = nil) | ||
case | ||
when domain_for(short_url) == :bitly && bitly_instance | ||
BitlyExpander.expand(short_url, bitly_instance) | ||
when domain_for(short_url) == :isgd | ||
IsgdExpander.expand(short_url) | ||
else | ||
DefaultExpander.expand(short_url) | ||
end | ||
end | ||
|
||
private | ||
|
||
def domain_for(short_url) | ||
DomainFinder.new.domain_for(short_url) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
class BitlyExpander | ||
def self.expand(short_url, bitly_instance) | ||
bitly_instance.expand(short_url).long_url | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
require 'open-uri' | ||
require 'net/http' | ||
|
||
class DefaultExpander | ||
class << self | ||
def expand(short_url) | ||
response = Typhoeus.head(short_url, :followlocation => true) | ||
location_from_response(response) | ||
end | ||
|
||
private | ||
|
||
def location_from_response(response) | ||
response.options[:effective_url] | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
require 'json' | ||
require 'typhoeus' | ||
|
||
class IsgdExpander | ||
ISGD_ROOT_URL = "http://is.gd/forward.php?format=json&shorturl=" | ||
|
||
class << self | ||
def expand(short_url) | ||
response = Typhoeus.get("#{ISGD_ROOT_URL}#{short_url}") | ||
isgd_location_from_response(response.response_body) | ||
end | ||
|
||
private | ||
|
||
def isgd_location_from_response(body) | ||
JSON.parse(body)["url"] | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
class Uncoil | ||
class Response | ||
attr_accessor :long_url, :short_url, :error | ||
|
||
def initialize(attributes = {}) | ||
attributes.each do |k, v| | ||
self.send("#{k.to_s}=", v) | ||
end | ||
end | ||
end | ||
end |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.