mongoid-slug 6.0.0 → 7.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +20 -20
  3. data/README.md +392 -336
  4. data/lib/mongoid/slug/criteria.rb +111 -107
  5. data/lib/mongoid/slug/{index.rb → index_builder.rb} +69 -45
  6. data/lib/mongoid/slug/railtie.rb +11 -9
  7. data/lib/mongoid/slug/slug_id_strategy.rb +5 -3
  8. data/lib/mongoid/slug/unique_slug.rb +172 -173
  9. data/lib/mongoid/slug/version.rb +7 -5
  10. data/lib/mongoid/slug.rb +333 -328
  11. data/lib/mongoid_slug.rb +4 -2
  12. data/lib/tasks/mongoid_slug.rake +17 -19
  13. metadata +13 -173
  14. data/spec/models/alias.rb +0 -6
  15. data/spec/models/article.rb +0 -9
  16. data/spec/models/artist.rb +0 -8
  17. data/spec/models/artwork.rb +0 -10
  18. data/spec/models/author.rb +0 -15
  19. data/spec/models/author_polymorphic.rb +0 -15
  20. data/spec/models/book.rb +0 -12
  21. data/spec/models/book_polymorphic.rb +0 -12
  22. data/spec/models/caption.rb +0 -17
  23. data/spec/models/entity.rb +0 -11
  24. data/spec/models/friend.rb +0 -7
  25. data/spec/models/incorrect_slug_persistence.rb +0 -9
  26. data/spec/models/integer_id.rb +0 -9
  27. data/spec/models/magazine.rb +0 -7
  28. data/spec/models/page.rb +0 -9
  29. data/spec/models/page_localize.rb +0 -9
  30. data/spec/models/page_slug_localized.rb +0 -9
  31. data/spec/models/page_slug_localized_custom.rb +0 -10
  32. data/spec/models/page_slug_localized_history.rb +0 -9
  33. data/spec/models/partner.rb +0 -7
  34. data/spec/models/person.rb +0 -12
  35. data/spec/models/relationship.rb +0 -8
  36. data/spec/models/string_id.rb +0 -9
  37. data/spec/models/subject.rb +0 -7
  38. data/spec/models/without_slug.rb +0 -5
  39. data/spec/mongoid/criteria_spec.rb +0 -207
  40. data/spec/mongoid/index_spec.rb +0 -33
  41. data/spec/mongoid/slug_spec.rb +0 -1169
  42. data/spec/shared/indexes.rb +0 -41
  43. data/spec/spec_helper.rb +0 -61
  44. data/spec/tasks/mongoid_slug_rake_spec.rb +0 -73
@@ -1,1169 +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
- it '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
- it '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
- it 'succeeds with a model with a max length' do
612
- expect do
613
- author = Author.create!(last_name: 't' * 1025)
614
- expect(author.slug.length).to eq 256
615
- end.to_not raise_error
616
- end
617
- end
618
-
619
- context 'when slug is scoped by a reference association' do
620
- subject { Author }
621
- it_should_behave_like 'does not have an index', _slugs: 1
622
- end
623
-
624
- context 'for subclass scope' do
625
- context 'when slug is not scoped by a reference association' do
626
- subject { BookPolymorphic }
627
- it_should_behave_like 'has an index', { _type: 1, _slugs: 1 }, unique: nil, sparse: nil
628
- end
629
-
630
- context 'when slug is scoped by a reference association' do
631
- subject { AuthorPolymorphic }
632
- it_should_behave_like 'does not have an index', _type: 1, _slugs: 1
633
- end
634
-
635
- context 'when the object has STI' do
636
- it 'scopes by the subclass' do
637
- b = BookPolymorphic.create!(title: 'Book')
638
- expect(b.slug).to eq('book')
639
-
640
- b2 = BookPolymorphic.create!(title: 'Book')
641
- expect(b2.slug).to eq('book-1')
642
-
643
- c = ComicBookPolymorphic.create!(title: 'Book')
644
- expect(c.slug).to eq('book')
645
-
646
- c2 = ComicBookPolymorphic.create!(title: 'Book')
647
- expect(c2.slug).to eq('book-1')
648
-
649
- expect(BookPolymorphic.find('book')).to eq(b)
650
- expect(BookPolymorphic.find('book-1')).to eq(b2)
651
- expect(ComicBookPolymorphic.find('book')).to eq(c)
652
- expect(ComicBookPolymorphic.find('book-1')).to eq(c2)
653
- end
654
- end
655
- end
656
- end
657
-
658
- context 'for reserved words' do
659
- context 'when the :reserve option is used on the model' do
660
- it 'does not use the reserved slugs' do
661
- friend1 = Friend.create(name: 'foo')
662
- expect(friend1.slugs).not_to include('foo')
663
- expect(friend1.slugs).to include('foo-1')
664
-
665
- friend2 = Friend.create(name: 'bar')
666
- expect(friend2.slugs).not_to include('bar')
667
- expect(friend2.slugs).to include('bar-1')
668
-
669
- friend3 = Friend.create(name: 'en')
670
- expect(friend3.slugs).not_to include('en')
671
- expect(friend3.slugs).to include('en-1')
672
- end
673
-
674
- it 'should start with concatenation -1' do
675
- friend1 = Friend.create(name: 'foo')
676
- expect(friend1.slugs).to include('foo-1')
677
- friend2 = Friend.create(name: 'foo')
678
- expect(friend2.slugs).to include('foo-2')
679
- end
680
-
681
- %w[new edit].each do |word|
682
- it "should overwrite the default reserved words allowing the word '#{word}'" do
683
- friend = Friend.create(name: word)
684
- expect(friend.slugs).to include word
685
- end
686
- end
687
- end
688
- context 'when the model does not have any reserved words set' do
689
- %w[new edit].each do |word|
690
- it "does not use the default reserved word '#{word}'" do
691
- book = Book.create(title: word)
692
- expect(book.slugs).not_to include word
693
- expect(book.slugs).to include("#{word}-1")
694
- end
695
- end
696
- end
697
-
698
- context 'when the object has STI' do
699
- it 'scopes by the superclass' do
700
- book = Book.create(title: 'Anti Oedipus')
701
- comic_book = ComicBook.create(title: 'Anti Oedipus')
702
- expect(comic_book.slugs).not_to eql(book.slugs)
703
- end
704
-
705
- it 'scopes by the subclass' do
706
- book = BookPolymorphic.create(title: 'Anti Oedipus')
707
- comic_book = ComicBookPolymorphic.create(title: 'Anti Oedipus')
708
- expect(comic_book.slugs).to eql(book.slugs)
709
-
710
- expect(BookPolymorphic.find(book.slug)).to eq(book)
711
- expect(ComicBookPolymorphic.find(comic_book.slug)).to eq(comic_book)
712
- end
713
- end
714
-
715
- context 'when slug defined on alias of field' do
716
- it 'should use accessor, not alias' do
717
- pseudonim = Alias.create(author_name: 'Max Stirner')
718
- expect(pseudonim.slugs).to include('max-stirner')
719
- end
720
- end
721
-
722
- describe '#to_param' do
723
- context 'when called on a new record' do
724
- let(:book) { Book.new }
725
-
726
- it 'should return nil' do
727
- expect(book.to_param).to eq book._id.to_s
728
- end
729
-
730
- it 'should not persist the record' do
731
- book.to_param
732
- expect(book).not_to be_persisted
733
- end
734
- end
735
-
736
- context 'when called on an existing record with no slug' do
737
- let!(:book_no_slug) do
738
- if Mongoid::Compatibility::Version.mongoid5_or_newer?
739
- Book.collection.insert_one(title: 'Proust and Signs')
740
- else
741
- Book.collection.insert(title: 'Proust and Signs')
742
- end
743
- Book.where(title: 'Proust and Signs').first
744
- end
745
-
746
- it 'should return the id if there is no slug' do
747
- expect(book_no_slug.to_param).to eq(book_no_slug.id.to_s)
748
- expect(book_no_slug.slugs).to be_nil
749
- end
750
-
751
- it 'should not persist the record' do
752
- expect(book_no_slug.to_param).to eq(book_no_slug._id.to_s)
753
- end
754
- end
755
- end
756
-
757
- describe '#_slugs_changed?' do
758
- before do
759
- Book.create(title: 'A Thousand Plateaus')
760
- end
761
-
762
- let(:book) { Book.first }
763
-
764
- it 'is initially unchanged' do
765
- expect(book._slugs_changed?).to be_falsey
766
- end
767
-
768
- it 'tracks changes' do
769
- book.slugs = ['Anti Oedipus']
770
- expect(book._slugs_changed?).to be_truthy
771
- end
772
- end
773
-
774
- describe 'when regular expression matches, but document does not' do
775
- let!(:book_1) { Book.create(title: 'book-1') }
776
- let!(:book_2) { Book.create(title: 'book') }
777
- let!(:book_3) { Book.create(title: 'book') }
778
-
779
- it 'book_2 should have the user supplied title without -1 after it' do
780
- expect(book_2.to_param).to eql 'book'
781
- end
782
-
783
- it 'book_3 should have a generated slug' do
784
- expect(book_3.to_param).to eql 'book-2'
785
- end
786
- end
787
-
788
- context 'when the slugged field is set manually' do
789
- context 'when it set to a non-empty string' do
790
- it 'respects the provided slug' do
791
- book = Book.create(title: 'A Thousand Plateaus', slugs: ['not-what-you-expected'])
792
- expect(book.to_param).to eql 'not-what-you-expected'
793
- end
794
-
795
- it 'ensures uniqueness' do
796
- book1 = Book.create(title: 'A Thousand Plateaus', slugs: ['not-what-you-expected'])
797
- expect(book1.to_param).to eql 'not-what-you-expected'
798
- if Mongoid::Compatibility::Version.mongoid5_or_newer?
799
- expect do
800
- Book.create(title: 'A Thousand Plateaus', slugs: ['not-what-you-expected'])
801
- end.to raise_error Mongo::Error::OperationFailure, /duplicate/
802
- elsif Mongoid::Compatibility::Version.mongoid4?
803
- expect do
804
- Book.create(title: 'A Thousand Plateaus', slugs: ['not-what-you-expected'])
805
- end.to raise_error Moped::Errors::OperationFailure, /duplicate/
806
- end
807
- end
808
-
809
- it 'updates the slug when a new one is passed in' do
810
- book = Book.create(title: 'A Thousand Plateaus', slugs: ['not-what-you-expected'])
811
- book.slugs = ['not-it-either']
812
- book.save
813
- expect(book.to_param).to eql 'not-it-either'
814
- end
815
-
816
- it 'updates the slug when a new one is appended' do
817
- book = Book.create(title: 'A Thousand Plateaus', slugs: ['not-what-you-expected'])
818
- book.slugs.push 'not-it-either'
819
- book.save
820
- expect(book.to_param).to eql 'not-it-either'
821
- end
822
-
823
- it 'updates the slug to a unique slug when a new one is appended' do
824
- Book.create(title: 'Sleepyhead')
825
- book2 = Book.create(title: 'A Thousand Plateaus')
826
- book2.slugs.push 'sleepyhead'
827
- if Mongoid::Compatibility::Version.mongoid5_or_newer?
828
- expect do
829
- book2.save
830
- end.to raise_error Mongo::Error::OperationFailure, /duplicate/
831
- elsif Mongoid::Compatibility::Version.mongoid4?
832
- expect do
833
- book2.save
834
- end.to raise_error Moped::Errors::OperationFailure, /duplicate/
835
- end
836
- end
837
- end
838
-
839
- context 'when it is set to an empty string' do
840
- it 'generate a new one' do
841
- book = Book.create(title: 'A Thousand Plateaus')
842
- expect(book.to_param).to eql 'a-thousand-plateaus'
843
- end
844
- end
845
- end
846
-
847
- context 'slug can be localized' do
848
- before(:each) do
849
- @old_locale = I18n.locale
850
- end
851
-
852
- after(:each) do
853
- I18n.locale = @old_locale
854
- end
855
-
856
- it 'generates a new slug for each localization' do
857
- page = PageSlugLocalized.new
858
- page.title = 'Title on English'
859
- page.save
860
- expect(page.slug).to eql 'title-on-english'
861
- I18n.locale = :nl
862
- page.title = 'Title on Netherlands'
863
- page.save
864
- expect(page.slug).to eql 'title-on-netherlands'
865
- end
866
-
867
- it 'returns _id if no slug' do
868
- page = PageSlugLocalized.new
869
- page.title = 'Title on English'
870
- page.save
871
- expect(page.slug).to eql 'title-on-english'
872
- I18n.locale = :nl
873
- expect(page.slug).to eql page._id.to_s
874
- end
875
-
876
- it 'fallbacks if slug not localized yet' do
877
- page = PageSlugLocalized.new
878
- page.title = 'Title on English'
879
- page.save
880
- expect(page.slug).to eql 'title-on-english'
881
- I18n.locale = :nl
882
- expect(page.slug).to eql page._id.to_s
883
-
884
- # Turn on i18n fallback
885
- require 'i18n/backend/fallbacks'
886
- I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
887
- ::I18n.fallbacks[:nl] = %i[nl en]
888
- expect(page.slug).to eql 'title-on-english'
889
- fallback_slug = page.slug
890
-
891
- fallback_page = begin
892
- PageSlugLocalized.find(fallback_slug)
893
- rescue StandardError
894
- nil
895
- end
896
- expect(fallback_page).to eq(page)
897
-
898
- # Restore fallback for next tests
899
- ::I18n.fallbacks[:nl] = [:nl]
900
- end
901
-
902
- it 'returns a default slug if not localized' do
903
- page = PageLocalize.new
904
- page.title = 'Title on English'
905
- page.save
906
- expect(page.slug).to eql 'title-on-english'
907
- I18n.locale = :nl
908
- page.title = 'Title on Netherlands'
909
- expect(page.slug).to eql 'title-on-english'
910
- page.save
911
- expect(page.slug).to eql 'title-on-netherlands'
912
- end
913
-
914
- it 'slugs properly when translations are set directly' do
915
- page = PageSlugLocalized.new
916
- page.title_translations = { 'en' => 'Title on English', 'nl' => 'Title on Netherlands' }
917
- page.save
918
- expect(page._slugs_translations).to eq('en' => ['title-on-english'], 'nl' => ['title-on-netherlands'])
919
- end
920
-
921
- it 'exact same title multiple langauges' do
922
- page = PageSlugLocalized.new
923
- page.title_translations = { 'en' => 'Title on English', 'nl' => 'Title on English' }
924
- page.save
925
- expect(page._slugs_translations).to eq('en' => ['title-on-english'], 'nl' => ['title-on-english'])
926
-
927
- page = PageSlugLocalized.create(title_translations: { 'en' => 'Title on English2', 'nl' => 'Title on English2' })
928
- expect(page._slugs_translations).to eq('en' => ['title-on-english2'], 'nl' => ['title-on-english2'])
929
- end
930
-
931
- it 'does not produce duplicate slugs' do
932
- old_locale = I18n.locale
933
-
934
- # Using a default locale of en.
935
- page = PageSlugLocalized.new
936
- page.title = 'Title on English'
937
- page.save
938
- I18n.locale = 'nl'
939
- page.title = 'Title on Netherlands'
940
- page.save
941
- expect(page.title_translations).to eq('en' => 'Title on English', 'nl' => 'Title on Netherlands')
942
-
943
- I18n.locale = old_locale
944
- page.title = 'Title on English'
945
- expect(page.title_translations).to eq('en' => 'Title on English', 'nl' => 'Title on Netherlands')
946
- expect(page._slugs_translations).to eq('en' => ['title-on-english'], 'nl' => ['title-on-netherlands'])
947
- end
948
-
949
- it 'does not produce duplicate slugs when one has changed' do
950
- old_locale = I18n.locale
951
-
952
- # Using a default locale of en.
953
- page = PageSlugLocalized.new
954
- page.title = 'Title on English'
955
- page.save
956
- I18n.locale = 'nl'
957
- page.title = 'Title on Netherlands'
958
- page.save
959
- expect(page.title_translations).to eq('en' => 'Title on English', 'nl' => 'Title on Netherlands')
960
-
961
- I18n.locale = old_locale
962
- page.title = 'Modified Title on English'
963
- page.save
964
- expect(page.title_translations).to eq('en' => 'Modified Title on English',
965
- 'nl' => 'Title on Netherlands')
966
- expect(page._slugs_translations).to eq('en' => ['modified-title-on-english'],
967
- 'nl' => ['title-on-netherlands'])
968
- end
969
-
970
- it 'does not produce duplicate slugs when transactions are set directly' do
971
- page = PageSlugLocalized.new
972
- page.title_translations = { 'en' => 'Title on English', 'nl' => 'Title on Netherlands' }
973
- page.save
974
- page.title_translations = { 'en' => 'Title on English', 'nl' => 'Title on Netherlands' }
975
- page.save
976
- expect(page._slugs_translations).to eq('en' => ['title-on-english'], 'nl' => ['title-on-netherlands'])
977
- end
978
-
979
- it 'does not produce duplicate slugs when transactions are set directly and one has changed' do
980
- page = PageSlugLocalized.new
981
- page.title_translations = { 'en' => 'Title on English', 'nl' => 'Title on Netherlands' }
982
- page.save
983
- page.title_translations = { 'en' => 'Modified Title on English',
984
- 'nl' => 'Title on Netherlands' }
985
- page.save
986
- expect(page._slugs_translations).to eq('en' => ['modified-title-on-english'],
987
- 'nl' => ['title-on-netherlands'])
988
- end
989
-
990
- it 'works with a custom slug strategy' do
991
- page = PageSlugLocalizedCustom.new
992
- page.title = 'a title for the slug'
993
- page.save
994
- expect(page._slugs_translations).to eq('en' => ['a-title-for-the-slug'], 'nl' => ['a-title-for-the-slug'])
995
- end
996
- end
997
-
998
- context 'slug can be localized when using history' do
999
- before(:each) do
1000
- @old_locale = I18n.locale
1001
- end
1002
-
1003
- after(:each) do
1004
- I18n.locale = @old_locale
1005
- end
1006
-
1007
- it 'generate a new slug for each localization and keep history' do
1008
- old_locale = I18n.locale
1009
-
1010
- page = PageSlugLocalizedHistory.new
1011
- page.title = 'Title on English'
1012
- page.save
1013
- expect(page.slug).to eql 'title-on-english'
1014
- I18n.locale = :nl
1015
- page.title = 'Title on Netherlands'
1016
- page.save
1017
- expect(page.slug).to eql 'title-on-netherlands'
1018
- I18n.locale = old_locale
1019
- page.title = 'Modified title on English'
1020
- page.save
1021
- expect(page.slug).to eql 'modified-title-on-english'
1022
- expect(page.slug).to include('title-on-english')
1023
- I18n.locale = :nl
1024
- page.title = 'Modified title on Netherlands'
1025
- page.save
1026
- expect(page.slug).to eql 'modified-title-on-netherlands'
1027
- expect(page.slug).to include('title-on-netherlands')
1028
- end
1029
-
1030
- it 'returns _id if no slug' do
1031
- page = PageSlugLocalizedHistory.new
1032
- page.title = 'Title on English'
1033
- page.save
1034
- expect(page.slug).to eql 'title-on-english'
1035
- I18n.locale = :nl
1036
- expect(page.slug).to eql page._id.to_s
1037
- end
1038
-
1039
- it 'fallbacks if slug not localized yet' do
1040
- page = PageSlugLocalizedHistory.new
1041
- page.title = 'Title on English'
1042
- page.save
1043
- expect(page.slug).to eql 'title-on-english'
1044
- I18n.locale = :nl
1045
- expect(page.slug).to eql page._id.to_s
1046
-
1047
- # Turn on i18n fallback
1048
- require 'i18n/backend/fallbacks'
1049
- I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
1050
- ::I18n.fallbacks[:nl] = %i[nl en]
1051
- expect(page.slug).to eql 'title-on-english'
1052
- fallback_slug = page.slug
1053
-
1054
- fallback_page = begin
1055
- PageSlugLocalizedHistory.find(fallback_slug)
1056
- rescue StandardError
1057
- nil
1058
- end
1059
- expect(fallback_page).to eq(page)
1060
- end
1061
-
1062
- it 'slugs properly when translations are set directly' do
1063
- page = PageSlugLocalizedHistory.new
1064
- page.title_translations = { 'en' => 'Title on English', 'nl' => 'Title on Netherlands' }
1065
- page.save
1066
- page.title_translations = { 'en' => 'Modified Title on English',
1067
- 'nl' => 'Modified Title on Netherlands' }
1068
- page.save
1069
- expect(page._slugs_translations).to eq('en' => ['title-on-english', 'modified-title-on-english'],
1070
- 'nl' => ['title-on-netherlands', 'modified-title-on-netherlands'])
1071
- end
1072
-
1073
- it 'does not produce duplicate slugs' do
1074
- old_locale = I18n.locale
1075
-
1076
- # Using a default locale of en.
1077
- page = PageSlugLocalizedHistory.new
1078
- page.title = 'Title on English'
1079
- page.save
1080
- I18n.locale = 'nl'
1081
- page.title = 'Title on Netherlands'
1082
- page.save
1083
- expect(page.title_translations).to eq('en' => 'Title on English', 'nl' => 'Title on Netherlands')
1084
-
1085
- I18n.locale = old_locale
1086
- page.title = 'Title on English'
1087
- expect(page.title_translations).to eq('en' => 'Title on English', 'nl' => 'Title on Netherlands')
1088
- expect(page._slugs_translations).to eq('en' => ['title-on-english'], 'nl' => ['title-on-netherlands'])
1089
- end
1090
-
1091
- it 'does not produce duplicate slugs when one has changed' do
1092
- old_locale = I18n.locale
1093
-
1094
- # Using a default locale of en.
1095
- page = PageSlugLocalizedHistory.new
1096
- page.title = 'Title on English'
1097
- page.save
1098
- I18n.locale = 'nl'
1099
- page.title = 'Title on Netherlands'
1100
- page.save
1101
- expect(page.title_translations).to eq('en' => 'Title on English', 'nl' => 'Title on Netherlands')
1102
-
1103
- I18n.locale = old_locale
1104
- page.title = 'Modified Title on English'
1105
- page.save
1106
- expect(page.title_translations).to eq('en' => 'Modified Title on English',
1107
- 'nl' => 'Title on Netherlands')
1108
- expect(page._slugs_translations).to eq('en' => ['title-on-english', 'modified-title-on-english'],
1109
- 'nl' => ['title-on-netherlands'])
1110
- end
1111
-
1112
- it 'does not produce duplicate slugs when transactions are set directly' do
1113
- page = PageSlugLocalizedHistory.new
1114
- page.title_translations = { 'en' => 'Title on English', 'nl' => 'Title on Netherlands' }
1115
- page.save
1116
- page.title_translations = { 'en' => 'Title on English', 'nl' => 'Title on Netherlands' }
1117
- page.save
1118
- expect(page._slugs_translations).to eq('en' => ['title-on-english'], 'nl' => ['title-on-netherlands'])
1119
- end
1120
-
1121
- it 'does not produce duplicate slugs when transactions are set directly and one has changed' do
1122
- page = PageSlugLocalizedHistory.new
1123
- page.title_translations = { 'en' => 'Title on English', 'nl' => 'Title on Netherlands' }
1124
- page.save
1125
- page.title_translations = { 'en' => 'Modified Title on English', 'nl' => 'Title on Netherlands' }
1126
- page.save
1127
- expect(page._slugs_translations).to eq('en' => ['title-on-english', 'modified-title-on-english'],
1128
- 'nl' => ['title-on-netherlands'])
1129
- end
1130
- end
1131
-
1132
- describe 'slug_max_length' do
1133
- before do
1134
- Author.create_indexes
1135
- end
1136
- after do
1137
- Author.remove_indexes
1138
- end
1139
- it 'can be assigned to nil' do
1140
- expect(Book.slug_max_length).to be nil
1141
- end
1142
- it 'defaults to MONGO_INDEX_KEY_LIMIT_BYTES - 32' do
1143
- expect(Article.slug_max_length).to eq Mongoid::Slug::MONGO_INDEX_KEY_LIMIT_BYTES - 32
1144
- end
1145
- it 'is assigned via max_length' do
1146
- expect(Author.slug_max_length).to eq 256
1147
- end
1148
- it 'enforces max length of slug' do
1149
- author1 = Author.create!(last_name: 't' * 1024)
1150
- expect(author1.slug.length).to eq 256
1151
- expect(author1.slug.ends_with?('ttt')).to be true
1152
- author2 = Author.create!(last_name: 't' * 1024)
1153
- expect(author2.slug.length).to eq 258
1154
- expect(author2.slug.ends_with?('tt-1')).to be true
1155
- author3 = Author.create!(last_name: 't' * 1024)
1156
- expect(author3.slug.length).to eq 258
1157
- expect(author3.slug.ends_with?('tt-2')).to be true
1158
- end
1159
- end
1160
-
1161
- context 'has_many / belongs_to' do
1162
- let(:book) { Book.create!(title: 'War and Peace') }
1163
- it 'allows for duplicates with different slugs' do
1164
- Author.create!(first_name: 'Leo', last_name: 'Tostoy')
1165
- expect { book.authors.create!(first_name: 'Leo', last_name: 'Tostoy') }.to_not raise_error
1166
- end
1167
- end
1168
- end
1169
- end