shortener 0.8.0 → 0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +3 -1
- data/.travis.yml +1 -0
- data/Appraisals +7 -1
- data/README.rdoc +13 -6
- data/app/models/shortener/shortened_url.rb +17 -10
- data/gemfiles/rails_4.gemfile +1 -0
- data/gemfiles/rails_5.0.gemfile +1 -0
- data/gemfiles/rails_5.2.gemfile +1 -1
- data/gemfiles/rails_6.0.gemfile +7 -0
- data/lib/shortener.rb +7 -3
- data/lib/shortener/railtie.rb +0 -3
- data/lib/shortener/version.rb +1 -1
- data/shortener.gemspec +1 -1
- data/spec/controllers/shortened_urls_controller_spec.rb +15 -0
- data/spec/dummy/app/assets/config/manifest.js +0 -0
- data/spec/dummy/app/controllers/home_controller.rb +3 -0
- data/spec/dummy/app/views/home/show.html.erb +1 -0
- data/spec/dummy/app/views/layouts/application.html.erb +0 -2
- data/spec/dummy/config/routes.rb +1 -1
- data/spec/features/shortener_feature_spec.rb +8 -0
- data/spec/models/shortened_url_spec.rb +38 -3
- data/spec/spec_helper.rb +6 -2
- metadata +22 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9a8820cdee8a530e85514c1c8b3c3749216c0cd4703c31c2307e96a4897eba25
|
4
|
+
data.tar.gz: 185560e234f5c00d49a4e32ed4c3a3bae2c69c33d58b827c414cd5b24c7a4f66
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d1a48b44d3938a2436116251515250a892a3aefd08e1a0c75f9abdaa16e050404e6cc43780336405dc8b447595199e7a8978aa915d0d68dc2d000090d16c62e6
|
7
|
+
data.tar.gz: 4e43a6b1f6189a779769649aae7bb37c50aa00433fbc61ad0d159031eb545b8142cb1eb454be1882f33800d4ac4c9c35fd5c89eafb78213aeba955f1cd118c8a
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/Appraisals
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
appraise "rails_4" do
|
2
2
|
gem "rails", "~> 4.2.10"
|
3
|
+
gem "sqlite3", "~> 1.3.6"
|
3
4
|
end
|
4
5
|
|
5
6
|
appraise "rails_5.0" do
|
6
7
|
gem "rails", "~> 5.0.7"
|
8
|
+
gem "sqlite3", "~> 1.3.6"
|
7
9
|
end
|
8
10
|
|
9
11
|
appraise "rails_5.1" do
|
@@ -11,5 +13,9 @@ appraise "rails_5.1" do
|
|
11
13
|
end
|
12
14
|
|
13
15
|
appraise "rails_5.2" do
|
14
|
-
gem "rails", "5.2.0"
|
16
|
+
gem "rails", "~> 5.2.0"
|
17
|
+
end
|
18
|
+
|
19
|
+
appraise "rails_6.0" do
|
20
|
+
gem "rails", "~> 6.0.0"
|
15
21
|
end
|
data/README.rdoc
CHANGED
@@ -1,4 +1,5 @@
|
|
1
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
|
+
{<img src="https://badge.fury.io/rb/shortener.png" alt="Gem version" />}[http://badge.fury.io/rb/shortener]
|
2
3
|
|
3
4
|
= Shortener
|
4
5
|
|
@@ -14,7 +15,7 @@ The majority of the Shortener consists of three parts:
|
|
14
15
|
|
15
16
|
=== Dependencies
|
16
17
|
|
17
|
-
Shortener is designed to work from within a
|
18
|
+
Shortener is designed to work from within a Ruby on Rail applications. It has dependancies Rails core components like ActiveRecord, ActionController, the rails routing engine and more.
|
18
19
|
|
19
20
|
=== Ruby Version Support
|
20
21
|
|
@@ -69,7 +70,7 @@ migration:
|
|
69
70
|
|
70
71
|
== Installation
|
71
72
|
|
72
|
-
Shortener is compatible with Rails v3, v4 &
|
73
|
+
Shortener is compatible with Rails v3, v4, v5, & v6. To install, add to your Gemfile:
|
73
74
|
|
74
75
|
gem 'shortener'
|
75
76
|
|
@@ -100,6 +101,10 @@ By default, Shortener will generate unique keys using numbers and lowercase a-z.
|
|
100
101
|
the upper and lower case charset, by including the following:
|
101
102
|
|
102
103
|
Shortener.charset = :alphanumcase
|
104
|
+
|
105
|
+
If you want to use a custom charset, you can create your own combination by creating an array of possible values, such as allowing underscore and dashes:
|
106
|
+
|
107
|
+
Shortener.charset = ("a".."z").to_a + (0..9).to_a + ["-", "_"]
|
103
108
|
|
104
109
|
== Usage
|
105
110
|
|
@@ -141,9 +146,11 @@ And to access those URLs:
|
|
141
146
|
|
142
147
|
=== Shortened URLs with custom unique key
|
143
148
|
|
144
|
-
You can pass in your own key when generating a shortened URL. This should be unique.
|
149
|
+
You can pass in your own key when generating a shortened URL. This should be unique.
|
150
|
+
|
151
|
+
*Important:* Custom keys can't contain characters other than those defined in *Shortener.charset*. Default is numbers and lowercase a-z (See *Configuration*).
|
145
152
|
|
146
|
-
Shortener::ShortenedUrl.generate("example.com", owner: user, custom_key: "
|
153
|
+
Shortener::ShortenedUrl.generate("example.com", owner: user, custom_key: "mykey")
|
147
154
|
|
148
155
|
short_url("http://example.com", custom_key: 'yourkey')
|
149
156
|
|
@@ -170,7 +177,7 @@ to create a fresh record, you can pass the following argument:
|
|
170
177
|
=== Forbidden keys
|
171
178
|
|
172
179
|
You can ensure that records with forbidden keys will not be generated.
|
173
|
-
In
|
180
|
+
In Rails you can put next line into config/initializers/shortener.rb
|
174
181
|
|
175
182
|
Shortener.forbidden_keys.concat %w(terms promo)
|
176
183
|
|
@@ -280,4 +287,4 @@ To contribute:
|
|
280
287
|
6. Create a new Pull Request
|
281
288
|
7. Ensure the build is passing
|
282
289
|
|
283
|
-
Note: We adhere to the community driven Ruby style guide: https://github.com/bbatsov/ruby-style-guide
|
290
|
+
Note: We adhere to the community driven Ruby style guide: https://github.com/bbatsov/ruby-style-guide
|
@@ -4,7 +4,7 @@ class Shortener::ShortenedUrl < ActiveRecord::Base
|
|
4
4
|
|
5
5
|
validates :url, presence: true
|
6
6
|
|
7
|
-
|
7
|
+
around_create :generate_unique_key
|
8
8
|
|
9
9
|
# allows the shortened link to be associated with a user
|
10
10
|
if ActiveRecord::VERSION::MAJOR >= 5
|
@@ -35,7 +35,7 @@ class Shortener::ShortenedUrl < ActiveRecord::Base
|
|
35
35
|
def self.generate!(destination_url, owner: nil, custom_key: nil, expires_at: nil, fresh: false, category: nil)
|
36
36
|
# if we get a shortened_url object with a different owner, generate
|
37
37
|
# new one for the new owner. Otherwise return same object
|
38
|
-
|
38
|
+
if destination_url.is_a? Shortener::ShortenedUrl
|
39
39
|
if destination_url.owner == owner
|
40
40
|
destination_url
|
41
41
|
else
|
@@ -58,8 +58,6 @@ class Shortener::ShortenedUrl < ActiveRecord::Base
|
|
58
58
|
expires_at: expires_at
|
59
59
|
)
|
60
60
|
end
|
61
|
-
|
62
|
-
result
|
63
61
|
end
|
64
62
|
|
65
63
|
# return shortened url on success, nil on failure
|
@@ -127,15 +125,24 @@ class Shortener::ShortenedUrl < ActiveRecord::Base
|
|
127
125
|
|
128
126
|
private
|
129
127
|
|
130
|
-
def
|
128
|
+
def self.unique_key_candidate
|
129
|
+
charset = ::Shortener.key_chars
|
130
|
+
(0...::Shortener.unique_key_length).map{ charset[rand(charset.size)] }.join
|
131
|
+
end
|
132
|
+
|
133
|
+
def generate_unique_key(retries = Shortener.persist_retries)
|
131
134
|
begin
|
132
135
|
self.unique_key = custom_key || self.class.unique_key_candidate
|
133
136
|
self.custom_key = nil
|
134
|
-
end while self.class.exists?(unique_key: unique_key)
|
135
|
-
end
|
137
|
+
end while self.class.unscoped.exists?(unique_key: unique_key)
|
136
138
|
|
137
|
-
|
138
|
-
|
139
|
-
|
139
|
+
yield
|
140
|
+
rescue ActiveRecord::RecordNotUnique
|
141
|
+
if retries <= 0
|
142
|
+
raise
|
143
|
+
else
|
144
|
+
retries -= 1
|
145
|
+
retry
|
146
|
+
end
|
140
147
|
end
|
141
148
|
end
|
data/gemfiles/rails_4.gemfile
CHANGED
data/gemfiles/rails_5.0.gemfile
CHANGED
data/gemfiles/rails_5.2.gemfile
CHANGED
data/lib/shortener.rb
CHANGED
@@ -19,8 +19,9 @@ module Shortener
|
|
19
19
|
self.unique_key_length = 5
|
20
20
|
|
21
21
|
# character set to chose from:
|
22
|
-
# :alphanum
|
23
|
-
# :alphanumcase
|
22
|
+
# :alphanum // a-z0-9 ## has about 60 million possible combos
|
23
|
+
# :alphanumcase // a-zA-Z0-9 ## has about 900 million possible combos
|
24
|
+
# ("a".."z").to_a + ("A".."Z").to_a + (0..9).to_a + ["-", "_"] ## define a custom set
|
24
25
|
mattr_accessor :charset
|
25
26
|
self.charset = :alphanum
|
26
27
|
|
@@ -36,9 +37,12 @@ module Shortener
|
|
36
37
|
mattr_accessor :ignore_robots
|
37
38
|
self.ignore_robots = false
|
38
39
|
|
40
|
+
# persist_retries - number of retries on ActiveRecord::RecordNotUnique error
|
41
|
+
mattr_accessor :persist_retries
|
42
|
+
self.persist_retries = 3
|
39
43
|
|
40
44
|
def self.key_chars
|
41
|
-
CHARSETS[charset]
|
45
|
+
charset.is_a?(Symbol) ? CHARSETS[charset] : charset
|
42
46
|
end
|
43
47
|
end
|
44
48
|
|
data/lib/shortener/railtie.rb
CHANGED
data/lib/shortener/version.rb
CHANGED
data/shortener.gemspec
CHANGED
@@ -12,7 +12,6 @@ Gem::Specification.new do |s|
|
|
12
12
|
s.authors = [ "James P. McGrath", "Michael Reinsch" ]
|
13
13
|
s.email = [ "gems@jamespmcgrath.com", "michael@mobalean.com" ]
|
14
14
|
s.homepage = "http://jamespmcgrath.com/projects/shortener"
|
15
|
-
s.rubyforge_project = "shortener"
|
16
15
|
s.required_rubygems_version = "> 2.1.0"
|
17
16
|
|
18
17
|
s.add_dependency "voight_kampff", '~> 1.1.2'
|
@@ -24,6 +23,7 @@ Gem::Specification.new do |s|
|
|
24
23
|
s.add_development_dependency "faker"
|
25
24
|
s.add_development_dependency "byebug"
|
26
25
|
s.add_development_dependency "appraisal"
|
26
|
+
s.add_development_dependency "capybara"
|
27
27
|
|
28
28
|
s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
|
29
29
|
s.require_path = 'lib'
|
@@ -132,6 +132,21 @@ describe Shortener::ShortenedUrlsController, type: :controller do
|
|
132
132
|
end
|
133
133
|
end
|
134
134
|
|
135
|
+
context "custom charset set" do
|
136
|
+
before do
|
137
|
+
Shortener::ShortenedUrl.delete_all
|
138
|
+
Shortener.charset = ("a".."z").to_a + ("A".."Z").to_a + (0..9).to_a + ["-", "_"]
|
139
|
+
end
|
140
|
+
|
141
|
+
context 'key with valid characters' do
|
142
|
+
let(:key) { "cust-Key_123" }
|
143
|
+
let(:custom_url) { Shortener::ShortenedUrl.generate(Faker::Internet.url, custom_key: key) }
|
144
|
+
it 'allows if in custom charset' do
|
145
|
+
expect(custom_url.unique_key).to eq key
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
135
150
|
context 'expired code' do
|
136
151
|
let(:expired_url) { Shortener::ShortenedUrl.generate(Faker::Internet.url, expires_at: 1.hour.ago) }
|
137
152
|
describe "GET show with expired code" do
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
Example: <%= short_url("http://example.com") %>
|
data/spec/dummy/config/routes.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
require 'spec_helper'
|
3
3
|
|
4
4
|
describe Shortener::ShortenedUrl, type: :model do
|
5
|
-
it { is_expected.to belong_to
|
5
|
+
it { is_expected.to belong_to(:owner).optional }
|
6
6
|
it { is_expected.to validate_presence_of :url }
|
7
7
|
|
8
8
|
describe '#generate!' do
|
@@ -123,10 +123,45 @@ describe Shortener::ShortenedUrl, type: :model do
|
|
123
123
|
|
124
124
|
context "duplicate unique key" do
|
125
125
|
let(:duplicate_key) { 'ABCDEF' }
|
126
|
-
|
126
|
+
context 'without retry' do
|
127
|
+
around do |spec|
|
128
|
+
tries = Shortener.persist_retries
|
129
|
+
Shortener.persist_retries = 0
|
130
|
+
spec.run
|
131
|
+
Shortener.persist_retries = tries
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'finds a non-dup key' do
|
135
|
+
Shortener::ShortenedUrl.where(unique_key: duplicate_key).delete_all
|
136
|
+
Shortener::ShortenedUrl.create!(url: Faker::Internet.url, custom_key: duplicate_key)
|
137
|
+
short_url = Shortener::ShortenedUrl.create!(url: Faker::Internet.url, custom_key: duplicate_key)
|
138
|
+
expect(short_url).not_to be_nil
|
139
|
+
expect(short_url.unique_key).not_to be_nil
|
140
|
+
expect(short_url.unique_key).not_to eq duplicate_key
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'unscoped query to finds a non-dup key' do
|
144
|
+
Shortener::ShortenedUrl.where(unique_key: duplicate_key).delete_all
|
145
|
+
Shortener::ShortenedUrl.create!(url: Faker::Internet.url, custom_key: duplicate_key)
|
146
|
+
short_url = Shortener::ShortenedUrl.where(
|
147
|
+
url: Faker::Internet.url, category: :test
|
148
|
+
).create!(url: Faker::Internet.url, custom_key: duplicate_key)
|
149
|
+
expect(short_url).not_to be_nil
|
150
|
+
expect(short_url.unique_key).not_to be_nil
|
151
|
+
expect(short_url.unique_key).not_to eq duplicate_key
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'use retry in case with DB unique constraint exception' do
|
127
156
|
Shortener::ShortenedUrl.where(unique_key: duplicate_key).delete_all
|
128
157
|
Shortener::ShortenedUrl.create!(url: Faker::Internet.url, custom_key: duplicate_key)
|
129
|
-
|
158
|
+
|
159
|
+
query = double
|
160
|
+
allow(query).to receive(:exists?).and_return(false)
|
161
|
+
allow(Shortener::ShortenedUrl).to receive(:unscoped).and_return(query).once
|
162
|
+
allow(Shortener::ShortenedUrl).to receive(:unscoped).and_call_original
|
163
|
+
|
164
|
+
short_url = Shortener::ShortenedUrl.create!(url: Faker::Internet.url, custom_key: duplicate_key)
|
130
165
|
expect(short_url).not_to be_nil
|
131
166
|
expect(short_url.unique_key).not_to be_nil
|
132
167
|
expect(short_url.unique_key).not_to eq duplicate_key
|
data/spec/spec_helper.rb
CHANGED
@@ -7,6 +7,7 @@ require 'rspec/rails'
|
|
7
7
|
require 'shoulda/matchers'
|
8
8
|
require 'byebug'
|
9
9
|
require 'faker'
|
10
|
+
require 'capybara/rspec'
|
10
11
|
|
11
12
|
Rails.backtrace_cleaner.remove_silencers!
|
12
13
|
|
@@ -18,8 +19,11 @@ Shoulda::Matchers.configure do |config|
|
|
18
19
|
end
|
19
20
|
|
20
21
|
# Run any available migration
|
22
|
+
migration_path = File.expand_path("../dummy/db/migrate/", __FILE__)
|
21
23
|
if ActiveRecord::Migrator.respond_to?(:migrate)
|
22
|
-
ActiveRecord::Migrator.migrate
|
24
|
+
ActiveRecord::Migrator.migrate(migration_path)
|
25
|
+
elsif Rails::VERSION::MAJOR < 6
|
26
|
+
ActiveRecord::MigrationContext.new(migration_path).migrate
|
23
27
|
else
|
24
|
-
ActiveRecord::MigrationContext.new(
|
28
|
+
ActiveRecord::MigrationContext.new(migration_path, ActiveRecord::Base.connection.schema_migration).migrate
|
25
29
|
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.8.
|
4
|
+
version: 0.8.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James P. McGrath
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2020-04-26 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: voight_kampff
|
@@ -123,6 +123,20 @@ dependencies:
|
|
123
123
|
- - ">="
|
124
124
|
- !ruby/object:Gem::Version
|
125
125
|
version: '0'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: capybara
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
requirements:
|
130
|
+
- - ">="
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: '0'
|
133
|
+
type: :development
|
134
|
+
prerelease: false
|
135
|
+
version_requirements: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - ">="
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: '0'
|
126
140
|
description: Shortener is a Rails Engine Gem that makes it easy to create and interpret
|
127
141
|
shortened URLs on your own domain from within your Rails application. Once installed
|
128
142
|
Shortener will generate, store URLS and "unshorten" shortened URLs for your applications
|
@@ -148,6 +162,7 @@ files:
|
|
148
162
|
- gemfiles/rails_5.0.gemfile
|
149
163
|
- gemfiles/rails_5.1.gemfile
|
150
164
|
- gemfiles/rails_5.2.gemfile
|
165
|
+
- gemfiles/rails_6.0.gemfile
|
151
166
|
- lib/generators/shortener/shortener_generator.rb
|
152
167
|
- lib/generators/shortener/templates/migration.rb
|
153
168
|
- lib/shortener.rb
|
@@ -160,9 +175,12 @@ files:
|
|
160
175
|
- spec/controllers/shortened_urls_controller_spec.rb
|
161
176
|
- spec/dummy/.gitignore
|
162
177
|
- spec/dummy/Rakefile
|
178
|
+
- spec/dummy/app/assets/config/manifest.js
|
163
179
|
- spec/dummy/app/controllers/application_controller.rb
|
180
|
+
- spec/dummy/app/controllers/home_controller.rb
|
164
181
|
- spec/dummy/app/helpers/application_helper.rb
|
165
182
|
- spec/dummy/app/models/user.rb
|
183
|
+
- spec/dummy/app/views/home/show.html.erb
|
166
184
|
- spec/dummy/app/views/layouts/application.html.erb
|
167
185
|
- spec/dummy/config.ru
|
168
186
|
- spec/dummy/config/application.rb
|
@@ -188,6 +206,7 @@ files:
|
|
188
206
|
- spec/dummy/db/schema.rb
|
189
207
|
- spec/dummy/public/favicon.ico
|
190
208
|
- spec/dummy/script/rails
|
209
|
+
- spec/features/shortener_feature_spec.rb
|
191
210
|
- spec/helpers/shortener_helper_spec.rb
|
192
211
|
- spec/models/shortened_url_spec.rb
|
193
212
|
- spec/models/user_spec.rb
|
@@ -211,8 +230,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
211
230
|
- !ruby/object:Gem::Version
|
212
231
|
version: 2.1.0
|
213
232
|
requirements: []
|
214
|
-
|
215
|
-
rubygems_version: 2.5.1
|
233
|
+
rubygems_version: 3.0.3
|
216
234
|
signing_key:
|
217
235
|
specification_version: 4
|
218
236
|
summary: Shortener is a Rails Engine that makes it easy to create shortened URLs for
|