friendly_id 5.2.2 → 5.4.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 +5 -5
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +2 -0
- data/.github/stale.yml +17 -0
- data/.github/workflows/test.yml +60 -0
- data/Changelog.md +39 -1
- data/Gemfile +3 -0
- data/README.md +54 -164
- data/Rakefile +2 -2
- data/UPGRADING.md +115 -0
- data/certs/parndt.pem +25 -0
- data/friendly_id.gemspec +9 -5
- data/gemfiles/Gemfile.rails-5.0.rb +2 -2
- data/gemfiles/{Gemfile.rails-4.2.rb → Gemfile.rails-5.1.rb} +4 -5
- data/gemfiles/{Gemfile.rails-4.1.rb → Gemfile.rails-5.2.rb} +5 -7
- data/gemfiles/{Gemfile.rails-4.0.rb → Gemfile.rails-6.0.rb} +5 -8
- data/lib/friendly_id/base.rb +4 -8
- data/lib/friendly_id/candidates.rb +0 -2
- data/lib/friendly_id/configuration.rb +3 -2
- data/lib/friendly_id/finder_methods.rb +18 -7
- data/lib/friendly_id/finders.rb +1 -1
- data/lib/friendly_id/history.rb +21 -12
- data/lib/friendly_id/initializer.rb +11 -0
- data/lib/friendly_id/migration.rb +9 -3
- data/lib/friendly_id/object_utils.rb +9 -2
- data/lib/friendly_id/reserved.rb +1 -0
- data/lib/friendly_id/scoped.rb +9 -2
- data/lib/friendly_id/sequentially_slugged.rb +12 -2
- data/lib/friendly_id/slug.rb +4 -0
- data/lib/friendly_id/slug_generator.rb +6 -1
- data/lib/friendly_id/slugged.rb +3 -3
- data/lib/friendly_id/version.rb +1 -1
- data/test/databases.yml +6 -4
- data/test/finders_test.rb +24 -0
- data/test/helper.rb +13 -3
- data/test/history_test.rb +86 -7
- data/test/numeric_slug_test.rb +31 -0
- data/test/object_utils_test.rb +5 -3
- data/test/reserved_test.rb +10 -0
- data/test/schema.rb +19 -2
- data/test/scoped_test.rb +13 -0
- data/test/sequentially_slugged_test.rb +59 -0
- data/test/shared.rb +4 -4
- data/test/simple_i18n_test.rb +2 -2
- data/test/slugged_test.rb +168 -4
- metadata +48 -19
- metadata.gz.sig +0 -0
- data/.travis.yml +0 -40
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class NumericSlugTest < TestCaseClass
|
4
|
+
include FriendlyId::Test
|
5
|
+
include FriendlyId::Test::Shared::Core
|
6
|
+
|
7
|
+
def model_class
|
8
|
+
Article
|
9
|
+
end
|
10
|
+
|
11
|
+
test "should generate numeric slugs" do
|
12
|
+
transaction do
|
13
|
+
record = model_class.create! :name => "123"
|
14
|
+
assert_equal "123", record.slug
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
test "should find by numeric slug" do
|
19
|
+
transaction do
|
20
|
+
record = model_class.create! :name => "123"
|
21
|
+
assert_equal model_class.friendly.find("123").id, record.id
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
test "should exist? by numeric slug" do
|
26
|
+
transaction do
|
27
|
+
record = model_class.create! :name => "123"
|
28
|
+
assert model_class.friendly.exists?("123")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/test/object_utils_test.rb
CHANGED
@@ -14,14 +14,16 @@ class ObjectUtilsTest < TestCaseClass
|
|
14
14
|
end
|
15
15
|
|
16
16
|
test "numeric strings are neither friendly nor unfriendly" do
|
17
|
-
|
18
|
-
|
17
|
+
assert_nil "1".friendly_id?
|
18
|
+
assert_nil "1".unfriendly_id?
|
19
19
|
end
|
20
20
|
|
21
21
|
test "ActiveRecord::Base instances should be unfriendly_ids" do
|
22
|
+
FriendlyId.mark_as_unfriendly(ActiveRecord::Base)
|
23
|
+
|
22
24
|
model_class = Class.new(ActiveRecord::Base) do
|
23
25
|
self.table_name = "authors"
|
24
26
|
end
|
25
27
|
assert model_class.new.unfriendly_id?
|
26
28
|
end
|
27
|
-
end
|
29
|
+
end
|
data/test/reserved_test.rb
CHANGED
@@ -62,4 +62,14 @@ class ReservedTest < TestCaseClass
|
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
65
|
+
test "should optionally treat reserved words as conflict" do
|
66
|
+
klass = Class.new(model_class) do
|
67
|
+
friendly_id :slug_candidates, :use => [:slugged, :reserved], :reserved_words => %w(new edit), :treat_reserved_as_conflict => true
|
68
|
+
end
|
69
|
+
|
70
|
+
with_instance_of(klass, name: 'new') do |record|
|
71
|
+
assert_match(/new-([0-9a-z]+\-){4}[0-9a-z]+\z/, record.slug)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
65
75
|
end
|
data/test/schema.rb
CHANGED
@@ -2,7 +2,14 @@ require "friendly_id/migration"
|
|
2
2
|
|
3
3
|
module FriendlyId
|
4
4
|
module Test
|
5
|
-
|
5
|
+
migration_class =
|
6
|
+
if ActiveRecord::VERSION::MAJOR >= 5
|
7
|
+
ActiveRecord::Migration[4.2]
|
8
|
+
else
|
9
|
+
ActiveRecord::Migration
|
10
|
+
end
|
11
|
+
|
12
|
+
class Schema < migration_class
|
6
13
|
class << self
|
7
14
|
def down
|
8
15
|
CreateFriendlyIdSlugs.down
|
@@ -41,6 +48,12 @@ module FriendlyId
|
|
41
48
|
add_column table_name, :slug, :string
|
42
49
|
end
|
43
50
|
|
51
|
+
paranoid_tables.each do |table_name|
|
52
|
+
add_column table_name, :slug, :string
|
53
|
+
add_column table_name, :deleted_at, :datetime
|
54
|
+
add_index table_name, :deleted_at
|
55
|
+
end
|
56
|
+
|
44
57
|
# This will be used to test scopes
|
45
58
|
add_column :novels, :novelist_id, :integer
|
46
59
|
add_column :novels, :publisher_id, :integer
|
@@ -78,6 +91,10 @@ module FriendlyId
|
|
78
91
|
%w[journalists articles novelists novels manuals cities]
|
79
92
|
end
|
80
93
|
|
94
|
+
def paranoid_tables
|
95
|
+
["paranoid_records"]
|
96
|
+
end
|
97
|
+
|
81
98
|
def tables_with_uuid_primary_key
|
82
99
|
["menu_items"]
|
83
100
|
end
|
@@ -91,7 +108,7 @@ module FriendlyId
|
|
91
108
|
end
|
92
109
|
|
93
110
|
def tables
|
94
|
-
simple_tables + slugged_tables + scoped_tables
|
111
|
+
simple_tables + slugged_tables + scoped_tables + paranoid_tables
|
95
112
|
end
|
96
113
|
end
|
97
114
|
end
|
data/test/scoped_test.rb
CHANGED
@@ -81,4 +81,17 @@ class ScopedTest < TestCaseClass
|
|
81
81
|
end
|
82
82
|
end
|
83
83
|
|
84
|
+
test "should generate new slug when scope changes" do
|
85
|
+
transaction do
|
86
|
+
novelist = Novelist.create! :name => "a"
|
87
|
+
publisher = Publisher.create! :name => "b"
|
88
|
+
novel1 = Novel.create! :name => "c", :novelist => novelist, :publisher => publisher
|
89
|
+
novel2 = Novel.create! :name => "c", :novelist => novelist, :publisher => Publisher.create(:name => "d")
|
90
|
+
assert_equal novel1.friendly_id, novel2.friendly_id
|
91
|
+
novel2.publisher = publisher
|
92
|
+
novel2.save!
|
93
|
+
assert novel2.friendly_id != novel1.friendly_id
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
84
97
|
end
|
@@ -137,4 +137,63 @@ class SequentiallySluggedTestWithHistory < TestCaseClass
|
|
137
137
|
assert_equal 'test-name-2', record2.slug
|
138
138
|
end
|
139
139
|
end
|
140
|
+
|
141
|
+
test "should work with regeneration with history when 2 slugs already exists and the second is changed" do
|
142
|
+
transaction do
|
143
|
+
record1 = model_class.create! :name => "Test name"
|
144
|
+
record2 = model_class.create! :name => "Test name"
|
145
|
+
record3 = model_class.create! :name => "Another test name"
|
146
|
+
assert_equal 'test-name', record1.slug
|
147
|
+
assert_equal 'test-name-2', record2.slug
|
148
|
+
assert_equal 'another-test-name', record3.slug
|
149
|
+
|
150
|
+
record2.name = "One more test name"
|
151
|
+
record2.slug = nil
|
152
|
+
record2.save!
|
153
|
+
assert_equal 'one-more-test-name', record2.slug
|
154
|
+
|
155
|
+
record3.name = "Test name"
|
156
|
+
record3.slug = nil
|
157
|
+
record3.save!
|
158
|
+
assert_equal 'test-name-3', record3.slug
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
class City < ActiveRecord::Base
|
164
|
+
has_many :restaurants
|
165
|
+
end
|
166
|
+
|
167
|
+
class Restaurant < ActiveRecord::Base
|
168
|
+
extend FriendlyId
|
169
|
+
belongs_to :city
|
170
|
+
friendly_id :name, :use => [:sequentially_slugged, :scoped, :history], :scope => :city
|
171
|
+
end
|
172
|
+
|
173
|
+
class SequentiallySluggedTestWithScopedHistory < TestCaseClass
|
174
|
+
include FriendlyId::Test
|
175
|
+
include FriendlyId::Test::Shared::Core
|
176
|
+
|
177
|
+
def model_class
|
178
|
+
Restaurant
|
179
|
+
end
|
180
|
+
|
181
|
+
test "should work with regeneration with scoped history" do
|
182
|
+
transaction do
|
183
|
+
city1 = City.create!
|
184
|
+
city2 = City.create!
|
185
|
+
record1 = model_class.create! :name => "Test name", :city => city1
|
186
|
+
record2 = model_class.create! :name => "Test name", :city => city1
|
187
|
+
|
188
|
+
assert_equal 'test-name', record1.slug
|
189
|
+
assert_equal 'test-name-2', record2.slug
|
190
|
+
|
191
|
+
record2.name = 'Another test name'
|
192
|
+
record2.slug = nil
|
193
|
+
record2.save!
|
194
|
+
|
195
|
+
record3 = model_class.create! :name => "Test name", :city => city1
|
196
|
+
assert_equal 'test-name-3', record3.slug
|
197
|
+
end
|
198
|
+
end
|
140
199
|
end
|
data/test/shared.rb
CHANGED
@@ -62,9 +62,9 @@ module FriendlyId
|
|
62
62
|
my_model_class = Class.new(model_class)
|
63
63
|
self.class.const_set("Foo", my_model_class)
|
64
64
|
with_instance_of my_model_class do |record|
|
65
|
-
record.
|
65
|
+
record.update my_model_class.friendly_id_config.slug_column => nil
|
66
66
|
record = my_model_class.friendly.find(record.id)
|
67
|
-
record.class.validate Proc.new {errors
|
67
|
+
record.class.validate Proc.new {errors.add(:name, "FAIL")}
|
68
68
|
record.save
|
69
69
|
assert_equal record.to_param, record.friendly_id
|
70
70
|
end
|
@@ -132,7 +132,7 @@ module FriendlyId
|
|
132
132
|
test "updating record's other values should not change the friendly_id" do
|
133
133
|
with_instance_of model_class do |record|
|
134
134
|
old = record.friendly_id
|
135
|
-
record.
|
135
|
+
record.update! active: false
|
136
136
|
assert model_class.friendly.find old
|
137
137
|
end
|
138
138
|
end
|
@@ -174,7 +174,7 @@ module FriendlyId
|
|
174
174
|
end
|
175
175
|
|
176
176
|
test "should return nil for to_param with a new record" do
|
177
|
-
|
177
|
+
assert_nil model_class.new.to_param
|
178
178
|
end
|
179
179
|
end
|
180
180
|
end
|
data/test/simple_i18n_test.rb
CHANGED
@@ -91,14 +91,14 @@ class SimpleI18nTest < TestCaseClass
|
|
91
91
|
class RegressionTest < TestCaseClass
|
92
92
|
include FriendlyId::Test
|
93
93
|
|
94
|
-
test "should not overwrite other locale's slugs on
|
94
|
+
test "should not overwrite other locale's slugs on update" do
|
95
95
|
transaction do
|
96
96
|
journalist = Journalist.create!(:name => "John Smith")
|
97
97
|
journalist.set_friendly_id("Juan Fulano", :es)
|
98
98
|
journalist.save!
|
99
99
|
assert_equal "john-smith", journalist.to_param
|
100
100
|
journalist.slug = nil
|
101
|
-
journalist.
|
101
|
+
journalist.update :name => "Johnny Smith"
|
102
102
|
assert_equal "johnny-smith", journalist.to_param
|
103
103
|
I18n.with_locale(:es) do
|
104
104
|
assert_equal "juan-fulano", journalist.to_param
|
data/test/slugged_test.rb
CHANGED
@@ -92,7 +92,7 @@ class SluggedTest < TestCaseClass
|
|
92
92
|
end
|
93
93
|
end
|
94
94
|
|
95
|
-
test "should
|
95
|
+
test "should set slug on create if unrelated validations fail" do
|
96
96
|
klass = Class.new model_class do
|
97
97
|
validates_presence_of :active
|
98
98
|
friendly_id :name, :use => :slugged
|
@@ -106,11 +106,30 @@ class SluggedTest < TestCaseClass
|
|
106
106
|
instance = klass.new :name => 'foo'
|
107
107
|
refute instance.save
|
108
108
|
refute instance.valid?
|
109
|
+
assert_equal 'foo', instance.slug
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
test "should not set slug on create if slug validation fails" do
|
114
|
+
klass = Class.new model_class do
|
115
|
+
validates_presence_of :active
|
116
|
+
validates_length_of :slug, :minimum => 2
|
117
|
+
friendly_id :name, :use => :slugged
|
118
|
+
|
119
|
+
def self.name
|
120
|
+
"Journalist"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
transaction do
|
125
|
+
instance = klass.new :name => 'x'
|
126
|
+
refute instance.save
|
127
|
+
refute instance.valid?
|
109
128
|
assert_nil instance.slug
|
110
129
|
end
|
111
130
|
end
|
112
131
|
|
113
|
-
test "should
|
132
|
+
test "should set slug on create if unrelated validations fail with custom slug_column" do
|
114
133
|
klass = Class.new(ActiveRecord::Base) do
|
115
134
|
self.table_name = 'authors'
|
116
135
|
extend FriendlyId
|
@@ -126,11 +145,31 @@ class SluggedTest < TestCaseClass
|
|
126
145
|
instance = klass.new :name => 'foo'
|
127
146
|
refute instance.save
|
128
147
|
refute instance.valid?
|
148
|
+
assert_equal 'foo', instance.subdomain
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
test "should not set slug on create if custom slug column validations fail" do
|
153
|
+
klass = Class.new(ActiveRecord::Base) do
|
154
|
+
self.table_name = 'authors'
|
155
|
+
extend FriendlyId
|
156
|
+
validates_length_of :subdomain, :minimum => 2
|
157
|
+
friendly_id :name, :use => :slugged, :slug_column => :subdomain
|
158
|
+
|
159
|
+
def self.name
|
160
|
+
"Author"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
transaction do
|
165
|
+
instance = klass.new :name => 'x'
|
166
|
+
refute instance.save
|
167
|
+
refute instance.valid?
|
129
168
|
assert_nil instance.subdomain
|
130
169
|
end
|
131
170
|
end
|
132
171
|
|
133
|
-
test "should
|
172
|
+
test "should keep new slug on save if unrelated validations fail" do
|
134
173
|
klass = Class.new model_class do
|
135
174
|
validates_presence_of :active
|
136
175
|
friendly_id :name, :use => :slugged
|
@@ -149,10 +188,31 @@ class SluggedTest < TestCaseClass
|
|
149
188
|
instance.active = nil
|
150
189
|
refute instance.save
|
151
190
|
refute instance.valid?
|
152
|
-
assert_equal '
|
191
|
+
assert_equal 'foobar', instance.slug
|
153
192
|
end
|
154
193
|
end
|
155
194
|
|
195
|
+
test "should not update slug on save if slug validations fail" do
|
196
|
+
klass = Class.new model_class do
|
197
|
+
validates_length_of :slug, :minimum => 2
|
198
|
+
friendly_id :name, :use => :slugged
|
199
|
+
|
200
|
+
def self.name
|
201
|
+
"Journalist"
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
transaction do
|
206
|
+
instance = klass.new :name => 'foo', :active => true
|
207
|
+
assert instance.save
|
208
|
+
assert instance.valid?
|
209
|
+
instance.name = 'x'
|
210
|
+
instance.slug = nil
|
211
|
+
instance.active = nil
|
212
|
+
refute instance.save
|
213
|
+
assert_equal 'foo', instance.slug
|
214
|
+
end
|
215
|
+
end
|
156
216
|
end
|
157
217
|
|
158
218
|
class SlugGeneratorTest < TestCaseClass
|
@@ -419,6 +479,110 @@ class FailedValidationAfterUpdateRegressionTest < TestCaseClass
|
|
419
479
|
|
420
480
|
end
|
421
481
|
|
482
|
+
class ToParamTest < TestCaseClass
|
483
|
+
|
484
|
+
include FriendlyId::Test
|
485
|
+
|
486
|
+
class Journalist < ActiveRecord::Base
|
487
|
+
extend FriendlyId
|
488
|
+
validates_presence_of :active
|
489
|
+
validates_length_of :slug, :minimum => 2
|
490
|
+
friendly_id :name, :use => :slugged
|
491
|
+
|
492
|
+
attr_accessor :to_param_in_callback
|
493
|
+
|
494
|
+
after_save do
|
495
|
+
self.to_param_in_callback = to_param
|
496
|
+
end
|
497
|
+
end
|
498
|
+
|
499
|
+
test "to_param should return nil if record is unpersisted" do
|
500
|
+
assert_nil Journalist.new.to_param
|
501
|
+
end
|
502
|
+
|
503
|
+
test "to_param should return original slug if record failed validation" do
|
504
|
+
journalist = Journalist.new :name => 'Clark Kent', :active => nil
|
505
|
+
refute journalist.save
|
506
|
+
assert_equal 'clark-kent', journalist.to_param
|
507
|
+
end
|
508
|
+
|
509
|
+
test "to_param should clear slug attributes if slug attribute fails validation" do
|
510
|
+
journalist = Journalist.new :name => 'x', :active => true
|
511
|
+
refute journalist.save
|
512
|
+
assert_nil journalist.to_param
|
513
|
+
end
|
514
|
+
|
515
|
+
test "to_param should clear slug attribute if slug attribute fails validation and unrelated validation fails" do
|
516
|
+
journalist = Journalist.new :name => 'x', :active => nil
|
517
|
+
refute journalist.save
|
518
|
+
assert_nil journalist.to_param
|
519
|
+
end
|
520
|
+
|
521
|
+
test "to_param should use slugged attribute if record saved successfully" do
|
522
|
+
transaction do
|
523
|
+
journalist = Journalist.new :name => 'Clark Kent', :active => true
|
524
|
+
assert journalist.save
|
525
|
+
assert_equal 'clark-kent', journalist.to_param
|
526
|
+
end
|
527
|
+
end
|
528
|
+
|
529
|
+
test "to_param should use new slug if existing record changes but fails to save" do
|
530
|
+
transaction do
|
531
|
+
journalist = Journalist.new :name => 'Clark Kent', :active => true
|
532
|
+
assert journalist.save
|
533
|
+
journalist.name = 'Superman'
|
534
|
+
journalist.slug = nil
|
535
|
+
journalist.active = nil
|
536
|
+
refute journalist.save
|
537
|
+
assert_equal 'superman', journalist.to_param
|
538
|
+
end
|
539
|
+
end
|
540
|
+
|
541
|
+
test "to_param should use original slug if new slug attribute is not valid" do
|
542
|
+
transaction do
|
543
|
+
journalist = Journalist.new :name => 'Clark Kent', :active => true
|
544
|
+
assert journalist.save
|
545
|
+
journalist.name = 'x'
|
546
|
+
journalist.slug = nil
|
547
|
+
journalist.active = nil
|
548
|
+
refute journalist.save
|
549
|
+
assert_equal 'clark-kent', journalist.to_param
|
550
|
+
end
|
551
|
+
end
|
552
|
+
|
553
|
+
test "to_param should use new slug if existing record changes successfully" do
|
554
|
+
transaction do
|
555
|
+
journalist = Journalist.new :name => 'Clark Kent', :active => true
|
556
|
+
assert journalist.save
|
557
|
+
journalist.name = 'Superman'
|
558
|
+
journalist.slug = nil
|
559
|
+
assert journalist.save
|
560
|
+
assert_equal 'superman', journalist.to_param
|
561
|
+
end
|
562
|
+
end
|
563
|
+
|
564
|
+
test "to_param should use new slug within callbacks if new record is saved successfully" do
|
565
|
+
transaction do
|
566
|
+
journalist = Journalist.new :name => 'Clark Kent', :active => true
|
567
|
+
assert journalist.save
|
568
|
+
assert_equal 'clark-kent', journalist.to_param_in_callback, "value of to_param in callback should use the new slug value"
|
569
|
+
end
|
570
|
+
end
|
571
|
+
|
572
|
+
test "to_param should use new slug within callbacks if existing record changes successfully" do
|
573
|
+
transaction do
|
574
|
+
journalist = Journalist.new :name => 'Clark Kent', :active => true
|
575
|
+
assert journalist.save
|
576
|
+
assert journalist.valid?
|
577
|
+
journalist.name = 'Superman'
|
578
|
+
journalist.slug = nil
|
579
|
+
assert journalist.save, "save should be successful"
|
580
|
+
assert_equal 'superman', journalist.to_param_in_callback, "value of to_param in callback should use the new slug value"
|
581
|
+
end
|
582
|
+
end
|
583
|
+
|
584
|
+
end
|
585
|
+
|
422
586
|
class ConfigurableRoutesTest < TestCaseClass
|
423
587
|
include FriendlyId::Test
|
424
588
|
|