shortener 0.4.1 → 0.5.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/.travis.yml +0 -1
- data/README.rdoc +34 -9
- data/app/helpers/shortener/shortener_helper.rb +2 -2
- data/app/models/shortener/shortened_url.rb +13 -11
- data/lib/generators/shortener/templates/migration.rb +1 -1
- data/lib/shortener/version.rb +1 -1
- data/spec/controllers/shortened_urls_controller_spec.rb +4 -1
- data/spec/helpers/shortener_helper_spec.rb +29 -11
- data/spec/models/shortened_url_spec.rb +19 -15
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c6357583710204365555ba59c8343c84a8dd17dc
|
4
|
+
data.tar.gz: ac7a8274dbee24478663ead28beef369895e1645
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9e3bcddeb02d11a0a49adc7484c7a62b09e2458aac54290e80aa9aa774ac78b82b28c034dd50dba673d5777c86dedc029b0a06de7e9490ca5cb695a2985f2d53
|
7
|
+
data.tar.gz: ee61134fedd6f2ddb85bff8e9f9e6ace9aaabdd3a430e60b14a3d8d36c747c91345310029e8823dd6d342742ddf316530c529216b45fffb5197bd18a78f81797
|
data/.travis.yml
CHANGED
data/README.rdoc
CHANGED
@@ -14,6 +14,22 @@ The majority of the Shortener consists of three parts:
|
|
14
14
|
* a controller to accept incoming requests and redirecting them to the target URL;
|
15
15
|
* a helper for generating shortened URLs from controllers and views.
|
16
16
|
|
17
|
+
=== Dependencies
|
18
|
+
|
19
|
+
Shortener is designed to work from within a Rails 3 and Rails 4 applications. It has dependancies Rails core components like ActiveRecord, ActionController, the rails routing engine and more.
|
20
|
+
|
21
|
+
=== Ruby Version Support
|
22
|
+
|
23
|
+
As Ruby 1.9.3 entered end of maintainance in early 2015, the last version of the Shortener gem
|
24
|
+
to support Ruby 1.9.3 is 0.4.x. Shortener v0.5 onwards will require Ruby 2+.
|
25
|
+
|
26
|
+
=== Upgrading
|
27
|
+
|
28
|
+
==== v0.4.0 to v0.5.0
|
29
|
+
There have been some breaking changes:
|
30
|
+
1. The owner argument is passed into the generator and helper methods as a named parameter.
|
31
|
+
2. Original URLs arguments without a hypertext protocol (http|https) will be assumed to be relative paths.
|
32
|
+
|
17
33
|
=== Some niceities of Shortener:
|
18
34
|
|
19
35
|
* The controller does a 301 redirect, which is the recommended type of redirect for maintaining maximum google juice to the original URL;
|
@@ -30,10 +46,9 @@ The majority of the Shortener consists of three parts:
|
|
30
46
|
* The system could store the shortened URL if the url is to be continually rendered;
|
31
47
|
* Some implementations might want duplicate links to be generated each time a user request a shortened link.
|
32
48
|
|
33
|
-
|
34
49
|
== Installation
|
35
50
|
|
36
|
-
|
51
|
+
Shortener is compatible with Rails v3 and v4. To install, add to your Gemfile:
|
37
52
|
|
38
53
|
gem 'shortener'
|
39
54
|
|
@@ -60,17 +75,17 @@ By default, when a unique key isn't matched the site is redirected to "/". You c
|
|
60
75
|
```
|
61
76
|
== Usage
|
62
77
|
|
63
|
-
To generate a Shortened URL object for the URL "http://
|
78
|
+
To generate a Shortened URL object for the URL "http://example.com" within your controller / models do the following:
|
64
79
|
|
65
|
-
Shortener::ShortenedUrl.generate("http://
|
80
|
+
Shortener::ShortenedUrl.generate("http://example.com")
|
66
81
|
|
67
|
-
|
82
|
+
Alternatively, you can create a shortened url to a relative path within your application:
|
68
83
|
|
69
|
-
Shortener::ShortenedUrl.generate("
|
84
|
+
Shortener::ShortenedUrl.generate("/relative-path?param=whatever")
|
70
85
|
|
71
86
|
To generate and display a shortened URL in your application use the helper method:
|
72
87
|
|
73
|
-
short_url("
|
88
|
+
short_url("http://example.com")
|
74
89
|
|
75
90
|
This will generate a shortened URL. store it to the db and return a string representing the shortened URL.
|
76
91
|
|
@@ -84,12 +99,22 @@ You can link shortened URLs to an owner, to scope them. To do so, add the follow
|
|
84
99
|
|
85
100
|
This will allow you to pass the owner when generating URLs:
|
86
101
|
|
87
|
-
Shortener::ShortenedUrl.generate("example.com", user)
|
102
|
+
Shortener::ShortenedUrl.generate("example.com", owner: user)
|
103
|
+
|
104
|
+
short_url("http://example.com", owner: user)
|
88
105
|
|
89
106
|
And to access those URLs:
|
90
107
|
|
91
108
|
user.shortened_urls
|
92
109
|
|
110
|
+
=== Shortened URLs with custom unqiue key
|
111
|
+
|
112
|
+
You can pass in your own key when generating a shortened URL. This should be unique.
|
113
|
+
|
114
|
+
Shortener::ShortenedUrl.instance("example.com", owner: user, custom_key: "my-key")
|
115
|
+
|
116
|
+
short_url("http://example.com", custom_key: 'yourkey')
|
117
|
+
|
93
118
|
=== Shorten URLs in generated emails
|
94
119
|
|
95
120
|
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:
|
@@ -108,7 +133,7 @@ The interceptor supports a few more arguments, see the implementation for detail
|
|
108
133
|
|
109
134
|
== Origins
|
110
135
|
|
111
|
-
|
136
|
+
For a bit of backstory to Shortener see this {blog post}[http://jamespmcgrath.com/a-simple-link-shortener-in-rails/].
|
112
137
|
|
113
138
|
== In The Wild
|
114
139
|
|
@@ -1,8 +1,8 @@
|
|
1
1
|
module Shortener::ShortenerHelper
|
2
2
|
|
3
3
|
# generate a url from a url string
|
4
|
-
def short_url(url, owner
|
5
|
-
short_url = Shortener::ShortenedUrl.generate(url, owner)
|
4
|
+
def short_url(url, owner: nil, custom_key: nil)
|
5
|
+
short_url = Shortener::ShortenedUrl.generate(url, owner: owner, custom_key: custom_key)
|
6
6
|
short_url ? url_for(controller: :"shortener/shortened_urls", action: :show, id: short_url.unique_key, only_path: false) : url
|
7
7
|
end
|
8
8
|
|
@@ -1,6 +1,5 @@
|
|
1
1
|
class Shortener::ShortenedUrl < ActiveRecord::Base
|
2
2
|
|
3
|
-
URL_PROTOCOL_HTTP = "http://"
|
4
3
|
REGEX_LINK_HAS_PROTOCOL = Regexp.new('\Ahttp:\/\/|\Ahttps:\/\/', Regexp::IGNORECASE)
|
5
4
|
|
6
5
|
validates :url, presence: true
|
@@ -10,35 +9,38 @@ class Shortener::ShortenedUrl < ActiveRecord::Base
|
|
10
9
|
|
11
10
|
# ensure the url starts with it protocol and is normalized
|
12
11
|
def self.clean_url(url)
|
13
|
-
|
14
|
-
url =
|
12
|
+
|
13
|
+
url = url.to_s.strip
|
14
|
+
if url !~ REGEX_LINK_HAS_PROTOCOL && url[0] != '/'
|
15
|
+
url = "/#{url}"
|
16
|
+
end
|
15
17
|
URI.parse(url).normalize.to_s
|
16
18
|
end
|
17
19
|
|
18
20
|
# generate a shortened link from a url
|
19
21
|
# link to a user if one specified
|
20
22
|
# throw an exception if anything goes wrong
|
21
|
-
def self.generate!(
|
23
|
+
def self.generate!(destination_url, owner: nil, custom_key: nil)
|
22
24
|
# if we get a shortened_url object with a different owner, generate
|
23
25
|
# new one for the new owner. Otherwise return same object
|
24
|
-
if
|
25
|
-
if
|
26
|
-
result =
|
26
|
+
if destination_url.is_a? Shortener::ShortenedUrl
|
27
|
+
if destination_url.owner == owner
|
28
|
+
result = destination_url
|
27
29
|
else
|
28
|
-
result = generate!(
|
30
|
+
result = generate!(destination_url.url, owner: owner, custom_key: custom_key)
|
29
31
|
end
|
30
32
|
else
|
31
33
|
scope = owner ? owner.shortened_urls : self
|
32
|
-
result = scope.where(url: clean_url(
|
34
|
+
result = scope.where(url: clean_url(destination_url)).first_or_create
|
33
35
|
end
|
34
36
|
|
35
37
|
result
|
36
38
|
end
|
37
39
|
|
38
40
|
# return shortened url on success, nil on failure
|
39
|
-
def self.generate(
|
41
|
+
def self.generate(destination_url, owner: nil, custom_key: nil)
|
40
42
|
begin
|
41
|
-
generate!(
|
43
|
+
generate!(destination_url, owner: owner, custom_key: custom_key)
|
42
44
|
rescue
|
43
45
|
nil
|
44
46
|
end
|
data/lib/shortener/version.rb
CHANGED
@@ -12,7 +12,10 @@ describe Shortener::ShortenedUrlsController, type: :controller do
|
|
12
12
|
|
13
13
|
context 'valid keys' do
|
14
14
|
context 'real key' do
|
15
|
-
|
15
|
+
before do
|
16
|
+
short_url
|
17
|
+
end
|
18
|
+
let(:key) { short_url.unique_key }
|
16
19
|
|
17
20
|
it 'redirects to the destination url' do
|
18
21
|
expect(response).to redirect_to destination
|
@@ -4,23 +4,41 @@ require 'spec_helper'
|
|
4
4
|
describe Shortener::ShortenerHelper, type: :helper do
|
5
5
|
describe '#short_url' do
|
6
6
|
let(:destination) { Faker::Internet.url }
|
7
|
-
before do
|
8
|
-
expect(Shortener::ShortenedUrl).to receive(:generate).with(destination, nil).and_return(shortened_url)
|
9
|
-
end
|
10
7
|
|
11
|
-
context '
|
12
|
-
|
8
|
+
context 'without user or custom key' do
|
9
|
+
before do
|
10
|
+
expect(Shortener::ShortenedUrl).to receive(:generate).with(destination, owner: nil, custom_key: nil).and_return(shortened_url)
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'short url was generated' do
|
14
|
+
let(:shortened_url) { instance_double('ShortenedUrl', unique_key: '12345') }
|
15
|
+
|
16
|
+
it "shortens the url" do
|
17
|
+
expect(helper.short_url(destination)).to eq "http://test.host/12345"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'short url could not be generated' do
|
22
|
+
let(:shortened_url) { nil }
|
13
23
|
|
14
|
-
|
15
|
-
|
24
|
+
it 'returns the original url' do
|
25
|
+
expect(helper.short_url(destination)).to eq destination
|
26
|
+
end
|
16
27
|
end
|
17
28
|
end
|
18
29
|
|
19
|
-
context '
|
20
|
-
let(:
|
30
|
+
context 'with owner' do
|
31
|
+
let(:owner) { double('User') }
|
32
|
+
it 'sends user to generate function' do
|
33
|
+
expect(Shortener::ShortenedUrl).to receive(:generate).with(destination, owner: owner, custom_key: nil)
|
34
|
+
helper.short_url(destination, owner: owner)
|
35
|
+
end
|
36
|
+
end
|
21
37
|
|
22
|
-
|
23
|
-
|
38
|
+
context 'with custom_key' do
|
39
|
+
it 'sends custom key code to generate function' do
|
40
|
+
expect(Shortener::ShortenedUrl).to receive(:generate).with(destination, owner: nil, custom_key: 'custkey')
|
41
|
+
helper.short_url(destination, custom_key: 'custkey')
|
24
42
|
end
|
25
43
|
end
|
26
44
|
end
|
@@ -11,7 +11,7 @@ describe Shortener::ShortenedUrl, type: :model do
|
|
11
11
|
let(:expected_url) { Faker::Internet.url }
|
12
12
|
|
13
13
|
shared_examples_for "shortened url" do
|
14
|
-
let(:short_url) { Shortener::ShortenedUrl.generate!(long_url, owner) }
|
14
|
+
let(:short_url) { Shortener::ShortenedUrl.generate!(long_url, owner: owner) }
|
15
15
|
it 'creates a shortened url record for the url' do
|
16
16
|
expect{short_url}.to change{Shortener::ShortenedUrl.count}.by(1)
|
17
17
|
expect(short_url.url).to eq expected_url
|
@@ -23,15 +23,16 @@ describe Shortener::ShortenedUrl, type: :model do
|
|
23
23
|
context 'userless url' do
|
24
24
|
let(:owner) { nil }
|
25
25
|
|
26
|
-
context 'shortened url
|
26
|
+
context 'shortened url' do
|
27
27
|
it_should_behave_like "shortened url" do
|
28
28
|
let(:long_url) { expected_url }
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
-
context 'shortened url with
|
32
|
+
context 'shortened url with relative path' do
|
33
33
|
it_should_behave_like "shortened url" do
|
34
|
-
let(:long_url) {
|
34
|
+
let(:long_url) { Faker::Internet.slug }
|
35
|
+
let(:expected_url) { "/#{long_url}" }
|
35
36
|
end
|
36
37
|
end
|
37
38
|
|
@@ -55,17 +56,6 @@ describe Shortener::ShortenedUrl, type: :model do
|
|
55
56
|
let(:url) { Faker::Internet.url }
|
56
57
|
let!(:existing_shortened_url) { Shortener::ShortenedUrl.generate!(url) }
|
57
58
|
|
58
|
-
context 'same url as existing' do
|
59
|
-
let(:protocol_free_url) { url.gsub('http://', '') }
|
60
|
-
|
61
|
-
it 'finds the shortened url from protocol free url' do
|
62
|
-
expect(Shortener::ShortenedUrl.generate!(protocol_free_url)).to eq existing_shortened_url
|
63
|
-
end
|
64
|
-
it "should look up exsiting URL" do
|
65
|
-
expect(Shortener::ShortenedUrl.generate!(url)).to eq existing_shortened_url
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
59
|
context 'different url from existing' do
|
70
60
|
it "generates a new shortened url record for a different url" do
|
71
61
|
expect(Shortener::ShortenedUrl.generate!(Faker::Internet.url)).not_to eq existing_shortened_url
|
@@ -85,6 +75,20 @@ describe Shortener::ShortenedUrl, type: :model do
|
|
85
75
|
end
|
86
76
|
end
|
87
77
|
end
|
78
|
+
|
79
|
+
context "existing shortened URL with relative path" do
|
80
|
+
let(:path) { Faker::Internet.slug }
|
81
|
+
let!(:existing_shortened_url) { Shortener::ShortenedUrl.generate!(path) }
|
82
|
+
|
83
|
+
context 'same relative path' do
|
84
|
+
it 'finds the shortened url from slashless oath' do
|
85
|
+
expect(Shortener::ShortenedUrl.generate!(path)).to eq existing_shortened_url
|
86
|
+
end
|
87
|
+
it "should look up exsiting URL" do
|
88
|
+
expect(Shortener::ShortenedUrl.generate!("/#{path}")).to eq existing_shortened_url
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
88
92
|
end
|
89
93
|
|
90
94
|
describe '#generate' do
|