uncoil 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MWM0ZjY2YWI1MTM5ZmQ4M2M4NjY0ZDYwMzI2MTg1NTM2YTQyNGI0YQ==
5
+ data.tar.gz: !binary |-
6
+ NDBlOTBjZDU5NTI5OGFjY2RjN2JkN2YyYzg5NmQ5Zjc0MjJmODlkNA==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ M2IxY2Y5NmZiNTI0MDE2NjllNzRlMzM2ODYwZmIwMWE4Y2IxNDM5N2M5M2Vl
10
+ MGFjYmQ0Yjc0OTI0NGFiNGZjYzM5ZThkOTlhYWNlMjZiYWU5NDQ0ZWY2MTZl
11
+ ZDJiYjI3YWFkYjNhNjVhNWMyNTFiOGM5NDNmZTJlYmQwOGE4NmU=
12
+ data.tar.gz: !binary |-
13
+ ODFmOTY3NzlhMjk5NWFhZWE0YjA2ZGI0YzdkMTIxNWE4YjBhNTFjMjEzYWIx
14
+ YmQzYWQ2OTQ5N2E4ZDk1Y2VmOGE1MTFhOTdkZTE1OGQ3MjgyZTlhNWE2Zjli
15
+ M2U3ODQ4YWIyNjNkOTc0MTdkYmIxYzVmMzZiMDgzMWVhYjYzODQ=
data/.gitignore CHANGED
@@ -3,4 +3,5 @@
3
3
  Gemfile.lock
4
4
  pkg/*
5
5
  coverage/
6
- credentials.yml
6
+ credentials.yml
7
+ tmp/*
data/README.md CHANGED
@@ -6,7 +6,7 @@ The uncoil gem is a one stop shop for un-shortening urls.
6
6
 
7
7
  The idea is based off of my site http://uncoil.me and I built this as part of my UWE-Ruby fall project.
8
8
 
9
- ## Why the heck does this exist?
9
+ ### Why the heck does this exist?
10
10
  This gem is all about transparency, safety and knowing where you are going on the internet.
11
11
 
12
12
  The last straw was seeing TechCrunch articles with titles like "Don't click on this one specific goo.gl link! It's a virus!" (like [this](http://techcrunch.com/2010/12/07/twitter-virus/) one), which was flabbergasting. How is it that a tech blog and community's only response to these kind of threats is so reactionary and linear? **We can do better than that.**
@@ -29,6 +29,17 @@ require 'uncoil'
29
29
  ```
30
30
 
31
31
  ## Usage
32
+
33
+ ### One-off Calls
34
+ If you want to just make a single call, give this a try:
35
+
36
+ ```ruby
37
+ Uncoil.expand('http://bit.ly/2EEjBl') # => <Uncoil::Response:0x00000100a0d948 @long_url="http://www.cnn.com/" @short_url="http://bit.ly/2EEjBl" @error=nil>
38
+ ```
39
+
40
+ ### Creating an Instance
41
+ If you need to make repeated calls or would like to access the bitly api, you need to create an instance.
42
+
32
43
  * If you have Bitly login credentials, use them to create a new instance:
33
44
 
34
45
  ```ruby
@@ -76,3 +87,15 @@ Here are a few ideas I have for the future:
76
87
  * Dynamic method assignment based on domain.
77
88
  * This goes with the modularized structure above, because it allows you to just drop more API files into the folder and not have to modify the main method
78
89
  * Better error handling (I'd appreciate any comments on what to catch and what to leave)
90
+
91
+ ## License
92
+
93
+ The MIT License (MIT)
94
+
95
+ Copyright (c) 2012 Joel Stimson
96
+
97
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
98
+
99
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
100
+
101
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile CHANGED
@@ -9,4 +9,9 @@ task :default => :spec
9
9
  desc "Run all the rspec examples"
10
10
  task :spec do
11
11
  system "bundle exec rspec -c spec"
12
+ end
13
+
14
+ desc "Remove the current VCR fixture files"
15
+ task :clean_cassettes do
16
+ system "rm -rf spec/fixtures/uncoil_cassettes"
12
17
  end
@@ -1,118 +1,43 @@
1
+ require 'uncoil/domain_finder'
2
+ require 'uncoil/expander'
3
+ require 'uncoil/response'
4
+
1
5
  require 'bitly'
2
- require_relative 'uncoil_submethods'
3
6
 
4
- # @author Joel Stimson
5
7
  class Uncoil
6
- ISGD_ROOT_URL = "http://is.gd/forward.php?format=json&shorturl="
7
- BITLY_DOM_ARRAY = %w[bit.ly, j.mp, bitlypro.com, cs.pn, nyti.ms]
8
-
9
- # Creates a new Uncoil object and will log into Bit.ly if you provide credentials
10
- #
11
- # @option options [String] :bitlyuser A key for your Bit.ly API username
12
- # @option options [String] :bitlykey A key for your Bit.ly API key
13
- #
14
- # @return [Class] the new instance of the Uncoil class
15
- #
16
- # @example Set up a new instance
17
- # Uncoil.new(:bitlyuser => CREDENTIALS['bitlyuser'], :bitlykey => CREDENTIALS['bitlykey']) => "#<Uncoil:0x00000102560d30 @bitly_access=true>"
18
- #
19
- def initialize options = {}
8
+ def initialize(options = {})
20
9
  Bitly.use_api_version_3
21
- @bitly_access = false
22
-
23
- # create bitly instance if the auth criteria are all entered by user
10
+
24
11
  if options.has_key?(:bitlyuser) && options.has_key?(:bitlykey)
25
12
  @bitly_instance = Bitly.new("#{options[:bitlyuser]}", "#{options[:bitlykey]}")
26
- @bitly_access = true
27
13
  end
28
14
  end
29
15
 
30
- # 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.
31
- #
32
- # @param [String, Array] short_url The single url or array of urls you would like to expand
33
- #
34
- # @example Use the class method for a one-off request
35
- # 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>
36
- #
37
- def self.expand short_url
38
- Uncoil.new.expand(short_url)
16
+ def self.expand(urls)
17
+ Uncoil.new.expand(urls)
39
18
  end
40
19
 
41
- # The main method used for all requests. This method will delegate to submethods based on the domain of the link given.
42
- #
43
- # @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
44
- #
45
- # @return [Uncoil::Response] Returns a response object with getters for the long and short url
46
- #
47
- def expand url_arr
48
- output_array = Array(url_arr).flatten.map do |short_url|
49
- short_url = clean_url(short_url)
50
- domain = identify_domain(short_url)
51
-
52
- begin
53
- long_url =
54
- if @bitly_access && ( BITLY_DOM_ARRAY.include?(domain) || check_bitly_pro(domain) )
55
- uncoil_bitly(short_url)
56
- elsif domain == "is.gd"
57
- uncoil_isgd(short_url)
58
- else
59
- uncoil_other(short_url)
60
- end
61
- rescue => exception
62
- long_url = nil
63
- error = exception.message
64
- end
65
- # return a response object for each time through the loop
66
- Response.new(long_url, short_url, error)
67
- end
68
- # here's the return
69
- output_array.length == 1 ? output_array[0] : output_array
20
+ def expand(urls)
21
+ format_output(expand_all(urls))
70
22
  end
71
23
 
24
+ private
72
25
 
73
- # 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
74
- #
75
- # @param [String] url_domain The domain to check against the bit.ly API.
76
- #
77
- def check_bitly_pro url_domain
78
- @bitly_instance.bitly_pro_domain(url_domain)
79
- end
80
-
81
-
82
- # Extracts the domain from the link to help match it with the right sub-method to expand the url
83
- #
84
- # @param [String] short_url A single url to extract a domain from.
85
- #
86
- def identify_domain short_url
87
- clean_url(short_url).split("/")[2].to_s
26
+ def expand_all(urls)
27
+ Array(urls).flatten.map { |short_url| response_for(short_url) }
88
28
  end
89
-
90
29
 
91
- # Standardizes the url by adding a protocol if there isn't one and removing trailing slashes
92
- #
93
- # @param [String] short_url A single url to be cleaned up.
94
- #
95
- def clean_url short_url
96
- short_url = "http://" << short_url unless short_url =~ /^https?:\/\//
97
- short_url.chop! if short_url[-1] == "/"
98
- short_url
30
+ def response_for(url)
31
+ response = Response.new({:short_url => url})
32
+ begin
33
+ response.long_url = Expander.expand(url, @bitly_instance)
34
+ rescue => exception
35
+ response.long_url, response.error = nil, exception.message
36
+ end
37
+ response
99
38
  end
100
-
101
- end
102
39
 
103
-
104
- class Uncoil::Response
105
- attr_reader :long_url, :short_url, :error
106
-
107
- # 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.
108
- #
109
- # @param [String] long_url The expanded url that we were looking for.
110
- # @param [String] short_url The original, short url that we used to look up the long url.
111
- # @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.
112
- #
113
- def initialize(long_url, short_url, error)
114
- @long_url = long_url
115
- @short_url = short_url
116
- @error = error
40
+ def format_output(output_array)
41
+ output_array.length == 1 ? output_array[0] : output_array
117
42
  end
118
43
  end
@@ -0,0 +1,33 @@
1
+ require 'open-uri'
2
+
3
+ class DomainFinder
4
+ def domain_for(short_url)
5
+ #TODO: new object here?
6
+ match_host_to_provider(host_for(short_url))
7
+ end
8
+
9
+ def host_for(url)
10
+ URI.parse(url).host
11
+ end
12
+
13
+ def match_host_to_provider(host)
14
+ case
15
+ when bitly_domains.include?(host)
16
+ :bitly
17
+ when isgd_domains.include?(host)
18
+ :isgd
19
+ else
20
+ host
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def bitly_domains
27
+ %w[bit.ly j.mp bitlypro.com cs.pn nyti.ms]
28
+ end
29
+
30
+ def isgd_domains
31
+ 'is.gd'
32
+ end
33
+ end
@@ -0,0 +1,24 @@
1
+ require_relative './expanders/default_expander'
2
+ require_relative './expanders/bitly_expander'
3
+ require_relative './expanders/isgd_expander'
4
+
5
+ module Expander
6
+ class << self
7
+ def expand(short_url, bitly_instance = nil)
8
+ case
9
+ when domain_for(short_url) == :bitly && bitly_instance
10
+ BitlyExpander.expand(short_url, bitly_instance)
11
+ when domain_for(short_url) == :isgd
12
+ IsgdExpander.expand(short_url)
13
+ else
14
+ DefaultExpander.expand(short_url)
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def domain_for(short_url)
21
+ DomainFinder.new.domain_for(short_url)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,5 @@
1
+ class BitlyExpander
2
+ def self.expand(short_url, bitly_instance)
3
+ bitly_instance.expand(short_url).long_url
4
+ end
5
+ end
@@ -0,0 +1,17 @@
1
+ require 'open-uri'
2
+ require 'net/http'
3
+
4
+ class DefaultExpander
5
+ class << self
6
+ def expand(short_url)
7
+ response = Typhoeus.head(short_url, :followlocation => true)
8
+ location_from_response(response)
9
+ end
10
+
11
+ private
12
+
13
+ def location_from_response(response)
14
+ response.options[:effective_url]
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,19 @@
1
+ require 'json'
2
+ require 'typhoeus'
3
+
4
+ class IsgdExpander
5
+ ISGD_ROOT_URL = "http://is.gd/forward.php?format=json&shorturl="
6
+
7
+ class << self
8
+ def expand(short_url)
9
+ response = Typhoeus.get("#{ISGD_ROOT_URL}#{short_url}")
10
+ isgd_location_from_response(response.response_body)
11
+ end
12
+
13
+ private
14
+
15
+ def isgd_location_from_response(body)
16
+ JSON.parse(body)["url"]
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,11 @@
1
+ class Uncoil
2
+ class Response
3
+ attr_accessor :long_url, :short_url, :error
4
+
5
+ def initialize(attributes = {})
6
+ attributes.each do |k, v|
7
+ self.send("#{k.to_s}=", v)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -1,3 +1,3 @@
1
1
  class Uncoil
2
- VERSION = "1.1.0"
2
+ VERSION = "1.2.0"
3
3
  end
@@ -0,0 +1,102 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: http://api.bit.ly/v3/expand?apiKey=R_7a6f6d845668a8a7bb3e0c80ee3c28d6&login=stim371&shortUrl=http://bit.ly/2EEjBl
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers: {}
10
+ response:
11
+ status:
12
+ code: 200
13
+ message: OK
14
+ headers:
15
+ Server:
16
+ - nginx
17
+ Date:
18
+ - Thu, 12 Sep 2013 22:46:57 GMT
19
+ Content-Type:
20
+ - application/json; charset=utf-8
21
+ Connection:
22
+ - keep-alive
23
+ Mime-Version:
24
+ - '1.0'
25
+ Content-Length:
26
+ - '199'
27
+ body:
28
+ encoding: US-ASCII
29
+ string: ! '{ "status_code": 200, "status_txt": "OK", "data": { "expand": [ {
30
+ "short_url": "http:\/\/bit.ly\/2EEjBl", "long_url": "http:\/\/www.cnn.com\/",
31
+ "user_hash": "2EEjBl", "global_hash": "2EEjBl" } ] } }
32
+
33
+ '
34
+ http_version:
35
+ recorded_at: Thu, 12 Sep 2013 22:47:09 GMT
36
+ - request:
37
+ method: get
38
+ uri: http://is.gd/forward.php?format=json&shorturl=http://is.gd/gbKNRq
39
+ body:
40
+ encoding: US-ASCII
41
+ string: ''
42
+ headers:
43
+ User-Agent:
44
+ - Typhoeus - https://github.com/typhoeus/typhoeus
45
+ response:
46
+ status:
47
+ code: 200
48
+ message: OK
49
+ headers:
50
+ Server:
51
+ - nginx
52
+ Date:
53
+ - Thu, 12 Sep 2013 22:46:57 GMT
54
+ Content-Type:
55
+ - text/javascript; charset=utf8
56
+ Transfer-Encoding:
57
+ - chunked
58
+ Connection:
59
+ - keep-alive
60
+ X-Powered-By:
61
+ - PHP/5.2.17
62
+ Access-Control-Allow-Origin:
63
+ - ! '*'
64
+ body:
65
+ encoding: US-ASCII
66
+ string: ! '{ "url": "http://www.google.com" }'
67
+ http_version:
68
+ recorded_at: Thu, 12 Sep 2013 22:47:09 GMT
69
+ - request:
70
+ method: get
71
+ uri: http://api.bit.ly/v3/expand?apiKey=R_7a6f6d845668a8a7bb3e0c80ee3c28d6&login=stim371&shortUrl=http://cs.pn/vsZpra
72
+ body:
73
+ encoding: US-ASCII
74
+ string: ''
75
+ headers: {}
76
+ response:
77
+ status:
78
+ code: 200
79
+ message: OK
80
+ headers:
81
+ Server:
82
+ - nginx
83
+ Date:
84
+ - Thu, 12 Sep 2013 22:46:57 GMT
85
+ Content-Type:
86
+ - application/json; charset=utf-8
87
+ Connection:
88
+ - keep-alive
89
+ Mime-Version:
90
+ - '1.0'
91
+ Content-Length:
92
+ - '222'
93
+ body:
94
+ encoding: US-ASCII
95
+ string: ! '{ "status_code": 200, "status_txt": "OK", "data": { "expand": [ {
96
+ "short_url": "http:\/\/cs.pn\/vsZpra", "long_url": "http:\/\/www.c-spanvideo.org\/program\/CainNew",
97
+ "user_hash": "vsZpra", "global_hash": "vPQYzq" } ] } }
98
+
99
+ '
100
+ http_version:
101
+ recorded_at: Thu, 12 Sep 2013 22:47:09 GMT
102
+ recorded_with: VCR 2.5.0
@@ -0,0 +1,69 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: http://api.bit.ly/v3/expand?apiKey=R_7a6f6d845668a8a7bb3e0c80ee3c28d6&login=stim371&shortUrl=http://bit.ly/2EEjBl
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers: {}
10
+ response:
11
+ status:
12
+ code: 200
13
+ message: OK
14
+ headers:
15
+ Server:
16
+ - nginx
17
+ Date:
18
+ - Thu, 12 Sep 2013 22:46:55 GMT
19
+ Content-Type:
20
+ - application/json; charset=utf-8
21
+ Connection:
22
+ - keep-alive
23
+ Mime-Version:
24
+ - '1.0'
25
+ Content-Length:
26
+ - '199'
27
+ body:
28
+ encoding: US-ASCII
29
+ string: ! '{ "status_code": 200, "status_txt": "OK", "data": { "expand": [ {
30
+ "short_url": "http:\/\/bit.ly\/2EEjBl", "long_url": "http:\/\/www.cnn.com\/",
31
+ "user_hash": "2EEjBl", "global_hash": "2EEjBl" } ] } }
32
+
33
+ '
34
+ http_version:
35
+ recorded_at: Thu, 12 Sep 2013 22:47:07 GMT
36
+ - request:
37
+ method: get
38
+ uri: http://api.bit.ly/v3/expand?apiKey=R_7a6f6d845668a8a7bb3e0c80ee3c28d6&login=stim371&shortUrl=http://cs.pn/vsZpra
39
+ body:
40
+ encoding: US-ASCII
41
+ string: ''
42
+ headers: {}
43
+ response:
44
+ status:
45
+ code: 200
46
+ message: OK
47
+ headers:
48
+ Server:
49
+ - nginx
50
+ Date:
51
+ - Thu, 12 Sep 2013 22:46:55 GMT
52
+ Content-Type:
53
+ - application/json; charset=utf-8
54
+ Connection:
55
+ - keep-alive
56
+ Mime-Version:
57
+ - '1.0'
58
+ Content-Length:
59
+ - '222'
60
+ body:
61
+ encoding: US-ASCII
62
+ string: ! '{ "status_code": 200, "status_txt": "OK", "data": { "expand": [ {
63
+ "short_url": "http:\/\/cs.pn\/vsZpra", "long_url": "http:\/\/www.c-spanvideo.org\/program\/CainNew",
64
+ "user_hash": "vsZpra", "global_hash": "vPQYzq" } ] } }
65
+
66
+ '
67
+ http_version:
68
+ recorded_at: Thu, 12 Sep 2013 22:47:08 GMT
69
+ recorded_with: VCR 2.5.0
@@ -0,0 +1,36 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: http://is.gd/forward.php?format=json&shorturl=http://is.gd/gbKNRq
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ User-Agent:
11
+ - Typhoeus - https://github.com/typhoeus/typhoeus
12
+ response:
13
+ status:
14
+ code: 200
15
+ message: OK
16
+ headers:
17
+ Server:
18
+ - nginx
19
+ Date:
20
+ - Thu, 12 Sep 2013 22:46:56 GMT
21
+ Content-Type:
22
+ - text/javascript; charset=utf8
23
+ Transfer-Encoding:
24
+ - chunked
25
+ Connection:
26
+ - keep-alive
27
+ X-Powered-By:
28
+ - PHP/5.2.17
29
+ Access-Control-Allow-Origin:
30
+ - ! '*'
31
+ body:
32
+ encoding: US-ASCII
33
+ string: ! '{ "url": "http://www.google.com" }'
34
+ http_version:
35
+ recorded_at: Thu, 12 Sep 2013 22:47:08 GMT
36
+ recorded_with: VCR 2.5.0
@@ -0,0 +1,36 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: http://is.gd/forward.php?format=json&shorturl=http://is.gd/gbKNRq
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ User-Agent:
11
+ - Typhoeus - https://github.com/typhoeus/typhoeus
12
+ response:
13
+ status:
14
+ code: 200
15
+ message: OK
16
+ headers:
17
+ Server:
18
+ - nginx
19
+ Date:
20
+ - Thu, 12 Sep 2013 22:46:55 GMT
21
+ Content-Type:
22
+ - text/javascript; charset=utf8
23
+ Transfer-Encoding:
24
+ - chunked
25
+ Connection:
26
+ - keep-alive
27
+ X-Powered-By:
28
+ - PHP/5.2.17
29
+ Access-Control-Allow-Origin:
30
+ - ! '*'
31
+ body:
32
+ encoding: US-ASCII
33
+ string: ! '{ "url": "http://www.google.com" }'
34
+ http_version:
35
+ recorded_at: Thu, 12 Sep 2013 22:47:07 GMT
36
+ recorded_with: VCR 2.5.0
@@ -1,4 +1,22 @@
1
1
  require 'simplecov'
2
+ require 'vcr'
3
+ require 'webmock/rspec'
2
4
  SimpleCov.start
3
5
 
6
+ require 'uncoil'
7
+
4
8
  CREDENTIALS = YAML.load_file("./credentials.yml")['bitly']
9
+
10
+ VCR.configure do |c|
11
+ c.cassette_library_dir = 'spec/fixtures/uncoil_cassettes'
12
+ c.hook_into :webmock
13
+ c.allow_http_connections_when_no_cassette = true
14
+ c.configure_rspec_metadata!
15
+ c.default_cassette_options = {
16
+ :record => :new_episodes
17
+ }
18
+ end
19
+
20
+ RSpec.configure do |c|
21
+ c.treat_symbols_as_metadata_keys_with_true_values = true
22
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ describe DomainFinder do
4
+ it 'should return the host from a url' do
5
+ subject.domain_for('http://bit.ly/abcde13').should eq :bitly
6
+ subject.domain_for('http://tinyurl.com/abcde13').should eq 'tinyurl.com'
7
+ end
8
+
9
+ it 'should raise error if url empty' do
10
+ lambda{ subject.domain_for(nil) }.should raise_error
11
+ end
12
+ end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe Expander do
4
+
5
+ end
@@ -1,21 +1,24 @@
1
1
  require 'spec_helper'
2
- require 'uncoil'
3
2
 
4
- describe "the response object" do
3
+ describe Uncoil::Response, :vcr => { :cassette_name => 'isgd_response' } do
5
4
 
6
- subject { Uncoil.new(:bitlyuser => CREDENTIALS['bitlyuser'], :bitlykey => CREDENTIALS['bitlykey'])}
5
+ subject { Uncoil.expand('http://is.gd/gbKNRq') }
7
6
 
8
- before(:all) {
9
- @subject = subject.expand("http://is.gd/gbKNRq")
10
- }
11
-
12
- it "should return a response object" do
13
- @subject.class.should eq Uncoil::Response
7
+ it 'should return a response object' do
8
+ subject.class.should eq Uncoil::Response
14
9
  end
15
10
 
16
- it "should respond to getter methods" do
17
- @subject.long_url.should_not eq nil
18
- @subject.short_url.should_not eq nil
11
+ it 'should respond to getter methods' do
12
+ lambda{subject.long_url}.should_not raise_error
13
+ lambda{subject.short_url}.should_not raise_error
14
+ lambda{subject.error}.should_not raise_error
19
15
  end
20
-
21
- end
16
+
17
+ describe 'response body' do
18
+ it 'should assign variables correctly' do
19
+ subject.long_url.should eq 'http://www.google.com'
20
+ subject.short_url.should eq 'http://is.gd/gbKNRq'
21
+ subject.error.should be_nil
22
+ end
23
+ end
24
+ end
@@ -1,101 +1,90 @@
1
1
  require 'spec_helper'
2
- require 'uncoil'
3
2
 
4
3
  describe Uncoil do
5
4
 
6
5
  subject { Uncoil.new(:bitlyuser => CREDENTIALS['bitlyuser'], :bitlykey => CREDENTIALS['bitlykey'])}
7
6
 
8
- describe "#expand class method" do
9
- it "should successfully return a response object" do
10
- Uncoil.expand("http://tinyurl.com/736swvl").class.should eq Uncoil::Response
7
+ describe '#expand class method', :vcr => { :cassette_name => 'isgd_response' } do
8
+ it 'should successfully return a response object' do
9
+ Uncoil.expand('http://is.gd/gbKNRq').class.should eq Uncoil::Response
11
10
  end
12
11
 
13
12
  it "should bring back the correct link" do
14
- response = Uncoil.expand("http://tinyurl.com/736swvl")
15
- response.long_url.should eq "http://www.chinadaily.com.cn/usa/business/2011-11/08/content_14057648.htm"
13
+ response = Uncoil.expand('http://is.gd/gbKNRq')
14
+ response.long_url.should eq "http://www.google.com"
16
15
  end
17
16
  end
18
17
 
19
- describe "when using the submethods" do
20
-
21
- context "when trying to undo a bit.ly and bit.ly pro link" do
22
- it "should bring back the correct long link" do
23
- {"http://bit.ly/2EEjBl" => "http://www.cnn.com/", "http://cs.pn/vsZpra" => "http://www.c-spanvideo.org/program/CainNew" }.each { |short_url, long_url|
24
- subject.uncoil_bitly(short_url).should eq long_url
25
- }
26
- end
27
- end
28
-
29
- context "when trying to undo an is.gd link" do
30
- it "should bring back the correct long link" do
31
- subject.uncoil_isgd("http://is.gd/gbKNRq").should eq "http://www.google.com"
32
- end
33
- end
34
-
35
- context "when trying to undo from other services" do
36
- it "should bring back the correct long link" do
37
- subject.uncoil_other("http://tinyurl.com/736swvl").should eq "http://www.chinadaily.com.cn/usa/business/2011-11/08/content_14057648.htm"
38
- end
39
- end
40
- end
41
-
42
- describe "the main expand method" do
18
+ describe "#expand instance method" do
43
19
 
44
20
  it "should raise an error for non-urls" do
45
21
  subject.expand("a").error.should_not be_nil
46
22
  end
47
23
 
48
- context "when expanding a link" do
49
-
50
- def check_response response, expected_result
51
- response.long_url.should eq expected_result[:long_url]
52
- response.short_url.should eq expected_result[:short_url]
53
- response.error.should eq expected_result[:error]
54
- end
24
+ def check_response(response, expected_result)
25
+ response.long_url.should eq expected_result[:long_url]
26
+ response.short_url.should eq expected_result[:short_url]
27
+ response.error.should eq expected_result[:error]
28
+ end
29
+
30
+ context 'for bitly api', :vcr => { :cassette_name => 'bitly_api' } do
55
31
 
56
- it "should expand bitly correctly" do
57
- expected_result = Hash[:long_url => "http://www.cnn.com/", :short_url => "http://bit.ly/2EEjBl", :error => nil]
58
- response = subject.expand("http://bit.ly/2EEjBl")
32
+ it 'should expand bitly correctly' do
33
+ expected_result = Hash[:long_url => 'http://www.cnn.com/', :short_url => 'http://bit.ly/2EEjBl', :error => nil]
34
+ response = subject.expand('http://bit.ly/2EEjBl')
59
35
  check_response response, expected_result
60
36
  end
61
37
 
62
- it "should expand bitlypro domains correctly" do
63
- expected_result = Hash[:long_url => "http://www.c-spanvideo.org/program/CainNew", :short_url => "http://cs.pn/vsZpra", :error => nil]
64
- response = subject.expand("http://cs.pn/vsZpra")
38
+ it 'should expand bitlypro domains correctly' do
39
+ expected_result = Hash[:long_url => 'http://www.c-spanvideo.org/program/CainNew', :short_url => 'http://cs.pn/vsZpra', :error => nil]
40
+ response = subject.expand('http://cs.pn/vsZpra')
65
41
  check_response response, expected_result
66
42
  end
67
-
68
- it "should expand isgd domains correctly" do
69
- expected_result = Hash[:long_url => "http://www.google.com", :short_url => "http://is.gd/gbKNRq", :error => nil]
70
- response = subject.expand("http://is.gd/gbKNRq")
43
+ end
44
+
45
+ context 'for isgd api', :vcr => { :cassette_name => 'isgd_api' } do
46
+ it 'should expand isgd domains correctly' do
47
+ expected_result = Hash[:long_url => 'http://www.google.com', :short_url => 'http://is.gd/gbKNRq', :error => nil]
48
+ response = subject.expand('http://is.gd/gbKNRq')
71
49
  check_response response, expected_result
72
50
  end
73
-
74
- it "should expand other shortened urls correctly" do
75
- expected_result = Hash[:long_url => "http://www.chinadaily.com.cn/usa/business/2011-11/08/content_14057648.htm", :short_url => "http://tinyurl.com/736swvl", :error => nil]
76
- response = subject.expand("http://tinyurl.com/736swvl")
51
+ end
52
+
53
+ context 'for other requests' do
54
+ it 'should expand other shortened urls correctly' do
55
+ expected_result = Hash[:long_url => 'http://paulgraham.com/', :short_url => 'http://tinyurl.com/p8o2', :error => nil]
56
+ response = subject.expand('http://tinyurl.com/p8o2')
77
57
  check_response response, expected_result
78
58
  end
59
+
60
+ it 'should expand https urls correctly' do
61
+ expected_result = Hash[:long_url => 'https://www.facebook.com/photo.php?fbid=436380063146407&l=7554e08219', :short_url => 'http://fb.me/6txI6APS5', :error => nil]
62
+ response = subject.expand('http://fb.me/6txI6APS5')
63
+ check_response response, expected_result
64
+ end
65
+ end
79
66
 
80
- context "with an array input" do
67
+ describe 'with an array input', :vcr => { :cassette_name => 'array_of_links' } do
81
68
 
82
- before(:all) {
83
- arr_of_links = ["http://bit.ly/2EEjBl","http://is.gd/gbKNRq","http://cs.pn/vsZpra"]
84
- @response = subject.expand(arr_of_links)
85
- }
69
+ arr_of_links = %w[http://bit.ly/2EEjBl http://is.gd/gbKNRq http://cs.pn/vsZpra]
86
70
 
87
- it "should return an array" do
88
- @response.class.should eq Array
89
- end
71
+ let(:resp) {subject.expand(arr_of_links) }
90
72
 
91
- it "should return an array of response objects" do
92
- @response.each { |r| r.class.should eq Uncoil::Response }
73
+ it 'should return an array' do
74
+ resp.class.should eq Array
93
75
  end
94
76
 
95
- it "should successfully expand all links" do
96
- @expected_result = [Hash[:long_url => "http://www.cnn.com/", :short_url => "http://bit.ly/2EEjBl", :error => nil], Hash[:long_url => "http://www.google.com", :short_url => "http://is.gd/gbKNRq", :error => nil],Hash[:long_url => "http://www.c-spanvideo.org/program/CainNew", :short_url => "http://cs.pn/vsZpra", :error => nil]]
97
- @response.each_with_index { |response_array, index| check_response response_array, @expected_result[index] }
77
+ it 'should return an array of response objects' do
78
+ resp.each { |r| r.class.should eq Uncoil::Response }
98
79
  end
80
+
81
+ it 'should successfully expand all links' do
82
+ @expected_result = [Hash[:long_url => 'http://www.cnn.com/', :short_url => 'http://bit.ly/2EEjBl', :error => nil],
83
+ Hash[:long_url => 'http://www.google.com', :short_url => 'http://is.gd/gbKNRq', :error => nil],
84
+ Hash[:long_url => 'http://www.c-spanvideo.org/program/CainNew', :short_url => 'http://cs.pn/vsZpra', :error => nil]]
85
+ resp.each_with_index { |response, index|
86
+ check_response(response, @expected_result[index])
87
+ }
99
88
  end
100
89
  end
101
90
  end
@@ -23,6 +23,9 @@ Gem::Specification.new do |s|
23
23
  s.add_development_dependency "guard-rspec"
24
24
  s.add_development_dependency "simplecov"
25
25
  s.add_development_dependency "rake"
26
+ s.add_development_dependency "vcr"
27
+ s.add_development_dependency "webmock"
26
28
 
27
29
  s.add_runtime_dependency "bitly"
30
+ s.add_runtime_dependency "typhoeus"
28
31
  end
metadata CHANGED
@@ -1,71 +1,127 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: uncoil
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
5
- prerelease:
4
+ version: 1.2.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Joel Stimson
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-03-07 00:00:00.000000000Z
11
+ date: 2013-09-13 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: rspec
16
- requirement: &2152984160 !ruby/object:Gem::Requirement
17
- none: false
15
+ requirement: !ruby/object:Gem::Requirement
18
16
  requirements:
19
17
  - - ! '>='
20
18
  - !ruby/object:Gem::Version
21
19
  version: '0'
22
20
  type: :development
23
21
  prerelease: false
24
- version_requirements: *2152984160
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ! '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
25
27
  - !ruby/object:Gem::Dependency
26
28
  name: guard-rspec
27
- requirement: &2152983000 !ruby/object:Gem::Requirement
28
- none: false
29
+ requirement: !ruby/object:Gem::Requirement
29
30
  requirements:
30
31
  - - ! '>='
31
32
  - !ruby/object:Gem::Version
32
33
  version: '0'
33
34
  type: :development
34
35
  prerelease: false
35
- version_requirements: *2152983000
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
36
41
  - !ruby/object:Gem::Dependency
37
42
  name: simplecov
38
- requirement: &2152981500 !ruby/object:Gem::Requirement
39
- none: false
43
+ requirement: !ruby/object:Gem::Requirement
40
44
  requirements:
41
45
  - - ! '>='
42
46
  - !ruby/object:Gem::Version
43
47
  version: '0'
44
48
  type: :development
45
49
  prerelease: false
46
- version_requirements: *2152981500
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
47
55
  - !ruby/object:Gem::Dependency
48
56
  name: rake
49
- requirement: &2152980660 !ruby/object:Gem::Requirement
50
- none: false
57
+ requirement: !ruby/object:Gem::Requirement
51
58
  requirements:
52
59
  - - ! '>='
53
60
  - !ruby/object:Gem::Version
54
61
  version: '0'
55
62
  type: :development
56
63
  prerelease: false
57
- version_requirements: *2152980660
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: vcr
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: webmock
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ! '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
58
97
  - !ruby/object:Gem::Dependency
59
98
  name: bitly
60
- requirement: &2152979620 !ruby/object:Gem::Requirement
61
- none: false
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ! '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ! '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: typhoeus
113
+ requirement: !ruby/object:Gem::Requirement
62
114
  requirements:
63
115
  - - ! '>='
64
116
  - !ruby/object:Gem::Version
65
117
  version: '0'
66
118
  type: :runtime
67
119
  prerelease: false
68
- version_requirements: *2152979620
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ! '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
69
125
  description: Uncoil is a gem to unshorten urls so you know where obscured links really
70
126
  go. If you have bitly login credentials, initialize an instance of Uncoil to use
71
127
  the bitly API.
@@ -77,50 +133,61 @@ extra_rdoc_files: []
77
133
  files:
78
134
  - .gitignore
79
135
  - .travis.yml
80
- - .yardoc/checksums
81
- - .yardoc/objects/root.dat
82
- - .yardoc/proxy_types
83
136
  - Gemfile
84
137
  - Guardfile
85
138
  - Manifest
86
139
  - README.md
87
140
  - Rakefile
88
141
  - lib/uncoil.rb
142
+ - lib/uncoil/domain_finder.rb
143
+ - lib/uncoil/expander.rb
144
+ - lib/uncoil/expanders/bitly_expander.rb
145
+ - lib/uncoil/expanders/default_expander.rb
146
+ - lib/uncoil/expanders/isgd_expander.rb
147
+ - lib/uncoil/response.rb
89
148
  - lib/uncoil/version.rb
90
- - lib/uncoil_submethods.rb
91
- - spec/helper_method_spec.rb
149
+ - spec/fixtures/uncoil_cassettes/array_of_links.yml
150
+ - spec/fixtures/uncoil_cassettes/bitly_api.yml
151
+ - spec/fixtures/uncoil_cassettes/isgd_api.yml
152
+ - spec/fixtures/uncoil_cassettes/isgd_response.yml
92
153
  - spec/spec_helper.rb
154
+ - spec/uncoil/domain_finder_spec.rb
155
+ - spec/uncoil/expander_spec.rb
93
156
  - spec/uncoil_response_spec.rb
94
157
  - spec/uncoil_spec.rb
95
158
  - uncoil.gemspec
96
159
  homepage: https://github.com/stim371/uncoil
97
160
  licenses: []
161
+ metadata: {}
98
162
  post_install_message:
99
163
  rdoc_options: []
100
164
  require_paths:
101
165
  - lib
102
166
  required_ruby_version: !ruby/object:Gem::Requirement
103
- none: false
104
167
  requirements:
105
168
  - - ! '>='
106
169
  - !ruby/object:Gem::Version
107
170
  version: '0'
108
171
  required_rubygems_version: !ruby/object:Gem::Requirement
109
- none: false
110
172
  requirements:
111
173
  - - ! '>='
112
174
  - !ruby/object:Gem::Version
113
175
  version: '0'
114
176
  requirements: []
115
177
  rubyforge_project: uncoil
116
- rubygems_version: 1.8.10
178
+ rubygems_version: 2.1.3
117
179
  signing_key:
118
- specification_version: 3
180
+ specification_version: 4
119
181
  summary: Uncoil is a gem to unshorten urls so you know where obscured links really
120
182
  go.
121
183
  test_files:
122
- - spec/helper_method_spec.rb
184
+ - spec/fixtures/uncoil_cassettes/array_of_links.yml
185
+ - spec/fixtures/uncoil_cassettes/bitly_api.yml
186
+ - spec/fixtures/uncoil_cassettes/isgd_api.yml
187
+ - spec/fixtures/uncoil_cassettes/isgd_response.yml
123
188
  - spec/spec_helper.rb
189
+ - spec/uncoil/domain_finder_spec.rb
190
+ - spec/uncoil/expander_spec.rb
124
191
  - spec/uncoil_response_spec.rb
125
192
  - spec/uncoil_spec.rb
126
193
  has_rdoc:
@@ -1,3 +0,0 @@
1
- lib/uncoil.rb 0ab3336a87ae06d8a3aa55e15104552fcd7f6e9e
2
- lib/uncoil/version.rb 2a25a2d46f761aa4d70eeee4a7f6adc42215db7a
3
- lib/uncoil_submethods.rb 94e18e69c539be74e772316cc9cdc9cfaad296fd
Binary file
@@ -1,2 +0,0 @@
1
- {I" Object:EF:
2
- class
@@ -1,38 +0,0 @@
1
- require 'open-uri'
2
- require 'net/http'
3
- require 'json'
4
-
5
- class Uncoil
6
-
7
- # Expands any links that are bitly or bitlypro domains as long as you have logged into the bitly API.
8
- #
9
- # @param [String] short_url The short url you would like to expand.
10
- #
11
- def uncoil_bitly short_url
12
- @bitly_instance.expand(short_url).long_url
13
- end
14
-
15
- # Expands any links that are isgd domains.
16
- #
17
- # @param [String] short_url The short url you would like to expand.
18
- #
19
- def uncoil_isgd short_url
20
- JSON.parse(open(ISGD_ROOT_URL + "#{short_url}") { |file| file.read } )["url"]
21
- end
22
-
23
- # Expands any links that do not match the above domains.
24
- #
25
- # @param [String] short_url The short url you would like to expand.
26
- # @param [Integer, 10] depth The number of redirects you would like this method to follow.
27
- #
28
- def uncoil_other short_url, depth = 10
29
- url = URI.encode(short_url)
30
- response = Net::HTTP.get_response(URI.parse(url))
31
-
32
- case response
33
- when Net::HTTPSuccess then url
34
- when Net::HTTPRedirection then uncoil_other(response['location'], depth - 1)
35
- end
36
- end
37
-
38
- end
@@ -1,55 +0,0 @@
1
- require 'spec_helper'
2
- require 'uncoil'
3
-
4
- describe Uncoil do
5
-
6
- subject { Uncoil.new(:bitlyuser => "stim371", :bitlykey => "R_7a6f6d845668a8a7bb3e0c80ee3c28d6")}
7
-
8
- context "when cleaning up the url" do
9
-
10
- it "should add the prefix if none exists" do
11
- subject.clean_url("cnn.com").should eq "http://cnn.com"
12
- subject.clean_url("cnn.com/").should eq "http://cnn.com"
13
- end
14
-
15
- it "should not add the prefix if one exists" do
16
- subject.clean_url("http://cnn.com").should eq "http://cnn.com"
17
- subject.clean_url("http://cnn.com/").should eq "http://cnn.com"
18
- end
19
-
20
- it "should not add the prefix if a secure one exists" do
21
- subject.clean_url("https://cnn.com").should eq "https://cnn.com"
22
- subject.clean_url("https://cnn.com/").should eq "https://cnn.com"
23
- end
24
-
25
- it "should remove the trailing slash" do
26
- subject.clean_url("http://cnn.com/").should eq "http://cnn.com"
27
- subject.clean_url("cnn.com/").should eq "http://cnn.com"
28
- end
29
-
30
- it "should not remove characters on the end that aren't slashes" do
31
- subject.clean_url("http://cnn.com").should eq "http://cnn.com"
32
- subject.clean_url("cnn.com").should eq "http://cnn.com"
33
- end
34
-
35
- end
36
-
37
- context "when extracting the domain" do
38
- it "should correctly extract the domain from a normal url" do
39
- subject.identify_domain("http://bit.ly/2EEjBl").should eq "bit.ly"
40
- end
41
- end
42
-
43
- context "when checking a domain" do
44
-
45
- it "should identify pro domains" do
46
- subject.check_bitly_pro("cs.pn").should be_true
47
- subject.check_bitly_pro("nyti.ms").should be_true
48
- end
49
-
50
- it "should identify non-pro domains" do
51
- subject.check_bitly_pro("bit.ly").should be_false #the bit.ly domain comes back false since its not a "pro" domain
52
- subject.check_bitly_pro("tinyurl.com").should be_false
53
- end
54
- end
55
- end