uncoil 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 +4 -0
- data/Gemfile +4 -0
- data/Guardfile +8 -0
- data/Manifest +9 -0
- data/README.md +39 -0
- data/Rakefile +12 -0
- data/lib/uncoil/version.rb +3 -0
- data/lib/uncoil.rb +81 -0
- data/spec/uncoil_spec.rb +130 -0
- data/uncoil.gemspec +26 -0
- metadata +92 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
data/Manifest
ADDED
data/README.md
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# uncoil
|
2
|
+
|
3
|
+
The uncoil gem is a one stop shop for un-shortening urls.
|
4
|
+
|
5
|
+
The idea is based off of my site http://uncoil.me and I built this as part of my UWE-Ruby fall project.
|
6
|
+
|
7
|
+
## Why the heck does this exist?
|
8
|
+
This gem is all about transparency and safety, and knowing where you are going on the internet.
|
9
|
+
|
10
|
+
There are a few instances where it may come in handy:
|
11
|
+
|
12
|
+
* You want to make sure you're not heading into an obviously sketchy site
|
13
|
+
* You're at work and want to keep out the NSFW
|
14
|
+
|
15
|
+
## Example
|
16
|
+
I have no idea where http://bit.ly/2EEjBl really goes.
|
17
|
+
|
18
|
+
But by expanding it with uncoil, I can see that it goes to http://www.cnn.com
|
19
|
+
|
20
|
+
## How the app works
|
21
|
+
|
22
|
+
1. Extract the domain from the url
|
23
|
+
2. See if it matches with any of the supported APIs
|
24
|
+
* If so, it calls the correct API method
|
25
|
+
* If no matching method is found, it runs through an HTTP loop until it receives a 200 response
|
26
|
+
3. It then returns a hash containing, among other items, the full url
|
27
|
+
|
28
|
+
## Current Issues
|
29
|
+
|
30
|
+
## Future Enhancements
|
31
|
+
Here are a few ideas I have for the future:
|
32
|
+
|
33
|
+
* Additional APIs as they become available
|
34
|
+
* These seem to be faster than a general HTTP request loop
|
35
|
+
* Modularized structure with each API's content in its own file
|
36
|
+
* Dynamic method assignment based on domain.
|
37
|
+
* 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
|
38
|
+
* Better error handling (I'd appreciate any comments on what to catch and what to leave)
|
39
|
+
* Offer the code as a gem
|
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require 'rubygems'
|
3
|
+
require 'rake'
|
4
|
+
require 'rspec'
|
5
|
+
|
6
|
+
desc "`rake` will default to running `rake:spec`"
|
7
|
+
task :default => :spec
|
8
|
+
|
9
|
+
desc "Run all the rspec examples"
|
10
|
+
task :spec do
|
11
|
+
system "bundle exec rspec -c spec"
|
12
|
+
end
|
data/lib/uncoil.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'bitly'
|
2
|
+
require 'open-uri'
|
3
|
+
require 'net/http'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
class Uncoil
|
7
|
+
ISGD_ROOT_URL = "http://is.gd/forward.php?format=json&shorturl="
|
8
|
+
BITLY_DOM_ARRAY = %w[bit.ly, j.mp, bitlypro.com, cs.pn, nyti.ms]
|
9
|
+
|
10
|
+
def initialize options = {}
|
11
|
+
Bitly.use_api_version_3
|
12
|
+
@bitly_access = false
|
13
|
+
|
14
|
+
if options.has_key?(:bitlyuser) && options.has_key?(:bitlykey)
|
15
|
+
@bitly_instance = Bitly.new("#{options[:bitlyuser]}", "#{options[:bitlykey]}")
|
16
|
+
@bitly_access = true
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def identify_domain short_url
|
21
|
+
clean_url(short_url).split("/")[2].to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
def clean_url short_url
|
25
|
+
short_url = "http://" << short_url unless short_url =~ /^https?:\/\//
|
26
|
+
short_url.chop! if short_url[-1] == "/"
|
27
|
+
short_url
|
28
|
+
end
|
29
|
+
|
30
|
+
def expand url_arr
|
31
|
+
out_arr = Array(url_arr).flatten.map do |short_url|
|
32
|
+
short_url = clean_url(short_url)
|
33
|
+
domain = identify_domain(short_url)
|
34
|
+
|
35
|
+
@bitly_access.nil? ? error = "Not logged in to Bitly API" : error = nil
|
36
|
+
|
37
|
+
begin
|
38
|
+
if @bitly_access && BITLY_DOM_ARRAY.include?(domain)
|
39
|
+
long_url = uncoil_bitly(short_url)
|
40
|
+
elsif @bitly_access && check_bitly_pro(domain)
|
41
|
+
long_url = uncoil_bitly(short_url)
|
42
|
+
elsif domain == "is.gd"
|
43
|
+
long_url = uncoil_isgd(short_url)
|
44
|
+
else
|
45
|
+
long_url = uncoil_other(short_url)
|
46
|
+
end
|
47
|
+
rescue => exception
|
48
|
+
long_url = nil
|
49
|
+
error = exception.message
|
50
|
+
end
|
51
|
+
|
52
|
+
{ :short_url => short_url , :long_url => long_url, :error => error }
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
out_arr.length == 1 ? out_arr[0] : out_arr
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
def check_bitly_pro url_domain
|
61
|
+
@bitly_instance.bitly_pro_domain(url_domain)
|
62
|
+
end
|
63
|
+
|
64
|
+
def uncoil_bitly short_url
|
65
|
+
@bitly_instance.expand(short_url).long_url
|
66
|
+
end
|
67
|
+
|
68
|
+
def uncoil_isgd short_url
|
69
|
+
JSON.parse(open(ISGD_ROOT_URL + "#{short_url}") { |file| file.read } )["url"]
|
70
|
+
end
|
71
|
+
|
72
|
+
def uncoil_other short_url, depth = 10
|
73
|
+
url = URI.encode(short_url)
|
74
|
+
response = Net::HTTP.get_response(URI.parse(url))
|
75
|
+
|
76
|
+
case response
|
77
|
+
when Net::HTTPSuccess then url
|
78
|
+
when Net::HTTPRedirection then uncoil_other(response['location'], depth - 1)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
data/spec/uncoil_spec.rb
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'uncoil'
|
2
|
+
|
3
|
+
describe Uncoil do
|
4
|
+
|
5
|
+
subject { Uncoil.new(:bitlyuser => "stim371", :bitlykey => "R_7a6f6d845668a8a7bb3e0c80ee3c28d6")}
|
6
|
+
|
7
|
+
context "when cleaning up the url" do
|
8
|
+
|
9
|
+
it "should add the prefix if none exists" do
|
10
|
+
subject.clean_url("cnn.com").should eq "http://cnn.com"
|
11
|
+
subject.clean_url("cnn.com/").should eq "http://cnn.com"
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should not add the prefix if one exists" do
|
15
|
+
subject.clean_url("http://cnn.com").should eq "http://cnn.com"
|
16
|
+
subject.clean_url("http://cnn.com/").should eq "http://cnn.com"
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should not add the prefix if a secure one exists" do
|
20
|
+
subject.clean_url("https://cnn.com").should eq "https://cnn.com"
|
21
|
+
subject.clean_url("https://cnn.com/").should eq "https://cnn.com"
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should remove the trailing slash" do
|
25
|
+
subject.clean_url("http://cnn.com/").should eq "http://cnn.com"
|
26
|
+
subject.clean_url("cnn.com/").should eq "http://cnn.com"
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should not remove characters on the end that aren't slashes" do
|
30
|
+
subject.clean_url("http://cnn.com").should eq "http://cnn.com"
|
31
|
+
subject.clean_url("cnn.com").should eq "http://cnn.com"
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
context "when extracting the domain" do
|
37
|
+
|
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
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
context "when checking a domain" do
|
45
|
+
|
46
|
+
it "should identify pro domains" do
|
47
|
+
subject.check_bitly_pro("cs.pn").should be_true
|
48
|
+
subject.check_bitly_pro("nyti.ms").should be_true
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should identify non-pro domains" do
|
52
|
+
subject.check_bitly_pro("bit.ly").should be_false #the bit.ly domain comes back false since its not a "pro" domain
|
53
|
+
subject.check_bitly_pro("tinyurl.com").should be_false
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "when using the submethods" do
|
59
|
+
|
60
|
+
context "when trying to undo a bit.ly link" do
|
61
|
+
|
62
|
+
it "should bring back the correct long link" do
|
63
|
+
expected_result = "http://www.cnn.com/"
|
64
|
+
subject.uncoil_bitly("http://bit.ly/2EEjBl").should eq expected_result
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
context "when trying to undo a bit.ly pro link" do
|
70
|
+
|
71
|
+
it "should bring back the correct long link" do
|
72
|
+
expected_result = "http://www.c-spanvideo.org/program/CainNew"
|
73
|
+
subject.uncoil_bitly("http://cs.pn/vsZpra").should eq expected_result
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
context "when trying to undo an is.gd link" do
|
79
|
+
|
80
|
+
it "should bring back the correct long link" do
|
81
|
+
subject.uncoil_isgd("http://is.gd/gbKNRq").should eq "http://www.google.com"
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
context "when trying to undo from other services" do
|
87
|
+
|
88
|
+
it "should bring back the correct long link" do
|
89
|
+
subject.uncoil_other("http://tinyurl.com/736swvl").should eq "http://www.chinadaily.com.cn/usa/business/2011-11/08/content_14057648.htm"
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
context "when using the main expand method" do
|
96
|
+
|
97
|
+
it "should expand bitly correctly" do
|
98
|
+
expected_result = Hash[:long_url => "http://www.cnn.com/", :short_url => "http://bit.ly/2EEjBl", :error => nil]
|
99
|
+
subject.expand("http://bit.ly/2EEjBl").should eq expected_result
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should expand bitlypro domains correctly" do
|
103
|
+
subject.expand("http://cs.pn/vsZpra").should eq Hash[:long_url => "http://www.c-spanvideo.org/program/CainNew", :short_url => "http://cs.pn/vsZpra", :error => nil]
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should expand isgd domains correctly" do
|
107
|
+
subject.expand("http://is.gd/gbKNRq").should eq Hash[:long_url => "http://www.google.com", :short_url => "http://is.gd/gbKNRq", :error => nil]
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should expand other shortened urls correctly" do
|
111
|
+
short_url = "http://tinyurl.com/736swvl"
|
112
|
+
results_array = Hash[:long_url => "http://www.chinadaily.com.cn/usa/business/2011-11/08/content_14057648.htm", :short_url => "http://tinyurl.com/736swvl", :error => nil]
|
113
|
+
|
114
|
+
subject.expand(short_url).should eq results_array
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should also take an array of quotes" do
|
118
|
+
arr_of_links = ["http://bit.ly/2EEjBl","http://is.gd/gbKNRq","http://cs.pn/vsZpra"]
|
119
|
+
results_array = [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]]
|
120
|
+
|
121
|
+
subject.expand(arr_of_links).should eq results_array
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should raise an error for non-urls" do
|
125
|
+
subject.expand("a")[:error].should_not be_nil
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
data/uncoil.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "uncoil/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "uncoil"
|
7
|
+
s.version = Uncoil::VERSION
|
8
|
+
s.authors = ["Joel Stimson"]
|
9
|
+
s.email = ["contact@cleanroomstudios.com"]
|
10
|
+
s.homepage = "http://uncoil.me"
|
11
|
+
s.summary = %q{Uncoil is a gem to unshorten urls so you know where obscured links really go.}
|
12
|
+
s.description = %q{Uncoil is a gem to unshorten urls so you know where obscured links really go. If you have bitly login credentials, initialize an instance of Uncoil to use the bitly API.}
|
13
|
+
|
14
|
+
s.rubyforge_project = "uncoil"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
# specify any dependencies here; for example:
|
22
|
+
s.add_development_dependency "rspec"
|
23
|
+
s.add_development_dependency "guard-rspec"
|
24
|
+
|
25
|
+
s.add_runtime_dependency "bitly"
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: uncoil
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Joel Stimson
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-01-20 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: &2154321340 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *2154321340
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: guard-rspec
|
27
|
+
requirement: &2154320920 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *2154320920
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: bitly
|
38
|
+
requirement: &2154320500 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *2154320500
|
47
|
+
description: Uncoil is a gem to unshorten urls so you know where obscured links really
|
48
|
+
go. If you have bitly login credentials, initialize an instance of Uncoil to use
|
49
|
+
the bitly API.
|
50
|
+
email:
|
51
|
+
- contact@cleanroomstudios.com
|
52
|
+
executables: []
|
53
|
+
extensions: []
|
54
|
+
extra_rdoc_files: []
|
55
|
+
files:
|
56
|
+
- .gitignore
|
57
|
+
- Gemfile
|
58
|
+
- Guardfile
|
59
|
+
- Manifest
|
60
|
+
- README.md
|
61
|
+
- Rakefile
|
62
|
+
- lib/uncoil.rb
|
63
|
+
- lib/uncoil/version.rb
|
64
|
+
- spec/uncoil_spec.rb
|
65
|
+
- uncoil.gemspec
|
66
|
+
homepage: http://uncoil.me
|
67
|
+
licenses: []
|
68
|
+
post_install_message:
|
69
|
+
rdoc_options: []
|
70
|
+
require_paths:
|
71
|
+
- lib
|
72
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
none: false
|
80
|
+
requirements:
|
81
|
+
- - ! '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
requirements: []
|
85
|
+
rubyforge_project: uncoil
|
86
|
+
rubygems_version: 1.8.10
|
87
|
+
signing_key:
|
88
|
+
specification_version: 3
|
89
|
+
summary: Uncoil is a gem to unshorten urls so you know where obscured links really
|
90
|
+
go.
|
91
|
+
test_files:
|
92
|
+
- spec/uncoil_spec.rb
|