slugs 2.0.1 → 4.0.0.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/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
|
[](http://badge.fury.io/rb/slugs)
|
2
2
|
[](https://codeclimate.com/github/mmontossi/slugs)
|
3
|
-
[](https://travis-ci.org/
|
3
|
+
[](https://travis-ci.org/mmontossi/slugs)
|
4
4
|
[](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
|