slugs 2.0.1 → 4.0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +59 -19
- data/Rakefile +1 -19
- data/lib/generators/slugs/install/install_generator.rb +24 -0
- data/lib/generators/slugs/{templates/configuration.rb → install/templates/initializer.rb} +1 -1
- data/lib/generators/slugs/install/templates/migration.rb +15 -0
- data/lib/slugs.rb +7 -4
- data/lib/slugs/concern.rb +16 -9
- data/lib/slugs/configuration.rb +11 -1
- data/lib/slugs/extensions/action_dispatch/generator.rb +1 -0
- data/lib/slugs/extensions/action_dispatch/optimized_url_helper.rb +1 -0
- data/lib/slugs/extensions/active_record/finders.rb +27 -0
- data/lib/slugs/railtie.rb +10 -4
- data/lib/slugs/slug.rb +9 -0
- data/lib/slugs/version.rb +1 -1
- data/test/dummy/bin/bundle +1 -0
- data/test/dummy/bin/rails +1 -0
- data/test/dummy/bin/rake +1 -0
- data/test/dummy/bin/setup +1 -0
- data/test/dummy/config/database.yml.travis +2 -11
- data/test/dummy/config/environments/development.rb +1 -1
- data/test/dummy/config/environments/production.rb +1 -1
- data/test/dummy/config/environments/test.rb +2 -2
- data/test/dummy/config/initializers/slugs.rb +1 -1
- data/test/dummy/config/secrets.yml +2 -2
- data/test/dummy/db/migrate/20161016174020_create_users.rb +2 -0
- data/test/dummy/db/migrate/20161016174126_create_shops.rb +2 -0
- data/test/dummy/db/migrate/20161016174202_create_products.rb +2 -0
- data/test/dummy/db/migrate/20161016174225_create_categories.rb +2 -0
- data/test/dummy/db/migrate/20161124162802_create_slugs.rb +15 -0
- data/test/dummy/db/schema.rb +33 -13
- data/test/dummy/log/development.log +394 -30
- data/test/dummy/log/test.log +3017 -3978
- data/test/dummy/public/404.html +57 -63
- data/test/dummy/public/422.html +57 -63
- data/test/dummy/public/500.html +56 -62
- data/test/generator_test.rb +3 -4
- data/test/record_test.rb +65 -0
- data/test/{routes_test.rb → route_test.rb} +1 -1
- data/test/test_helper.rb +2 -2
- metadata +14 -9
- data/lib/generators/slugs/install_generator.rb +0 -15
- data/test/records_test.rb +0 -27
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 40739528bced84f0f44d1d2b4b703195e5200fad
|
4
|
+
data.tar.gz: 61ae632c29f54550bed3fc4ecd6e2919fa56d4fe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 454843d24608dad78b5c634201499918bd99d7c1cc59b46d8af85a2197c1be055be353f25ae9449846682a86174e06c1222aa9cf2a1ca41c675c8043ae95bc00
|
7
|
+
data.tar.gz: 81f1c28b5d1b2bdc1a482ce3fd5e2775bf63ec85d0f2946ee100d000976aa776880a8c3e5669608eeede8dbcb7ab23e7e26a35f9a2929fb93a75484b3f107e2b
|
data/README.md
CHANGED
@@ -1,12 +1,20 @@
|
|
1
1
|
[![Gem Version](https://badge.fury.io/rb/slugs.svg)](http://badge.fury.io/rb/slugs)
|
2
2
|
[![Code Climate](https://codeclimate.com/github/mmontossi/slugs/badges/gpa.svg)](https://codeclimate.com/github/mmontossi/slugs)
|
3
|
-
[![Build Status](https://travis-ci.org/mmontossi/slugs.svg)](https://travis-ci.org/
|
3
|
+
[![Build Status](https://travis-ci.org/mmontossi/slugs.svg)](https://travis-ci.org/mmontossi/slugs)
|
4
4
|
[![Dependency Status](https://gemnasium.com/mmontossi/slugs.svg)](https://gemnasium.com/mmontossi/slugs)
|
5
5
|
|
6
6
|
# Slugs
|
7
7
|
|
8
8
|
Manages slugs for records with minimal efford in rails.
|
9
9
|
|
10
|
+
## Why
|
11
|
+
|
12
|
+
I did this gem to:
|
13
|
+
|
14
|
+
- Generalize how to control when routes will use the slug param.
|
15
|
+
- Keep old slugs active until the record is destroyed.
|
16
|
+
- Ensure unique slugs by appending an index automatically on duplicates.
|
17
|
+
|
10
18
|
## Install
|
11
19
|
|
12
20
|
Put this line in your Gemfile:
|
@@ -21,47 +29,79 @@ $ bundle
|
|
21
29
|
|
22
30
|
## Configuration
|
23
31
|
|
24
|
-
|
32
|
+
Run the install generator:
|
25
33
|
```
|
26
|
-
bundle exec rails g slugs:install
|
34
|
+
$ bundle exec rails g slugs:install
|
27
35
|
```
|
28
36
|
|
29
|
-
|
37
|
+
Set the global settings:
|
38
|
+
```ruby
|
39
|
+
Slugs.configure do |config|
|
40
|
+
config.use_slug? do |record, params|
|
41
|
+
params[:controller] != 'admin'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
```
|
45
|
+
|
46
|
+
## Usage
|
47
|
+
|
48
|
+
### Definitions
|
49
|
+
|
50
|
+
Add the column to your tables:
|
30
51
|
```ruby
|
31
|
-
|
52
|
+
class AddSlug < ActiveRecord::Migration
|
53
|
+
def change
|
54
|
+
add_column :products, :slug, :string
|
55
|
+
end
|
56
|
+
end
|
32
57
|
```
|
33
58
|
|
34
59
|
Update your db:
|
35
60
|
```
|
36
|
-
bundle exec rake db:migrate
|
61
|
+
$ bundle exec rake db:migrate
|
37
62
|
```
|
38
63
|
|
39
|
-
|
64
|
+
Define slugs in your models:
|
40
65
|
```ruby
|
41
|
-
|
42
|
-
|
43
|
-
params[:controller] != 'admin'
|
44
|
-
end
|
66
|
+
class Product < ActiveRecord::Base
|
67
|
+
has_slug :model, :name, scope: :shop_id
|
45
68
|
end
|
46
69
|
```
|
47
70
|
|
48
|
-
|
71
|
+
### Generation
|
49
72
|
|
50
|
-
|
73
|
+
A slug will be generated every time you create/update a record:
|
74
|
+
```ruby
|
75
|
+
product = Product.create(name: 'Stratocaster', model: 'American Standar', ...)
|
76
|
+
product.slug # => 'american-standard-stratocaster'
|
77
|
+
```
|
78
|
+
|
79
|
+
An index will be appended if another record with the same slug is created:
|
80
|
+
```ruby
|
81
|
+
product = Product.create(name: 'Stratocaster', model: 'American Standard', ...)
|
82
|
+
product.slug # => 'american-standard-stratocaster-1'
|
83
|
+
```
|
51
84
|
|
52
|
-
|
85
|
+
Every time you change a record, the slug will be updated:
|
53
86
|
```ruby
|
54
|
-
|
87
|
+
product.update name: 'Strat'
|
88
|
+
product.slug # => 'american-standard-strat'
|
55
89
|
```
|
56
90
|
|
57
|
-
|
91
|
+
### Finders
|
92
|
+
|
93
|
+
The find method of models will start accepting slugs and remember old ones:
|
58
94
|
```ruby
|
59
|
-
|
95
|
+
Product.find 'american-standard-stratocaster' # => product
|
96
|
+
Product.find 'american-standard-strat' # => product
|
60
97
|
```
|
61
98
|
|
62
|
-
|
99
|
+
### Routes
|
100
|
+
|
101
|
+
The logic of the use_slug? block is used to determine when to sluggize:
|
63
102
|
```ruby
|
64
|
-
|
103
|
+
admin_product_path product # => 'admin/products/34443'
|
104
|
+
product_path product # => 'products/american-standard-strat'
|
65
105
|
```
|
66
106
|
|
67
107
|
## Credits
|
data/Rakefile
CHANGED
@@ -4,26 +4,8 @@ rescue LoadError
|
|
4
4
|
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
5
|
end
|
6
6
|
|
7
|
-
require 'rdoc/task'
|
8
|
-
|
9
|
-
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
-
rdoc.rdoc_dir = 'rdoc'
|
11
|
-
rdoc.title = 'Slugs'
|
12
|
-
rdoc.options << '--line-numbers'
|
13
|
-
rdoc.rdoc_files.include('README.rdoc')
|
14
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
-
end
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
7
|
Bundler::GemHelper.install_tasks
|
23
8
|
|
24
|
-
APP_RAKEFILE = File.expand_path('../test/dummy/Rakefile', __FILE__)
|
25
|
-
load 'rails/tasks/engine.rake'
|
26
|
-
|
27
9
|
require 'rake/testtask'
|
28
10
|
|
29
11
|
Rake::TestTask.new(:test) do |t|
|
@@ -31,7 +13,7 @@ Rake::TestTask.new(:test) do |t|
|
|
31
13
|
t.libs << 'test'
|
32
14
|
t.pattern = 'test/**/*_test.rb'
|
33
15
|
t.verbose = false
|
16
|
+
t.warning = false
|
34
17
|
end
|
35
18
|
|
36
|
-
|
37
19
|
task default: :test
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
|
3
|
+
module Slugs
|
4
|
+
module Generators
|
5
|
+
class InstallGenerator < ::Rails::Generators::Base
|
6
|
+
include Rails::Generators::Migration
|
7
|
+
|
8
|
+
source_root File.expand_path('../templates', __FILE__)
|
9
|
+
|
10
|
+
def create_initializer_file
|
11
|
+
copy_file 'initializer.rb', 'config/initializers/slugs.rb'
|
12
|
+
end
|
13
|
+
|
14
|
+
def create_migration_file
|
15
|
+
migration_template 'migration.rb', 'db/migrate/create_slugs.rb'
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.next_migration_number(path)
|
19
|
+
Time.now.utc.strftime '%Y%m%d%H%M%S'
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class CreateSlugs < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :slugs do |t|
|
4
|
+
t.integer :sluggable_id
|
5
|
+
t.string :sluggable_type
|
6
|
+
t.string :value
|
7
|
+
|
8
|
+
t.timestamps null: false
|
9
|
+
end
|
10
|
+
|
11
|
+
add_index :slugs, :sluggable_id
|
12
|
+
add_index :slugs, :sluggable_type
|
13
|
+
add_index :slugs, :value
|
14
|
+
end
|
15
|
+
end
|
data/lib/slugs.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
1
|
require 'slugs/extensions/action_dispatch/generator'
|
2
2
|
require 'slugs/extensions/action_dispatch/optimized_url_helper'
|
3
3
|
require 'slugs/extensions/active_record/base'
|
4
|
+
require 'slugs/extensions/active_record/finders'
|
5
|
+
require 'slugs/slug'
|
4
6
|
require 'slugs/concern'
|
5
7
|
require 'slugs/configuration'
|
6
8
|
require 'slugs/railtie'
|
9
|
+
require 'slugs/version'
|
7
10
|
|
8
11
|
module Slugs
|
9
12
|
class << self
|
@@ -17,7 +20,7 @@ module Slugs
|
|
17
20
|
end
|
18
21
|
|
19
22
|
def parameterize(record, params)
|
20
|
-
if
|
23
|
+
if use_slug?(record, params)
|
21
24
|
if record.slug_changed?
|
22
25
|
record.slug_was
|
23
26
|
else
|
@@ -28,9 +31,9 @@ module Slugs
|
|
28
31
|
end
|
29
32
|
end
|
30
33
|
|
31
|
-
def
|
32
|
-
if record.try(:sluggable?)
|
33
|
-
configuration.
|
34
|
+
def use_slug?(record, params)
|
35
|
+
if record.try(:sluggable?)
|
36
|
+
configuration.use_slug? record, params
|
34
37
|
else
|
35
38
|
false
|
36
39
|
end
|
data/lib/slugs/concern.rb
CHANGED
@@ -3,10 +3,9 @@ module Slugs
|
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
5
|
included do
|
6
|
-
|
7
|
-
|
6
|
+
has_many :slugs, as: :sluggable, class_name: 'Slugs::Slug'
|
7
|
+
before_save :set_slug
|
8
8
|
validates_format_of :slug, with: /\A[a-z0-9\-]+\z/, allow_blank: true
|
9
|
-
validates_length_of :slug, maximum: 255, allow_blank: true
|
10
9
|
end
|
11
10
|
|
12
11
|
def sluggable?
|
@@ -18,10 +17,6 @@ module Slugs
|
|
18
17
|
def set_slug
|
19
18
|
options = self.class.slug
|
20
19
|
self.slug = slice(*options[:attributes]).values.join(' ').parameterize
|
21
|
-
end
|
22
|
-
|
23
|
-
def ensure_slug_uniqueness
|
24
|
-
options = self.class.slug
|
25
20
|
case options[:scope]
|
26
21
|
when Symbol
|
27
22
|
attribute = options[:scope]
|
@@ -30,8 +25,20 @@ module Slugs
|
|
30
25
|
attributes = options[:scope]
|
31
26
|
scope = attributes.map{ |a| [a, send(a)] }.to_h
|
32
27
|
end
|
33
|
-
|
34
|
-
|
28
|
+
relation = self.class.where(scope).where('slug ~ ?', "^#{slug}(-[0-9]+)?$")
|
29
|
+
if persisted?
|
30
|
+
relation = relation.where.not(id: id)
|
31
|
+
end
|
32
|
+
previous_slug = relation.order(slug: :desc).limit(1).pluck(:slug).first
|
33
|
+
if previous_slug.present?
|
34
|
+
if result = previous_slug.match(/^#{slug}-(\d+)$/)
|
35
|
+
self.slug += "-#{result[1].to_i + 1}"
|
36
|
+
else
|
37
|
+
self.slug += '-1'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
if slug_changed?
|
41
|
+
slugs.build value: slug
|
35
42
|
end
|
36
43
|
end
|
37
44
|
|
data/lib/slugs/configuration.rb
CHANGED
@@ -0,0 +1,27 @@
|
|
1
|
+
module Slugs
|
2
|
+
module Extensions
|
3
|
+
module ActiveRecord
|
4
|
+
module Finders
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
def find(id)
|
8
|
+
if sluggable? && id.is_a?(String) && id !~ /\A\d+\z/
|
9
|
+
order = Slugs::Slug.order(id: :desc)
|
10
|
+
joins(:slugs).merge(order).find_by! slugs: { value: id }
|
11
|
+
else
|
12
|
+
super
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def exists?(value=:none)
|
17
|
+
if sluggable? && value.is_a?(String) && value !~ /\A\d+\z/
|
18
|
+
joins(:slugs).exists? slugs: { value: value }
|
19
|
+
else
|
20
|
+
super
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/slugs/railtie.rb
CHANGED
@@ -1,16 +1,22 @@
|
|
1
1
|
module Slugs
|
2
2
|
class Railtie < Rails::Railtie
|
3
3
|
|
4
|
-
initializer 'slugs' do
|
5
|
-
::ActionDispatch::Routing::RouteSet::NamedRouteCollection::UrlHelper::OptimizedUrlHelper.prepend(
|
6
|
-
Slugs::Extensions::ActionDispatch::OptimizedUrlHelper
|
7
|
-
)
|
4
|
+
initializer 'slugs.extensions' do
|
8
5
|
::ActionDispatch::Routing::RouteSet::Generator.prepend(
|
9
6
|
Slugs::Extensions::ActionDispatch::Generator
|
10
7
|
)
|
8
|
+
::ActionDispatch::Routing::RouteSet::NamedRouteCollection::UrlHelper::OptimizedUrlHelper.prepend(
|
9
|
+
Slugs::Extensions::ActionDispatch::OptimizedUrlHelper
|
10
|
+
)
|
11
11
|
::ActiveRecord::Base.include(
|
12
12
|
Slugs::Extensions::ActiveRecord::Base
|
13
13
|
)
|
14
|
+
::ActiveRecord::Base.extend(
|
15
|
+
Slugs::Extensions::ActiveRecord::Finders
|
16
|
+
)
|
17
|
+
::ActiveRecord::Relation.include(
|
18
|
+
Slugs::Extensions::ActiveRecord::Finders
|
19
|
+
)
|
14
20
|
end
|
15
21
|
|
16
22
|
end
|
data/lib/slugs/slug.rb
ADDED
data/lib/slugs/version.rb
CHANGED
data/test/dummy/bin/bundle
CHANGED
data/test/dummy/bin/rails
CHANGED
data/test/dummy/bin/rake
CHANGED
data/test/dummy/bin/setup
CHANGED
@@ -1,12 +1,3 @@
|
|
1
|
-
mysql: &mysql
|
2
|
-
adapter: <%= 'jdbc' if RUBY_ENGINE == 'jruby' %>mysql<%= '2' if RUBY_ENGINE != 'jruby' %>
|
3
|
-
|
4
|
-
postgres: &postgres
|
5
|
-
adapter: <%= 'jdbc' if RUBY_ENGINE == 'jruby' %>postgresql
|
6
|
-
|
7
|
-
sqlite: &sqlite
|
8
|
-
adapter: <%= 'jdbc' if RUBY_ENGINE == 'jruby' %>sqlite3
|
9
|
-
|
10
1
|
test:
|
11
|
-
|
12
|
-
database:
|
2
|
+
adapter: <%= 'jdbc' if RUBY_ENGINE == 'jruby' %>postgresql
|
3
|
+
database: travis
|