mongoid_slug 3.0.0 → 3.1.0

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