friendly_id 5.6.0 → 5.7.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.
@@ -1,38 +0,0 @@
1
- require "helper"
2
- require "rails/generators"
3
- require "generators/friendly_id_generator"
4
-
5
- class FriendlyIdGeneratorTest < Rails::Generators::TestCase
6
- tests FriendlyIdGenerator
7
- destination File.expand_path("../../tmp", __FILE__)
8
-
9
- setup :prepare_destination
10
-
11
- test "should generate a migration" do
12
- run_generator
13
- assert_migration "db/migrate/create_friendly_id_slugs"
14
- ensure
15
- FileUtils.rm_rf destination_root
16
- end
17
-
18
- test "should skip the migration when told to do so" do
19
- run_generator ["--skip-migration"]
20
- assert_no_migration "db/migrate/create_friendly_id_slugs"
21
- ensure
22
- FileUtils.rm_rf destination_root
23
- end
24
-
25
- test "should generate an initializer" do
26
- run_generator
27
- assert_file "config/initializers/friendly_id.rb"
28
- ensure
29
- FileUtils.rm_rf destination_root
30
- end
31
-
32
- test "should skip the initializer when told to do so" do
33
- run_generator ["--skip-initializer"]
34
- assert_no_file "config/initializers/friendly_id.rb"
35
- ensure
36
- FileUtils.rm_rf destination_root
37
- end
38
- end
data/test/helper.rb DELETED
@@ -1,125 +0,0 @@
1
- require "bundler/setup"
2
-
3
- if ENV["COVERALLS"] || ENV["COVERAGE"]
4
- require "simplecov"
5
- if ENV["COVERALLS"]
6
- require "coveralls"
7
- SimpleCov.formatter = Coveralls::SimpleCov::Formatter
8
- end
9
- SimpleCov.start do
10
- add_filter "test"
11
- add_filter "friendly_id/migration"
12
- end
13
- end
14
-
15
- begin
16
- require "minitest"
17
- rescue LoadError
18
- require "minitest/unit"
19
- end
20
-
21
- begin
22
- TestCaseClass = Minitest::Test
23
- rescue NameError
24
- TestCaseClass = Minitest::Unit::TestCase
25
- end
26
-
27
- require "mocha/minitest"
28
- require "active_record"
29
- require "active_support/core_ext/time/conversions"
30
- require "erb"
31
-
32
- I18n.enforce_available_locales = false
33
-
34
- require "friendly_id"
35
-
36
- # If you want to see the ActiveRecord log, invoke the tests using `rake test LOG=true`
37
- if ENV["LOG"]
38
- require "logger"
39
- ActiveRecord::Base.logger = Logger.new($stdout)
40
- end
41
-
42
- if ActiveSupport::VERSION::STRING >= "4.2"
43
- ActiveSupport.test_order = :random
44
- end
45
-
46
- module FriendlyId
47
- module Test
48
- def self.included(base)
49
- if Minitest.respond_to?(:autorun)
50
- Minitest.autorun
51
- else
52
- require "minitest/autorun"
53
- end
54
- rescue LoadError
55
- end
56
-
57
- def transaction
58
- ActiveRecord::Base.transaction do
59
- yield
60
-
61
- raise ActiveRecord::Rollback
62
- end
63
- end
64
-
65
- def with_instance_of(*args)
66
- model_class = args.shift
67
- args[0] ||= {name: "a b c"}
68
- transaction { yield model_class.create!(*args) }
69
- end
70
-
71
- module Database
72
- extend self
73
-
74
- def connect
75
- version = ActiveRecord::VERSION::STRING
76
- engine = begin
77
- RUBY_ENGINE
78
- rescue
79
- "ruby"
80
- end
81
-
82
- ActiveRecord::Base.establish_connection config[driver]
83
- message = "Using #{engine} #{RUBY_VERSION} AR #{version} with #{driver}"
84
-
85
- puts "-" * 72
86
- if in_memory?
87
- ActiveRecord::Migration.verbose = false
88
- Schema.migrate :up
89
- puts "#{message} (in-memory)"
90
- else
91
- puts message
92
- end
93
- end
94
-
95
- def config
96
- @config ||= YAML.safe_load(
97
- ERB.new(
98
- File.read(File.expand_path("../databases.yml", __FILE__))
99
- ).result
100
- )
101
- end
102
-
103
- def driver
104
- db_driver = ENV.fetch("DB", "sqlite3").downcase
105
- db_driver = "postgres" if %w[postgresql pg].include?(db_driver)
106
- db_driver
107
- end
108
-
109
- def in_memory?
110
- config[driver]["database"] == ":memory:"
111
- end
112
- end
113
- end
114
- end
115
-
116
- class Module
117
- def test(name, &block)
118
- define_method("test_#{name.gsub(/[^a-z0-9']/i, "_")}".to_sym, &block)
119
- end
120
- end
121
-
122
- require "schema"
123
- require "shared"
124
- FriendlyId::Test::Database.connect
125
- at_exit { ActiveRecord::Base.connection.disconnect! }
data/test/history_test.rb DELETED
@@ -1,434 +0,0 @@
1
- require "helper"
2
-
3
- class HistoryTest < TestCaseClass
4
- include FriendlyId::Test
5
- include FriendlyId::Test::Shared::Core
6
-
7
- class Manual < ActiveRecord::Base
8
- extend FriendlyId
9
- friendly_id :name, use: [:slugged, :history]
10
- end
11
-
12
- def model_class
13
- Manual
14
- end
15
-
16
- test "should insert record in slugs table on create" do
17
- with_instance_of(model_class) { |record| assert record.slugs.any? }
18
- end
19
-
20
- test "should not create new slug record if friendly_id is not changed" do
21
- with_instance_of(model_class) do |record|
22
- record.active = true
23
- record.save!
24
- assert_equal 1, FriendlyId::Slug.count
25
- end
26
- end
27
-
28
- test "should create new slug record when friendly_id changes" do
29
- with_instance_of(model_class) do |record|
30
- record.name = record.name + "b"
31
- record.slug = nil
32
- record.save!
33
- assert_equal 2, FriendlyId::Slug.count
34
- end
35
- end
36
-
37
- test "should be findable by old slugs" do
38
- with_instance_of(model_class) do |record|
39
- old_friendly_id = record.friendly_id
40
- record.name = record.name + "b"
41
- record.slug = nil
42
- record.save!
43
- begin
44
- assert model_class.friendly.find(old_friendly_id)
45
- assert model_class.friendly.exists?(old_friendly_id), "should exist? by old id"
46
- rescue ActiveRecord::RecordNotFound
47
- flunk "Could not find record by old id"
48
- end
49
- end
50
- end
51
-
52
- test "should create slug records on each change" do
53
- transaction do
54
- model_class.create! name: "hello"
55
- assert_equal 1, FriendlyId::Slug.count
56
-
57
- record = model_class.friendly.find("hello")
58
- record.name = "hello again"
59
- record.slug = nil
60
- record.save!
61
- assert_equal 2, FriendlyId::Slug.count
62
- end
63
- end
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 name: "foo"
69
- end
70
- end
71
-
72
- test "should not be read only when found by old slug" do
73
- with_instance_of(model_class) do |record|
74
- old_friendly_id = record.friendly_id
75
- record.name = record.name + "b"
76
- record.save!
77
- assert !model_class.friendly.find(old_friendly_id).readonly?
78
- end
79
- end
80
-
81
- test "should handle renames" do
82
- with_instance_of(model_class) do |record|
83
- record.name = "x"
84
- record.slug = nil
85
- assert record.save
86
- record.name = "y"
87
- record.slug = nil
88
- assert record.save
89
- record.name = "x"
90
- record.slug = nil
91
- assert record.save
92
- end
93
- end
94
-
95
- test "should maintain history even if current slug is not the most recent one" do
96
- with_instance_of(model_class) do |record|
97
- record.name = "current"
98
- assert record.save
99
-
100
- # this feels like a hack. only thing i can get to work with the HistoryTestWithSti
101
- # test cases. (Editorialist vs Journalist.)
102
- sluggable_type = FriendlyId::Slug.first.sluggable_type
103
- # create several slugs for record
104
- # current slug does not have max id
105
- FriendlyId::Slug.delete_all
106
- FriendlyId::Slug.create(sluggable_type: sluggable_type, sluggable_id: record.id, slug: "current")
107
- FriendlyId::Slug.create(sluggable_type: sluggable_type, sluggable_id: record.id, slug: "outdated")
108
-
109
- record.reload
110
- record.slug = nil
111
- assert record.save
112
-
113
- assert_equal 2, FriendlyId::Slug.count
114
- end
115
- end
116
-
117
- test "should not create new slugs that match old slugs" do
118
- transaction do
119
- first_record = model_class.create! name: "foo"
120
- first_record.name = "bar"
121
- first_record.save!
122
- second_record = model_class.create! name: "foo"
123
- assert second_record.slug != "foo"
124
- assert_match(/foo-.+/, second_record.slug)
125
- end
126
- end
127
-
128
- test "should not fail when updating historic slugs" do
129
- transaction do
130
- first_record = model_class.create! name: "foo"
131
- second_record = model_class.create! name: "another"
132
-
133
- second_record.update name: "foo", slug: nil
134
- assert_match(/foo-.*/, second_record.slug)
135
-
136
- first_record.update name: "another", slug: nil
137
- assert_match(/another-.*/, first_record.slug)
138
- end
139
- end
140
-
141
- test "should prefer product that used slug most recently" do
142
- transaction do
143
- first_record = model_class.create! name: "foo"
144
- second_record = model_class.create! name: "bar"
145
-
146
- first_record.update! slug: "not_foo"
147
- second_record.update! slug: "foo" # now both records have used foo; second_record most recently
148
- second_record.update! slug: "not_bar"
149
-
150
- assert_equal model_class.friendly.find("foo"), second_record
151
- end
152
- end
153
-
154
- test "should name table according to prefix and suffix" do
155
- transaction do
156
- prefix = "prefix_"
157
- without_prefix = FriendlyId::Slug.table_name
158
- ActiveRecord::Base.table_name_prefix = prefix
159
- FriendlyId::Slug.reset_table_name
160
- assert_equal prefix + without_prefix, FriendlyId::Slug.table_name
161
- ensure
162
- ActiveRecord::Base.table_name_prefix = ""
163
- FriendlyId::Slug.table_name = without_prefix
164
- end
165
- end
166
- end
167
-
168
- class HistoryTestWithAutomaticSlugRegeneration < HistoryTest
169
- class Manual < ActiveRecord::Base
170
- extend FriendlyId
171
- friendly_id :name, use: [:slugged, :history]
172
-
173
- def should_generate_new_friendly_id?
174
- slug.blank? or name_changed?
175
- end
176
- end
177
-
178
- def model_class
179
- Manual
180
- end
181
-
182
- test "should allow reversion back to a previously used slug" do
183
- with_instance_of(model_class, name: "foo") do |record|
184
- record.name = "bar"
185
- record.save!
186
- assert_equal "bar", record.friendly_id
187
- record.name = "foo"
188
- record.save!
189
- assert_equal "foo", record.friendly_id
190
- end
191
- end
192
- end
193
-
194
- class DependentDestroyTest < TestCaseClass
195
- include FriendlyId::Test
196
-
197
- class FalseManual < ActiveRecord::Base
198
- self.table_name = "manuals"
199
-
200
- extend FriendlyId
201
- friendly_id :name, use: :history, dependent: false
202
- end
203
-
204
- class DefaultManual < ActiveRecord::Base
205
- self.table_name = "manuals"
206
-
207
- extend FriendlyId
208
- friendly_id :name, use: :history
209
- end
210
-
211
- test "should allow disabling of dependent destroy" do
212
- transaction do
213
- assert FriendlyId::Slug.find_by_slug("foo").nil?
214
- l = FalseManual.create! name: "foo"
215
- assert FriendlyId::Slug.find_by_slug("foo").present?
216
- l.destroy
217
- assert FriendlyId::Slug.find_by_slug("foo").present?
218
- end
219
- end
220
-
221
- test "should dependently destroy by default" do
222
- transaction do
223
- assert FriendlyId::Slug.find_by_slug("baz").nil?
224
- l = DefaultManual.create! name: "baz"
225
- assert FriendlyId::Slug.find_by_slug("baz").present?
226
- l.destroy
227
- assert FriendlyId::Slug.find_by_slug("baz").nil?
228
- end
229
- end
230
- end
231
-
232
- if ActiveRecord::VERSION::STRING >= "5.0"
233
- class HistoryTestWithParanoidDeletes < HistoryTest
234
- class ParanoidRecord < ActiveRecord::Base
235
- extend FriendlyId
236
- friendly_id :name, use: :history, dependent: false
237
-
238
- default_scope { where(deleted_at: nil) }
239
- end
240
-
241
- def model_class
242
- ParanoidRecord
243
- end
244
-
245
- test "slug should have a sluggable even when soft deleted by a library" do
246
- transaction do
247
- assert FriendlyId::Slug.find_by_slug("paranoid").nil?
248
- record = model_class.create(name: "paranoid")
249
- assert FriendlyId::Slug.find_by_slug("paranoid").present?
250
-
251
- record.update deleted_at: Time.now
252
-
253
- orphan_slug = FriendlyId::Slug.find_by_slug("paranoid")
254
- assert orphan_slug.present?, "Orphaned slug should exist"
255
-
256
- assert orphan_slug.valid?, "Errors: #{orphan_slug.errors.full_messages}"
257
- assert orphan_slug.sluggable.present?, "Orphaned slug should still find corresponding paranoid sluggable"
258
- end
259
- end
260
- end
261
- end
262
-
263
- class HistoryTestWithSti < HistoryTest
264
- class Journalist < ActiveRecord::Base
265
- extend FriendlyId
266
- friendly_id :name, use: [:slugged, :history]
267
- end
268
-
269
- class Editorialist < Journalist
270
- end
271
-
272
- def model_class
273
- Editorialist
274
- end
275
- end
276
-
277
- class HistoryTestWithFriendlyFinders < HistoryTest
278
- class Journalist < ActiveRecord::Base
279
- extend FriendlyId
280
- friendly_id :name, use: [:slugged, :finders, :history]
281
- end
282
-
283
- class Restaurant < ActiveRecord::Base
284
- extend FriendlyId
285
- belongs_to :city
286
- friendly_id :name, use: [:slugged, :history, :finders]
287
- end
288
-
289
- test "should be findable by old slugs" do
290
- [Journalist, Restaurant].each do |model_class|
291
- with_instance_of(model_class) do |record|
292
- old_friendly_id = record.friendly_id
293
- record.name = record.name + "b"
294
- record.slug = nil
295
- record.save!
296
- begin
297
- assert model_class.find(old_friendly_id)
298
- assert model_class.exists?(old_friendly_id), "should exist? by old id for #{model_class.name}"
299
- rescue ActiveRecord::RecordNotFound
300
- flunk "Could not find record by old id for #{model_class.name}"
301
- end
302
- end
303
- end
304
- end
305
- end
306
-
307
- class HistoryTestWithFindersBeforeHistory < HistoryTest
308
- class Novelist < ActiveRecord::Base
309
- has_many :novels
310
- end
311
-
312
- class Novel < ActiveRecord::Base
313
- extend FriendlyId
314
-
315
- belongs_to :novelist
316
-
317
- friendly_id :name, use: [:finders, :history]
318
-
319
- def should_generate_new_friendly_id?
320
- slug.blank? || name_changed?
321
- end
322
- end
323
-
324
- test "should be findable by old slug through has_many association" do
325
- transaction do
326
- novelist = Novelist.create!(name: "Stephen King")
327
- novel = novelist.novels.create(name: "Rita Hayworth and Shawshank Redemption")
328
- slug = novel.slug
329
- novel.name = "Shawshank Redemption"
330
- novel.save!
331
- assert_equal novel, Novel.find(slug)
332
- assert_equal novel, novelist.novels.find(slug)
333
- end
334
- end
335
- end
336
-
337
- class City < ActiveRecord::Base
338
- has_many :restaurants
339
- end
340
-
341
- class Restaurant < ActiveRecord::Base
342
- extend FriendlyId
343
- belongs_to :city
344
- friendly_id :name, use: [:scoped, :history], scope: :city
345
- end
346
-
347
- class ScopedHistoryTest < TestCaseClass
348
- include FriendlyId::Test
349
- include FriendlyId::Test::Shared::Core
350
-
351
- def model_class
352
- Restaurant
353
- end
354
-
355
- test "should find old scoped slugs" do
356
- transaction do
357
- city = City.create!
358
- with_instance_of(Restaurant) do |record|
359
- record.city = city
360
-
361
- record.name = "x"
362
- record.slug = nil
363
- record.save!
364
-
365
- record.name = "y"
366
- record.slug = nil
367
- record.save!
368
-
369
- assert_equal city.restaurants.friendly.find("x"), city.restaurants.friendly.find("y")
370
- end
371
- end
372
- end
373
-
374
- test "should consider old scoped slugs when creating slugs" do
375
- transaction do
376
- city = City.create!
377
- with_instance_of(Restaurant) do |record|
378
- record.city = city
379
-
380
- record.name = "x"
381
- record.slug = nil
382
- record.save!
383
-
384
- record.name = "y"
385
- record.slug = nil
386
- record.save!
387
-
388
- second_record = model_class.create! city: city, name: "x"
389
- assert_match(/x-.+/, second_record.friendly_id)
390
-
391
- third_record = model_class.create! city: city, name: "y"
392
- assert_match(/y-.+/, third_record.friendly_id)
393
- end
394
- end
395
- end
396
-
397
- test "should record history when scope changes" do
398
- transaction do
399
- city1 = City.create!
400
- city2 = City.create!
401
- with_instance_of(Restaurant) do |record|
402
- record.name = "x"
403
- record.slug = nil
404
-
405
- record.city = city1
406
- record.save!
407
- assert_equal("city_id:#{city1.id}", record.slugs.reload.first.scope)
408
- assert_equal("x", record.slugs.reload.first.slug)
409
-
410
- record.city = city2
411
- record.save!
412
- assert_equal("city_id:#{city2.id}", record.slugs.reload.first.scope)
413
-
414
- record.name = "y"
415
- record.slug = nil
416
- record.city = city1
417
- record.save!
418
- assert_equal("city_id:#{city1.id}", record.slugs.reload.first.scope)
419
- assert_equal("y", record.slugs.reload.first.slug)
420
- end
421
- end
422
- end
423
-
424
- test "should allow equal slugs in different scopes" do
425
- transaction do
426
- city = City.create!
427
- second_city = City.create!
428
- record = model_class.create! city: city, name: "x"
429
- second_record = model_class.create! city: second_city, name: "x"
430
-
431
- assert_equal record.slug, second_record.slug
432
- end
433
- end
434
- end
@@ -1,100 +0,0 @@
1
- require "helper"
2
-
3
- class Article < ActiveRecord::Base
4
- extend FriendlyId
5
- friendly_id :name, use: :slugged
6
- end
7
-
8
- class ArticleWithNumericPrevention < ActiveRecord::Base
9
- self.table_name = "articles"
10
- extend FriendlyId
11
- friendly_id :name, use: :slugged
12
- friendly_id_config.treat_numeric_as_conflict = true
13
- end
14
-
15
- class NumericSlugTest < TestCaseClass
16
- include FriendlyId::Test
17
- include FriendlyId::Test::Shared::Core
18
-
19
- def model_class
20
- Article
21
- end
22
-
23
- test "should generate numeric slugs" do
24
- transaction do
25
- record = model_class.create! name: "123"
26
- assert_equal "123", record.slug
27
- end
28
- end
29
-
30
- test "should find by numeric slug" do
31
- transaction do
32
- record = model_class.create! name: "123"
33
- assert_equal model_class.friendly.find("123").id, record.id
34
- end
35
- end
36
-
37
- test "should exist? by numeric slug" do
38
- transaction do
39
- model_class.create! name: "123"
40
- assert model_class.friendly.exists?("123")
41
- end
42
- end
43
-
44
- test "should prevent purely numeric slugs when treat_numeric_as_conflict is enabled" do
45
- transaction do
46
- record = ArticleWithNumericPrevention.create! name: "123"
47
- refute_equal "123", record.slug
48
- assert_match(/\A123-[0-9a-f-]{36}\z/, record.slug)
49
- end
50
- end
51
-
52
- test "should allow non-numeric slugs when treat_numeric_as_conflict is enabled" do
53
- transaction do
54
- record = ArticleWithNumericPrevention.create! name: "abc123"
55
- assert_equal "abc123", record.slug
56
- end
57
- end
58
-
59
- test "should allow alphanumeric slugs when treat_numeric_as_conflict is enabled" do
60
- transaction do
61
- record = ArticleWithNumericPrevention.create! name: "product-123"
62
- assert_equal "product-123", record.slug
63
- end
64
- end
65
-
66
- test "should handle zero as numeric when treat_numeric_as_conflict is enabled" do
67
- transaction do
68
- record = ArticleWithNumericPrevention.create! name: "0"
69
- refute_equal "0", record.slug
70
- assert_match(/\A0-[0-9a-f-]{36}\z/, record.slug)
71
- end
72
- end
73
-
74
- test "should handle large numbers as numeric when treat_numeric_as_conflict is enabled" do
75
- transaction do
76
- record = ArticleWithNumericPrevention.create! name: "999999999"
77
- refute_equal "999999999", record.slug
78
- assert_match(/\A999999999-[0-9a-f-]{36}\z/, record.slug)
79
- end
80
- end
81
-
82
- test "should find records with UUID-suffixed numeric slugs when treat_numeric_as_conflict is enabled" do
83
- transaction do
84
- record = ArticleWithNumericPrevention.create! name: "123"
85
- found = ArticleWithNumericPrevention.friendly.find(record.slug)
86
- assert_equal record.id, found.id
87
- end
88
- end
89
-
90
- test "should resolve conflicts between multiple numeric slugs when treat_numeric_as_conflict is enabled" do
91
- transaction do
92
- record1 = ArticleWithNumericPrevention.create! name: "456"
93
- record2 = ArticleWithNumericPrevention.create! name: "456"
94
-
95
- refute_equal record1.slug, record2.slug
96
- assert_match(/\A456-[0-9a-f-]{36}\z/, record1.slug)
97
- assert_match(/\A456-[0-9a-f-]{36}\z/, record2.slug)
98
- end
99
- end
100
- end