friendly_id 5.0.0.rc1 → 5.0.0.rc2
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/CONTRIBUTING.md +7 -37
- data/Changelog.md +11 -0
- data/README.md +14 -2
- data/bench.rb +4 -0
- data/lib/friendly_id/base.rb +4 -0
- data/lib/friendly_id/finder_methods.rb +17 -6
- data/lib/friendly_id/history.rb +17 -4
- data/lib/friendly_id/initializer.rb +5 -1
- data/lib/friendly_id/migration.rb +1 -6
- data/lib/friendly_id/slug_generator.rb +1 -1
- data/lib/friendly_id/slugged.rb +10 -3
- data/lib/friendly_id/version.rb +1 -1
- data/test/helper.rb +1 -1
- data/test/history_test.rb +37 -0
- data/test/schema.rb +14 -1
- data/test/slugged_test.rb +41 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2b8e4a5f27bb5857aa5761f04a7300d96df81078
|
4
|
+
data.tar.gz: 85674fed68afdc2ba0122cff6edb0424eeb3dcaa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 39e09066a6e62b4243e19f53c8640e15bfb05ed0c70fd4dbbfdfaf3cbb8aab162392e875ec79545e460ecdb716d2f8fd007c97a356963ccde9852b273216828c
|
7
|
+
data.tar.gz: c1b52cbedd571df3644ce78a94b22df6a747b9b22d5d40bede443dcedd9f69c7ad6771f18e3183b1761f6ace0d512590767c349bafe75f9d5c209c537777cd7c
|
data/CONTRIBUTING.md
CHANGED
@@ -1,41 +1,11 @@
|
|
1
1
|
# FriendlyId
|
2
2
|
|
3
|
-
|
3
|
+
Please ask questions on [Stack
|
4
|
+
Overflow](http://stackoverflow.com/questions/tagged/friendly-id) using the
|
5
|
+
"friendly_id" or "friendly-id" tag. Prior to asking, search and see if your
|
6
|
+
question has already been anwered.
|
4
7
|
|
5
|
-
|
6
|
-
question has been answered before.
|
7
|
-
|
8
|
-
Please also take a look at the extensive documentation, especially the Guide.
|
9
|
-
|
10
|
-
## FAQ
|
11
|
-
|
12
|
-
Here are the answers to some common questions.
|
13
|
-
|
14
|
-
### I'm using Rails 4...
|
15
|
-
|
16
|
-
Then you **must** use FriendlyId 5, which is currently available in the master
|
17
|
-
branch of the repository, and will be released to Rubygems soon.
|
18
|
-
|
19
|
-
### I'm seeing a bunch of odd characters at the end of the id
|
20
|
-
|
21
|
-
For example: `2bc08962-b3dd-4f29-b2e6-244710c86106`. FriendlyId 5 uses a UUID
|
22
|
-
rather than a sequence to simplify slug generation and avoid concurrency problems.
|
23
|
-
|
24
|
-
### How do I set up my routes for FriendlyId?
|
25
|
-
|
26
|
-
Exactly like in any other Rails app. FriendlyId doesn't change any routing
|
27
|
-
functionality, it only changes the ids that are shown in your routes.
|
28
|
-
|
29
|
-
TIP: Make your app work the way you want to with numeric id's. Then add
|
30
|
-
FriendlyId later.
|
31
|
-
|
32
|
-
### I'm seeing `undefined method `friendly_id'`
|
33
|
-
|
34
|
-
You probably just added the friendly_id gem. Run `bundle` and restart your
|
35
|
-
Rails app.
|
36
|
-
|
37
|
-
### I'm seeing `uninitialized constant FriendlyId`
|
38
|
-
|
39
|
-
You probably just added the friendly_id gem. Run `bundle` and restart your
|
40
|
-
Rails app.
|
8
|
+
Please only post issues in Github issues for actual bugs.
|
41
9
|
|
10
|
+
I am asking people to do this because the same questions keep getting asked
|
11
|
+
over and over and over again in the issues.
|
data/Changelog.md
CHANGED
@@ -3,6 +3,17 @@
|
|
3
3
|
We would like to think our many {file:Contributors contributors} for
|
4
4
|
suggestions, ideas and improvements to FriendlyId.
|
5
5
|
|
6
|
+
## 5.0.0.rc2 (2013-09-29)
|
7
|
+
|
8
|
+
* When the :finders addon has been included, use it in FriendlyId's internal
|
9
|
+
finds to boost performance.
|
10
|
+
* Use instance methods rather than class methods in migrations.
|
11
|
+
* On find, fall back to super when the primary key is a character type. Thanks
|
12
|
+
to [Jamie Davidson](https://github.com/jhdavids8).
|
13
|
+
* Fix reversion to previously used slug from history table when
|
14
|
+
`should_generate_new_friendly_id?` is overridden.
|
15
|
+
* Fix sequencing of numeric slugs
|
16
|
+
|
6
17
|
## 5.0.0.rc1 (2013-08-28)
|
7
18
|
|
8
19
|
* Removed some outdated tests.
|
data/README.md
CHANGED
@@ -1,5 +1,17 @@
|
|
1
1
|
[](https://travis-ci.org/norman/friendly_id)
|
2
2
|
|
3
|
+
**GETTING HELP**
|
4
|
+
|
5
|
+
Please ask questions on [Stack
|
6
|
+
Overflow](http://stackoverflow.com/questions/tagged/friendly-id) using the
|
7
|
+
"friendly_id" or "friendly-id" tag. Prior to asking, search and see if your
|
8
|
+
question has already been anwered.
|
9
|
+
|
10
|
+
Please only post issues in Github issues for actual bugs.
|
11
|
+
|
12
|
+
I am asking people to do this because the same questions keep getting asked
|
13
|
+
over and over and over again in the issues.
|
14
|
+
|
3
15
|
**VERSION NOTE**
|
4
16
|
|
5
17
|
**Rails 4**:
|
@@ -115,7 +127,7 @@ generated in `config/initializers/friendly_id.rb`. This file contains notes
|
|
115
127
|
describing how to restore (or not) some of the defaults from FriendlyId 4.0.
|
116
128
|
|
117
129
|
If you want to use the `:history` and `:scoped` addons together, you must add a
|
118
|
-
`:scope` column to your
|
130
|
+
`:scope` column to your friendly_id_slugs table and replace the unique index on
|
119
131
|
`:slug` and `:sluggable_type` with a unique index on those two columns, plus
|
120
132
|
the new `:scope` column.
|
121
133
|
|
@@ -151,7 +163,7 @@ cd my_app
|
|
151
163
|
```
|
152
164
|
```ruby
|
153
165
|
# Gemfile
|
154
|
-
gem 'friendly_id', '5.0.0.
|
166
|
+
gem 'friendly_id', '5.0.0.rc2' # Note: You MUST use 5.0.0 or greater for Rails 4.0+
|
155
167
|
```
|
156
168
|
```shell
|
157
169
|
rails generate friendly_id
|
data/bench.rb
CHANGED
@@ -72,6 +72,10 @@ Benchmark.bmbm do |x|
|
|
72
72
|
N.times {transaction {Journalist.create :name => Faker::Name.name}}
|
73
73
|
end
|
74
74
|
|
75
|
+
x.report 'insert (in-table-slug; included FinderMethods)' do
|
76
|
+
N.times {transaction {Restaurant.create :name => Faker::Name.name}}
|
77
|
+
end
|
78
|
+
|
75
79
|
x.report 'insert (external slug)' do
|
76
80
|
N.times {transaction {Manual.create :name => Faker::Name.name}}
|
77
81
|
end
|
data/lib/friendly_id/base.rb
CHANGED
@@ -216,6 +216,10 @@ often better and easier to use {FriendlyId::Slugged slugs}.
|
|
216
216
|
config.model_class = self
|
217
217
|
end
|
218
218
|
end
|
219
|
+
|
220
|
+
def primary_key_type
|
221
|
+
@primary_key_type ||= columns.find(&:primary).type
|
222
|
+
end
|
219
223
|
end
|
220
224
|
|
221
225
|
# Instance methods that will be added to all classes using FriendlyId.
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module FriendlyId
|
2
2
|
|
3
3
|
module FinderMethods
|
4
|
-
|
4
|
+
|
5
5
|
# Finds a record using the given id.
|
6
6
|
#
|
7
7
|
# If the id is "unfriendly", it will call the original find method.
|
@@ -19,7 +19,7 @@ module FriendlyId
|
|
19
19
|
id = args.first
|
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
|
-
return super if
|
22
|
+
return super if potential_primary_key?(id)
|
23
23
|
raise ActiveRecord::RecordNotFound
|
24
24
|
end
|
25
25
|
|
@@ -36,14 +36,25 @@ module FriendlyId
|
|
36
36
|
first_by_friendly_id(id) or raise ActiveRecord::RecordNotFound
|
37
37
|
end
|
38
38
|
|
39
|
+
def exists_by_friendly_id?(id)
|
40
|
+
where(friendly_id_config.query_field => id).exists?
|
41
|
+
end
|
42
|
+
|
39
43
|
private
|
40
44
|
|
41
|
-
def
|
42
|
-
|
45
|
+
def potential_primary_key?(id)
|
46
|
+
case primary_key_type
|
47
|
+
when :integer
|
48
|
+
Integer(id, 10) rescue false
|
49
|
+
when :uuid
|
50
|
+
id.match /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/
|
51
|
+
else
|
52
|
+
true
|
53
|
+
end
|
43
54
|
end
|
44
55
|
|
45
|
-
def
|
46
|
-
where(friendly_id_config.query_field => id).
|
56
|
+
def first_by_friendly_id(id)
|
57
|
+
where(friendly_id_config.query_field => id).first
|
47
58
|
end
|
48
59
|
|
49
60
|
end
|
data/lib/friendly_id/history.rb
CHANGED
@@ -82,16 +82,16 @@ method.
|
|
82
82
|
module HistoryFinderMethods
|
83
83
|
include FinderMethods
|
84
84
|
|
85
|
+
def exists_by_friendly_id?(id)
|
86
|
+
joins(:slugs).where(arel_table[friendly_id_config.query_field].eq(id).or(slug_history_clause(id))).exists?
|
87
|
+
end
|
88
|
+
|
85
89
|
private
|
86
90
|
|
87
91
|
def first_by_friendly_id(id)
|
88
92
|
joins(:slugs).where(slug_history_clause(id)).readonly(false).first
|
89
93
|
end
|
90
94
|
|
91
|
-
def exists_by_friendly_id?(id)
|
92
|
-
joins(:slugs).where(arel_table[friendly_id_config.query_field].eq(id).or(slug_history_clause(id))).exists?
|
93
|
-
end
|
94
|
-
|
95
95
|
def slug_history_clause(id)
|
96
96
|
Slug.arel_table[:sluggable_type].eq(base_class.to_s).and(Slug.arel_table[:slug].eq(id))
|
97
97
|
end
|
@@ -99,6 +99,19 @@ method.
|
|
99
99
|
|
100
100
|
private
|
101
101
|
|
102
|
+
# If we're updating, don't consider historic slugs for the same record
|
103
|
+
# to be conflicts. This will allow a record to revert to a previously
|
104
|
+
# used slug.
|
105
|
+
def scope_for_slug_generator
|
106
|
+
relation = super
|
107
|
+
return relation if new_record?
|
108
|
+
relation = relation.merge(Slug.where('sluggable_id <> ?', id))
|
109
|
+
if friendly_id_config.uses?(:scoped)
|
110
|
+
relation = relation.where(:scope => serialized_scope)
|
111
|
+
end
|
112
|
+
relation
|
113
|
+
end
|
114
|
+
|
102
115
|
def create_slug
|
103
116
|
return unless friendly_id
|
104
117
|
return if slugs.first.try(:slug) == friendly_id
|
@@ -34,6 +34,10 @@ FriendlyId.defaults do |config|
|
|
34
34
|
# all applications, so you must explicity opt-in to this behavior. You can
|
35
35
|
# always also configure it on a per-model basis if you prefer.
|
36
36
|
#
|
37
|
+
# Something else to consider is that using the :finders addon boosts
|
38
|
+
# performance because it will avoid Rails-internal code that makes runtime
|
39
|
+
# calls to `Module.extend`.
|
40
|
+
#
|
37
41
|
# config.use :finders
|
38
42
|
#
|
39
43
|
# ## Slugs
|
@@ -81,4 +85,4 @@ FriendlyId.defaults do |config|
|
|
81
85
|
# text.to_slug.normalize! :transliterations => [:russian, :latin]
|
82
86
|
# end
|
83
87
|
# }
|
84
|
-
end
|
88
|
+
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
class CreateFriendlyIdSlugs < ActiveRecord::Migration
|
2
|
-
|
3
|
-
def self.up
|
2
|
+
def change
|
4
3
|
create_table :friendly_id_slugs do |t|
|
5
4
|
t.string :slug, :null => false
|
6
5
|
t.integer :sluggable_id, :null => false
|
@@ -13,8 +12,4 @@ class CreateFriendlyIdSlugs < ActiveRecord::Migration
|
|
13
12
|
add_index :friendly_id_slugs, [:slug, :sluggable_type, :scope], :unique => true
|
14
13
|
add_index :friendly_id_slugs, :sluggable_type
|
15
14
|
end
|
16
|
-
|
17
|
-
def self.down
|
18
|
-
drop_table :friendly_id_slugs
|
19
|
-
end
|
20
15
|
end
|
data/lib/friendly_id/slugged.rb
CHANGED
@@ -297,13 +297,20 @@ Github issue](https://github.com/norman/friendly_id/issues/185) for discussion.
|
|
297
297
|
end
|
298
298
|
private :set_slug
|
299
299
|
|
300
|
-
def
|
301
|
-
scope = self.class.base_class.unscoped
|
300
|
+
def scope_for_slug_generator
|
301
|
+
scope = self.class.base_class.unscoped
|
302
|
+
scope = scope.friendly unless friendly_id_config.uses? :finders
|
303
|
+
|
302
304
|
if changed.include?(friendly_id_config.slug_column)
|
303
305
|
column = self.class.quoted_table_name + '.' + self.class.quoted_primary_key
|
304
306
|
scope = scope.where("#{column} <> ?", send(self.class.primary_key))
|
305
307
|
end
|
306
|
-
|
308
|
+
scope
|
309
|
+
end
|
310
|
+
private :scope_for_slug_generator
|
311
|
+
|
312
|
+
def slug_generator
|
313
|
+
friendly_id_config.slug_generator_class.new(scope_for_slug_generator)
|
307
314
|
end
|
308
315
|
private :slug_generator
|
309
316
|
|
data/lib/friendly_id/version.rb
CHANGED
data/test/helper.rb
CHANGED
data/test/history_test.rb
CHANGED
@@ -62,6 +62,14 @@ class HistoryTest < MiniTest::Unit::TestCase
|
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
65
|
+
test "should not be read only when found by slug" do
|
66
|
+
with_instance_of(model_class) do |record|
|
67
|
+
refute model_class.friendly.find(record.friendly_id).readonly?
|
68
|
+
assert record.update_attribute :name, 'foo'
|
69
|
+
assert record.update_attributes name: 'foo'
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
65
73
|
test "should not be read only when found by old slug" do
|
66
74
|
with_instance_of(model_class) do |record|
|
67
75
|
old_friendly_id = record.friendly_id
|
@@ -74,10 +82,13 @@ class HistoryTest < MiniTest::Unit::TestCase
|
|
74
82
|
test "should handle renames" do
|
75
83
|
with_instance_of(model_class) do |record|
|
76
84
|
record.name = 'x'
|
85
|
+
record.slug = nil
|
77
86
|
assert record.save
|
78
87
|
record.name = 'y'
|
88
|
+
record.slug = nil
|
79
89
|
assert record.save
|
80
90
|
record.name = 'x'
|
91
|
+
record.slug = nil
|
81
92
|
assert record.save
|
82
93
|
end
|
83
94
|
end
|
@@ -122,6 +133,32 @@ class HistoryTest < MiniTest::Unit::TestCase
|
|
122
133
|
end
|
123
134
|
end
|
124
135
|
|
136
|
+
class HistoryTestWithAutomaticSlugRegeneration < HistoryTest
|
137
|
+
class Manual < ActiveRecord::Base
|
138
|
+
extend FriendlyId
|
139
|
+
friendly_id :name, :use => [:slugged, :history]
|
140
|
+
|
141
|
+
def should_generate_new_friendly_id?
|
142
|
+
slug.blank? or name_changed?
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def model_class
|
147
|
+
Manual
|
148
|
+
end
|
149
|
+
|
150
|
+
test 'should allow reversion back to a previously used slug' do
|
151
|
+
with_instance_of(model_class, name: 'foo') do |record|
|
152
|
+
record.name = 'bar'
|
153
|
+
record.save!
|
154
|
+
assert_equal 'bar', record.friendly_id
|
155
|
+
record.name = 'foo'
|
156
|
+
record.save!
|
157
|
+
assert_equal 'foo', record.friendly_id
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
125
162
|
class HistoryTestWithSti < HistoryTest
|
126
163
|
class Journalist < ActiveRecord::Base
|
127
164
|
extend FriendlyId
|
data/test/schema.rb
CHANGED
@@ -14,7 +14,7 @@ module FriendlyId
|
|
14
14
|
def up
|
15
15
|
# TODO: use schema version to avoid ugly hacks like this
|
16
16
|
return if @done
|
17
|
-
CreateFriendlyIdSlugs.up
|
17
|
+
CreateFriendlyIdSlugs.migrate :up
|
18
18
|
|
19
19
|
tables.each do |table_name|
|
20
20
|
create_table table_name do |t|
|
@@ -23,6 +23,15 @@ module FriendlyId
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
+
tables_with_string_primary_key.each do |table_name|
|
27
|
+
create_table table_name, primary_key: :string_key, id: false do |t|
|
28
|
+
t.string :name
|
29
|
+
t.string :string_key, null: false
|
30
|
+
t.string :slug
|
31
|
+
end
|
32
|
+
add_index table_name, :slug, unique: true
|
33
|
+
end
|
34
|
+
|
26
35
|
slugged_tables.each do |table_name|
|
27
36
|
add_column table_name, :slug, :string
|
28
37
|
add_index table_name, :slug, :unique => true
|
@@ -64,6 +73,10 @@ module FriendlyId
|
|
64
73
|
%w[journalists articles novelists novels manuals]
|
65
74
|
end
|
66
75
|
|
76
|
+
def tables_with_string_primary_key
|
77
|
+
["menu_items"]
|
78
|
+
end
|
79
|
+
|
67
80
|
def scoped_tables
|
68
81
|
["restaurants"]
|
69
82
|
end
|
data/test/slugged_test.rb
CHANGED
@@ -134,6 +134,13 @@ class SlugGeneratorTest < MiniTest::Unit::TestCase
|
|
134
134
|
end
|
135
135
|
end
|
136
136
|
|
137
|
+
test "should correctly sequence numeric slugs" do
|
138
|
+
transaction do
|
139
|
+
n2 = 2.times.map {Article.create :name => '123'}.last
|
140
|
+
assert_match(/\A123-.*/, n2.friendly_id)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
137
144
|
end
|
138
145
|
|
139
146
|
class SlugSeparatorTest < MiniTest::Unit::TestCase
|
@@ -205,6 +212,40 @@ class DefaultScopeTest < MiniTest::Unit::TestCase
|
|
205
212
|
end
|
206
213
|
end
|
207
214
|
|
215
|
+
class StringAsPrimaryKeyFindTest < MiniTest::Unit::TestCase
|
216
|
+
include FriendlyId::Test
|
217
|
+
|
218
|
+
class MenuItem < ActiveRecord::Base
|
219
|
+
extend FriendlyId
|
220
|
+
friendly_id :name, :use => :slugged
|
221
|
+
before_create :init_primary_key
|
222
|
+
|
223
|
+
def self.primary_key
|
224
|
+
"string_key"
|
225
|
+
end
|
226
|
+
|
227
|
+
private
|
228
|
+
def init_primary_key
|
229
|
+
self.string_key = SecureRandom.uuid
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def model_class
|
234
|
+
MenuItem
|
235
|
+
end
|
236
|
+
|
237
|
+
test "should have a string as a primary key" do
|
238
|
+
assert_equal model_class.primary_key, "string_key"
|
239
|
+
assert_equal model_class.columns.find(&:primary).name, "string_key"
|
240
|
+
end
|
241
|
+
|
242
|
+
test "should be findable by the string primary key" do
|
243
|
+
with_instance_of(model_class) do |record|
|
244
|
+
assert model_class.friendly.find record.id
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
208
249
|
class UnderscoreAsSequenceSeparatorRegressionTest < MiniTest::Unit::TestCase
|
209
250
|
include FriendlyId::Test
|
210
251
|
|
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.0.0.
|
4
|
+
version: 5.0.0.rc2
|
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: 2013-
|
12
|
+
date: 2013-09-29 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|