mongoid-slug 4.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 +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