vidibus-inheritance 0.3.6 → 0.3.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,66 +1,10 @@
1
1
  require "spec_helper"
2
2
 
3
- class Model
4
- include Mongoid::Document
5
- include Mongoid::Timestamps
6
- include Vidibus::Uuid::Mongoid
7
- include Vidibus::Inheritance::Mongoid
8
- field :name
9
- field :age, :type => Integer
10
- embeds_many :children
11
- embeds_many :puppets
12
- embeds_one :location
13
- end
14
-
15
- class Child
16
- include Mongoid::Document
17
- field :name
18
- field :mutated, :type => Boolean
19
- validates :name, :presence => true
20
- embedded_in :model, :inverse_of => :children
21
- embeds_many :puppets
22
- embeds_one :location
23
- end
24
-
25
- class Puppet
26
- include Mongoid::Document
27
- field :name
28
- validates :name, :presence => true
29
- embedded_in :child, :inverse_of => :puppets
30
- embedded_in :model, :inverse_of => :puppets
31
- embedded_in :location, :inverse_of => :puppets
32
- embeds_one :location
33
- end
34
-
35
- class Location
36
- include Mongoid::Document
37
- field :name
38
- field :mutated, :type => Boolean
39
- validates :name, :presence => true
40
- embedded_in :model, :inverse_of => :location
41
- embedded_in :child, :inverse_of => :location
42
- embedded_in :puppet, :inverse_of => :location
43
- embeds_many :puppets
44
- end
45
-
46
- class Manager
47
- include Mongoid::Document
48
- include Vidibus::Uuid::Mongoid
49
- include Vidibus::Inheritance::Mongoid
50
- field :name
51
- validates :name, :presence => true
52
- end
53
-
54
- class Clerk
55
- include Mongoid::Document
56
- field :name
57
- validates :name, :presence => true
58
- end
59
-
60
3
  describe "Vidibus::Inheritance::Mongoid" do
61
4
  let(:ancestor) { Model.create }
62
- let(:anna) { Model.create!(:name => "Anna", :age => 35) }
63
5
  let(:inheritor) { Model.new }
6
+ let(:anna) { Model.create!(:name => "Anna", :age => 35) }
7
+ let(:jeanny) { Model.create!(:name => "Jeanny", :age => 17) }
64
8
 
65
9
  describe "validation" do
66
10
  it "should fail if ancestor does not have an UUID" do
@@ -114,6 +58,12 @@ describe "Vidibus::Inheritance::Mongoid" do
114
58
  inheritor.update_attributes(:name => "Jenny")
115
59
  inheritor.mutated_attributes.should eql(["name"])
116
60
  end
61
+
62
+ it "should be tracked if data gets modified in a before_validation callback" do
63
+ model = Trude.create
64
+ model.name.should eql("Trude")
65
+ model.mutated_attributes.should eql(["name"])
66
+ end
117
67
  end
118
68
 
119
69
  describe "#mutated?" do
@@ -136,13 +86,6 @@ describe "Vidibus::Inheritance::Mongoid" do
136
86
  end
137
87
  end
138
88
 
139
- describe "#ancestor" do
140
- it "should return an ancestor object by uuid" do
141
- inheritor.ancestor_uuid = ancestor.uuid
142
- inheritor.ancestor.should eql(ancestor)
143
- end
144
- end
145
-
146
89
  describe "#ancestor=" do
147
90
  it "should set an ancestor object" do
148
91
  inheritor.ancestor = ancestor
@@ -168,6 +111,21 @@ describe "Vidibus::Inheritance::Mongoid" do
168
111
  end
169
112
  end
170
113
 
114
+ describe "#ancestor" do
115
+ it "should return an ancestor object by uuid" do
116
+ inheritor.ancestor_uuid = ancestor.uuid
117
+ inheritor.ancestor.should eql(ancestor)
118
+ end
119
+ end
120
+
121
+ describe "#ancestors" do
122
+ it "should return a list of all ancestors" do
123
+ inheritor = Model.create!(:ancestor => ancestor)
124
+ nextgen = Model.create!(:ancestor => inheritor)
125
+ nextgen.ancestors.should eql([inheritor, ancestor])
126
+ end
127
+ end
128
+
171
129
  describe "#inherit!" do
172
130
  it "should call #inherit_attributes once" do
173
131
  stub(inheritor)._inherited { true }
@@ -233,368 +191,117 @@ describe "Vidibus::Inheritance::Mongoid" do
233
191
  it "should return all inheritors" do
234
192
  inheritor1 = Model.create(:ancestor => ancestor)
235
193
  inheritor2 = Model.create(:ancestor => ancestor)
236
- ancestor.inheritors.should have(2).inheritors
237
- ancestor.inheritors.should include(inheritor1)
238
- ancestor.inheritors.should include(inheritor2)
194
+ list = ancestor.inheritors.to_a
195
+ list.should have(2).inheritors
196
+ list.should include(inheritor1)
197
+ list.should include(inheritor2)
239
198
  end
240
199
  end
241
200
 
242
- describe "inheritance" do
243
- it "should happen when creating objects" do
244
- ancestor # trigger object creation before mocking
245
- mock.instance_of(Model).inherit_attributes
246
- Model.create!(:ancestor => ancestor)
201
+ describe "#inheritable_documents" do
202
+ it "should perform .inheritable_documents with current object" do
203
+ mock(Model).inheritable_documents(anna, {})
204
+ anna.inheritable_documents
247
205
  end
248
206
 
249
- it "should happen when ancestor did change" do
250
- inheritor = Model.create!
251
- inheritor.ancestor = ancestor
252
- stub(inheritor).inherit_attributes
253
- inheritor.save
254
- inheritor.should have_received.inherit_attributes
207
+ it "should allow options" do
208
+ mock(Model).inheritable_documents(anna, :keys => true)
209
+ anna.inheritable_documents(:keys => true)
255
210
  end
256
-
257
- it "should not happen when ancestor did not change" do
258
- inheritor = Model.create!(:ancestor => ancestor)
259
- dont_allow(inheritor).inherit
260
- inheritor.save
261
- # Does not work with RR:
262
- # inheritor.should_not have_received.inherit
211
+ end
212
+
213
+ describe "#clone!" do
214
+ it "should create a sibling" do
215
+ twin = anna.clone!
216
+ twin.should_not eql(anna)
217
+ twin.name.should eql(anna.name)
218
+ twin.age.should eql(anna.age)
263
219
  end
264
220
 
265
- it "should apply ancestor's attributes to inheritor" do
266
- inheritor.update_attributes(:ancestor => anna)
267
- inheritor.name.should eql("Anna")
268
- inheritor.age.should eql(35)
221
+ it "should preserve ancestor relation" do
222
+ anna.inherit_from!(ancestor)
223
+ twin = anna.clone!
224
+ twin.ancestor.should eql(ancestor)
269
225
  end
270
226
 
271
- it "should not inherit acquired attributes" do
272
- inheritor.update_attributes(:ancestor => ancestor)
273
- Model::ACQUIRED_ATTRIBUTES.should include("uuid")
274
- inheritor.uuid.should_not eql(ancestor.uuid)
227
+ it "should not clone inheritors (should it?)" do
228
+ anna.inherit_from!(ancestor)
229
+ twin = ancestor.clone!
230
+ twin.inheritors.should be_empty
275
231
  end
276
232
 
277
- it "should apply ancestor's attributes to inheritor but keep previously mutated attributes" do
278
- inheritor.update_attributes(:name => "Jenny")
279
- inheritor.update_attributes(:ancestor => anna)
280
- inheritor.name.should eql("Jenny")
281
- inheritor.age.should eql(35)
233
+ it "should clone embedded documents" do
234
+ anna.children.create(:name => "Lisa")
235
+ twin = anna.clone!
236
+ twin.children.should have(1).child
282
237
  end
283
238
 
284
- it "should apply ancestor's attributes to inheritor but keep recently mutated attributes" do
285
- inheritor.update_attributes(:ancestor => anna, :name => "Jenny")
286
- inheritor.name.should eql("Jenny")
287
- inheritor.age.should eql(35)
239
+ it "should clone a single embedded document" do
240
+ anna.create_location(:name => "Bathroom")
241
+ twin = anna.clone!
242
+ twin.location.name.should eql("Bathroom")
288
243
  end
289
244
 
290
- it "should allow switching the ancestor" do
291
- inheritor.inherit_from!(anna)
292
- another_ancestor = Model.create!(:name => "Leah", :age => 30)
293
- inheritor.inherit_from!(another_ancestor)
294
- inheritor.ancestor.should eql(another_ancestor)
295
- inheritor.name.should eql("Leah")
296
- inheritor.age.should eql(30)
245
+ it "should clone children of embedded documents" do
246
+ lisa = anna.children.create(:name => "Lisa")
247
+ lisa.puppets.create(:name => "Gonzo")
248
+ twin = anna.clone!
249
+ lisa_twin = twin.children.first
250
+ lisa_twin.puppets.should have(1).puppet
251
+ # lisa_twin.puppets.first._id.should_not eql(lisa_twin.puppets.first._id)
297
252
  end
298
-
299
- it "should apply changes on ancestor to inheritor" do
300
- inheritor.inherit_from!(anna)
301
- inheritor.name.should eql("Anna")
302
- anna.update_attributes(:name => "Leah")
303
- inheritor.reload
304
- inheritor.name.should eql("Leah")
253
+ end
254
+
255
+ describe ".inheritable_documents" do
256
+ it "should return embedded relations of a given object" do
257
+ docs = Model.inheritable_documents(anna)
258
+ docs.length.should eql(3)
259
+ docs.should have_key("location")
260
+ docs.should have_key("children")
261
+ docs.should have_key("puppets")
305
262
  end
306
263
 
307
- it "should preserve changes on inheritor" do
308
- inheritor = Model.create(:ancestor => anna)
309
- inheritor.update_attributes(:name => "Sara")
310
- inheritor.mutated_attributes.should eql(["name"])
311
- anna.update_attributes(:name => "Leah")
312
- inheritor.reload
313
- inheritor.name.should eql("Sara")
264
+ it "should return a collection of documents embedded by embeds_many" do
265
+ anna.children.create(:name => "Lisa")
266
+ docs = Model.inheritable_documents(anna)
267
+ docs["children"].should eql([anna.children.first])
314
268
  end
315
269
 
316
- it "should not update inheritor if acquired attributes were changed on ancestor" do
317
- inheritor.inherit_from!(ancestor)
318
- Model::ACQUIRED_ATTRIBUTES.should include("updated_at")
319
- dont_allow(ancestor.inheritors.first).inherit!
320
- ancestor.update_attributes(:updated_at => Time.now)
270
+ it "should return a document embedded by embeds_one" do
271
+ anna.create_location(:name => "Beach")
272
+ docs = Model.inheritable_documents(anna)
273
+ docs["location"].should eql(anna.location)
321
274
  end
322
275
 
323
- it "should not update inheritor if no inheritable attributes were changed on ancestor" do
324
- inheritor.inherit_from!(anna)
325
- dont_allow(anna.inheritors.first).inherit!
326
- anna.update_attributes(:name => "Anna")
276
+ it "should return the keys of embedded relations if option :keys is given" do
277
+ keys = Model.inheritable_documents(anna, :keys => true)
278
+ keys.should eql(%w[location puppets children])
327
279
  end
328
-
329
- it "should be applied before validation" do
330
- ancestor = Manager.create!(:name => "John")
331
- invalid = Manager.new
332
- invalid.should_not be_valid
333
- valid = Manager.new(:ancestor => ancestor)
334
- valid.should be_valid
280
+ end
281
+
282
+ describe ".roots" do
283
+ before do
284
+ inheritor.inherit_from!(anna)
285
+ jeanny
335
286
  end
336
287
 
337
- context "with embedded collections" do
338
- before do
339
- inheritor.inherit_from!(ancestor)
340
- ancestor.children.create(:name => "Han")
341
- ancestor.save
342
- inheritor.reload
343
- end
344
-
345
- it "should inherit subobjects on existing relationship" do
346
- inheritor.children.should have(1).child
347
- end
348
-
349
- it "should inherit subobjects when relationship gets established" do
350
- inheritor = Model.new
351
- inheritor.inherit_from!(ancestor)
352
- inheritor.children.should have(1).child
353
- inheritor.reload.children.should have(1).child
354
- end
355
-
356
- it "should add subobjects" do
357
- ancestor.children << Child.new(:name => "Leah")
358
- ancestor.save
359
- ancestor.children.should have(2).children
360
- inheritor.reload
361
- inheritor.children.should have(2).children
362
- end
363
-
364
- it "should add subobjects with saving" do
365
- ancestor.children << Child.new(:name => "Leah")
366
- ancestor.save
367
- ancestor.children.should have(2).children
368
- inheritor.save
369
- inheritor.reload
370
- inheritor.children.should have(2).children
371
- end
372
-
373
- it "should not add existing subobjects twice" do
374
- inheritor.inherit_from!(ancestor)
375
- inheritor.children.should have(1).child
376
- inheritor.reload.children.should have(1).child
377
- end
378
-
379
- it "should remove subobjects" do
380
- inheritor.children.should have(1).child
381
- ancestor.children.first.destroy
382
- ancestor.save
383
- inheritor.reload
384
- ancestor.children.should have(0).children
385
- inheritor.children.should have(0).children
386
- end
387
-
388
- it "should update subobjects" do
389
- ancestor.children.first.name = "Luke"
390
- ancestor.save
391
- inheritor.reload
392
- inheritor.children.first.name.should eql("Luke")
393
- end
394
-
395
- it "should call #update_inherited_attributes for updating subobjects, if available" do
396
- Child.send(:define_method, :update_inherited_attributes) do
397
- self.update_attributes(:name => "Callback")
398
- end
399
- Child.send(:protected, :update_inherited_attributes)
400
- ancestor.children.first.name = "Luke"
401
- ancestor.save
402
- Child.send(:remove_method, :update_inherited_attributes)
403
- inheritor.reload
404
- inheritor.children.first.name.should eql("Callback")
405
- end
406
-
407
- it "should exclude acquired attributes of subobjects" do
408
- ancestor.children.first.mutated = true
409
- ancestor.save
410
- ancestor.children.first.mutated.should be_true
411
- inheritor.reload
412
- inheritor.children.first.mutated.should be_false
413
- end
414
-
415
- it "should inherit embedded documents of subobjects" do
416
- ancestor.children.first.puppets.create(:name => "Goofy")
417
- ancestor.save
418
- inheritor.reload
419
- inheritor.children.first.puppets.should have(1).puppet
420
- end
288
+ it "should return all model that have no ancestor" do
289
+ Model.all.to_a.should have(3).models
290
+ list = Model.roots.to_a
291
+ list.should have(2).models
421
292
  end
422
293
 
423
- context "with embedded items" do
424
- before do
425
- inheritor.inherit_from!(ancestor)
426
- ancestor.create_location(:name => "Home")
427
- ancestor.save
428
- inheritor.reload
429
- end
430
-
431
- it "should inherit subobject on existing relationship" do
432
- inheritor.location.should_not be_nil
433
- end
434
-
435
- it "should inherit subobjects when relationship gets established" do
436
- inheritor = Model.new
437
- inheritor.inherit_from!(ancestor)
438
- inheritor.location.should_not be_nil
439
- end
440
-
441
- it "should update subobject" do
442
- ancestor.location.name = "Studio"
443
- ancestor.save
444
- inheritor.reload
445
- ancestor.location.name.should eql("Studio")
446
- inheritor.location.name.should eql("Studio")
447
- end
448
-
449
- it "should remove subobject" do
450
- ancestor.location.destroy
451
- ancestor.save
452
- inheritor.reload
453
- ancestor.location.should be_nil
454
- inheritor.location.should be_nil
455
- end
456
-
457
- it "should exclude acquired attributes of subobject" do
458
- ancestor.location.mutated = true
459
- ancestor.save
460
- ancestor.location.mutated.should be_true
461
- inheritor.reload
462
- inheritor.location.mutated.should be_false
463
- end
464
-
465
- it "should inherit embedded documents of subobject" do
466
- ancestor.location.puppets.create(:name => "Goofy")
467
- ancestor.save
468
- inheritor.reload
469
- inheritor.location.puppets.should have(1).puppet
470
- end
294
+ it "should accept sorting criteria" do
295
+ Model.all.to_a.should have(3).models
296
+ stub_time!(1.hour.since)
297
+ anna.update_attributes(:age => 20)
298
+ list = Model.roots.desc(:updated_at).to_a
299
+ list[0].updated_at.should > list[1].updated_at
471
300
  end
472
301
 
473
- context "across several generations" do
474
- let(:grand_ancestor) { Model.create!(:name => "Anna", :age => 97) }
475
-
476
- before do
477
- ancestor.inherit_from!(grand_ancestor)
478
- inheritor.inherit_from!(ancestor)
479
- end
480
-
481
- it "should apply changes on grand ancestor to inheritor" do
482
- inheritor.name.should eql("Anna")
483
- grand_ancestor.update_attributes(:name => "Leah")
484
- inheritor.reload
485
- inheritor.name.should eql("Leah")
486
- end
487
-
488
- it "should not apply changes on grand ancestor to inheritor if predecessor has mutations" do
489
- ancestor.update_attributes(:name => "Jenny")
490
- grand_ancestor.update_attributes(:name => "Leah")
491
- inheritor.reload
492
- inheritor.name.should eql("Jenny")
493
- end
494
-
495
- it "should allow resetting mutated attributes" do
496
- ancestor.update_attributes(:name => "Sara")
497
- ancestor.name.should eql("Sara")
498
- inheritor.reload
499
- inheritor.name.should eql("Sara")
500
- ancestor.inherit!(:reset => :name)
501
- ancestor.name.should eql("Anna")
502
- inheritor.reload
503
- inheritor.name.should eql("Anna")
504
- end
505
-
506
- context "with embedded collections" do
507
- before do
508
- grand_ancestor.children.create(:name => "Han")
509
- grand_ancestor.save
510
- ancestor.reload
511
- inheritor.reload
512
- end
513
-
514
- it "should inherit subobjects" do
515
- inheritor.children.should have(1).child
516
- end
517
-
518
- it "should add subobjects" do
519
- grand_ancestor.children << Child.new(:name => "Leah")
520
- grand_ancestor.save
521
- inheritor.reload
522
- inheritor.children.should have(2).children
523
- end
524
-
525
- it "should not add existing subobjects twice" do
526
- ancestor.inherit_from!(grand_ancestor)
527
- inheritor.reload
528
- inheritor.children.should have(1).child
529
- end
530
-
531
- it "should remove subobjects" do
532
- inheritor.children.should have(1).child
533
- grand_ancestor.children.first.destroy
534
- grand_ancestor.save
535
- grand_ancestor.children.should have(0).children
536
- inheritor.reload
537
- inheritor.children.should have(0).children
538
- end
539
-
540
- it "should update subobjects" do
541
- grand_ancestor.children.first.name = "Luke"
542
- grand_ancestor.save
543
- grand_ancestor.children.first.name.should eql("Luke")
544
- inheritor.reload
545
- inheritor.children.first.name.should eql("Luke")
546
- end
547
-
548
- it "should inherit embedded documents of subobjects" do
549
- grand_ancestor.children.first.puppets.create(:name => "Goofy")
550
- grand_ancestor.save
551
- inheritor.reload
552
- inheritor.children.first.puppets.should have(1).puppet
553
- end
554
- end
555
-
556
- context "with embedded items" do
557
- before do
558
- grand_ancestor.create_location(:name => "Home")
559
- grand_ancestor.save
560
- ancestor.reload
561
- inheritor.reload
562
- end
563
-
564
- it "should inherit subobject on existing relationship" do
565
- inheritor.location.should_not be_nil
566
- end
567
-
568
- it "should inherit subobject when relationship gets established" do
569
- ancestor = Model.new
570
- ancestor.inherit_from!(grand_ancestor)
571
- inheritor = Model.new
572
- inheritor.inherit_from!(ancestor)
573
- inheritor.location.should_not be_nil
574
- end
575
-
576
- it "should update subobject" do
577
- grand_ancestor.location.name = "Studio"
578
- grand_ancestor.save
579
- inheritor.reload
580
- inheritor.location.name.should eql("Studio")
581
- end
582
-
583
- it "should remove subobject" do
584
- grand_ancestor.location.destroy
585
- grand_ancestor.save
586
- inheritor.reload
587
- grand_ancestor.location.should be_nil
588
- inheritor.location.should be_nil
589
- end
590
-
591
- it "should inherit embedded documents of subobject" do
592
- grand_ancestor.location.puppets.create(:name => "Goofy")
593
- grand_ancestor.save
594
- inheritor.reload
595
- inheritor.location.puppets.should have(1).puppet
596
- end
597
- end
302
+ it "should accept conditions" do
303
+ list = Model.roots.where(:name => ancestor.name).to_a
304
+ list.should have(1).models
598
305
  end
599
306
  end
600
307
  end