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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e042af897bb6b3d3f859ef7f8b98b2baa82a549c
4
- data.tar.gz: de3ac88d70c05591727ba4cbd291dea762add074
3
+ metadata.gz: f799848928c0c97fed7139de5465eaf62b564c35
4
+ data.tar.gz: 6da8a48878868a89f5837b15c0cceca531eece18
5
5
  SHA512:
6
- metadata.gz: 52ea1b1bdd91f6d958ff730e94dafa63475794b28803743fce061980288bd9d75d80f1937eaad6c998d7b6b967daf9c274500e3fa6979f590a5044ec121f20b7
7
- data.tar.gz: 920c7a714f80a7d75ade6a8982a68c6791bc4bfda7aca81e7956469a2d8ec0e1d92c9c71995577bf6ef319d2a45c62e352b8f4fe8ef415e1f88b39e33023c3c1
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.2.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.2'
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.2'
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:HTTPS https://www.youtube.com/watch?v=dQw4w9WgXcQ&feature=youtu.be>
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:HTTPS https://www.youtube.com/watch?v=dQw4w9WgXcQ&feature=youtu.be>
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:HTTP http://www.altmetric.com>
41
+ #=> #<URI::HTTP http://www.altmetric.com>
42
42
 
43
- # Noisier expand! for explicit error handling
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:HTTPS https://www.youtube.com/watch?v=dQw4w9WgXcQ&feature=youtu.be>
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 cannot be expanded. Will not raise any
111
- exceptions thrown during expansion (e.g. timeouts, network errors, invalid
112
- return URIs); see `expand!` for an alternative.
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` or
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` or `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 ||= Set.new(%w(
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 'net/http'
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.fetch(:redirects) { Configuration.redirects }
25
- check_redirects(redirects)
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
- URI.new(location).
31
- expand!(request_options.merge(:redirects => redirects - 1))
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.any? { |domain| uri.host =~ /\b#{domain}\z/i }
27
+ Configuration.shorteners.include?(uri)
36
28
  end
37
29
 
38
30
  private
39
31
 
40
- def check_redirects(redirects)
41
- return unless redirects.zero?
42
-
43
- fail TooManyRedirects.new("#{uri} redirected too many times", uri)
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
- fail BadShortenedURI, "following #{uri} did not redirect"
37
+ redirects
50
38
  end
51
39
 
52
- def head_location(request_options = {})
40
+ def follow(request_options = {})
53
41
  timeout = request_options.fetch(:timeout) { Configuration.timeout }
54
42
 
55
- http.open_timeout = timeout
56
- http.read_timeout = timeout
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
- response.fetch('Location') if response.is_a?(::Net::HTTPRedirection)
47
+ location
61
48
  end
62
49
 
63
- def http
64
- http = ::Net::HTTP.new(uri.host, uri.port)
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
- def initialize(message, uri)
78
- super(message)
79
- @uri = uri
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
@@ -61,46 +61,103 @@ module Embiggen
61
61
  expect { uri.expand }.to_not raise_error
62
62
  end
63
63
 
64
- it 'does not expand erroring URIs' do
65
- stub_request(:head, 'http://bit.ly/bad').to_return(:status => 500)
66
- uri = described_class.new(URI('http://bit.ly/bad'))
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(uri.expand).to eq(URI('http://bit.ly/bad'))
70
+ expect { uri.expand(:redirects => 2) }.
71
+ to raise_error(TooManyRedirects)
69
72
  end
70
73
 
71
- it 'does not expand URIs that time out' do
72
- stub_request(:head, 'http://bit.ly/bad').to_timeout
73
- uri = described_class.new(URI('http://bit.ly/bad'))
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(uri.expand).to eq(URI('http://bit.ly/bad'))
88
+ expect(last_uri).to eq(URI('http://bit.ly/3'))
76
89
  end
77
90
 
78
- it 'does not expand URIs whose connection resets' do
79
- stub_request(:head, 'http://bit.ly/bad').to_raise(Errno::ECONNRESET)
80
- uri = described_class.new(URI('http://bit.ly/bad'))
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(uri.expand).to eq(URI('http://bit.ly/bad'))
95
+ expect { uri.expand }.to raise_error(BadShortenedURI)
83
96
  end
84
97
 
85
- it 'does not expand URIs whose host is unreachable' do
86
- stub_request(:head, 'http://bit.ly/bad').to_raise(Errno::EHOSTUNREACH)
87
- uri = described_class.new(URI('http://bit.ly/bad'))
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(uri.expand).to eq(URI('http://bit.ly/bad'))
102
+ expect { uri.expand }.to raise_error(BadShortenedURI)
90
103
  end
91
104
 
92
- it 'does not expand URIs whose name or service is not known' do
93
- stub_request(:head, 'http://bit.ly/bad').to_raise(SocketError)
94
- uri = described_class.new(URI('http://bit.ly/bad'))
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(uri.expand).to eq(URI('http://bit.ly/bad'))
109
+ expect { uri.expand }.to raise_error(BadShortenedURI)
97
110
  end
98
111
 
99
- it 'takes an optional timeout' do
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(URI('http://bit.ly/bad'))
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(uri.expand(:timeout => 5)).to eq(URI('http://bit.ly/bad'))
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(uri.expand).to eq(URI('http://bit.ly/6'))
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(uri.expand(:redirects => 2)).to eq(URI('http://bit.ly/3'))
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(uri.expand).to eq(URI('http://bit.ly/3'))
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 the original URI' do
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.2.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-05-31 00:00:00.000000000 Z
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: