embiggen 0.2.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +68 -42
- data/lib/embiggen/configuration.rb +2 -1
- data/lib/embiggen/error.rb +14 -0
- data/lib/embiggen/http_client.rb +33 -0
- data/lib/embiggen/shortener_list.rb +25 -0
- data/lib/embiggen/uri.rb +26 -49
- data/spec/embiggen/shortener_list_spec.rb +86 -0
- data/spec/embiggen/uri_spec.rb +92 -74
- metadata +8 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f799848928c0c97fed7139de5465eaf62b564c35
|
4
|
+
data.tar.gz: 6da8a48878868a89f5837b15c0cceca531eece18
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 185ef0720cd5c8a438aad74c4f15f68c68a5d37934ae829d777377330a57c2cfb070ac880945f5e85277a1359bad9070c45be273e567529377ce033a1e316acf
|
7
|
+
data.tar.gz: 84d6bc662878057e2dfe1f8a0e212947d41f53b4d0cd664810a47a2f96d7617de1957d79005f1cc3e700b74cfb270770f6da19cb492f279f7451a92303b1672d
|
data/README.md
CHANGED
@@ -2,19 +2,19 @@
|
|
2
2
|
|
3
3
|
A Ruby library to expand shortened URLs.
|
4
4
|
|
5
|
-
**Current version:** 0.
|
5
|
+
**Current version:** 1.0.0
|
6
6
|
**Supported Ruby versions:** 1.8.7, 1.9.2, 1.9.3, 2.0, 2.1, 2.2
|
7
7
|
|
8
8
|
## Installation
|
9
9
|
|
10
10
|
```
|
11
|
-
gem install embiggen -v '~> 0
|
11
|
+
gem install embiggen -v '~> 1.0'
|
12
12
|
```
|
13
13
|
|
14
14
|
Or, in your `Gemfile`:
|
15
15
|
|
16
16
|
```ruby
|
17
|
-
gem 'embiggen', '~> 0
|
17
|
+
gem 'embiggen', '~> 1.0'
|
18
18
|
```
|
19
19
|
|
20
20
|
## Usage
|
@@ -24,24 +24,24 @@ require 'embiggen'
|
|
24
24
|
|
25
25
|
# Basic usage
|
26
26
|
Embiggen::URI('https://youtu.be/dQw4w9WgXcQ').expand
|
27
|
-
#=> #<URI
|
27
|
+
#=> #<URI::HTTPS https://www.youtube.com/watch?v=dQw4w9WgXcQ&feature=youtu.be>
|
28
28
|
|
29
29
|
# Longer-form usage
|
30
30
|
uri = Embiggen::URI.new(URI('https://youtu.be/dQw4w9WgXcQ'))
|
31
31
|
uri.shortened?
|
32
32
|
#=> true
|
33
33
|
uri.expand
|
34
|
-
#=> #<URI
|
34
|
+
#=> #<URI::HTTPS https://www.youtube.com/watch?v=dQw4w9WgXcQ&feature=youtu.be>
|
35
35
|
|
36
36
|
# Gracefully deals with unshortened URIs
|
37
37
|
uri = Embiggen::URI('http://www.altmetric.com')
|
38
38
|
uri.shortened?
|
39
39
|
#=> false
|
40
40
|
uri.expand
|
41
|
-
#=> #<URI
|
41
|
+
#=> #<URI::HTTP http://www.altmetric.com>
|
42
42
|
|
43
|
-
#
|
44
|
-
Embiggen::URI('http://bit.ly/bad').expand
|
43
|
+
# Raises errors with bad shortened URIs
|
44
|
+
Embiggen::URI('http://bit.ly/bad').expand
|
45
45
|
#=> TooManyRedirects: http://bit.ly/bad redirected too many times
|
46
46
|
# or...
|
47
47
|
#=> BadShortenedURI: following http://bit.ly/bad did not redirect
|
@@ -66,13 +66,55 @@ end
|
|
66
66
|
Embiggen ships with a default list of URL shortening service domains (c.f.
|
67
67
|
[Acknowledgements](#acknowledgements)) but as it is likely to be outdated and
|
68
68
|
incomplete, you are strongly encouraged to supply your own via
|
69
|
-
`Embiggen.configure
|
69
|
+
`Embiggen.configure`.
|
70
|
+
|
71
|
+
The list of shorteners is an object that responds to `include?` and will be
|
72
|
+
passed a URI. By default, Embiggen ships with a `ShortenerList` class which
|
73
|
+
takes a list of string domains and will return `true` if given a URI with a
|
74
|
+
matching host.
|
75
|
+
|
76
|
+
You can supply your own values and logic like so:
|
70
77
|
|
71
78
|
```ruby
|
72
79
|
Embiggen.configure do |config|
|
73
|
-
config.shorteners = %w(myshorten.er anoth.er)
|
80
|
+
config.shorteners = Embiggen::ShortenerList.new(%w(myshorten.er anoth.er))
|
74
81
|
# or load from a file...
|
75
|
-
config.shorteners = File.readlines('shorteners.txt').map(&:chomp)
|
82
|
+
config.shorteners = Embiggen::ShortenerList.new(File.readlines('shorteners.txt').map(&:chomp))
|
83
|
+
end
|
84
|
+
|
85
|
+
# Custom logic to attempt to expand every URI
|
86
|
+
class ExpandEverything
|
87
|
+
def self.include?(_uri)
|
88
|
+
true
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
Embiggen.configure do |config|
|
93
|
+
config.shorteners = ExpandEverything
|
94
|
+
end
|
95
|
+
|
96
|
+
# Use the Bitly API to only expand URIs on Bitly Pro domains
|
97
|
+
require 'bitly'
|
98
|
+
require 'forwardable'
|
99
|
+
|
100
|
+
class BitlyDomains
|
101
|
+
extend Forwardable
|
102
|
+
attr_reader :client
|
103
|
+
def_delegator :client, :pro?, :include?
|
104
|
+
|
105
|
+
def initialize(client)
|
106
|
+
@client = client
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
Bitly.use_api_version_3
|
111
|
+
Bitly.configure do |config|
|
112
|
+
config.api_version = 3
|
113
|
+
config.access_token = ENV.fetch('BITLY_ACCESS_TOKEN')
|
114
|
+
end
|
115
|
+
|
116
|
+
Embiggen.configure do |config|
|
117
|
+
config.shorteners = BitlyDomains.new(Bitly.client)
|
76
118
|
end
|
77
119
|
```
|
78
120
|
|
@@ -97,7 +139,7 @@ valid URI.
|
|
97
139
|
|
98
140
|
```ruby
|
99
141
|
Embiggen::URI('https://youtu.be/dQw4w9WgXcQ').expand
|
100
|
-
#=> #<URI
|
142
|
+
#=> #<URI::HTTPS https://www.youtube.com/watch?v=dQw4w9WgXcQ&feature=youtu.be>
|
101
143
|
|
102
144
|
Embiggen::URI('http://www.altmetric.com/').expand
|
103
145
|
#=> #<URI::HTTP http://www.altmetric.com/>
|
@@ -107,9 +149,18 @@ Embiggen::URI('https://youtu.be/dQw4w9WgXcQ').expand(:redirects => 2)
|
|
107
149
|
```
|
108
150
|
|
109
151
|
Expand the given URI, returning the full version as a [`URI`][URI] if it is
|
110
|
-
shortened or the original if it
|
111
|
-
|
112
|
-
|
152
|
+
shortened or the original if it is not. Can raise the following exceptions
|
153
|
+
during expansion:
|
154
|
+
|
155
|
+
* `Embiggen::TooManyRedirects`: when a URI redirects more than the configured
|
156
|
+
number of times;
|
157
|
+
* `Embiggen::BadShortenedURI`: when a URI appears to be shortened but
|
158
|
+
following it does not result in a redirect;
|
159
|
+
* `Embiggen::NetworkError`: when an error occurs during expansion (e.g. a
|
160
|
+
network timeout, connection reset, unreachable host, etc.).
|
161
|
+
|
162
|
+
All of the above inherit from `Embiggen::Error` and have a `uri` method for
|
163
|
+
determining the problematic URI.
|
113
164
|
|
114
165
|
Takes an optional hash of options for expansion:
|
115
166
|
|
@@ -120,30 +171,6 @@ Uses a whitelist of shortening domains (as configured through
|
|
120
171
|
`Embiggen.configure`) to determine whether a URI is shortened or not. Be sure
|
121
172
|
to [configure this to suit your needs](#shorteners).
|
122
173
|
|
123
|
-
### `Embiggen::URI#expand!`
|
124
|
-
|
125
|
-
```ruby
|
126
|
-
Embiggen::URI('https://youtu.be/dQw4w9WgXcQ').expand!
|
127
|
-
#=> #<URI:HTTPS https://www.youtube.com/watch?v=dQw4w9WgXcQ&feature=youtu.be>
|
128
|
-
|
129
|
-
Embiggen::URI('http://bit.ly/some-bad-link').expand!
|
130
|
-
# TooManyRedirects: http://bit.ly/some-bad-link redirected too many times
|
131
|
-
```
|
132
|
-
|
133
|
-
Expand the given URI as with `Embiggen::URI#expand` but don't suppress any
|
134
|
-
exceptions raised during expansion (including timeouts, network errors,
|
135
|
-
invalid return URIs, too many redirects or no redirects whatsoever).
|
136
|
-
|
137
|
-
Takes the same options as `Embiggen::URI#expand`.
|
138
|
-
|
139
|
-
Two Embiggen-specific errors (both inheriting from `Embiggen::Error`) can be
|
140
|
-
raised:
|
141
|
-
|
142
|
-
* `Embiggen::TooManyRedirects`: when a URI redirects more than the configured
|
143
|
-
number of times;
|
144
|
-
* `Embiggen::BadShortenedURI`: when a URI appears to be shortened but
|
145
|
-
following it does not result in a redirect.
|
146
|
-
|
147
174
|
### `Embiggen::URI#shortened?`
|
148
175
|
|
149
176
|
```ruby
|
@@ -170,10 +197,9 @@ end
|
|
170
197
|
Override the following settings:
|
171
198
|
|
172
199
|
* `timeout`: the default timeout for following any redirects (can be
|
173
|
-
overridden by passing options to `Embiggen::URI#expand`
|
174
|
-
`Embiggen::URI#expand!`);
|
200
|
+
overridden by passing options to `Embiggen::URI#expand`);
|
175
201
|
* `redirects`: the default number of redirects to follow (can be overridden by
|
176
|
-
passing options to `Embiggen::URI#expand`
|
202
|
+
passing options to `Embiggen::URI#expand`);
|
177
203
|
* `shorteners`: the list of domains of shortening services, c.f.
|
178
204
|
[Shorteners](#shorteners).
|
179
205
|
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
require 'set'
|
3
|
+
require 'embiggen/shortener_list'
|
3
4
|
|
4
5
|
module Embiggen
|
5
6
|
class Configuration
|
@@ -17,7 +18,7 @@ module Embiggen
|
|
17
18
|
|
18
19
|
# From http://longurl.org/services
|
19
20
|
def self.shorteners
|
20
|
-
@shorteners ||=
|
21
|
+
@shorteners ||= ShortenerList.new(%w(
|
21
22
|
0rz.tw 1link.in 1url.com 2.gp 2big.at 2tu.us 3.ly 307.to 4ms.me
|
22
23
|
4sq.com 4url.cc 6url.com 7.ly a.gg a.nf aa.cx abcurl.net ad.vu adf.ly
|
23
24
|
adjix.com afx.cc all.fuseurl.com alturl.com amzn.to ar.gy arst.ch
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Embiggen
|
2
|
+
class Error < ::StandardError
|
3
|
+
attr_reader :uri
|
4
|
+
|
5
|
+
def initialize(message, uri)
|
6
|
+
super(message)
|
7
|
+
@uri = uri
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class BadShortenedURI < Error; end
|
12
|
+
class NetworkError < Error; end
|
13
|
+
class TooManyRedirects < Error; end
|
14
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'embiggen/error'
|
2
|
+
require 'net/http'
|
3
|
+
|
4
|
+
module Embiggen
|
5
|
+
class HttpClient
|
6
|
+
attr_reader :uri, :http
|
7
|
+
|
8
|
+
def initialize(uri)
|
9
|
+
@uri = uri
|
10
|
+
@http = ::Net::HTTP.new(uri.host, uri.port)
|
11
|
+
@http.use_ssl = true if uri.scheme == 'https'
|
12
|
+
end
|
13
|
+
|
14
|
+
def follow(timeout)
|
15
|
+
response = request(timeout)
|
16
|
+
return unless response.is_a?(::Net::HTTPRedirection)
|
17
|
+
|
18
|
+
response.fetch('Location')
|
19
|
+
rescue StandardError, ::Timeout::Error => e
|
20
|
+
raise NetworkError.new(
|
21
|
+
"could not follow #{uri}: #{e.message}", uri)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def request(timeout)
|
27
|
+
http.open_timeout = timeout
|
28
|
+
http.read_timeout = timeout
|
29
|
+
|
30
|
+
http.head(uri.request_uri)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'set'
|
3
|
+
|
4
|
+
module Embiggen
|
5
|
+
class ShortenerList
|
6
|
+
extend Forwardable
|
7
|
+
include Enumerable
|
8
|
+
|
9
|
+
attr_reader :domains
|
10
|
+
|
11
|
+
def initialize(domains)
|
12
|
+
@domains = Set.new(domains)
|
13
|
+
end
|
14
|
+
|
15
|
+
def include?(uri)
|
16
|
+
domains.any? { |domain| uri.host =~ /\b#{domain}\z/i }
|
17
|
+
end
|
18
|
+
|
19
|
+
def +(other)
|
20
|
+
self.class.new(domains + other)
|
21
|
+
end
|
22
|
+
|
23
|
+
def_delegators :domains, :<<, :size, :delete, :empty?, :each
|
24
|
+
end
|
25
|
+
end
|
data/lib/embiggen/uri.rb
CHANGED
@@ -1,82 +1,59 @@
|
|
1
1
|
require 'embiggen/configuration'
|
2
|
+
require 'embiggen/error'
|
3
|
+
require 'embiggen/http_client'
|
2
4
|
require 'addressable/uri'
|
3
|
-
require '
|
5
|
+
require 'uri'
|
4
6
|
|
5
7
|
module Embiggen
|
6
8
|
class URI
|
7
|
-
attr_reader :uri
|
9
|
+
attr_reader :uri, :http_client
|
8
10
|
|
9
11
|
def initialize(uri)
|
10
12
|
@uri = URI(::Addressable::URI.parse(uri).normalize.to_s)
|
13
|
+
@http_client = HttpClient.new(@uri)
|
11
14
|
end
|
12
15
|
|
13
16
|
def expand(request_options = {})
|
14
|
-
expand!(request_options)
|
15
|
-
rescue TooManyRedirects => error
|
16
|
-
error.uri
|
17
|
-
rescue StandardError, ::Timeout::Error
|
18
|
-
uri
|
19
|
-
end
|
20
|
-
|
21
|
-
def expand!(request_options = {})
|
22
17
|
return uri unless shortened?
|
23
18
|
|
24
|
-
redirects = request_options
|
25
|
-
|
26
|
-
|
27
|
-
location = head_location(request_options)
|
28
|
-
check_location(location)
|
19
|
+
redirects = extract_redirects(request_options)
|
20
|
+
location = follow(request_options)
|
29
21
|
|
30
|
-
|
31
|
-
expand
|
22
|
+
self.class.new(location).
|
23
|
+
expand(request_options.merge(:redirects => redirects - 1))
|
32
24
|
end
|
33
25
|
|
34
26
|
def shortened?
|
35
|
-
Configuration.shorteners.
|
27
|
+
Configuration.shorteners.include?(uri)
|
36
28
|
end
|
37
29
|
|
38
30
|
private
|
39
31
|
|
40
|
-
def
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
end
|
45
|
-
|
46
|
-
def check_location(location)
|
47
|
-
return if location
|
32
|
+
def extract_redirects(request_options = {})
|
33
|
+
redirects = request_options.fetch(:redirects) { Configuration.redirects }
|
34
|
+
fail TooManyRedirects.new(
|
35
|
+
"following #{uri} reached the redirect limit", uri) if redirects.zero?
|
48
36
|
|
49
|
-
|
37
|
+
redirects
|
50
38
|
end
|
51
39
|
|
52
|
-
def
|
40
|
+
def follow(request_options = {})
|
53
41
|
timeout = request_options.fetch(:timeout) { Configuration.timeout }
|
54
42
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
response = http.head(uri.request_uri)
|
43
|
+
location = http_client.follow(timeout)
|
44
|
+
fail BadShortenedURI.new(
|
45
|
+
"following #{uri} did not redirect", uri) unless followable?(location)
|
59
46
|
|
60
|
-
|
47
|
+
location
|
61
48
|
end
|
62
49
|
|
63
|
-
def
|
64
|
-
|
65
|
-
http.use_ssl = true if uri.scheme == 'https'
|
66
|
-
|
67
|
-
http
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
class Error < ::StandardError; end
|
72
|
-
class BadShortenedURI < Error; end
|
73
|
-
|
74
|
-
class TooManyRedirects < Error
|
75
|
-
attr_reader :uri
|
50
|
+
def followable?(location)
|
51
|
+
return unless location
|
76
52
|
|
77
|
-
|
78
|
-
|
79
|
-
|
53
|
+
Addressable::URI.parse(location).absolute?
|
54
|
+
rescue Addressable::URI::InvalidURIError => e
|
55
|
+
raise BadShortenedURI.new(
|
56
|
+
"following #{uri} returns an invalid URI: #{e}", uri)
|
80
57
|
end
|
81
58
|
end
|
82
59
|
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'embiggen/shortener_list'
|
2
|
+
|
3
|
+
RSpec.describe Embiggen::ShortenerList do
|
4
|
+
describe '#new' do
|
5
|
+
it 'can be given a set' do
|
6
|
+
list = described_class.new(Set['a.com', 'b.com', 'b.com'])
|
7
|
+
|
8
|
+
expect(list.size).to eq(2)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'converts a given enumerable to a set' do
|
12
|
+
list = described_class.new(%w(a.com a.com))
|
13
|
+
|
14
|
+
expect(list.size).to eq(1)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '#include?' do
|
19
|
+
it 'returns true if a URL host is on the whitelist' do
|
20
|
+
list = described_class.new(%w(bit.ly))
|
21
|
+
|
22
|
+
expect(list).to include(URI('http://bit.ly/foo'))
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'returns false if a URL host is not on the whitelist' do
|
26
|
+
list = described_class.new(%w(bit.ly))
|
27
|
+
|
28
|
+
expect(list).to_not include(URI('http://www.altmetric.com'))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe '#<<' do
|
33
|
+
it 'appends domains to the list' do
|
34
|
+
list = described_class.new([])
|
35
|
+
list << 'bit.ly'
|
36
|
+
|
37
|
+
expect(list).to include(URI('http://bit.ly/foo'))
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'can be chained' do
|
41
|
+
list = described_class.new([])
|
42
|
+
list << 'bit.ly' << 'a.com'
|
43
|
+
|
44
|
+
expect(list).to include(URI('http://bit.ly/foo'), URI('http://a.com/bar'))
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '#size' do
|
49
|
+
it 'returns the number of domains in the list' do
|
50
|
+
list = described_class.new(%w(bit.ly ow.ly))
|
51
|
+
|
52
|
+
expect(list.size).to eq(2)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe '#delete' do
|
57
|
+
it 'removes domains from the list' do
|
58
|
+
list = described_class.new(%w(bit.ly))
|
59
|
+
list.delete('bit.ly')
|
60
|
+
|
61
|
+
expect(list).to be_empty
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '#+' do
|
66
|
+
it 'appends a list of domains to the existing one' do
|
67
|
+
list = described_class.new(%w(bit.ly))
|
68
|
+
list += %w(a.com)
|
69
|
+
|
70
|
+
expect(list).to include(URI('http://a.com/foo'))
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'can combine two lists' do
|
74
|
+
list = described_class.new(%w(bit.ly))
|
75
|
+
list += described_class.new(%w(a.com))
|
76
|
+
|
77
|
+
expect(list).to include(URI('http://a.com/foo'))
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'is enumerable for 1.8 compatiblity' do
|
82
|
+
list = described_class.new([])
|
83
|
+
|
84
|
+
expect(list).to be_kind_of(Enumerable)
|
85
|
+
end
|
86
|
+
end
|
data/spec/embiggen/uri_spec.rb
CHANGED
@@ -61,46 +61,103 @@ module Embiggen
|
|
61
61
|
expect { uri.expand }.to_not raise_error
|
62
62
|
end
|
63
63
|
|
64
|
-
it '
|
65
|
-
|
66
|
-
|
64
|
+
it 'raises an error if the URI redirects too many times' do
|
65
|
+
stub_redirect('http://bit.ly/1', 'http://bit.ly/2')
|
66
|
+
stub_redirect('http://bit.ly/2', 'http://bit.ly/3')
|
67
|
+
stub_redirect('http://bit.ly/3', 'http://bit.ly/4')
|
68
|
+
uri = described_class.new('http://bit.ly/1')
|
67
69
|
|
68
|
-
expect
|
70
|
+
expect { uri.expand(:redirects => 2) }.
|
71
|
+
to raise_error(TooManyRedirects)
|
69
72
|
end
|
70
73
|
|
71
|
-
it '
|
72
|
-
|
73
|
-
|
74
|
+
it 'retains the last URI when redirecting too many times' do
|
75
|
+
stub_redirect('http://bit.ly/1', 'http://bit.ly/2')
|
76
|
+
stub_redirect('http://bit.ly/2', 'http://bit.ly/3')
|
77
|
+
stub_redirect('http://bit.ly/3', 'http://bit.ly/4')
|
78
|
+
uri = described_class.new('http://bit.ly/1')
|
79
|
+
|
80
|
+
last_uri = nil
|
81
|
+
|
82
|
+
begin
|
83
|
+
uri.expand(:redirects => 2)
|
84
|
+
rescue TooManyRedirects => ex
|
85
|
+
last_uri = ex.uri
|
86
|
+
end
|
74
87
|
|
75
|
-
expect(
|
88
|
+
expect(last_uri).to eq(URI('http://bit.ly/3'))
|
76
89
|
end
|
77
90
|
|
78
|
-
it '
|
79
|
-
stub_request(:head, 'http://bit.ly/bad').
|
80
|
-
uri = described_class.new(
|
91
|
+
it 'raises an error if a shortened URI does not redirect' do
|
92
|
+
stub_request(:head, 'http://bit.ly/bad').to_return(:status => 500)
|
93
|
+
uri = described_class.new('http://bit.ly/bad')
|
81
94
|
|
82
|
-
expect
|
95
|
+
expect { uri.expand }.to raise_error(BadShortenedURI)
|
83
96
|
end
|
84
97
|
|
85
|
-
it '
|
86
|
-
|
87
|
-
uri = described_class.new(
|
98
|
+
it 'raises an error if the URI returned is not valid' do
|
99
|
+
stub_redirect('http://bit.ly/suspicious', '|cat /etc/passwd')
|
100
|
+
uri = described_class.new('http://bit.ly/suspicious')
|
88
101
|
|
89
|
-
expect
|
102
|
+
expect { uri.expand }.to raise_error(BadShortenedURI)
|
90
103
|
end
|
91
104
|
|
92
|
-
it '
|
93
|
-
|
94
|
-
uri = described_class.new(
|
105
|
+
it 'raises an error if the URI returned is not valid' do
|
106
|
+
stub_redirect('http://bit.ly/suspicious', 'http:')
|
107
|
+
uri = described_class.new('http://bit.ly/suspicious')
|
95
108
|
|
96
|
-
expect
|
109
|
+
expect { uri.expand }.to raise_error(BadShortenedURI)
|
97
110
|
end
|
98
111
|
|
99
|
-
it '
|
112
|
+
it 'retains the last URI if a shortened URI does not redirect' do
|
113
|
+
stub_redirect('http://bit.ly/bad', 'http://bit.ly/bad2')
|
114
|
+
stub_request(:head, 'http://bit.ly/bad2').to_return(:status => 500)
|
115
|
+
uri = described_class.new('http://bit.ly/bad')
|
116
|
+
|
117
|
+
last_uri = nil
|
118
|
+
|
119
|
+
begin
|
120
|
+
uri.expand
|
121
|
+
rescue BadShortenedURI => ex
|
122
|
+
last_uri = ex.uri
|
123
|
+
end
|
124
|
+
|
125
|
+
expect(last_uri).to eq(URI('http://bit.ly/bad2'))
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'raises a network error if the URI times out' do
|
100
129
|
stub_request(:head, 'http://bit.ly/bad').to_timeout
|
101
|
-
uri = described_class.new(
|
130
|
+
uri = described_class.new('http://bit.ly/bad')
|
131
|
+
|
132
|
+
expect { uri.expand }.to raise_error(NetworkError)
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'raises a network error if the connection resets' do
|
136
|
+
stub_request(:head, 'http://bit.ly/bad').to_raise(::Errno::ECONNRESET)
|
137
|
+
uri = described_class.new('http://bit.ly/bad')
|
102
138
|
|
103
|
-
expect
|
139
|
+
expect { uri.expand }.to raise_error(NetworkError)
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'raises a network error if the host cannot be reached' do
|
143
|
+
stub_request(:head, 'http://bit.ly/bad').to_raise(::Errno::EHOSTUNREACH)
|
144
|
+
uri = described_class.new('http://bit.ly/bad')
|
145
|
+
|
146
|
+
expect { uri.expand }.to raise_error(NetworkError)
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'retains the last URI if there is a network error' do
|
150
|
+
stub_redirect('http://bit.ly/bad', 'http://bit.ly/bad2')
|
151
|
+
stub_request(:head, 'http://bit.ly/bad2').to_timeout
|
152
|
+
uri = described_class.new('http://bit.ly/bad')
|
153
|
+
|
154
|
+
begin
|
155
|
+
uri.expand
|
156
|
+
rescue NetworkError => ex
|
157
|
+
last_uri = ex.uri
|
158
|
+
end
|
159
|
+
|
160
|
+
expect(last_uri).to eq(URI('http://bit.ly/bad2'))
|
104
161
|
end
|
105
162
|
|
106
163
|
it 'expands redirects to other shorteners' do
|
@@ -122,7 +179,7 @@ module Embiggen
|
|
122
179
|
stub_redirect('http://bit.ly/6', 'http://bit.ly/7')
|
123
180
|
uri = described_class.new(URI('http://bit.ly/1'))
|
124
181
|
|
125
|
-
expect
|
182
|
+
expect { uri.expand }.to raise_error(TooManyRedirects)
|
126
183
|
end
|
127
184
|
|
128
185
|
it 'takes an optional redirect threshold' do
|
@@ -131,7 +188,7 @@ module Embiggen
|
|
131
188
|
stub_redirect('http://bit.ly/3', 'http://bit.ly/4')
|
132
189
|
uri = described_class.new(URI('http://bit.ly/1'))
|
133
190
|
|
134
|
-
expect
|
191
|
+
expect { uri.expand(:redirects => 2) }.to raise_error(TooManyRedirects)
|
135
192
|
end
|
136
193
|
|
137
194
|
it 'uses the threshold from the configuration' do
|
@@ -141,7 +198,7 @@ module Embiggen
|
|
141
198
|
uri = described_class.new(URI('http://bit.ly/1'))
|
142
199
|
Configuration.redirects = 2
|
143
200
|
|
144
|
-
expect
|
201
|
+
expect { uri.expand }.to raise_error(TooManyRedirects)
|
145
202
|
end
|
146
203
|
|
147
204
|
it 'uses shorteners from the configuration' do
|
@@ -158,55 +215,8 @@ module Embiggen
|
|
158
215
|
end
|
159
216
|
end
|
160
217
|
|
161
|
-
describe '#expand!' do
|
162
|
-
it 'expands shortened URLs' do
|
163
|
-
stub_redirect('https://youtu.be/dQw4w9WgXcQ',
|
164
|
-
'https://www.youtube.com/watch?v=dQw4w9WgXcQ&feature=youtu.be')
|
165
|
-
uri = described_class.new(URI('https://youtu.be/dQw4w9WgXcQ'))
|
166
|
-
|
167
|
-
expect(uri.expand!).to eq(URI('https://www.youtube.com/watch?v=dQw4w9WgXcQ&feature=youtu.be'))
|
168
|
-
end
|
169
|
-
|
170
|
-
it 'does not expand unshortened URIs' do
|
171
|
-
uri = described_class.new(URI('http://www.altmetric.com'))
|
172
|
-
|
173
|
-
expect(uri.expand!).to eq(URI('http://www.altmetric.com'))
|
174
|
-
end
|
175
|
-
|
176
|
-
it 'raises an error if the URI redirects too many times' do
|
177
|
-
stub_redirect('http://bit.ly/1', 'http://bit.ly/2')
|
178
|
-
stub_redirect('http://bit.ly/2', 'http://bit.ly/3')
|
179
|
-
stub_redirect('http://bit.ly/3', 'http://bit.ly/4')
|
180
|
-
uri = described_class.new(URI('http://bit.ly/1'))
|
181
|
-
|
182
|
-
expect { uri.expand!(:redirects => 2) }.
|
183
|
-
to raise_error(TooManyRedirects)
|
184
|
-
end
|
185
|
-
|
186
|
-
it 'raises an error if a shortened URI does not redirect' do
|
187
|
-
stub_request(:head, 'http://bit.ly/bad').to_return(:status => 500)
|
188
|
-
uri = described_class.new(URI('http://bit.ly/bad'))
|
189
|
-
|
190
|
-
expect { uri.expand! }.to raise_error(BadShortenedURI)
|
191
|
-
end
|
192
|
-
|
193
|
-
it 'raises an error if the URI times out' do
|
194
|
-
stub_request(:head, 'http://bit.ly/bad').to_timeout
|
195
|
-
uri = described_class.new(URI('http://bit.ly/bad'))
|
196
|
-
|
197
|
-
expect { uri.expand! }.to raise_error(::Timeout::Error)
|
198
|
-
end
|
199
|
-
|
200
|
-
it 'raises an error if the URI errors' do
|
201
|
-
stub_request(:head, 'http://bit.ly/bad').to_raise(::Errno::ECONNRESET)
|
202
|
-
uri = described_class.new(URI('http://bit.ly/bad'))
|
203
|
-
|
204
|
-
expect { uri.expand! }.to raise_error(::Errno::ECONNRESET)
|
205
|
-
end
|
206
|
-
end
|
207
|
-
|
208
218
|
describe '#uri' do
|
209
|
-
it 'returns
|
219
|
+
it 'returns a URI' do
|
210
220
|
uri = described_class.new(URI('http://www.altmetric.com'))
|
211
221
|
|
212
222
|
expect(uri.uri).to eq(URI('http://www.altmetric.com'))
|
@@ -246,6 +256,14 @@ module Embiggen
|
|
246
256
|
end
|
247
257
|
end
|
248
258
|
|
259
|
+
describe '#http_client' do
|
260
|
+
it 'returns the HTTP client for the given URI' do
|
261
|
+
uri = described_class.new('http://www.altmetric.com')
|
262
|
+
|
263
|
+
expect(uri.http_client.uri).to eq(URI('http://www.altmetric.com'))
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
249
267
|
def stub_redirect(short_url, expanded_url, status = 301)
|
250
268
|
stub_request(:head, short_url).
|
251
269
|
to_return(:status => status, :headers => { 'Location' => expanded_url })
|
metadata
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: embiggen
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paul Mucur
|
8
|
+
- Jonathan Hernandez
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
|
-
date: 2015-
|
12
|
+
date: 2015-11-21 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
13
14
|
- !ruby/object:Gem::Dependency
|
14
15
|
name: addressable
|
@@ -64,8 +65,12 @@ files:
|
|
64
65
|
- README.md
|
65
66
|
- lib/embiggen.rb
|
66
67
|
- lib/embiggen/configuration.rb
|
68
|
+
- lib/embiggen/error.rb
|
69
|
+
- lib/embiggen/http_client.rb
|
70
|
+
- lib/embiggen/shortener_list.rb
|
67
71
|
- lib/embiggen/uri.rb
|
68
72
|
- spec/embiggen/configuration_spec.rb
|
73
|
+
- spec/embiggen/shortener_list_spec.rb
|
69
74
|
- spec/embiggen/uri_spec.rb
|
70
75
|
- spec/embiggen_spec.rb
|
71
76
|
- spec/spec_helper.rb
|
@@ -95,7 +100,7 @@ specification_version: 4
|
|
95
100
|
summary: A library to expand shortened URLs
|
96
101
|
test_files:
|
97
102
|
- spec/embiggen/configuration_spec.rb
|
103
|
+
- spec/embiggen/shortener_list_spec.rb
|
98
104
|
- spec/embiggen/uri_spec.rb
|
99
105
|
- spec/embiggen_spec.rb
|
100
106
|
- spec/spec_helper.rb
|
101
|
-
has_rdoc:
|