shortener 0.5.5 → 0.5.6
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.rdoc +43 -5
- data/app/controllers/shortener/shortened_urls_controller.rb +7 -36
- data/app/helpers/shortener/shortener_helper.rb +1 -1
- data/app/models/shortener/shortened_url.rb +60 -13
- data/lib/shortener.rb +5 -0
- data/lib/shortener/version.rb +1 -1
- data/shortener.gemspec +1 -0
- data/spec/controllers/shortened_urls_controller_spec.rb +20 -0
- data/spec/models/shortened_url_spec.rb +110 -0
- metadata +33 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0f710d548cc2a095e643ed04c786691a723f1449
|
4
|
+
data.tar.gz: e3fc832851d5e4f72750883844cc6eaee97c5e65
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e89866e4b81029b37adcadbe79e42cca296b57a60fb1c6fd4e50860258b2837d9c8250e2e4924f9d56f3026f1ac76b35b54a45ffdb87f43b72c55a44139c371d
|
7
|
+
data.tar.gz: 3b79ef1a1fbf5b9145431bc239f1ab7aac331d37188cb37516f13a92415b48bac1a6601abb7190526cf14b19c86816c44b1eb3d0c9eb6925081f987d2007d8bd
|
data/README.rdoc
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
{<img src="https://secure.travis-ci.org/jpmcgrath/shortener.png?branch=master" alt="Build Status" />}[http://travis-ci.org/jpmcgrath/shortener]
|
1
|
+
{<img src="https://secure.travis-ci.org/jpmcgrath/shortener.png?branch=master" alt="Build Status" />}[http://travis-ci.org/jpmcgrath/shortener] {<img src="https://codeclimate.com/github/jpmcgrath/shortener/badges/gpa.svg" />}[https://codeclimate.com/github/jpmcgrath/shortener]
|
2
2
|
|
3
3
|
= Shortener
|
4
4
|
|
@@ -52,7 +52,6 @@ migration:
|
|
52
52
|
* There has not been an attempt to remove ambiguous characters (i.e. 1 l and capital i, or 0 and O etc.) from the unique key generated for the link. This means people might copy the link incorrectly if copying the link by hand;
|
53
53
|
* The system could pre-generate unique keys in advance, avoiding the database penalty when checking that a newly generated key is unique;
|
54
54
|
* The system could store the shortened URL if the url is to be continually rendered;
|
55
|
-
* Some implementations might want duplicate links to be generated each time a user request a shortened link.
|
56
55
|
|
57
56
|
== Installation
|
58
57
|
|
@@ -102,7 +101,7 @@ To generate and display a shortened URL in your application use the helper metho
|
|
102
101
|
|
103
102
|
Pass in subdomain, protocol and other options that the UrlHelper url_for accepts:
|
104
103
|
|
105
|
-
short_url("http://example.com", url_options: { subdomain: 'foo', host: 'bar, protocol: 'https' } )
|
104
|
+
short_url("http://example.com", url_options: { subdomain: 'foo', host: 'bar', protocol: 'https' } )
|
106
105
|
|
107
106
|
This will generate a shortened URL. store it to the db and return a string representing the shortened URL.
|
108
107
|
|
@@ -159,12 +158,30 @@ In rails you can put next line into config/initializers/shortener.rb
|
|
159
158
|
|
160
159
|
Shortener.forbidden_keys.concat %w(terms promo)
|
161
160
|
|
162
|
-
===
|
161
|
+
=== Ignoring Robots
|
162
|
+
|
163
|
+
By default Shortener will count all visits to a shortened url, including any crawler
|
164
|
+
robots like the Google Web Crawler, or Twitter's link unshortening bot. To ignore
|
165
|
+
these visits, Shortener makes use of the excellent voight_kampff gem to identify
|
166
|
+
web robots. This feature is disabled by default. To enable add the following to
|
167
|
+
your shortener configuration:
|
168
|
+
|
169
|
+
Shortener.ignore_robots = true
|
170
|
+
|
171
|
+
=== URL Parameters
|
163
172
|
|
164
173
|
Parameters are passed though from the shortened url, to the destination URL. If the destination
|
165
174
|
URL has the same parameters as the destination URL, the parameters on the shortened url take
|
166
175
|
precedence over those on the destination URL.
|
167
176
|
|
177
|
+
For example, if we have an orginal URL of:
|
178
|
+
> http://destination.com?test=yes&happy=defo (idenfified with token ABCDEF)
|
179
|
+
Which is shortened into:
|
180
|
+
> http://coolapp.io/s/ABCDEF?test=no&why=not
|
181
|
+
Then, the resulting URL will be:
|
182
|
+
>http://destination.com?test=no&happy=defo&why=not
|
183
|
+
Note how the test parameter takes the value given on the short URL.
|
184
|
+
|
168
185
|
=== Shorten URLs in generated emails
|
169
186
|
|
170
187
|
You can register the included mail interceptor to shorten all links in the emails generated by your Rails app. For example, add to your mailer:
|
@@ -181,6 +198,26 @@ This will replace all long URLs in the emails generated by MyMailer with shorten
|
|
181
198
|
|
182
199
|
The interceptor supports a few more arguments, see the implementation for details.
|
183
200
|
|
201
|
+
=== Logging, stats and other tricks
|
202
|
+
|
203
|
+
If you want more things to happen when a user accesses one of your short urls, you can create your own `show` action as follows:
|
204
|
+
|
205
|
+
def show
|
206
|
+
token = ::Shortener::ShortenedUrl.extract_token(params[:id])
|
207
|
+
url = ::Shortener::ShortenedUrl.fetch_with_token(token: token, additional_params: params)
|
208
|
+
# do some logging, store some stats
|
209
|
+
redirect_to url[:url], status: :moved_permanently
|
210
|
+
end
|
211
|
+
|
212
|
+
=== Fetch with Token
|
213
|
+
The `::Shortener::ShortenedUrl.fetch_with_token(token: token, additional_params: params)` does the following:
|
214
|
+
1. finds the ShortenedUrl for the supplied token
|
215
|
+
2. increments the use_count for the retrieved ShortenedURL
|
216
|
+
3. combines the additional parameters with the URL in the retrieved ShortenedURL
|
217
|
+
4. returns a hash of the format `{url: <the original url>, shortened_url: <the retrieved ShortenedURL>}
|
218
|
+
|
219
|
+
*note:* If no shortened URL is found, the url will be `default_redirect` or `/`
|
220
|
+
|
184
221
|
== Origins
|
185
222
|
|
186
223
|
For a bit of backstory to Shortener see this {blog post}[http://jamespmcgrath.com/a-simple-link-shortener-in-rails/].
|
@@ -189,7 +226,8 @@ For a bit of backstory to Shortener see this {blog post}[http://jamespmcgrath.co
|
|
189
226
|
|
190
227
|
Shortener is used in a number of production systems, including, but not limited to:
|
191
228
|
|
192
|
-
{Doorkeeper - An Event Management Tool}[http://www.doorkeeperhq.com/]
|
229
|
+
- {Doorkeeper - An Event Management Tool}[http://www.doorkeeperhq.com/]
|
230
|
+
- {1001tweets - Repost your tweets to get more clicks}[http://www.1001tweets.com/]
|
193
231
|
|
194
232
|
If you are using Shortener in your project and would like to be added to this list, please get in touch!
|
195
233
|
|
@@ -1,41 +1,12 @@
|
|
1
|
+
require 'voight_kampff'
|
2
|
+
|
1
3
|
class Shortener::ShortenedUrlsController < ActionController::Base
|
4
|
+
include Shortener
|
2
5
|
|
3
|
-
# find the real link for the shortened link key and redirect
|
4
6
|
def show
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
sl = ::Shortener::ShortenedUrl.unexpired.where(unique_key: token).first
|
10
|
-
|
11
|
-
if sl
|
12
|
-
# don't want to wait for the increment to happen, make it snappy!
|
13
|
-
# this is the place to enhance the metrics captured
|
14
|
-
# for the system. You could log the request origin
|
15
|
-
# browser type, ip address etc.
|
16
|
-
Thread.new do
|
17
|
-
sl.increment!(:use_count)
|
18
|
-
ActiveRecord::Base.connection.close
|
19
|
-
end
|
20
|
-
|
21
|
-
params.except! *[:id, :action, :controller]
|
22
|
-
url = sl.url
|
23
|
-
|
24
|
-
if params.present?
|
25
|
-
uri = URI.parse(sl.url)
|
26
|
-
existing_params = Rack::Utils.parse_nested_query(uri.query)
|
27
|
-
merged_params = existing_params.merge(params)
|
28
|
-
uri.query = merged_params.to_query
|
29
|
-
url = uri.to_s
|
30
|
-
end
|
31
|
-
|
32
|
-
# do a 301 redirect to the destination url
|
33
|
-
redirect_to url, status: :moved_permanently
|
34
|
-
else
|
35
|
-
# if we don't find the shortened link, redirect to the root
|
36
|
-
# make this configurable in future versions
|
37
|
-
redirect_to Shortener.default_redirect
|
38
|
-
end
|
7
|
+
token = ::Shortener::ShortenedUrl.extract_token(params[:id])
|
8
|
+
track = Shortener.ignore_robots.blank? || request.human?
|
9
|
+
url = ::Shortener::ShortenedUrl.fetch_with_token(token: token, additional_params: params, track: track)
|
10
|
+
redirect_to url[:url], status: :moved_permanently
|
39
11
|
end
|
40
|
-
|
41
12
|
end
|
@@ -10,7 +10,7 @@ module Shortener::ShortenerHelper
|
|
10
10
|
)
|
11
11
|
|
12
12
|
if short_url
|
13
|
-
options = { controller: :"shortener/shortened_urls", action: :show, id: short_url.unique_key, only_path: false }.merge(url_options)
|
13
|
+
options = { controller: :"/shortener/shortened_urls", action: :show, id: short_url.unique_key, only_path: false }.merge(url_options)
|
14
14
|
url_for(options)
|
15
15
|
else
|
16
16
|
url
|
@@ -10,6 +10,8 @@ class Shortener::ShortenedUrl < ActiveRecord::Base
|
|
10
10
|
# exclude records in which expiration time is set and expiration time is greater than current time
|
11
11
|
scope :unexpired, -> { where(arel_table[:expires_at].eq(nil).or(arel_table[:expires_at].gt(::Time.current.to_s(:db)))) }
|
12
12
|
|
13
|
+
attr_accessor :custom_key
|
14
|
+
|
13
15
|
# ensure the url starts with it protocol and is normalized
|
14
16
|
def self.clean_url(url)
|
15
17
|
|
@@ -26,11 +28,11 @@ class Shortener::ShortenedUrl < ActiveRecord::Base
|
|
26
28
|
def self.generate!(destination_url, owner: nil, custom_key: nil, expires_at: nil, fresh: false)
|
27
29
|
# if we get a shortened_url object with a different owner, generate
|
28
30
|
# new one for the new owner. Otherwise return same object
|
29
|
-
if destination_url.is_a? Shortener::ShortenedUrl
|
31
|
+
result = if destination_url.is_a? Shortener::ShortenedUrl
|
30
32
|
if destination_url.owner == owner
|
31
|
-
|
33
|
+
destination_url
|
32
34
|
else
|
33
|
-
|
35
|
+
generate!(destination_url.url,
|
34
36
|
owner: owner,
|
35
37
|
custom_key: custom_key,
|
36
38
|
expires_at: expires_at,
|
@@ -39,12 +41,8 @@ class Shortener::ShortenedUrl < ActiveRecord::Base
|
|
39
41
|
end
|
40
42
|
else
|
41
43
|
scope = owner ? owner.shortened_urls : self
|
42
|
-
|
43
|
-
|
44
|
-
result = scope.where(url: clean_url(destination_url)).create(unique_key: custom_key, expires_at: expires_at)
|
45
|
-
else
|
46
|
-
result = scope.where(url: clean_url(destination_url)).first_or_create(unique_key: custom_key, expires_at: expires_at)
|
47
|
-
end
|
44
|
+
creation_method = fresh ? 'create' : 'first_or_create'
|
45
|
+
scope.where(url: clean_url(destination_url)).send(creation_method, unique_key: custom_key, custom_key: custom_key, expires_at: expires_at)
|
48
46
|
end
|
49
47
|
|
50
48
|
result
|
@@ -60,14 +58,61 @@ class Shortener::ShortenedUrl < ActiveRecord::Base
|
|
60
58
|
end
|
61
59
|
end
|
62
60
|
|
61
|
+
def self.extract_token(token_str)
|
62
|
+
# only use the leading valid characters
|
63
|
+
# escape to ensure custom charsets with protected chars do not fail
|
64
|
+
/^([#{Regexp.escape(Shortener.key_chars.join)}]*).*/.match(token_str)[1]
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.fetch_with_token(token: nil, additional_params: {}, track: true)
|
68
|
+
shortened_url = ::Shortener::ShortenedUrl.unexpired.where(unique_key: token).first
|
69
|
+
|
70
|
+
url = if shortened_url
|
71
|
+
shortened_url.increment_usage_count if track
|
72
|
+
merge_params_to_url(url: shortened_url.url, params: additional_params)
|
73
|
+
else
|
74
|
+
Shortener.default_redirect || '/'
|
75
|
+
end
|
76
|
+
|
77
|
+
{ url: url, shortened_url: shortened_url }
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.merge_params_to_url(url: nil, params: {})
|
81
|
+
params.try(:except!, *[:id, :action, :controller])
|
82
|
+
|
83
|
+
if params.present?
|
84
|
+
uri = URI.parse(url)
|
85
|
+
existing_params = Rack::Utils.parse_nested_query(uri.query)
|
86
|
+
uri.query = existing_params.symbolize_keys.merge(params).to_query
|
87
|
+
url = uri.to_s
|
88
|
+
end
|
89
|
+
|
90
|
+
url
|
91
|
+
end
|
92
|
+
|
93
|
+
def increment_usage_count
|
94
|
+
Thread.new do
|
95
|
+
ActiveRecord::Base.connection_pool.with_connection do |conn|
|
96
|
+
increment!(:use_count)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def to_param
|
102
|
+
unique_key
|
103
|
+
end
|
104
|
+
|
63
105
|
private
|
64
106
|
|
65
107
|
# the create method changed in rails 4...
|
66
108
|
CREATE_METHOD_NAME =
|
67
|
-
if Rails::VERSION::MAJOR >=
|
109
|
+
if Rails::VERSION::MAJOR >= 5
|
110
|
+
"_create_record"
|
111
|
+
elsif Rails::VERSION::MAJOR == 4
|
68
112
|
# And again in 4.0.6/4.1.2
|
69
|
-
if
|
70
|
-
|
113
|
+
if Rails::VERSION::MAJOR == 4 && (
|
114
|
+
((Rails::VERSION::MINOR == 0) && (Rails::VERSION::TINY < 6)) ||
|
115
|
+
((Rails::VERSION::MINOR == 1) && (Rails::VERSION::TINY < 2)))
|
71
116
|
"create_record"
|
72
117
|
else
|
73
118
|
"_create_record"
|
@@ -81,9 +126,11 @@ class Shortener::ShortenedUrl < ActiveRecord::Base
|
|
81
126
|
define_method CREATE_METHOD_NAME do
|
82
127
|
count = 0
|
83
128
|
begin
|
84
|
-
self.unique_key = generate_unique_key
|
129
|
+
self.unique_key = custom_key || generate_unique_key
|
85
130
|
super()
|
86
131
|
rescue ActiveRecord::RecordNotUnique, ActiveRecord::StatementInvalid => err
|
132
|
+
logger.info("Failed to generate ShortenedUrl with unique_key: #{unique_key}")
|
133
|
+
self.unique_key = nil
|
87
134
|
if (count +=1) < 5
|
88
135
|
logger.info("retrying with different unique key")
|
89
136
|
retry
|
data/lib/shortener.rb
CHANGED
@@ -28,6 +28,11 @@ module Shortener
|
|
28
28
|
mattr_accessor :forbidden_keys
|
29
29
|
self.forbidden_keys = []
|
30
30
|
|
31
|
+
# ignore_robots - set to true to not count visits by identified webcrawlers
|
32
|
+
mattr_accessor :ignore_robots
|
33
|
+
self.ignore_robots = false
|
34
|
+
|
35
|
+
|
31
36
|
def self.key_chars
|
32
37
|
CHARSETS[charset]
|
33
38
|
end
|
data/lib/shortener/version.rb
CHANGED
data/shortener.gemspec
CHANGED
@@ -15,6 +15,7 @@ Gem::Specification.new do |s|
|
|
15
15
|
s.rubyforge_project = "shortener"
|
16
16
|
s.required_rubygems_version = "> 1.3.6"
|
17
17
|
s.add_dependency "rails", ">= 3.0.7"
|
18
|
+
s.add_dependency "voight_kampff", '~>1.0.2'
|
18
19
|
s.add_development_dependency "sqlite3"
|
19
20
|
s.add_development_dependency "rspec-rails", '~> 3.3.0'
|
20
21
|
s.add_development_dependency "shoulda-matchers", '~> 3'
|
@@ -18,6 +18,26 @@ describe Shortener::ShortenedUrlsController, type: :controller do
|
|
18
18
|
it 'redirects to the destination url' do
|
19
19
|
expect(response).to redirect_to destination
|
20
20
|
end
|
21
|
+
|
22
|
+
context "when request is not from an human" do
|
23
|
+
before do
|
24
|
+
allow_any_instance_of(Rack::Request).to receive(:human?).and_return(false)
|
25
|
+
end
|
26
|
+
context 'Shortener.ignore_robots == true' do
|
27
|
+
it "calls fetch with token with track argument as false" do
|
28
|
+
Shortener.ignore_robots = true
|
29
|
+
expect(Shortener::ShortenedUrl).to receive(:fetch_with_token).with(hash_including(track: false)).and_call_original
|
30
|
+
get :show, { id: key }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
context 'Shortener.ignore_robots == false (default)' do
|
34
|
+
it "calls fetch with token with track argument as true" do
|
35
|
+
Shortener.ignore_robots = false
|
36
|
+
expect(Shortener::ShortenedUrl).to receive(:fetch_with_token).with(hash_including(track: true)).and_call_original
|
37
|
+
get :show, { id: key }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
21
41
|
end
|
22
42
|
|
23
43
|
context 'real key with trailing characters' do
|
@@ -154,4 +154,114 @@ describe Shortener::ShortenedUrl, type: :model do
|
|
154
154
|
expect(unexpired_scope).not_to include(expired_url)
|
155
155
|
end
|
156
156
|
end
|
157
|
+
|
158
|
+
describe '#increment_usage_count' do
|
159
|
+
let(:url) { 'https://example.com'}
|
160
|
+
let(:short_url) { Shortener::ShortenedUrl.generate!(url) }
|
161
|
+
it 'increments the use_count on the shortenedLink' do
|
162
|
+
original_count = short_url.use_count
|
163
|
+
short_url.increment_usage_count
|
164
|
+
sleep 0.001
|
165
|
+
expect(short_url.use_count).to eq (original_count + 1)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
describe '#merge_params_to_url' do
|
170
|
+
context 'without params on original url' do
|
171
|
+
let(:url) { 'https://example.com'}
|
172
|
+
|
173
|
+
context 'no additional params' do
|
174
|
+
let(:params) { nil }
|
175
|
+
|
176
|
+
it 'returns the original url' do
|
177
|
+
expect(Shortener::ShortenedUrl.merge_params_to_url(url: url, params: params)).to eq 'https://example.com'
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
context 'additional params' do
|
182
|
+
let(:params) { {foo: 'bar', hello: 'world' } }
|
183
|
+
|
184
|
+
it 'merges the params to create a new url' do
|
185
|
+
expect(Shortener::ShortenedUrl.merge_params_to_url(url: url, params: params)).to eq 'https://example.com?foo=bar&hello=world'
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
context 'with params on original url' do
|
191
|
+
let(:url) { 'http://example.com/pathname?different=yes&foo=test'}
|
192
|
+
|
193
|
+
context 'no additional params' do
|
194
|
+
let(:params) { nil }
|
195
|
+
|
196
|
+
it 'returns the original url' do
|
197
|
+
expect(Shortener::ShortenedUrl.merge_params_to_url(url: url, params: params)).to eq 'http://example.com/pathname?different=yes&foo=test'
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
context 'additional params' do
|
202
|
+
let(:params) { {foo: 'manchoo', hello: 'world' } }
|
203
|
+
|
204
|
+
it 'merges the params to create a new url with the new params taking preceedence' do
|
205
|
+
expect(Shortener::ShortenedUrl.merge_params_to_url(url: url, params: params)).to eq 'http://example.com/pathname?different=yes&foo=manchoo&hello=world'
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
describe '#fetch_with_token' do
|
212
|
+
shared_examples_for 'invalid token supplied' do
|
213
|
+
context 'default_path set' do
|
214
|
+
it 'returns the default_redirect url, but no shortened url' do
|
215
|
+
Shortener.default_redirect = "/default_path"
|
216
|
+
result = Shortener::ShortenedUrl.fetch_with_token(token: nil)
|
217
|
+
expect(result[:url]).to eq '/default_path'
|
218
|
+
expect(result[:shortened_url]).to eq nil
|
219
|
+
end
|
220
|
+
end
|
221
|
+
context 'no default_path set' do
|
222
|
+
it 'returns the root path, but no shortened url' do
|
223
|
+
Shortener.default_redirect = nil
|
224
|
+
result = Shortener::ShortenedUrl.fetch_with_token(token: nil)
|
225
|
+
expect(result[:url]).to eq '/'
|
226
|
+
expect(result[:shortened_url]).to eq nil
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
context 'invalid token' do
|
232
|
+
it_should_behave_like 'invalid token supplied' do
|
233
|
+
let(:token) { 'invalid_token'}
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
context 'valid token' do
|
238
|
+
let(:url) { Faker::Internet.url }
|
239
|
+
|
240
|
+
context 'non expired short url' do
|
241
|
+
let(:short_url) { Shortener::ShortenedUrl.generate!(url) }
|
242
|
+
it 'returns both the url and the shortened url' do
|
243
|
+
result = Shortener::ShortenedUrl.fetch_with_token(token: short_url.unique_key)
|
244
|
+
expect(result[:url]).to eq url
|
245
|
+
expect(result[:shortened_url]).to eq short_url
|
246
|
+
end
|
247
|
+
it "increments use count" do
|
248
|
+
expect_any_instance_of(Shortener::ShortenedUrl).to receive(:increment_usage_count)
|
249
|
+
Shortener::ShortenedUrl.fetch_with_token(token: short_url.unique_key)
|
250
|
+
end
|
251
|
+
context "with track set to false" do
|
252
|
+
it "does not increment use count" do
|
253
|
+
expect_any_instance_of(Shortener::ShortenedUrl).not_to receive(:increment_usage_count)
|
254
|
+
Shortener::ShortenedUrl.fetch_with_token(token: short_url.unique_key, track: false)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
context 'expired short url' do
|
260
|
+
let(:short_url) { Shortener::ShortenedUrl.generate!(url, expires_at: 1.second.ago) }
|
261
|
+
it_should_behave_like 'invalid token supplied' do
|
262
|
+
let(:token) { short_url.unique_key }
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
157
267
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shortener
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James P. McGrath
|
@@ -9,90 +9,104 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2016-11-04 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
|
-
- -
|
18
|
+
- - ">="
|
19
19
|
- !ruby/object:Gem::Version
|
20
20
|
version: 3.0.7
|
21
21
|
type: :runtime
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
|
-
- -
|
25
|
+
- - ">="
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
version: 3.0.7
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: voight_kampff
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 1.0.2
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: 1.0.2
|
28
42
|
- !ruby/object:Gem::Dependency
|
29
43
|
name: sqlite3
|
30
44
|
requirement: !ruby/object:Gem::Requirement
|
31
45
|
requirements:
|
32
|
-
- -
|
46
|
+
- - ">="
|
33
47
|
- !ruby/object:Gem::Version
|
34
48
|
version: '0'
|
35
49
|
type: :development
|
36
50
|
prerelease: false
|
37
51
|
version_requirements: !ruby/object:Gem::Requirement
|
38
52
|
requirements:
|
39
|
-
- -
|
53
|
+
- - ">="
|
40
54
|
- !ruby/object:Gem::Version
|
41
55
|
version: '0'
|
42
56
|
- !ruby/object:Gem::Dependency
|
43
57
|
name: rspec-rails
|
44
58
|
requirement: !ruby/object:Gem::Requirement
|
45
59
|
requirements:
|
46
|
-
- - ~>
|
60
|
+
- - "~>"
|
47
61
|
- !ruby/object:Gem::Version
|
48
62
|
version: 3.3.0
|
49
63
|
type: :development
|
50
64
|
prerelease: false
|
51
65
|
version_requirements: !ruby/object:Gem::Requirement
|
52
66
|
requirements:
|
53
|
-
- - ~>
|
67
|
+
- - "~>"
|
54
68
|
- !ruby/object:Gem::Version
|
55
69
|
version: 3.3.0
|
56
70
|
- !ruby/object:Gem::Dependency
|
57
71
|
name: shoulda-matchers
|
58
72
|
requirement: !ruby/object:Gem::Requirement
|
59
73
|
requirements:
|
60
|
-
- - ~>
|
74
|
+
- - "~>"
|
61
75
|
- !ruby/object:Gem::Version
|
62
76
|
version: '3'
|
63
77
|
type: :development
|
64
78
|
prerelease: false
|
65
79
|
version_requirements: !ruby/object:Gem::Requirement
|
66
80
|
requirements:
|
67
|
-
- - ~>
|
81
|
+
- - "~>"
|
68
82
|
- !ruby/object:Gem::Version
|
69
83
|
version: '3'
|
70
84
|
- !ruby/object:Gem::Dependency
|
71
85
|
name: faker
|
72
86
|
requirement: !ruby/object:Gem::Requirement
|
73
87
|
requirements:
|
74
|
-
- -
|
88
|
+
- - ">="
|
75
89
|
- !ruby/object:Gem::Version
|
76
90
|
version: '0'
|
77
91
|
type: :development
|
78
92
|
prerelease: false
|
79
93
|
version_requirements: !ruby/object:Gem::Requirement
|
80
94
|
requirements:
|
81
|
-
- -
|
95
|
+
- - ">="
|
82
96
|
- !ruby/object:Gem::Version
|
83
97
|
version: '0'
|
84
98
|
- !ruby/object:Gem::Dependency
|
85
99
|
name: byebug
|
86
100
|
requirement: !ruby/object:Gem::Requirement
|
87
101
|
requirements:
|
88
|
-
- -
|
102
|
+
- - ">="
|
89
103
|
- !ruby/object:Gem::Version
|
90
104
|
version: '0'
|
91
105
|
type: :development
|
92
106
|
prerelease: false
|
93
107
|
version_requirements: !ruby/object:Gem::Requirement
|
94
108
|
requirements:
|
95
|
-
- -
|
109
|
+
- - ">="
|
96
110
|
- !ruby/object:Gem::Version
|
97
111
|
version: '0'
|
98
112
|
description: Shortener is a Rails Engine Gem that makes it easy to create and interpret
|
@@ -106,8 +120,8 @@ executables: []
|
|
106
120
|
extensions: []
|
107
121
|
extra_rdoc_files: []
|
108
122
|
files:
|
109
|
-
- .gitignore
|
110
|
-
- .travis.yml
|
123
|
+
- ".gitignore"
|
124
|
+
- ".travis.yml"
|
111
125
|
- Gemfile
|
112
126
|
- MIT-LICENSE
|
113
127
|
- README.rdoc
|
@@ -167,17 +181,17 @@ require_paths:
|
|
167
181
|
- lib
|
168
182
|
required_ruby_version: !ruby/object:Gem::Requirement
|
169
183
|
requirements:
|
170
|
-
- -
|
184
|
+
- - ">="
|
171
185
|
- !ruby/object:Gem::Version
|
172
186
|
version: '0'
|
173
187
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
174
188
|
requirements:
|
175
|
-
- -
|
189
|
+
- - ">"
|
176
190
|
- !ruby/object:Gem::Version
|
177
191
|
version: 1.3.6
|
178
192
|
requirements: []
|
179
193
|
rubyforge_project: shortener
|
180
|
-
rubygems_version: 2.
|
194
|
+
rubygems_version: 2.5.1
|
181
195
|
signing_key:
|
182
196
|
specification_version: 4
|
183
197
|
summary: Shortener is a Rails Engine that makes it easy to create shortened URLs for
|