mongoid-slug 6.0.1 → 7.0.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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +34 -3
  3. data/lib/mongoid/slug/criteria.rb +10 -6
  4. data/lib/mongoid/slug/index_builder.rb +2 -0
  5. data/lib/mongoid/slug/railtie.rb +2 -0
  6. data/lib/mongoid/slug/slug_id_strategy.rb +2 -0
  7. data/lib/mongoid/slug/unique_slug.rb +8 -9
  8. data/lib/mongoid/slug/version.rb +4 -2
  9. data/lib/mongoid/slug.rb +15 -10
  10. data/lib/mongoid_slug.rb +2 -0
  11. data/lib/tasks/mongoid_slug.rake +3 -1
  12. metadata +9 -168
  13. data/spec/models/alias.rb +0 -6
  14. data/spec/models/article.rb +0 -9
  15. data/spec/models/artist.rb +0 -8
  16. data/spec/models/artwork.rb +0 -10
  17. data/spec/models/author.rb +0 -15
  18. data/spec/models/author_polymorphic.rb +0 -15
  19. data/spec/models/book.rb +0 -12
  20. data/spec/models/book_polymorphic.rb +0 -12
  21. data/spec/models/caption.rb +0 -17
  22. data/spec/models/entity.rb +0 -11
  23. data/spec/models/friend.rb +0 -7
  24. data/spec/models/incorrect_slug_persistence.rb +0 -9
  25. data/spec/models/integer_id.rb +0 -9
  26. data/spec/models/magazine.rb +0 -7
  27. data/spec/models/page.rb +0 -9
  28. data/spec/models/page_localize.rb +0 -9
  29. data/spec/models/page_slug_localized.rb +0 -9
  30. data/spec/models/page_slug_localized_custom.rb +0 -10
  31. data/spec/models/page_slug_localized_history.rb +0 -9
  32. data/spec/models/partner.rb +0 -7
  33. data/spec/models/person.rb +0 -12
  34. data/spec/models/relationship.rb +0 -8
  35. data/spec/models/string_id.rb +0 -9
  36. data/spec/models/subject.rb +0 -7
  37. data/spec/models/without_slug.rb +0 -5
  38. data/spec/mongoid/criteria_spec.rb +0 -207
  39. data/spec/mongoid/index_builder_spec.rb +0 -105
  40. data/spec/mongoid/slug_spec.rb +0 -1175
  41. data/spec/shared/indexes.rb +0 -41
  42. data/spec/spec_helper.rb +0 -61
  43. data/spec/tasks/mongoid_slug_rake_spec.rb +0 -73
@@ -1,1175 +0,0 @@
1
- require 'spec_helper'
2
-
3
- module Mongoid
4
- describe Slug do
5
- let(:book) do
6
- Book.create(title: 'A Thousand Plateaus')
7
- end
8
-
9
- context 'special cases' do
10
- it 'slugs are not be generated from invalid documents' do
11
- # this will fail now
12
- x = IncorrectSlugPersistence.create!(name: 'test')
13
- expect(x.slug).to eq('test')
14
-
15
- # I believe this will now fail
16
- x.name = 'te'
17
- x.valid?
18
- expect(x.slug).not_to eq('te')
19
-
20
- # I believe this will persist the 'te'
21
- x.name = 'testb'
22
- x.save!
23
- end
24
-
25
- it 'has no slugs for blank strings' do
26
- book = Book.create!(title: '')
27
- expect(book.reload.slugs).to be nil
28
- end
29
-
30
- it 'has no slugs for dashes' do
31
- book = Book.create!(title: '-')
32
- expect(book.reload.slugs).to be nil
33
- end
34
-
35
- it 'has no slugs for underscores' do
36
- book = Book.create!(title: '_')
37
- expect(book.reload.slugs).to be nil
38
- end
39
-
40
- it 'has no slugs for nil strings' do
41
- book = Book.create!
42
- expect(book.reload.slugs).to be nil
43
- end
44
-
45
- it 'works for multiple nils' do
46
- expect do
47
- 2.times do
48
- Book.create!
49
- end
50
- end.to_not raise_error # Mongo::Error::OperationFailure
51
- expect(Book.all.map(&:slug)).to eq Book.all.map(&:id).map(&:to_s)
52
- end
53
- end
54
-
55
- context 'when option skip_id_check is used with UUID _id ' do
56
- let(:entity0) do
57
- Entity.create(_id: UUID.generate, name: 'Pelham 1 2 3', user_edited_variation: 'pelham-1-2-3')
58
- end
59
- let(:entity1) do
60
- Entity.create(_id: UUID.generate, name: 'Jackson 5', user_edited_variation: 'jackson-5')
61
- end
62
- let(:entity2) do
63
- Entity.create(_id: UUID.generate, name: 'Jackson 5', user_edited_variation: 'jackson-5')
64
- end
65
-
66
- it 'generates a unique slug by appending a counter to duplicate text' do
67
- expect(entity0.to_param).to eql 'pelham-1-2-3'
68
-
69
- 5.times do |x|
70
- dup = Entity.create(_id: UUID.generate, name: entity0.name, user_edited_variation: entity0.user_edited_variation)
71
- expect(dup.to_param).to eql "pelham-1-2-3-#{x.succ}"
72
- end
73
- end
74
-
75
- it 'allows the user to edit the sluggable field' do
76
- expect(entity1.to_param).to eql 'jackson-5'
77
- expect(entity2.to_param).to eql 'jackson-5-1'
78
- entity2.user_edited_variation = 'jackson-5-indiana'
79
- entity2.save
80
- expect(entity2.to_param).to eql 'jackson-5-indiana'
81
- end
82
-
83
- it 'allows users to edit the sluggable field' do
84
- expect(entity1.to_param).to eql 'jackson-5'
85
- expect(entity2.to_param).to eql 'jackson-5-1'
86
- entity2.user_edited_variation = 'jackson-5-indiana'
87
- entity2.save
88
- expect(entity2.to_param).to eql 'jackson-5-indiana'
89
- end
90
-
91
- it 'it restores the slug if the editing user tries to use an existing slug' do
92
- expect(entity1.to_param).to eql 'jackson-5'
93
- expect(entity2.to_param).to eql 'jackson-5-1'
94
- entity2.user_edited_variation = 'jackson-5'
95
- entity2.save
96
- expect(entity2.to_param).to eql 'jackson-5-1'
97
- end
98
-
99
- it 'does not force an appended counter on a plain string' do
100
- entity = Entity.create(_id: UUID.generate, name: 'Adele', user_edited_variation: 'adele')
101
- expect(entity.to_param).to eql 'adele'
102
- end
103
- end
104
-
105
- context 'when the object is top-level' do
106
- it 'generates a slug' do
107
- expect(book.to_param).to eql 'a-thousand-plateaus'
108
- end
109
-
110
- it 'updates the slug' do
111
- book.title = 'Anti Oedipus'
112
- book.save
113
- expect(book.to_param).to eql 'anti-oedipus'
114
- end
115
-
116
- it 'generates a unique slug by appending a counter to duplicate text' do
117
- 15.times do |x|
118
- dup = Book.create(title: book.title)
119
- expect(dup.to_param).to eql "a-thousand-plateaus-#{x + 1}"
120
- end
121
- end
122
-
123
- it 'does not allow a BSON::ObjectId as use for a slug' do
124
- bson_id = Mongoid::Compatibility::Version.mongoid3? ? Moped::BSON::ObjectId.new.to_s : BSON::ObjectId.new.to_s
125
- bad = Book.create(title: bson_id)
126
- expect(bad.slugs).not_to include(bson_id)
127
- end
128
-
129
- it 'does not update slug if slugged fields have not changed' do
130
- book.save
131
- expect(book.to_param).to eql 'a-thousand-plateaus'
132
- end
133
-
134
- it 'does not change slug if slugged fields have changed but generated slug is identical' do
135
- book.title = 'a thousand plateaus'
136
- book.save
137
- expect(book.to_param).to eql 'a-thousand-plateaus'
138
- end
139
-
140
- context 'using find' do
141
- it 'finds by id as string' do
142
- expect(Book.find(book.id.to_s)).to eql book
143
- end
144
-
145
- it 'finds by id as array of strings' do
146
- expect(Book.find([book.id.to_s])).to eql [book]
147
- end
148
-
149
- it 'finds by id as BSON::ObjectId' do
150
- expect(Book.find(book.id)).to eql book
151
- end
152
-
153
- it 'finds by id as an array of BSON::ObjectIds' do
154
- expect(Book.find([book.id])).to eql [book]
155
- end
156
-
157
- it 'returns an empty array if given an empty array' do
158
- expect(Book.find([])).to eql []
159
- end
160
- end
161
- end
162
-
163
- context 'when the object is embedded' do
164
- let(:subject) do
165
- book.subjects.create(name: 'Psychoanalysis')
166
- end
167
-
168
- it 'generates a slug' do
169
- expect(subject.to_param).to eql 'psychoanalysis'
170
- end
171
-
172
- it 'updates the slug' do
173
- subject.name = 'Schizoanalysis'
174
- subject.save
175
- expect(subject.to_param).to eql 'schizoanalysis'
176
- end
177
-
178
- it 'generates a unique slug by appending a counter to duplicate text' do
179
- dup = book.subjects.create(name: subject.name)
180
- expect(dup.to_param).to eql 'psychoanalysis-1'
181
- end
182
-
183
- it 'does not allow a BSON::ObjectId as use for a slug' do
184
- bad = book.subjects.create(name: '4ea0389f0364313d79104fb3')
185
- expect(bad.slugs).not_to eql '4ea0389f0364313d79104fb3'
186
- end
187
-
188
- it 'does not update slug if slugged fields have not changed' do
189
- subject.save
190
- expect(subject.to_param).to eql 'psychoanalysis'
191
- end
192
-
193
- it 'does not change slug if slugged fields have changed but generated slug is identical' do
194
- subject.name = 'PSYCHOANALYSIS'
195
- expect(subject.to_param).to eql 'psychoanalysis'
196
- end
197
-
198
- context 'using find' do
199
- it 'finds by id as string' do
200
- expect(book.subjects.find(subject.id.to_s)).to eql subject
201
- end
202
-
203
- it 'finds by id as array of strings' do
204
- expect(book.subjects.find([subject.id.to_s])).to eql [subject]
205
- end
206
-
207
- it 'finds by id as BSON::ObjectId' do
208
- expect(book.subjects.find(subject.id)).to eql subject
209
- end
210
-
211
- it 'finds by id as an array of BSON::ObjectIds' do
212
- expect(book.subjects.find([subject.id])).to eql [subject]
213
- end
214
-
215
- it 'returns an empty array if given an empty array' do
216
- expect(book.subjects.find([])).to eql []
217
- end
218
- end
219
- end
220
-
221
- context 'when the object is embedded in another embedded object' do
222
- let(:person) do
223
- Person.create(name: 'John Doe')
224
- end
225
-
226
- let(:relationship) do
227
- person.relationships.create(name: 'Engagement')
228
- end
229
-
230
- let(:partner) do
231
- relationship.partners.create(name: 'Jane Smith')
232
- end
233
-
234
- it 'generates a slug' do
235
- expect(partner.to_param).to eql 'jane-smith'
236
- end
237
-
238
- it 'updates the slug' do
239
- partner.name = 'Jane Doe'
240
- partner.save
241
- expect(partner.to_param).to eql 'jane-doe'
242
- end
243
-
244
- it 'generates a unique slug by appending a counter to duplicate text' do
245
- dup = relationship.partners.create(name: partner.name)
246
- expect(dup.to_param).to eql 'jane-smith-1'
247
- end
248
-
249
- it 'does not allow a BSON::ObjectId as use for a slug' do
250
- bad = relationship.partners.create(name: '4ea0389f0364313d79104fb3')
251
- expect(bad.slugs).not_to eql '4ea0389f0364313d79104fb3'
252
- end
253
-
254
- it 'does not update slug if slugged fields have not changed' do
255
- partner.save
256
- expect(partner.to_param).to eql 'jane-smith'
257
- end
258
-
259
- it 'does not change slug if slugged fields have changed but generated slug is identical' do
260
- partner.name = 'JANE SMITH'
261
- expect(partner.to_param).to eql 'jane-smith'
262
- end
263
-
264
- it 'scopes by parent object' do
265
- affair = person.relationships.create(name: 'Affair')
266
- lover = affair.partners.create(name: partner.name)
267
- expect(lover.to_param).to eql partner.to_param
268
- end
269
-
270
- context 'using find' do
271
- it 'finds by id as string' do
272
- expect(relationship.partners.find(partner.id.to_s)).to eql partner
273
- end
274
-
275
- it 'finds by id as array of strings' do
276
- expect(relationship.partners.find([partner.id.to_s])).to eql [partner]
277
- end
278
-
279
- it 'finds by id as BSON::ObjectId' do
280
- expect(relationship.partners.find(partner.id)).to eql partner
281
- end
282
-
283
- it 'finds by id as an array of BSON::ObjectIds' do
284
- expect(relationship.partners.find([partner.id])).to eql [partner]
285
- end
286
-
287
- it 'returns an empty array if given an empty array' do
288
- expect(relationship.partners.find([])).to eql []
289
- end
290
- end
291
- end
292
-
293
- context 'when the slug is composed of multiple fields' do
294
- let!(:author) do
295
- Author.create(
296
- first_name: 'Gilles',
297
- last_name: 'Deleuze'
298
- )
299
- end
300
-
301
- it 'generates a slug' do
302
- expect(author.to_param).to eql 'gilles-deleuze'
303
- end
304
-
305
- it 'updates the slug' do
306
- author.first_name = 'Félix'
307
- author.last_name = 'Guattari'
308
- author.save
309
- expect(author.to_param).to eql 'felix-guattari'
310
- end
311
-
312
- it 'generates a unique slug by appending a counter to duplicate text' do
313
- dup = Author.create(
314
- first_name: author.first_name,
315
- last_name: author.last_name
316
- )
317
- expect(dup.to_param).to eql 'gilles-deleuze-1'
318
-
319
- dup2 = Author.create(
320
- first_name: author.first_name,
321
- last_name: author.last_name
322
- )
323
-
324
- dup.save
325
- expect(dup2.to_param).to eql 'gilles-deleuze-2'
326
- end
327
-
328
- it 'does not allow a BSON::ObjectId as use for a slug' do
329
- bad = Author.create(first_name: '4ea0389f0364',
330
- last_name: '313d79104fb3')
331
- expect(bad.to_param).not_to eql '4ea0389f0364313d79104fb3'
332
- end
333
-
334
- it 'does not update slug if slugged fields have changed but generated slug is identical' do
335
- author.last_name = 'DELEUZE'
336
- author.save
337
- expect(author.to_param).to eql 'gilles-deleuze'
338
- end
339
- end
340
-
341
- context 'when :as is passed as an argument' do
342
- let!(:person) do
343
- Person.create(name: 'John Doe')
344
- end
345
-
346
- it 'sets an alternative slug field name' do
347
- expect(person).to respond_to(:_slugs)
348
- expect(person.slugs).to eql ['john-doe']
349
- end
350
-
351
- it 'defines #slug' do
352
- expect(person).to respond_to :slugs
353
- end
354
-
355
- it 'defines #slug_changed?' do
356
- expect(person).to respond_to :_slugs_changed?
357
- end
358
-
359
- it 'defines #slug_was' do
360
- expect(person).to respond_to :_slugs_was
361
- end
362
- end
363
-
364
- context 'when :permanent is passed as an argument' do
365
- let(:person) do
366
- Person.create(name: 'John Doe')
367
- end
368
-
369
- it 'does not update the slug when the slugged fields change' do
370
- person.name = 'Jane Doe'
371
- person.save
372
- expect(person.to_param).to eql 'john-doe'
373
- end
374
- end
375
-
376
- context 'when :history is passed as an argument' do
377
- context 'true' do
378
- let(:book) do
379
- Book.create(title: 'Book Title')
380
- end
381
-
382
- before(:each) do
383
- book.title = 'Other Book Title'
384
- book.save
385
- end
386
-
387
- it "saves the old slug in the owner's history" do
388
- expect(book.slugs).to include('book-title')
389
- end
390
-
391
- it 'generates a unique slug by appending a counter to duplicate text' do
392
- dup = Book.create(title: 'Book Title')
393
- expect(dup.to_param).to eql 'book-title-1'
394
- end
395
-
396
- it 'does not allow a BSON::ObjectId as use for a slug' do
397
- bad = Book.create(title: '4ea0389f0364313d79104fb3')
398
- expect(bad.to_param).not_to eql '4ea0389f0364313d79104fb3'
399
- end
400
-
401
- it 'ensures no duplicate values are stored in history' do
402
- book.update_attributes title: 'Book Title'
403
- book.update_attributes title: 'Foo'
404
- expect(book.slugs.find_all { |slug| slug == 'book-title' }.size).to eql 1
405
- end
406
- end
407
- context 'false' do
408
- let(:author) do
409
- Author.create(first_name: 'Gilles', last_name: 'Deleuze')
410
- end
411
-
412
- before(:each) do
413
- author.first_name = 'John'
414
- author.save
415
- end
416
-
417
- it "does not save the old slug in the owner's history" do
418
- expect(author.slugs.count).to eq 1
419
- expect(author.slugs).to_not include('gilles-deleuze')
420
- end
421
- end
422
- end
423
-
424
- context 'when slug is scoped by a reference association' do
425
- let(:author) do
426
- book.authors.create(first_name: 'Gilles', last_name: 'Deleuze')
427
- end
428
-
429
- it 'scopes by parent object' do
430
- book2 = Book.create(title: 'Anti Oedipus')
431
- dup = book2.authors.create(
432
- first_name: author.first_name,
433
- last_name: author.last_name
434
- )
435
- expect(dup.to_param).to eql author.to_param
436
- end
437
-
438
- it 'generates a unique slug by appending a counter to duplicate text' do
439
- dup = book.authors.create(
440
- first_name: author.first_name,
441
- last_name: author.last_name
442
- )
443
- expect(dup.to_param).to eql 'gilles-deleuze-1'
444
- end
445
-
446
- it 'does not allow a BSON::ObjectId as use for a slug' do
447
- bad = book.authors.create(first_name: '4ea0389f0364',
448
- last_name: '313d79104fb3')
449
- expect(bad.to_param).not_to eql '4ea0389f0364313d79104fb3'
450
- end
451
-
452
- context 'with an irregular association name' do
453
- let(:character) do
454
- # well we've got to make up something... :-)
455
- author.characters.create(name: 'Oedipus')
456
- end
457
-
458
- let!(:author2) do
459
- Author.create(
460
- first_name: 'Sophocles',
461
- last_name: 'son of Sophilos'
462
- )
463
- end
464
-
465
- it 'scopes by parent object provided that inverse_of is specified' do
466
- dup = author2.characters.create(name: character.name)
467
- expect(dup.to_param).to eql character.to_param
468
- end
469
- end
470
- end
471
-
472
- context "when slug is scoped by one of the class's own fields" do
473
- let!(:magazine) do
474
- Magazine.create(title: 'Big Weekly', publisher_id: 'abc123')
475
- end
476
-
477
- it 'should scope by local field' do
478
- expect(magazine.to_param).to eql 'big-weekly'
479
- magazine2 = Magazine.create(title: 'Big Weekly', publisher_id: 'def456')
480
- expect(magazine2.to_param).to eql magazine.to_param
481
- end
482
-
483
- it 'should generate a unique slug by appending a counter to duplicate text' do
484
- dup = Magazine.create(title: 'Big Weekly', publisher_id: 'abc123')
485
- expect(dup.to_param).to eql 'big-weekly-1'
486
- end
487
-
488
- it 'does not allow a BSON::ObjectId as use for a slug' do
489
- bad = Magazine.create(title: '4ea0389f0364313d79104fb3', publisher_id: 'abc123')
490
- expect(bad.to_param).not_to eql '4ea0389f0364313d79104fb3'
491
- end
492
- end
493
-
494
- context 'when #slug is given a block' do
495
- let(:caption) do
496
- Caption.create(my_identity: 'Edward Hopper (American, 1882-1967)',
497
- title: 'Soir Bleu, 1914',
498
- medium: 'Oil on Canvas')
499
- end
500
-
501
- it 'generates a slug' do
502
- expect(caption.to_param).to eql 'edward-hopper-soir-bleu-1914'
503
- end
504
-
505
- it 'updates the slug' do
506
- caption.title = 'Road in Maine, 1914'
507
- caption.save
508
- expect(caption.to_param).to eql 'edward-hopper-road-in-maine-1914'
509
- end
510
-
511
- it 'does not change slug if slugged fields have changed but generated slug is identical' do
512
- caption.my_identity = 'Edward Hopper'
513
- caption.save
514
- expect(caption.to_param).to eql 'edward-hopper-soir-bleu-1914'
515
- end
516
- end
517
-
518
- context 'when block is configured globally' do
519
- before do
520
- Mongoid::Slug.configure do |c|
521
- c.slug do |cur_obj|
522
- slug = cur_obj.slug_builder
523
- "#{slug}-#{cur_obj.id}".to_url
524
- end
525
- end
526
- end
527
-
528
- after do
529
- # Remove global configuration to avoid affect on
530
- # other specs run after this spec
531
- Mongoid::Slug.default_slug = nil
532
- expect(Mongoid::Slug.default_slug).to be_nil
533
- end
534
-
535
- it 'generates a slug' do
536
- expect(Mongoid::Slug.default_slug).to be_present
537
- class Person
538
- include Mongoid::Document
539
- include Mongoid::Slug
540
-
541
- field :name
542
- slug :name
543
- end
544
-
545
- person = Person.create(name: 'John')
546
- expect(person.to_param).to eql "john-#{person.id}"
547
- end
548
-
549
- it 'can be overridden at model level' do
550
- expect(Mongoid::Slug.default_slug).to be_present
551
- class Person
552
- include Mongoid::Document
553
- include Mongoid::Slug
554
-
555
- field :name
556
- slug :name do |cur_object|
557
- cur_object.slug_builder.to_url
558
- end
559
- end
560
-
561
- person = Person.create(name: 'John')
562
- expect(person.to_param).to eql 'john'
563
- end
564
- end
565
-
566
- context 'when slugged field contains non-ASCII characters' do
567
- it 'slugs Cyrillic characters' do
568
- book.title = 'Капитал'
569
- book.save
570
- expect(book.to_param).to eql 'kapital'
571
- end
572
-
573
- it 'slugs Greek characters' do
574
- book.title = 'Ελλάδα'
575
- book.save
576
- expect(book.to_param).to eql 'ellada'
577
- end
578
-
579
- it 'slugs Chinese characters' do
580
- book.title = '中文'
581
- book.save
582
- expect(book.to_param).to eql 'zhong-wen'
583
- end
584
-
585
- it 'slugs non-ASCII Latin characters' do
586
- book.title = 'Paul Cézanne'
587
- book.save
588
- expect(book.to_param).to eql 'paul-cezanne'
589
- end
590
- end
591
-
592
- context 'when slug is not scoped by a reference association' do
593
- subject { Book }
594
- it_should_behave_like 'has an index', { _slugs: 1 }, unique: true, sparse: true
595
- end
596
-
597
- context 'with a value exceeding mongodb max index key' do
598
- if Mongoid::Compatibility::Version.mongoid5_or_newer?
599
- xit 'errors with a model without a max length' do
600
- expect do
601
- Book.create!(title: 't' * 1025)
602
- end.to raise_error Mongo::Error::OperationFailure, /key too large to index/
603
- end
604
- elsif Mongoid::Compatibility::Version.mongoid4?
605
- xit 'errors with a model without a max length' do
606
- expect do
607
- Book.create!(title: 't' * 1025)
608
- end.to raise_error Moped::Errors::OperationFailure, /key too large to index/
609
- end
610
- end
611
-
612
- it 'succeeds with a model with a max length' do
613
- expect do
614
- author = Author.create!(last_name: 't' * 1025)
615
- expect(author.slug.length).to eq 256
616
- end.to_not raise_error
617
- end
618
- end
619
-
620
- context 'when slug is scoped by a reference association' do
621
- subject { Author }
622
- it_should_behave_like 'does not have an index', _slugs: 1
623
- end
624
-
625
- context 'for subclass scope' do
626
- context 'when slug is not scoped by a reference association' do
627
- subject { BookPolymorphic }
628
- it_should_behave_like 'has an index', { _type: 1, _slugs: 1 }, unique: nil, sparse: nil
629
- end
630
-
631
- context 'when slug is scoped by a reference association' do
632
- subject { AuthorPolymorphic }
633
- it_should_behave_like 'does not have an index', _type: 1, _slugs: 1
634
- end
635
-
636
- context 'when the object has STI' do
637
- it 'scopes by the subclass' do
638
- b = BookPolymorphic.create!(title: 'Book')
639
- expect(b.slug).to eq('book')
640
-
641
- b2 = BookPolymorphic.create!(title: 'Book')
642
- expect(b2.slug).to eq('book-1')
643
-
644
- c = ComicBookPolymorphic.create!(title: 'Book')
645
- expect(c.slug).to eq('book')
646
-
647
- c2 = ComicBookPolymorphic.create!(title: 'Book')
648
- expect(c2.slug).to eq('book-1')
649
-
650
- expect(BookPolymorphic.find('book')).to eq(b)
651
- expect(BookPolymorphic.find('book-1')).to eq(b2)
652
- expect(ComicBookPolymorphic.find('book')).to eq(c)
653
- expect(ComicBookPolymorphic.find('book-1')).to eq(c2)
654
- end
655
- end
656
- end
657
- end
658
-
659
- context 'for reserved words' do
660
- context 'when the :reserve option is used on the model' do
661
- it 'does not use the reserved slugs' do
662
- friend1 = Friend.create(name: 'foo')
663
- expect(friend1.slugs).not_to include('foo')
664
- expect(friend1.slugs).to include('foo-1')
665
-
666
- friend2 = Friend.create(name: 'bar')
667
- expect(friend2.slugs).not_to include('bar')
668
- expect(friend2.slugs).to include('bar-1')
669
-
670
- friend3 = Friend.create(name: 'en')
671
- expect(friend3.slugs).not_to include('en')
672
- expect(friend3.slugs).to include('en-1')
673
- end
674
-
675
- it 'should start with concatenation -1' do
676
- friend1 = Friend.create(name: 'foo')
677
- expect(friend1.slugs).to include('foo-1')
678
- friend2 = Friend.create(name: 'foo')
679
- expect(friend2.slugs).to include('foo-2')
680
- end
681
-
682
- %w[new edit].each do |word|
683
- it "should overwrite the default reserved words allowing the word '#{word}'" do
684
- friend = Friend.create(name: word)
685
- expect(friend.slugs).to include word
686
- end
687
- end
688
- end
689
- context 'when the model does not have any reserved words set' do
690
- %w[new edit].each do |word|
691
- it "does not use the default reserved word '#{word}'" do
692
- book = Book.create(title: word)
693
- expect(book.slugs).not_to include word
694
- expect(book.slugs).to include("#{word}-1")
695
- end
696
- end
697
- end
698
-
699
- context 'when the object has STI' do
700
- it 'scopes by the superclass' do
701
- book = Book.create(title: 'Anti Oedipus')
702
- comic_book = ComicBook.create(title: 'Anti Oedipus')
703
- expect(comic_book.slugs).not_to eql(book.slugs)
704
- end
705
-
706
- it 'scopes by the subclass' do
707
- book = BookPolymorphic.create(title: 'Anti Oedipus')
708
- comic_book = ComicBookPolymorphic.create(title: 'Anti Oedipus')
709
- expect(comic_book.slugs).to eql(book.slugs)
710
-
711
- expect(BookPolymorphic.find(book.slug)).to eq(book)
712
- expect(ComicBookPolymorphic.find(comic_book.slug)).to eq(comic_book)
713
- end
714
- end
715
-
716
- context 'when slug defined on alias of field' do
717
- it 'should use accessor, not alias' do
718
- pseudonim = Alias.create(author_name: 'Max Stirner')
719
- expect(pseudonim.slugs).to include('max-stirner')
720
- end
721
- end
722
-
723
- describe '#to_param' do
724
- context 'when called on a new record' do
725
- let(:book) { Book.new }
726
-
727
- it 'should return nil' do
728
- expect(book.to_param).to eq book._id.to_s
729
- end
730
-
731
- it 'should not persist the record' do
732
- book.to_param
733
- expect(book).not_to be_persisted
734
- end
735
- end
736
-
737
- context 'when called on an existing record with no slug' do
738
- let!(:book_no_slug) do
739
- if Mongoid::Compatibility::Version.mongoid5_or_newer?
740
- Book.collection.insert_one(title: 'Proust and Signs')
741
- else
742
- Book.collection.insert(title: 'Proust and Signs')
743
- end
744
- Book.where(title: 'Proust and Signs').first
745
- end
746
-
747
- it 'should return the id if there is no slug' do
748
- expect(book_no_slug.to_param).to eq(book_no_slug.id.to_s)
749
- expect(book_no_slug.slugs).to be_nil
750
- end
751
-
752
- it 'should not persist the record' do
753
- expect(book_no_slug.to_param).to eq(book_no_slug._id.to_s)
754
- end
755
- end
756
- end
757
-
758
- describe '#_slugs_changed?' do
759
- before do
760
- Book.create(title: 'A Thousand Plateaus')
761
- end
762
-
763
- let(:book) { Book.first }
764
-
765
- it 'is initially unchanged' do
766
- expect(book._slugs_changed?).to be_falsey
767
- end
768
-
769
- it 'tracks changes' do
770
- book.slugs = ['Anti Oedipus']
771
- expect(book._slugs_changed?).to be_truthy
772
- end
773
- end
774
-
775
- describe 'when regular expression matches, but document does not' do
776
- let!(:book_1) { Book.create(title: 'book-1') }
777
- let!(:book_2) { Book.create(title: 'book') }
778
- let!(:book_3) { Book.create(title: 'book') }
779
-
780
- it 'book_2 should have the user supplied title without -1 after it' do
781
- expect(book_2.to_param).to eql 'book'
782
- end
783
-
784
- it 'book_3 should have a generated slug' do
785
- expect(book_3.to_param).to eql 'book-2'
786
- end
787
- end
788
-
789
- context 'when the slugged field is set manually' do
790
- context 'when it set to a non-empty string' do
791
- it 'respects the provided slug' do
792
- book = Book.create(title: 'A Thousand Plateaus', slugs: ['not-what-you-expected'])
793
- expect(book.to_param).to eql 'not-what-you-expected'
794
- end
795
-
796
- it 'ensures uniqueness' do
797
- book1 = Book.create(title: 'A Thousand Plateaus', slugs: ['not-what-you-expected'])
798
- expect(book1.to_param).to eql 'not-what-you-expected'
799
- if Mongoid::Compatibility::Version.mongoid5_or_newer?
800
- expect do
801
- Book.create(title: 'A Thousand Plateaus', slugs: ['not-what-you-expected'])
802
- end.to raise_error Mongo::Error::OperationFailure, /duplicate/
803
- elsif Mongoid::Compatibility::Version.mongoid4?
804
- expect do
805
- Book.create(title: 'A Thousand Plateaus', slugs: ['not-what-you-expected'])
806
- end.to raise_error Moped::Errors::OperationFailure, /duplicate/
807
- end
808
- end
809
-
810
- it 'updates the slug when a new one is passed in' do
811
- book = Book.create(title: 'A Thousand Plateaus', slugs: ['not-what-you-expected'])
812
- book.slugs = ['not-it-either']
813
- book.save
814
- expect(book.to_param).to eql 'not-it-either'
815
- end
816
-
817
- it 'updates the slug when a new one is appended' do
818
- book = Book.create(title: 'A Thousand Plateaus', slugs: ['not-what-you-expected'])
819
- book.slugs.push 'not-it-either'
820
- book.save
821
- expect(book.to_param).to eql 'not-it-either'
822
- end
823
-
824
- it 'updates the slug to a unique slug when a new one is appended' do
825
- Book.create(title: 'Sleepyhead')
826
- book2 = Book.create(title: 'A Thousand Plateaus')
827
- book2.slugs.push 'sleepyhead'
828
- if Mongoid::Compatibility::Version.mongoid5_or_newer?
829
- expect do
830
- book2.save
831
- end.to raise_error Mongo::Error::OperationFailure, /duplicate/
832
- elsif Mongoid::Compatibility::Version.mongoid4?
833
- expect do
834
- book2.save
835
- end.to raise_error Moped::Errors::OperationFailure, /duplicate/
836
- end
837
- end
838
- end
839
-
840
- context 'when it is set to an empty string' do
841
- it 'generate a new one' do
842
- book = Book.create(title: 'A Thousand Plateaus')
843
- expect(book.to_param).to eql 'a-thousand-plateaus'
844
- end
845
- end
846
- end
847
-
848
- context 'slug can be localized' do
849
- before(:each) do
850
- @old_locale = I18n.locale
851
- end
852
-
853
- after(:each) do
854
- I18n.locale = @old_locale
855
- end
856
-
857
- it 'generates a new slug for each localization' do
858
- page = PageSlugLocalized.new
859
- page.title = 'Title on English'
860
- page.save
861
- expect(page.slug).to eql 'title-on-english'
862
- I18n.locale = :nl
863
- page.title = 'Title on Netherlands'
864
- page.save
865
- expect(page.slug).to eql 'title-on-netherlands'
866
- end
867
-
868
- it 'returns _id if no slug' do
869
- page = PageSlugLocalized.new
870
- page.title = 'Title on English'
871
- page.save
872
- expect(page.slug).to eql 'title-on-english'
873
- I18n.locale = :nl
874
- expect(page.slug).to eql page._id.to_s
875
- end
876
-
877
- it 'fallbacks if slug not localized yet' do
878
- page = PageSlugLocalized.new
879
- page.title = 'Title on English'
880
- page.save
881
- expect(page.slug).to eql 'title-on-english'
882
- I18n.locale = :nl
883
- expect(page.slug).to eql page._id.to_s
884
-
885
- # Turn on i18n fallback
886
- require 'i18n/backend/fallbacks'
887
- I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
888
- ::I18n.fallbacks[:nl] = %i[nl en]
889
- expect(page.slug).to eql 'title-on-english'
890
- fallback_slug = page.slug
891
-
892
- fallback_page = begin
893
- PageSlugLocalized.find(fallback_slug)
894
- rescue StandardError
895
- nil
896
- end
897
- expect(fallback_page).to eq(page)
898
-
899
- # Restore fallback for next tests
900
- ::I18n.fallbacks[:nl] = [:nl]
901
- end
902
-
903
- it 'returns a default slug if not localized' do
904
- page = PageLocalize.new
905
- page.title = 'Title on English'
906
- page.save
907
- expect(page.slug).to eql 'title-on-english'
908
- I18n.locale = :nl
909
- page.title = 'Title on Netherlands'
910
- expect(page.slug).to eql 'title-on-english'
911
- page.save
912
- expect(page.slug).to eql 'title-on-netherlands'
913
- end
914
-
915
- it 'slugs properly when translations are set directly' do
916
- page = PageSlugLocalized.new
917
- page.title_translations = { 'en' => 'Title on English', 'nl' => 'Title on Netherlands' }
918
- page.save
919
- expect(page._slugs_translations).to eq('en' => ['title-on-english'], 'nl' => ['title-on-netherlands'])
920
- end
921
-
922
- it 'exact same title multiple langauges' do
923
- page = PageSlugLocalized.new
924
- page.title_translations = { 'en' => 'Title on English', 'nl' => 'Title on English' }
925
- page.save
926
- expect(page._slugs_translations).to eq('en' => ['title-on-english'], 'nl' => ['title-on-english'])
927
-
928
- page = PageSlugLocalized.create(title_translations: { 'en' => 'Title on English2', 'nl' => 'Title on English2' })
929
- expect(page._slugs_translations).to eq('en' => ['title-on-english2'], 'nl' => ['title-on-english2'])
930
- end
931
-
932
- it 'does not produce duplicate slugs' do
933
- old_locale = I18n.locale
934
-
935
- # Using a default locale of en.
936
- page = PageSlugLocalized.new
937
- page.title = 'Title on English'
938
- page.save
939
- I18n.locale = 'nl'
940
- page.title = 'Title on Netherlands'
941
- page.save
942
- expect(page.title_translations).to eq('en' => 'Title on English', 'nl' => 'Title on Netherlands')
943
-
944
- I18n.locale = old_locale
945
- page.title = 'Title on English'
946
- expect(page.title_translations).to eq('en' => 'Title on English', 'nl' => 'Title on Netherlands')
947
- expect(page._slugs_translations).to eq('en' => ['title-on-english'], 'nl' => ['title-on-netherlands'])
948
- end
949
-
950
- it 'does not produce duplicate slugs when one has changed' do
951
- old_locale = I18n.locale
952
-
953
- # Using a default locale of en.
954
- page = PageSlugLocalized.new
955
- page.title = 'Title on English'
956
- page.save
957
- I18n.locale = 'nl'
958
- page.title = 'Title on Netherlands'
959
- page.save
960
- expect(page.title_translations).to eq('en' => 'Title on English', 'nl' => 'Title on Netherlands')
961
-
962
- I18n.locale = old_locale
963
- page.title = 'Modified Title on English'
964
- page.save
965
- expect(page.title_translations).to eq('en' => 'Modified Title on English',
966
- 'nl' => 'Title on Netherlands')
967
- expect(page._slugs_translations).to eq('en' => ['modified-title-on-english'],
968
- 'nl' => ['title-on-netherlands'])
969
- end
970
-
971
- it 'does not produce duplicate slugs when transactions are set directly' do
972
- page = PageSlugLocalized.new
973
- page.title_translations = { 'en' => 'Title on English', 'nl' => 'Title on Netherlands' }
974
- page.save
975
- page.title_translations = { 'en' => 'Title on English', 'nl' => 'Title on Netherlands' }
976
- page.save
977
- expect(page._slugs_translations).to eq('en' => ['title-on-english'], 'nl' => ['title-on-netherlands'])
978
- end
979
-
980
- it 'does not produce duplicate slugs when transactions are set directly and one has changed' do
981
- page = PageSlugLocalized.new
982
- page.title_translations = { 'en' => 'Title on English', 'nl' => 'Title on Netherlands' }
983
- page.save
984
- page.title_translations = { 'en' => 'Modified Title on English',
985
- 'nl' => 'Title on Netherlands' }
986
- page.save
987
- expect(page._slugs_translations).to eq('en' => ['modified-title-on-english'],
988
- 'nl' => ['title-on-netherlands'])
989
- end
990
-
991
- it 'works with a custom slug strategy' do
992
- page = PageSlugLocalizedCustom.new
993
- page.title = 'a title for the slug'
994
- page.save
995
- expect(page._slugs_translations).to eq('en' => ['a-title-for-the-slug'], 'nl' => ['a-title-for-the-slug'])
996
- end
997
- end
998
-
999
- context 'slug can be localized when using history' do
1000
- before(:each) do
1001
- @old_locale = I18n.locale
1002
- end
1003
-
1004
- after(:each) do
1005
- I18n.locale = @old_locale
1006
- end
1007
-
1008
- it 'generate a new slug for each localization and keep history' do
1009
- old_locale = I18n.locale
1010
-
1011
- page = PageSlugLocalizedHistory.new
1012
- page.title = 'Title on English'
1013
- page.save
1014
- expect(page.slug).to eql 'title-on-english'
1015
- I18n.locale = :nl
1016
- page.title = 'Title on Netherlands'
1017
- page.save
1018
- expect(page.slug).to eql 'title-on-netherlands'
1019
- I18n.locale = old_locale
1020
- page.title = 'Modified title on English'
1021
- page.save
1022
- expect(page.slug).to eql 'modified-title-on-english'
1023
- expect(page.slug).to include('title-on-english')
1024
- I18n.locale = :nl
1025
- page.title = 'Modified title on Netherlands'
1026
- page.save
1027
- expect(page.slug).to eql 'modified-title-on-netherlands'
1028
- expect(page.slug).to include('title-on-netherlands')
1029
- end
1030
-
1031
- it 'returns _id if no slug' do
1032
- page = PageSlugLocalizedHistory.new
1033
- page.title = 'Title on English'
1034
- page.save
1035
- expect(page.slug).to eql 'title-on-english'
1036
- I18n.locale = :nl
1037
- expect(page.slug).to eql page._id.to_s
1038
- end
1039
-
1040
- it 'fallbacks if slug not localized yet' do
1041
- page = PageSlugLocalizedHistory.new
1042
- page.title = 'Title on English'
1043
- page.save
1044
- expect(page.slug).to eql 'title-on-english'
1045
- I18n.locale = :nl
1046
- expect(page.slug).to eql page._id.to_s
1047
-
1048
- # Turn on i18n fallback
1049
- require 'i18n/backend/fallbacks'
1050
- I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
1051
- ::I18n.fallbacks[:nl] = %i[nl en]
1052
- expect(page.slug).to eql 'title-on-english'
1053
- fallback_slug = page.slug
1054
-
1055
- fallback_page = begin
1056
- PageSlugLocalizedHistory.find(fallback_slug)
1057
- rescue StandardError
1058
- nil
1059
- end
1060
- expect(fallback_page).to eq(page)
1061
- end
1062
-
1063
- it 'slugs properly when translations are set directly' do
1064
- page = PageSlugLocalizedHistory.new
1065
- page.title_translations = { 'en' => 'Title on English', 'nl' => 'Title on Netherlands' }
1066
- page.save
1067
- page.title_translations = { 'en' => 'Modified Title on English',
1068
- 'nl' => 'Modified Title on Netherlands' }
1069
- page.save
1070
- expect(page._slugs_translations).to eq('en' => ['title-on-english', 'modified-title-on-english'],
1071
- 'nl' => ['title-on-netherlands', 'modified-title-on-netherlands'])
1072
- end
1073
-
1074
- it 'does not produce duplicate slugs' do
1075
- old_locale = I18n.locale
1076
-
1077
- # Using a default locale of en.
1078
- page = PageSlugLocalizedHistory.new
1079
- page.title = 'Title on English'
1080
- page.save
1081
- I18n.locale = 'nl'
1082
- page.title = 'Title on Netherlands'
1083
- page.save
1084
- expect(page.title_translations).to eq('en' => 'Title on English', 'nl' => 'Title on Netherlands')
1085
-
1086
- I18n.locale = old_locale
1087
- page.title = 'Title on English'
1088
- expect(page.title_translations).to eq('en' => 'Title on English', 'nl' => 'Title on Netherlands')
1089
- expect(page._slugs_translations).to eq('en' => ['title-on-english'], 'nl' => ['title-on-netherlands'])
1090
- end
1091
-
1092
- it 'does not produce duplicate slugs when one has changed' do
1093
- old_locale = I18n.locale
1094
-
1095
- # Using a default locale of en.
1096
- page = PageSlugLocalizedHistory.new
1097
- page.title = 'Title on English'
1098
- page.save
1099
- I18n.locale = 'nl'
1100
- page.title = 'Title on Netherlands'
1101
- page.save
1102
- expect(page.title_translations).to eq('en' => 'Title on English', 'nl' => 'Title on Netherlands')
1103
-
1104
- I18n.locale = old_locale
1105
- page.title = 'Modified Title on English'
1106
- page.save
1107
- expect(page.title_translations).to eq('en' => 'Modified Title on English',
1108
- 'nl' => 'Title on Netherlands')
1109
- expect(page._slugs_translations).to eq('en' => ['title-on-english', 'modified-title-on-english'],
1110
- 'nl' => ['title-on-netherlands'])
1111
- end
1112
-
1113
- it 'does not produce duplicate slugs when transactions are set directly' do
1114
- page = PageSlugLocalizedHistory.new
1115
- page.title_translations = { 'en' => 'Title on English', 'nl' => 'Title on Netherlands' }
1116
- page.save
1117
- page.title_translations = { 'en' => 'Title on English', 'nl' => 'Title on Netherlands' }
1118
- page.save
1119
- expect(page._slugs_translations).to eq('en' => ['title-on-english'], 'nl' => ['title-on-netherlands'])
1120
- end
1121
-
1122
- it 'does not produce duplicate slugs when transactions are set directly and one has changed' do
1123
- page = PageSlugLocalizedHistory.new
1124
- page.title_translations = { 'en' => 'Title on English', 'nl' => 'Title on Netherlands' }
1125
- page.save
1126
- page.title_translations = { 'en' => 'Modified Title on English', 'nl' => 'Title on Netherlands' }
1127
- page.save
1128
- expect(page._slugs_translations).to eq('en' => ['title-on-english', 'modified-title-on-english'],
1129
- 'nl' => ['title-on-netherlands'])
1130
- end
1131
- end
1132
-
1133
- describe 'slug_max_length' do
1134
- before do
1135
- Author.create_indexes
1136
- end
1137
-
1138
- after do
1139
- Author.remove_indexes
1140
- end
1141
-
1142
- it 'can be assigned to nil' do
1143
- expect(Book.slug_max_length).to be nil
1144
- end
1145
-
1146
- it 'defaults to MONGO_INDEX_KEY_LIMIT_BYTES - 32' do
1147
- expect(Article.slug_max_length).to eq Mongoid::Slug::MONGO_INDEX_KEY_LIMIT_BYTES - 32
1148
- end
1149
-
1150
- it 'is assigned via max_length' do
1151
- expect(Author.slug_max_length).to eq 256
1152
- end
1153
-
1154
- it 'enforces max length of slug' do
1155
- author1 = Author.create!(last_name: 't' * 1024)
1156
- expect(author1.slug.length).to eq 256
1157
- expect(author1.slug.ends_with?('ttt')).to be true
1158
- author2 = Author.create!(last_name: 't' * 1024)
1159
- expect(author2.slug.length).to eq 258
1160
- expect(author2.slug.ends_with?('tt-1')).to be true
1161
- author3 = Author.create!(last_name: 't' * 1024)
1162
- expect(author3.slug.length).to eq 258
1163
- expect(author3.slug.ends_with?('tt-2')).to be true
1164
- end
1165
- end
1166
-
1167
- context 'has_many / belongs_to' do
1168
- let(:book) { Book.create!(title: 'War and Peace') }
1169
- it 'allows for duplicates with different slugs' do
1170
- Author.create!(first_name: 'Leo', last_name: 'Tostoy')
1171
- expect { book.authors.create!(first_name: 'Leo', last_name: 'Tostoy') }.to_not raise_error
1172
- end
1173
- end
1174
- end
1175
- end