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