friendly_id 5.2.0.beta.1 → 5.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +15 -4
- data/CONTRIBUTING.md +1 -1
- data/Changelog.md +6 -2
- data/MIT-LICENSE +1 -1
- data/README.md +2 -2
- data/Rakefile +10 -0
- data/bench.rb +6 -6
- data/friendly_id.gemspec +1 -1
- data/gemfiles/Gemfile.rails-4.0.rb +1 -1
- data/gemfiles/Gemfile.rails-4.1.rb +1 -1
- data/gemfiles/Gemfile.rails-5.0.rb +28 -0
- data/lib/friendly_id.rb +1 -13
- data/lib/friendly_id/base.rb +21 -4
- data/lib/friendly_id/configuration.rb +7 -0
- data/lib/friendly_id/finder_methods.rb +4 -4
- data/lib/friendly_id/finders.rb +2 -5
- data/lib/friendly_id/history.rb +12 -10
- data/lib/friendly_id/migration.rb +2 -2
- data/lib/friendly_id/object_utils.rb +29 -7
- data/lib/friendly_id/sequentially_slugged.rb +3 -5
- data/lib/friendly_id/simple_i18n.rb +3 -3
- data/lib/friendly_id/slugged.rb +1 -0
- data/lib/friendly_id/version.rb +1 -1
- data/test/benchmarks/finders.rb +88 -0
- data/test/benchmarks/object_utils.rb +56 -0
- data/test/history_test.rb +74 -5
- data/test/sequentially_slugged_test.rb +16 -0
- data/test/slugged_test.rb +32 -0
- metadata +9 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 606af0800382602b2f88d11be3a839a3e8c23255
|
4
|
+
data.tar.gz: a501dd78829d595fe897475bdf4a6c6d98caee9c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cb2afe89084b532541152c46056649ec6bfca0a2fe51493706f7f996ee233bae010a248a735fb55451e89b1918b29e60b5471e78c0272f29a5c5c410220eacc8
|
7
|
+
data.tar.gz: 8a527f735585ce88c1627bc54f206745f69a826d27574f6d306ee4a29dff23421e82225b0b9e9751cdd5a3a9911f3dba755d10d7a9951758052a3bd869b26441
|
data/.travis.yml
CHANGED
@@ -2,11 +2,11 @@ language: ruby
|
|
2
2
|
cache: bundler
|
3
3
|
|
4
4
|
rvm:
|
5
|
+
- 2.3.0
|
5
6
|
- 2.2.0
|
6
|
-
- 2.0.0
|
7
7
|
- 2.1.0
|
8
|
-
-
|
9
|
-
-
|
8
|
+
- 2.0.0
|
9
|
+
- jruby-9.0.4.0
|
10
10
|
|
11
11
|
env:
|
12
12
|
- DB=postgres
|
@@ -16,15 +16,26 @@ gemfile:
|
|
16
16
|
- gemfiles/Gemfile.rails-4.0.rb
|
17
17
|
- gemfiles/Gemfile.rails-4.1.rb
|
18
18
|
- gemfiles/Gemfile.rails-4.2.rb
|
19
|
+
- gemfiles/Gemfile.rails-5.0.rb
|
19
20
|
|
20
21
|
matrix:
|
21
22
|
allow_failures:
|
22
23
|
- rvm: jruby-19mode
|
23
24
|
gemfile: gemfiles/Gemfile.rails-4.2.rb
|
24
25
|
env: DB=postgres
|
26
|
+
- rvm: jruby-9.0.4.0
|
27
|
+
gemfile: gemfiles/Gemfile.rails-5.0.rb
|
28
|
+
exclude:
|
29
|
+
- rvm: 2.0.0
|
30
|
+
gemfile: gemfiles/Gemfile.rails-5.0.rb
|
31
|
+
- rvm: 2.1.0
|
32
|
+
gemfile: gemfiles/Gemfile.rails-5.0.rb
|
33
|
+
- rvm: 2.2.0
|
34
|
+
gemfile: gemfiles/Gemfile.rails-5.0.rb
|
25
35
|
|
26
36
|
sudo: false
|
27
37
|
|
28
|
-
before_script:
|
38
|
+
before_script:
|
39
|
+
- bundle exec rake db:create db:up
|
29
40
|
|
30
41
|
script: 'COVERALLS=true bundle exec rake test'
|
data/CONTRIBUTING.md
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
Please ask questions on [Stack
|
4
4
|
Overflow](http://stackoverflow.com/questions/tagged/friendly-id) using the
|
5
5
|
"friendly_id" or "friendly-id" tag. Prior to asking, search and see if your
|
6
|
-
question has already been
|
6
|
+
question has already been answered.
|
7
7
|
|
8
8
|
Please only post issues in Github issues for actual bugs.
|
9
9
|
|
data/Changelog.md
CHANGED
@@ -1,15 +1,19 @@
|
|
1
1
|
# FriendlyId Changelog
|
2
2
|
|
3
|
-
We would like to think our many
|
3
|
+
We would like to think our many [contributors](https://github.com/norman/friendly_id/graphs/contributors) for
|
4
4
|
suggestions, ideas and improvements to FriendlyId.
|
5
5
|
|
6
|
-
## 5.2.0 (
|
6
|
+
## 5.2.0 (2016-12-01)
|
7
7
|
|
8
8
|
* Add sequential slug module for FriendlyId 4.x-style sequential slugs. ([#644](https://github.com/norman/friendly_id/pull/644)).
|
9
9
|
* Make Candidates#each iterable without block ([#651](https://github.com/norman/friendly_id/pull/651)).
|
10
10
|
* Ensure slug history prefers the record that most recently used the slug ([#663](https://github.com/norman/friendly_id/pull/663)).
|
11
11
|
* Don't calculate all changes just to check if the param field has changed ([#667](https://github.com/norman/friendly_id/pull/667)).
|
12
12
|
* Don't set or change slug when unrelated validation failures block the record from being saved ([#642](https://github.com/norman/friendly_id/issues/642)).
|
13
|
+
* Fix order dependence bug between history and finders modules ([#718](https://github.com/norman/friendly_id/pull/718))
|
14
|
+
* Added ability to conditionally turn off :dependent => :destory on FriendlyId::Slugs([#724](https://github.com/norman/friendly_id/pull/724))
|
15
|
+
* Add support for Rails 5. ([#728](https://github.com/norman/friendly_id/pull/728))
|
16
|
+
* Allow per-model conditional disabling of friendly path generation using a :routes option to friendly_id ([#735](https://github.com/norman/friendly_id/pull/735))
|
13
17
|
|
14
18
|
## 5.1.0 (2015-01-15)
|
15
19
|
|
data/MIT-LICENSE
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c) 2008-
|
1
|
+
Copyright (c) 2008-2016 Norman Clarke, Adrian Mugnolo and Emilio Tagua.
|
2
2
|
|
3
3
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
4
|
of this software and associated documentation files (the "Software"), to deal
|
data/README.md
CHANGED
@@ -16,7 +16,7 @@ over and over and over again in the issues.
|
|
16
16
|
|
17
17
|
# FriendlyId
|
18
18
|
|
19
|
-
|
19
|
+
**For the most complete, user-friendly documentation, see the [FriendlyId Guide](http://norman.github.io/friendly_id/file.Guide.html).**
|
20
20
|
|
21
21
|
FriendlyId is the "Swiss Army bulldozer" of slugging and permalink plugins for
|
22
22
|
Active Record. It lets you create pretty URLs and work with human-friendly
|
@@ -236,7 +236,7 @@ volunteers](https://github.com/norman/friendly_id/contributors).
|
|
236
236
|
|
237
237
|
## License
|
238
238
|
|
239
|
-
Copyright (c) 2008-
|
239
|
+
Copyright (c) 2008-2016 Norman Clarke and contributors, released under the MIT
|
240
240
|
license.
|
241
241
|
|
242
242
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
data/Rakefile
CHANGED
@@ -36,6 +36,16 @@ task :bench => :load_path do
|
|
36
36
|
require File.expand_path("../bench", __FILE__)
|
37
37
|
end
|
38
38
|
|
39
|
+
desc "Run benchmarks on finders"
|
40
|
+
task :bench_finders => :load_path do
|
41
|
+
require File.expand_path("../test/benchmarks/finders", __FILE__)
|
42
|
+
end
|
43
|
+
|
44
|
+
desc "Run benchmarks on ObjectUtils"
|
45
|
+
task :bench_object_utils => :load_path do
|
46
|
+
require File.expand_path("../test/benchmarks/object_utils", __FILE__)
|
47
|
+
end
|
48
|
+
|
39
49
|
desc "Generate Guide.md"
|
40
50
|
task :guide do
|
41
51
|
load File.expand_path('../guide.rb', __FILE__)
|
data/bench.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require File.expand_path("../test/helper", __FILE__)
|
2
2
|
require "ffaker"
|
3
3
|
|
4
|
-
N =
|
4
|
+
N = 10000
|
5
5
|
|
6
6
|
def transaction
|
7
7
|
ActiveRecord::Base.transaction { yield ; raise ActiveRecord::Rollback }
|
@@ -37,7 +37,7 @@ MANUALS = []
|
|
37
37
|
RESTAURANTS = []
|
38
38
|
|
39
39
|
100.times do
|
40
|
-
name =
|
40
|
+
name = FFaker::Name.name
|
41
41
|
BOOKS << (Book.create! :name => name).id
|
42
42
|
JOURNALISTS << (Journalist.create! :name => name).friendly_id
|
43
43
|
MANUALS << (Manual.create! :name => name).friendly_id
|
@@ -64,18 +64,18 @@ Benchmark.bmbm do |x|
|
|
64
64
|
end
|
65
65
|
|
66
66
|
x.report 'insert (without FriendlyId)' do
|
67
|
-
N.times {transaction {Book.create :name =>
|
67
|
+
N.times {transaction {Book.create :name => FFaker::Name.name}}
|
68
68
|
end
|
69
69
|
|
70
70
|
x.report 'insert (in-table-slug)' do
|
71
|
-
N.times {transaction {Journalist.create :name =>
|
71
|
+
N.times {transaction {Journalist.create :name => FFaker::Name.name}}
|
72
72
|
end
|
73
73
|
|
74
74
|
x.report 'insert (in-table-slug; using finders module)' do
|
75
|
-
N.times {transaction {Restaurant.create :name =>
|
75
|
+
N.times {transaction {Restaurant.create :name => FFaker::Name.name}}
|
76
76
|
end
|
77
77
|
|
78
78
|
x.report 'insert (external slug)' do
|
79
|
-
N.times {transaction {Manual.create :name =>
|
79
|
+
N.times {transaction {Manual.create :name => FFaker::Name.name}}
|
80
80
|
end
|
81
81
|
end
|
data/friendly_id.gemspec
CHANGED
@@ -6,7 +6,7 @@ Gem::Specification.new do |s|
|
|
6
6
|
s.version = FriendlyId::VERSION
|
7
7
|
s.authors = ["Norman Clarke", "Philip Arndt"]
|
8
8
|
s.email = ["norman@njclarke.com", "p@arndt.io"]
|
9
|
-
s.homepage = "
|
9
|
+
s.homepage = "https://github.com/norman/friendly_id"
|
10
10
|
s.summary = "A comprehensive slugging and pretty-URL plugin."
|
11
11
|
s.rubyforge_project = "friendly_id"
|
12
12
|
s.files = `git ls-files`.split("\n")
|
@@ -0,0 +1,28 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
gemspec path: '../'
|
4
|
+
|
5
|
+
gem 'activerecord', '~> 5.0.0'
|
6
|
+
gem 'railties', '~> 5.0.0'
|
7
|
+
gem 'i18n', '~> 0.7.0'
|
8
|
+
|
9
|
+
# Database Configuration
|
10
|
+
group :development, :test do
|
11
|
+
platforms :jruby do
|
12
|
+
gem 'activerecord-jdbcmysql-adapter', '~> 1.3.14'
|
13
|
+
gem 'activerecord-jdbcpostgresql-adapter', '~> 1.3.14'
|
14
|
+
gem 'kramdown'
|
15
|
+
end
|
16
|
+
|
17
|
+
platforms :ruby, :rbx do
|
18
|
+
gem 'sqlite3'
|
19
|
+
gem 'mysql2'
|
20
|
+
gem 'pg'
|
21
|
+
gem 'redcarpet'
|
22
|
+
end
|
23
|
+
|
24
|
+
platforms :rbx do
|
25
|
+
gem 'rubysl', '~> 2.0'
|
26
|
+
gem 'rubinius-developer_tools'
|
27
|
+
end
|
28
|
+
end
|
data/lib/friendly_id.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
require 'active_record'
|
2
3
|
require "friendly_id/base"
|
3
4
|
require "friendly_id/object_utils"
|
4
5
|
require "friendly_id/configuration"
|
@@ -51,19 +52,6 @@ module FriendlyId
|
|
51
52
|
autoload :Finders, "friendly_id/finders"
|
52
53
|
autoload :SequentiallySlugged, "friendly_id/sequentially_slugged"
|
53
54
|
|
54
|
-
# Instances of these classes will never be considered a friendly id.
|
55
|
-
# @see FriendlyId::ObjectUtils#friendly_id
|
56
|
-
UNFRIENDLY_CLASSES = [
|
57
|
-
ActiveRecord::Base,
|
58
|
-
Array,
|
59
|
-
FalseClass,
|
60
|
-
Hash,
|
61
|
-
NilClass,
|
62
|
-
Numeric,
|
63
|
-
Symbol,
|
64
|
-
TrueClass
|
65
|
-
]
|
66
|
-
|
67
55
|
# FriendlyId takes advantage of `extended` to do basic model setup, primarily
|
68
56
|
# extending {FriendlyId::Base} to add {FriendlyId::Base#friendly_id
|
69
57
|
# friendly_id} as a class method.
|
data/lib/friendly_id/base.rb
CHANGED
@@ -187,9 +187,22 @@ often better and easier to use {FriendlyId::Slugged slugs}.
|
|
187
187
|
# allows an alternate configuration syntax, and conditional configuration
|
188
188
|
# logic.
|
189
189
|
#
|
190
|
+
# @option options [Symbol,Boolean] :dependent Available when using `:history`.
|
191
|
+
# Sets the value used for the slugged association's dependent option. Use
|
192
|
+
# `false` if you do not want to dependently destroy the associated slugged
|
193
|
+
# record. Defaults to `:destroy`.
|
194
|
+
#
|
195
|
+
# @option options [Symbol] :routes When set to anything other than :friendly,
|
196
|
+
# ensures that all routes generated by default do *not* use the slug. This
|
197
|
+
# allows `form_for` and `polymorphic_path` to continue to generate paths like
|
198
|
+
# `/team/1` instead of `/team/number-one`. You can still generate paths
|
199
|
+
# like the latter using: team_path(team.slug). When set to :friendly, or
|
200
|
+
# omitted, the default friendly_id behavior is maintained.
|
201
|
+
#
|
190
202
|
# @yieldparam config The model class's {FriendlyId::Configuration friendly_id_config}.
|
191
203
|
def friendly_id(base = nil, options = {}, &block)
|
192
204
|
yield friendly_id_config if block_given?
|
205
|
+
friendly_id_config.dependent = options.delete :dependent
|
193
206
|
friendly_id_config.use options.delete :use
|
194
207
|
friendly_id_config.send :set, base ? options.merge(:base => base) : options
|
195
208
|
include Model
|
@@ -244,11 +257,15 @@ often better and easier to use {FriendlyId::Slugged slugs}.
|
|
244
257
|
|
245
258
|
# Either the friendly_id, or the numeric id cast to a string.
|
246
259
|
def to_param
|
247
|
-
if
|
248
|
-
|
249
|
-
|
260
|
+
if friendly_id_config.routes == :friendly
|
261
|
+
if attribute_changed?(friendly_id_config.query_field)
|
262
|
+
diff = changes[friendly_id_config.query_field]
|
263
|
+
diff.first || diff.second
|
264
|
+
else
|
265
|
+
friendly_id.presence.to_param || super
|
266
|
+
end
|
250
267
|
else
|
251
|
-
|
268
|
+
super
|
252
269
|
end
|
253
270
|
end
|
254
271
|
|
@@ -18,11 +18,18 @@ module FriendlyId
|
|
18
18
|
# The module to use for finders
|
19
19
|
attr_accessor :finder_methods
|
20
20
|
|
21
|
+
# The value used for the slugged association's dependent option
|
22
|
+
attr_accessor :dependent
|
23
|
+
|
24
|
+
# Route generation preferences
|
25
|
+
attr_accessor :routes
|
26
|
+
|
21
27
|
def initialize(model_class, values = nil)
|
22
28
|
@model_class = model_class
|
23
29
|
@defaults = {}
|
24
30
|
@modules = []
|
25
31
|
@finder_methods = FriendlyId::FinderMethods
|
32
|
+
self.routes = :friendly
|
26
33
|
set values
|
27
34
|
end
|
28
35
|
|
@@ -20,7 +20,7 @@ module FriendlyId
|
|
20
20
|
return super if args.count != 1 || id.unfriendly_id?
|
21
21
|
first_by_friendly_id(id).tap {|result| return result unless result.nil?}
|
22
22
|
return super if potential_primary_key?(id)
|
23
|
-
raise ActiveRecord::RecordNotFound
|
23
|
+
raise ActiveRecord::RecordNotFound, "can't find record with friendly id: #{id.inspect}"
|
24
24
|
end
|
25
25
|
|
26
26
|
# Returns true if a record with the given id exists.
|
@@ -33,7 +33,7 @@ module FriendlyId
|
|
33
33
|
# `find`.
|
34
34
|
# @raise ActiveRecord::RecordNotFound
|
35
35
|
def find_by_friendly_id(id)
|
36
|
-
first_by_friendly_id(id) or raise ActiveRecord::RecordNotFound
|
36
|
+
first_by_friendly_id(id) or raise ActiveRecord::RecordNotFound, "can't find record with friendly id: #{id.inspect}"
|
37
37
|
end
|
38
38
|
|
39
39
|
def exists_by_friendly_id?(id)
|
@@ -54,8 +54,8 @@ module FriendlyId
|
|
54
54
|
end
|
55
55
|
|
56
56
|
def first_by_friendly_id(id)
|
57
|
-
|
57
|
+
find_by(friendly_id_config.query_field => id)
|
58
58
|
end
|
59
59
|
|
60
60
|
end
|
61
|
-
end
|
61
|
+
end
|
data/lib/friendly_id/finders.rb
CHANGED
@@ -76,17 +76,14 @@ for models that use FriendlyId with something similar to the following:
|
|
76
76
|
def self.setup(model_class)
|
77
77
|
model_class.instance_eval do
|
78
78
|
relation.class.send(:include, friendly_id_config.finder_methods)
|
79
|
-
if ActiveRecord::VERSION::MAJOR == 4 && ActiveRecord::VERSION::MINOR == 2
|
79
|
+
if (ActiveRecord::VERSION::MAJOR == 4 && ActiveRecord::VERSION::MINOR == 2) || ActiveRecord::VERSION::MAJOR == 5
|
80
80
|
model_class.send(:extend, friendly_id_config.finder_methods)
|
81
81
|
end
|
82
82
|
end
|
83
|
-
end
|
84
|
-
|
85
|
-
def self.included(model_class)
|
86
|
-
model_class.extend(ClassMethods)
|
87
83
|
|
88
84
|
# Support for friendly finds on associations for Rails 4.0.1 and above.
|
89
85
|
if ::ActiveRecord.const_defined?('AssociationRelation')
|
86
|
+
model_class.extend(ClassMethods)
|
90
87
|
association_relation_delegate_class = model_class.relation_delegate_class(::ActiveRecord::AssociationRelation)
|
91
88
|
association_relation_delegate_class.send(:include, model_class.friendly_id_config.finder_methods)
|
92
89
|
end
|
data/lib/friendly_id/history.rb
CHANGED
@@ -41,7 +41,7 @@ method.
|
|
41
41
|
...
|
42
42
|
|
43
43
|
def find_post
|
44
|
-
@post = Post.find params[:id]
|
44
|
+
@post = Post.friendly.find params[:id]
|
45
45
|
|
46
46
|
# If an old id or a numeric id was used to find the record, then
|
47
47
|
# the request path will not match the post_path, and we should do
|
@@ -54,16 +54,18 @@ method.
|
|
54
54
|
=end
|
55
55
|
module History
|
56
56
|
|
57
|
+
module Configuration
|
58
|
+
def dependent_value
|
59
|
+
dependent.nil? ? :destroy : dependent
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
57
63
|
def self.setup(model_class)
|
58
64
|
model_class.instance_eval do
|
59
65
|
friendly_id_config.use :slugged
|
66
|
+
friendly_id_config.class.send :include, History::Configuration
|
60
67
|
friendly_id_config.finder_methods = FriendlyId::History::FinderMethods
|
61
|
-
if friendly_id_config.uses? :finders
|
62
|
-
relation.class.send(:include, friendly_id_config.finder_methods)
|
63
|
-
if ActiveRecord::VERSION::MAJOR == 4 && ActiveRecord::VERSION::MINOR == 2
|
64
|
-
model_class.send(:extend, friendly_id_config.finder_methods)
|
65
|
-
end
|
66
|
-
end
|
68
|
+
FriendlyId::Finders.setup(model_class) if friendly_id_config.uses? :finders
|
67
69
|
end
|
68
70
|
end
|
69
71
|
|
@@ -72,7 +74,7 @@ method.
|
|
72
74
|
model_class.class_eval do
|
73
75
|
has_many :slugs, -> {order("#{Slug.quoted_table_name}.id DESC")}, {
|
74
76
|
:as => :sluggable,
|
75
|
-
:dependent =>
|
77
|
+
:dependent => @friendly_id_config.dependent_value,
|
76
78
|
:class_name => Slug.to_s
|
77
79
|
}
|
78
80
|
|
@@ -84,7 +86,7 @@ method.
|
|
84
86
|
include ::FriendlyId::FinderMethods
|
85
87
|
|
86
88
|
def exists_by_friendly_id?(id)
|
87
|
-
|
89
|
+
where(arel_table[friendly_id_config.query_field].eq(id)).exists? || joins(:slugs).where(slug_history_clause(id)).exists?
|
88
90
|
end
|
89
91
|
|
90
92
|
private
|
@@ -111,7 +113,7 @@ method.
|
|
111
113
|
def scope_for_slug_generator
|
112
114
|
relation = super
|
113
115
|
return relation if new_record?
|
114
|
-
relation = relation.merge(Slug.where('sluggable_id <> ?', id))
|
116
|
+
relation = relation.joins(:slugs).merge(Slug.where('sluggable_id <> ?', id))
|
115
117
|
if friendly_id_config.uses?(:scoped)
|
116
118
|
relation = relation.where(Slug.arel_table[:scope].eq(serialized_scope))
|
117
119
|
end
|
@@ -8,8 +8,8 @@ class CreateFriendlyIdSlugs < ActiveRecord::Migration
|
|
8
8
|
t.datetime :created_at
|
9
9
|
end
|
10
10
|
add_index :friendly_id_slugs, :sluggable_id
|
11
|
-
add_index :friendly_id_slugs, [:slug, :sluggable_type]
|
12
|
-
add_index :friendly_id_slugs, [:slug, :sluggable_type, :scope], :unique
|
11
|
+
add_index :friendly_id_slugs, [:slug, :sluggable_type], length: { slug: 140, sluggable_type: 50 }
|
12
|
+
add_index :friendly_id_slugs, [:slug, :sluggable_type, :scope], length: { slug: 70, sluggable_type: 50, scope: 70 }, unique: true
|
13
13
|
add_index :friendly_id_slugs, :sluggable_type
|
14
14
|
end
|
15
15
|
end
|
@@ -1,4 +1,17 @@
|
|
1
1
|
module FriendlyId
|
2
|
+
# Instances of these classes will never be considered a friendly id.
|
3
|
+
# @see FriendlyId::ObjectUtils#friendly_id
|
4
|
+
UNFRIENDLY_CLASSES = [
|
5
|
+
ActiveRecord::Base,
|
6
|
+
Array,
|
7
|
+
FalseClass,
|
8
|
+
Hash,
|
9
|
+
NilClass,
|
10
|
+
Numeric,
|
11
|
+
Symbol,
|
12
|
+
TrueClass
|
13
|
+
]
|
14
|
+
|
2
15
|
# Utility methods for determining whether any object is a friendly id.
|
3
16
|
#
|
4
17
|
# Monkey-patching Object is a somewhat extreme measure not to be taken lightly
|
@@ -27,13 +40,7 @@ module FriendlyId
|
|
27
40
|
# "123".friendly_id? #=> nil
|
28
41
|
# "abc123".friendly_id? #=> true
|
29
42
|
def friendly_id?
|
30
|
-
|
31
|
-
# one of its descendants.
|
32
|
-
if FriendlyId::UNFRIENDLY_CLASSES.detect {|klass| self.class <= klass}
|
33
|
-
false
|
34
|
-
elsif respond_to?(:to_i) && to_i.to_s != to_s
|
35
|
-
true
|
36
|
-
end
|
43
|
+
true if respond_to?(:to_i) && to_i.to_s != to_s
|
37
44
|
end
|
38
45
|
|
39
46
|
# True if the id is definitely unfriendly, false if definitely friendly,
|
@@ -42,6 +49,21 @@ module FriendlyId
|
|
42
49
|
val = friendly_id? ; !val unless val.nil?
|
43
50
|
end
|
44
51
|
end
|
52
|
+
|
53
|
+
module UnfriendlyUtils
|
54
|
+
def friendly_id?
|
55
|
+
false
|
56
|
+
end
|
57
|
+
|
58
|
+
def unfriendly_id?
|
59
|
+
true
|
60
|
+
end
|
61
|
+
end
|
45
62
|
end
|
46
63
|
|
47
64
|
Object.send :include, FriendlyId::ObjectUtils
|
65
|
+
|
66
|
+
# Considered unfriendly if object is an instance of an unfriendly class or
|
67
|
+
# one of its descendants.
|
68
|
+
|
69
|
+
FriendlyId::UNFRIENDLY_CLASSES.each { |klass| klass.send(:include, FriendlyId::UnfriendlyUtils) }
|
@@ -4,13 +4,11 @@ module FriendlyId
|
|
4
4
|
model_class.friendly_id_config.use :slugged
|
5
5
|
end
|
6
6
|
|
7
|
-
def should_generate_new_friendly_id?
|
8
|
-
send(friendly_id_config.base).present? && super
|
9
|
-
end
|
10
|
-
|
11
7
|
def resolve_friendly_id_conflict(candidate_slugs)
|
8
|
+
candidate = candidate_slugs.first
|
9
|
+
return if candidate.nil?
|
12
10
|
SequentialSlugCalculator.new(scope_for_slug_generator,
|
13
|
-
|
11
|
+
candidate,
|
14
12
|
friendly_id_config.slug_column,
|
15
13
|
friendly_id_config.sequence_separator).next_slug
|
16
14
|
end
|
@@ -37,15 +37,15 @@ friendly_id_globalize gem instead.
|
|
37
37
|
Finds will take into consideration the current locale:
|
38
38
|
|
39
39
|
I18n.locale = :es
|
40
|
-
Post.find("la-guerra-de-las-galaxias")
|
40
|
+
Post.friendly.find("la-guerra-de-las-galaxias")
|
41
41
|
I18n.locale = :en
|
42
|
-
Post.find("star-wars")
|
42
|
+
Post.friendly.find("star-wars")
|
43
43
|
|
44
44
|
To find a slug by an explicit locale, perform the find inside a block
|
45
45
|
passed to I18n's `with_locale` method:
|
46
46
|
|
47
47
|
I18n.with_locale(:es) do
|
48
|
-
Post.find("la-guerra-de-las-galaxias")
|
48
|
+
Post.friendly.find("la-guerra-de-las-galaxias")
|
49
49
|
end
|
50
50
|
|
51
51
|
### Creating Records
|
data/lib/friendly_id/slugged.rb
CHANGED
data/lib/friendly_id/version.rb
CHANGED
@@ -0,0 +1,88 @@
|
|
1
|
+
require File.expand_path("../../helper", __FILE__)
|
2
|
+
require "ffaker"
|
3
|
+
|
4
|
+
# This benchmark tests ActiveRecord and FriendlyId methods for performing a find
|
5
|
+
#
|
6
|
+
# ActiveRecord: where.first 8.970000 0.040000 9.010000 ( 9.029544)
|
7
|
+
# ActiveRecord: where.take 8.100000 0.030000 8.130000 ( 8.157024)
|
8
|
+
# ActiveRecord: find 2.720000 0.010000 2.730000 ( 2.733527)
|
9
|
+
# ActiveRecord: find_by(:id) 2.920000 0.000000 2.920000 ( 2.926318)
|
10
|
+
# ActiveRecord: find_by(:slug) 2.650000 0.020000 2.670000 ( 2.662677)
|
11
|
+
# FriendlyId: find (in-table slug w/ finders) 9.820000 0.030000 9.850000 ( 9.873358)
|
12
|
+
# FriendlyId: friendly.find (in-table slug) 12.890000 0.050000 12.940000 ( 12.951156)
|
13
|
+
|
14
|
+
N = 50000
|
15
|
+
|
16
|
+
def transaction
|
17
|
+
ActiveRecord::Base.transaction { yield ; raise ActiveRecord::Rollback }
|
18
|
+
end
|
19
|
+
|
20
|
+
class Array
|
21
|
+
def rand
|
22
|
+
self[Kernel.rand(length)]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
Book = Class.new ActiveRecord::Base
|
27
|
+
|
28
|
+
class Journalist < ActiveRecord::Base
|
29
|
+
extend FriendlyId
|
30
|
+
friendly_id :name, :use => :slugged
|
31
|
+
end
|
32
|
+
|
33
|
+
class Manual < ActiveRecord::Base
|
34
|
+
extend FriendlyId
|
35
|
+
friendly_id :name, :use => :history
|
36
|
+
end
|
37
|
+
|
38
|
+
class Restaurant < ActiveRecord::Base
|
39
|
+
extend FriendlyId
|
40
|
+
friendly_id :name, :use => :finders
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
BOOKS = []
|
45
|
+
JOURNALISTS = []
|
46
|
+
MANUALS = []
|
47
|
+
RESTAURANTS = []
|
48
|
+
|
49
|
+
100.times do
|
50
|
+
name = FFaker::Name.name
|
51
|
+
BOOKS << (Book.create! :name => name).id
|
52
|
+
JOURNALISTS << (Journalist.create! :name => name).friendly_id
|
53
|
+
MANUALS << (Manual.create! :name => name).friendly_id
|
54
|
+
RESTAURANTS << (Restaurant.create! :name => name).friendly_id
|
55
|
+
end
|
56
|
+
|
57
|
+
ActiveRecord::Base.connection.execute "UPDATE manuals SET slug = NULL"
|
58
|
+
|
59
|
+
Benchmark.bmbm do |x|
|
60
|
+
x.report 'ActiveRecord: where.first' do
|
61
|
+
N.times {Book.where(:id=>BOOKS.rand).first}
|
62
|
+
end
|
63
|
+
|
64
|
+
x.report 'ActiveRecord: where.take' do
|
65
|
+
N.times {Book.where(:id=>BOOKS.rand).take}
|
66
|
+
end
|
67
|
+
|
68
|
+
x.report 'ActiveRecord: find' do
|
69
|
+
N.times {Book.find BOOKS.rand}
|
70
|
+
end
|
71
|
+
|
72
|
+
x.report 'ActiveRecord: find_by(:id)' do
|
73
|
+
N.times {Book.find_by(:id=>BOOKS.rand)}
|
74
|
+
end
|
75
|
+
|
76
|
+
x.report 'ActiveRecord: find_by(:slug)' do
|
77
|
+
N.times {Restaurant.find_by(:slug=>RESTAURANTS.rand)}
|
78
|
+
end
|
79
|
+
|
80
|
+
x.report 'FriendlyId: find (in-table slug w/ finders)' do
|
81
|
+
N.times {Restaurant.find RESTAURANTS.rand}
|
82
|
+
end
|
83
|
+
|
84
|
+
x.report 'FriendlyId: friendly.find (in-table slug)' do
|
85
|
+
N.times {Restaurant.friendly.find RESTAURANTS.rand}
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require File.expand_path("../../helper", __FILE__)
|
2
|
+
|
3
|
+
# This benchmark compares the timings of the friendly_id? and unfriendly_id? on various objects
|
4
|
+
#
|
5
|
+
# integer friendly_id? 6.370000 0.000000 6.370000 ( 6.380925)
|
6
|
+
# integer unfriendly_id? 6.640000 0.010000 6.650000 ( 6.646057)
|
7
|
+
# AR::Base friendly_id? 2.340000 0.000000 2.340000 ( 2.340743)
|
8
|
+
# AR::Base unfriendly_id? 2.560000 0.000000 2.560000 ( 2.560039)
|
9
|
+
# hash friendly_id? 5.090000 0.010000 5.100000 ( 5.097662)
|
10
|
+
# hash unfriendly_id? 5.430000 0.000000 5.430000 ( 5.437160)
|
11
|
+
# nil friendly_id? 5.610000 0.010000 5.620000 ( 5.611487)
|
12
|
+
# nil unfriendly_id? 5.870000 0.000000 5.870000 ( 5.880484)
|
13
|
+
# numeric string friendly_id? 9.270000 0.030000 9.300000 ( 9.308452)
|
14
|
+
# numeric string unfriendly_id? 9.190000 0.040000 9.230000 ( 9.252890)
|
15
|
+
# test_string friendly_id? 8.380000 0.010000 8.390000 ( 8.411762)
|
16
|
+
# test_string unfriendly_id? 8.450000 0.010000 8.460000 ( 8.463662)
|
17
|
+
|
18
|
+
# From the ObjectUtils docs...
|
19
|
+
# 123.friendly_id? #=> false
|
20
|
+
# :id.friendly_id? #=> false
|
21
|
+
# {:name => 'joe'}.friendly_id? #=> false
|
22
|
+
# ['name = ?', 'joe'].friendly_id? #=> false
|
23
|
+
# nil.friendly_id? #=> false
|
24
|
+
# "123".friendly_id? #=> nil
|
25
|
+
# "abc123".friendly_id? #=> true
|
26
|
+
|
27
|
+
Book = Class.new ActiveRecord::Base
|
28
|
+
|
29
|
+
test_integer = 123
|
30
|
+
test_active_record_object = Book.new
|
31
|
+
test_hash = {:name=>'joe'}
|
32
|
+
test_nil = nil
|
33
|
+
test_numeric_string = "123"
|
34
|
+
test_string = "abc123"
|
35
|
+
|
36
|
+
N = 5_000_000
|
37
|
+
|
38
|
+
Benchmark.bmbm do |x|
|
39
|
+
x.report('integer friendly_id?') { N.times {test_integer.friendly_id?} }
|
40
|
+
x.report('integer unfriendly_id?') { N.times {test_integer.unfriendly_id?} }
|
41
|
+
|
42
|
+
x.report('AR::Base friendly_id?') { N.times {test_active_record_object.friendly_id?} }
|
43
|
+
x.report('AR::Base unfriendly_id?') { N.times {test_active_record_object.unfriendly_id?} }
|
44
|
+
|
45
|
+
x.report('hash friendly_id?') { N.times {test_hash.friendly_id?} }
|
46
|
+
x.report('hash unfriendly_id?') { N.times {test_hash.unfriendly_id?} }
|
47
|
+
|
48
|
+
x.report('nil friendly_id?') { N.times {test_nil.friendly_id?} }
|
49
|
+
x.report('nil unfriendly_id?') { N.times {test_nil.unfriendly_id?} }
|
50
|
+
|
51
|
+
x.report('numeric string friendly_id?') { N.times {test_numeric_string.friendly_id?} }
|
52
|
+
x.report('numeric string unfriendly_id?') { N.times {test_numeric_string.unfriendly_id?} }
|
53
|
+
|
54
|
+
x.report('test_string friendly_id?') { N.times {test_string.friendly_id?} }
|
55
|
+
x.report('test_string unfriendly_id?') { N.times {test_string.unfriendly_id?} }
|
56
|
+
end
|
data/test/history_test.rb
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
require "helper"
|
2
2
|
|
3
|
-
class Manual < ActiveRecord::Base
|
4
|
-
extend FriendlyId
|
5
|
-
friendly_id :name, :use => [:slugged, :history]
|
6
|
-
end
|
7
|
-
|
8
3
|
class HistoryTest < TestCaseClass
|
9
4
|
|
10
5
|
include FriendlyId::Test
|
11
6
|
include FriendlyId::Test::Shared::Core
|
12
7
|
|
8
|
+
class Manual < ActiveRecord::Base
|
9
|
+
extend FriendlyId
|
10
|
+
friendly_id :name, :use => [:slugged, :history]
|
11
|
+
end
|
12
|
+
|
13
13
|
def model_class
|
14
14
|
Manual
|
15
15
|
end
|
@@ -172,6 +172,45 @@ class HistoryTestWithAutomaticSlugRegeneration < HistoryTest
|
|
172
172
|
end
|
173
173
|
end
|
174
174
|
|
175
|
+
class DependentDestroyTest < HistoryTest
|
176
|
+
|
177
|
+
include FriendlyId::Test
|
178
|
+
|
179
|
+
class FalseManual < ActiveRecord::Base
|
180
|
+
self.table_name = 'manuals'
|
181
|
+
|
182
|
+
extend FriendlyId
|
183
|
+
friendly_id :name, :use => :history, :dependent => false
|
184
|
+
end
|
185
|
+
|
186
|
+
class DefaultManual < ActiveRecord::Base
|
187
|
+
self.table_name = 'manuals'
|
188
|
+
|
189
|
+
extend FriendlyId
|
190
|
+
friendly_id :name, :use => :history
|
191
|
+
end
|
192
|
+
|
193
|
+
test 'should allow disabling of dependent destroy' do
|
194
|
+
transaction do
|
195
|
+
assert FriendlyId::Slug.find_by_slug('foo').nil?
|
196
|
+
l = FalseManual.create! :name => 'foo'
|
197
|
+
assert FriendlyId::Slug.find_by_slug('foo').present?
|
198
|
+
l.destroy
|
199
|
+
assert FriendlyId::Slug.find_by_slug('foo').present?
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
test 'should dependently destroy by default' do
|
204
|
+
transaction do
|
205
|
+
assert FriendlyId::Slug.find_by_slug('baz').nil?
|
206
|
+
l = DefaultManual.create! :name => 'baz'
|
207
|
+
assert FriendlyId::Slug.find_by_slug('baz').present?
|
208
|
+
l.destroy
|
209
|
+
assert FriendlyId::Slug.find_by_slug('baz').nil?
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
175
214
|
class HistoryTestWithSti < HistoryTest
|
176
215
|
class Journalist < ActiveRecord::Base
|
177
216
|
extend FriendlyId
|
@@ -217,6 +256,36 @@ class HistoryTestWithFriendlyFinders < HistoryTest
|
|
217
256
|
end
|
218
257
|
end
|
219
258
|
|
259
|
+
class HistoryTestWithFindersBeforeHistory < HistoryTest
|
260
|
+
class Novelist < ActiveRecord::Base
|
261
|
+
has_many :novels
|
262
|
+
end
|
263
|
+
|
264
|
+
class Novel < ActiveRecord::Base
|
265
|
+
extend FriendlyId
|
266
|
+
|
267
|
+
belongs_to :novelist
|
268
|
+
|
269
|
+
friendly_id :name, :use => [:finders, :history]
|
270
|
+
|
271
|
+
def should_generate_new_friendly_id?
|
272
|
+
slug.blank? || name_changed?
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
test "should be findable by old slug through has_many association" do
|
277
|
+
transaction do
|
278
|
+
novelist = Novelist.create!(:name => "Stephen King")
|
279
|
+
novel = novelist.novels.create(:name => "Rita Hayworth and Shawshank Redemption")
|
280
|
+
slug = novel.slug
|
281
|
+
novel.name = "Shawshank Redemption"
|
282
|
+
novel.save!
|
283
|
+
assert_equal novel, Novel.find(slug)
|
284
|
+
assert_equal novel, novelist.novels.find(slug)
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
220
289
|
class City < ActiveRecord::Base
|
221
290
|
has_many :restaurants
|
222
291
|
end
|
@@ -89,6 +89,22 @@ class SequentiallySluggedTest < TestCaseClass
|
|
89
89
|
end
|
90
90
|
end
|
91
91
|
|
92
|
+
test "should not generate a slug when canidates set is empty" do
|
93
|
+
model_class = Class.new(ActiveRecord::Base) do
|
94
|
+
self.table_name = "cities"
|
95
|
+
extend FriendlyId
|
96
|
+
friendly_id :slug_candidates, :use => [ :sequentially_slugged ]
|
97
|
+
|
98
|
+
def slug_candidates
|
99
|
+
[name, [name, code]]
|
100
|
+
end
|
101
|
+
end
|
102
|
+
transaction do
|
103
|
+
record = model_class.create!(:name => nil, :code => nil)
|
104
|
+
assert_nil record.slug
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
92
108
|
test "should not generate a slug when the sluggable attribute is blank" do
|
93
109
|
record = model_class.create!(:name => '')
|
94
110
|
assert_nil record.slug
|
data/test/slugged_test.rb
CHANGED
@@ -373,3 +373,35 @@ class FailedValidationAfterUpdateRegressionTest < TestCaseClass
|
|
373
373
|
end
|
374
374
|
|
375
375
|
end
|
376
|
+
|
377
|
+
class ConfigurableRoutesTest < TestCaseClass
|
378
|
+
include FriendlyId::Test
|
379
|
+
|
380
|
+
class Article < ActiveRecord::Base
|
381
|
+
extend FriendlyId
|
382
|
+
|
383
|
+
friendly_id :name, :use => :slugged, :routes => :friendly
|
384
|
+
end
|
385
|
+
|
386
|
+
class Novel < ActiveRecord::Base
|
387
|
+
extend FriendlyId
|
388
|
+
|
389
|
+
friendly_id :name, :use => :slugged, :routes => :default
|
390
|
+
end
|
391
|
+
|
392
|
+
test "to_param should return a friendly id when the routes option is set to :friendly" do
|
393
|
+
transaction do
|
394
|
+
article = Article.create! :name => "Titanic Hits; Iceberg Sinks"
|
395
|
+
|
396
|
+
assert_equal "titanic-hits-iceberg-sinks", article.to_param
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
test "to_param should return the id when the routes option is set to anything but friendly" do
|
401
|
+
transaction do
|
402
|
+
novel = Novel.create! :name => "Don Quixote"
|
403
|
+
|
404
|
+
assert_equal novel.id.to_s, novel.to_param
|
405
|
+
end
|
406
|
+
end
|
407
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: friendly_id
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.2.0
|
4
|
+
version: 5.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Norman Clarke
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2016-12-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
@@ -163,6 +163,7 @@ files:
|
|
163
163
|
- gemfiles/Gemfile.rails-4.0.rb
|
164
164
|
- gemfiles/Gemfile.rails-4.1.rb
|
165
165
|
- gemfiles/Gemfile.rails-4.2.rb
|
166
|
+
- gemfiles/Gemfile.rails-5.0.rb
|
166
167
|
- guide.rb
|
167
168
|
- lib/friendly_id.rb
|
168
169
|
- lib/friendly_id/.gitattributes
|
@@ -185,6 +186,8 @@ files:
|
|
185
186
|
- lib/friendly_id/version.rb
|
186
187
|
- lib/generators/friendly_id_generator.rb
|
187
188
|
- test/base_test.rb
|
189
|
+
- test/benchmarks/finders.rb
|
190
|
+
- test/benchmarks/object_utils.rb
|
188
191
|
- test/candidates_test.rb
|
189
192
|
- test/configuration_test.rb
|
190
193
|
- test/core_test.rb
|
@@ -202,7 +205,7 @@ files:
|
|
202
205
|
- test/simple_i18n_test.rb
|
203
206
|
- test/slugged_test.rb
|
204
207
|
- test/sti_test.rb
|
205
|
-
homepage:
|
208
|
+
homepage: https://github.com/norman/friendly_id
|
206
209
|
licenses:
|
207
210
|
- MIT
|
208
211
|
metadata: {}
|
@@ -217,14 +220,13 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
217
220
|
version: 1.9.3
|
218
221
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
219
222
|
requirements:
|
220
|
-
- - "
|
223
|
+
- - ">="
|
221
224
|
- !ruby/object:Gem::Version
|
222
|
-
version:
|
225
|
+
version: '0'
|
223
226
|
requirements: []
|
224
227
|
rubyforge_project: friendly_id
|
225
|
-
rubygems_version: 2.
|
228
|
+
rubygems_version: 2.5.2
|
226
229
|
signing_key:
|
227
230
|
specification_version: 4
|
228
231
|
summary: A comprehensive slugging and pretty-URL plugin.
|
229
232
|
test_files: []
|
230
|
-
has_rdoc:
|