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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 0ff66256668eb71d5b42010c191233c7346c2651
4
- data.tar.gz: f4cf3ffadd4b4f74ae6076be7f9fa13edf55b5ea
2
+ SHA256:
3
+ metadata.gz: 9a8820cdee8a530e85514c1c8b3c3749216c0cd4703c31c2307e96a4897eba25
4
+ data.tar.gz: 185560e234f5c00d49a4e32ed4c3a3bae2c69c33d58b827c414cd5b24c7a4f66
5
5
  SHA512:
6
- metadata.gz: c88fd4cda719d6759af9f1faa5fc70829f18ff99662f77dc10ae33f20e9debcfabd051c5fe7157a75b505f45d164752cd40ead26ed110be6c09c1427e81e382e
7
- data.tar.gz: 0e4ae113ede69db12a0b9901747bb20bb0ed4a72e51817e619524c166cc8cf1a8159f69c0352af843a48650773c3aa98e62d92a44f5e5ffcbcfd083265a0c629
6
+ metadata.gz: d1a48b44d3938a2436116251515250a892a3aefd08e1a0c75f9abdaa16e050404e6cc43780336405dc8b447595199e7a8978aa915d0d68dc2d000090d16c62e6
7
+ data.tar.gz: 4e43a6b1f6189a779769649aae7bb37c50aa00433fbc61ad0d159031eb545b8142cb1eb454be1882f33800d4ac4c9c35fd5c89eafb78213aeba955f1cd118c8a
data/.gitignore CHANGED
@@ -12,4 +12,6 @@ bin/*
12
12
  *.swp
13
13
  Gemfile.lock
14
14
  .rvmrc
15
- .redcar/*
15
+ .redcar/*
16
+ gemfiles/*.lock
17
+ spec/dummy/tmp
data/.travis.yml CHANGED
@@ -14,3 +14,4 @@ gemfile:
14
14
  - gemfiles/rails_5.0.gemfile
15
15
  - gemfiles/rails_5.1.gemfile
16
16
  - gemfiles/rails_5.2.gemfile
17
+ - gemfiles/rails_6.0.gemfile
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 Rails 3 and Rails 4 applications. It has dependancies Rails core components like ActiveRecord, ActionController, the rails routing engine and more.
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 & v5. To install, add to your Gemfile:
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: "my-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 rails you can put next line into config/initializers/shortener.rb
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
- before_create :generate_unique_key
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
- result = if destination_url.is_a? Shortener::ShortenedUrl
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 generate_unique_key
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) && custom_key.blank?
135
- end
137
+ end while self.class.unscoped.exists?(unique_key: unique_key)
136
138
 
137
- def self.unique_key_candidate
138
- charset = ::Shortener.key_chars
139
- (0...::Shortener.unique_key_length).map{ charset[rand(charset.size)] }.join
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
@@ -3,5 +3,6 @@
3
3
  source "http://rubygems.org"
4
4
 
5
5
  gem "rails", "~> 4.2.10"
6
+ gem "sqlite3", "~> 1.3.6"
6
7
 
7
8
  gemspec path: "../"
@@ -3,5 +3,6 @@
3
3
  source "http://rubygems.org"
4
4
 
5
5
  gem "rails", "~> 5.0.7"
6
+ gem "sqlite3", "~> 1.3.6"
6
7
 
7
8
  gemspec path: "../"
@@ -2,6 +2,6 @@
2
2
 
3
3
  source "http://rubygems.org"
4
4
 
5
- gem "rails", "5.2.0"
5
+ gem "rails", "~> 5.2.0"
6
6
 
7
7
  gemspec path: "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "http://rubygems.org"
4
+
5
+ gem "rails", "~> 6.0.0"
6
+
7
+ gemspec path: "../"
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 - a-z0-9 - has about 60 million possible combos
23
- # :alphanumcase - a-zA-Z0-9 - has about 900 million possible combos
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
 
@@ -6,8 +6,5 @@ class Shortener::Railtie < ::Rails::Railtie #:nodoc:
6
6
  ActiveSupport.on_load :active_record do
7
7
  extend Shortener::ActiveRecordExtension
8
8
  end
9
- ActiveSupport.on_load :action_view do
10
- include Shortener::ShortenerHelper
11
- end
12
9
  end
13
10
  end
@@ -1,3 +1,3 @@
1
1
  module Shortener
2
- VERSION = '0.8.0'.freeze
2
+ VERSION = '0.8.1'.freeze
3
3
  end
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,3 @@
1
+ class HomeController < ApplicationController
2
+ def show; end
3
+ end
@@ -0,0 +1 @@
1
+ Example: <%= short_url("http://example.com") %>
@@ -2,8 +2,6 @@
2
2
  <html>
3
3
  <head>
4
4
  <title>Dummy</title>
5
- <%= stylesheet_link_tag :all %>
6
- <%= javascript_include_tag :defaults %>
7
5
  <%= csrf_meta_tag %>
8
6
  </head>
9
7
  <body>
@@ -1,4 +1,4 @@
1
1
  Dummy::Application.routes.draw do
2
2
  get '/:id' => "shortener/shortened_urls#show"
3
- root to: "application_controller#show"
3
+ root to: "home#show"
4
4
  end
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+
3
+ describe Shortener, type: :feature do
4
+ it "includes 'short_url'-helper into public namespace" do
5
+ visit root_path
6
+ expect(page).to have_content(%r{http://www.example.com/[a-zA-Z0-9]{5}})
7
+ end
8
+ end
@@ -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 :owner }
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
- it 'should try until it finds a non-dup key' do
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
- short_url = Shortener::ShortenedUrl.create!(url: Faker::Internet.url, unique_key: duplicate_key)
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 File.expand_path("../dummy/db/migrate/", __FILE__)
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(File.expand_path("../dummy/db/migrate/", __FILE__)).migrate
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.0
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: 2018-07-27 00:00:00.000000000 Z
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
- rubyforge_project: shortener
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