mongoid-slug 6.0.1 → 7.0.0

Sign up to get free protection for your applications and to get access to all the features.
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