embiggen 0.2.0 → 1.0.0
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.
- 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:
|