friendly_id 5.2.0.beta.1 → 5.2.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/.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:
|