shortener 0.8.0 → 0.8.1
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 +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
|