vidibus-inheritance 0.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.rdoc +93 -0
- data/Rakefile +49 -0
- data/TODO +5 -0
- data/VERSION +1 -0
- data/lib/vidibus/inheritance/mongoid.rb +239 -0
- data/lib/vidibus/inheritance/validators/ancestor_validator.rb +13 -0
- data/lib/vidibus/inheritance/validators.rb +4 -0
- data/lib/vidibus/inheritance.rb +8 -0
- data/lib/vidibus-inheritance.rb +3 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +32 -0
- data/spec/vidibus/inheritance/mongoid_spec.rb +600 -0
- data/spec/vidibus/inheritance/validators/ancestor_validator_spec.rb +28 -0
- data/vidibus-inheritance.gemspec +78 -0
- metadata +176 -0
@@ -0,0 +1,600 @@
|
|
1
|
+
require "spec_helper"
|
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
|
+
describe "Vidibus::Inheritance::Mongoid" do
|
61
|
+
let(:ancestor) { Model.create }
|
62
|
+
let(:anna) { Model.create!(:name => "Anna", :age => 35) }
|
63
|
+
let(:inheritor) { Model.new }
|
64
|
+
|
65
|
+
describe "validation" do
|
66
|
+
it "should fail if ancestor does not have an UUID" do
|
67
|
+
invalid_ancestor = Clerk.create(:name => "John")
|
68
|
+
expect {
|
69
|
+
inheritor.ancestor = invalid_ancestor
|
70
|
+
}.to raise_error
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should fail if ancestor is of a different object type" do
|
74
|
+
invalid_ancestor = Manager.create(:name => "Robin")
|
75
|
+
inheritor.ancestor = invalid_ancestor
|
76
|
+
inheritor.should be_invalid
|
77
|
+
inheritor.errors[:ancestor].should have(1).error
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe "#mutated_attributes" do
|
82
|
+
it "should be an empty array by default" do
|
83
|
+
inheritor.mutated_attributes.should be_a_kind_of(Array)
|
84
|
+
inheritor.mutated_attributes.should be_empty
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should hold all attributes that have been set on creation" do
|
88
|
+
inheritor = Model.create(:name => "Anna")
|
89
|
+
inheritor.mutated_attributes.should eql(["name"])
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should hold all attributes that have been changed after creation" do
|
93
|
+
inheritor = Model.create
|
94
|
+
inheritor.name = "Anna"
|
95
|
+
inheritor.save
|
96
|
+
inheritor.mutated_attributes.should eql(["name"])
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should not contain inherited attributes when ancestor was added on creation" do
|
100
|
+
ancestor = Model.create(:name => "Jenny", :age => 23)
|
101
|
+
inheritor = Model.create(:ancestor => ancestor)
|
102
|
+
inheritor.mutated_attributes.should be_empty
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should not contain inherited attributes when ancestor was added after creation" do
|
106
|
+
inheritor = Model.create
|
107
|
+
inheritor.ancestor = Model.create(:name => "Jenny", :age => 23)
|
108
|
+
inheritor.save
|
109
|
+
inheritor.mutated_attributes.should be_empty
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should contain unique values" do
|
113
|
+
inheritor = Model.create(:name => "Anna")
|
114
|
+
inheritor.update_attributes(:name => "Jenny")
|
115
|
+
inheritor.mutated_attributes.should eql(["name"])
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe "#mutated?" do
|
120
|
+
it "should be false by default" do
|
121
|
+
inheritor.mutated?.should be_false
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should be true if attributes have been changed" do
|
125
|
+
inheritor.name = "Anna"
|
126
|
+
inheritor.save
|
127
|
+
inheritor.reload
|
128
|
+
inheritor.mutated?.should be_true
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should be true if mutated has been set to true" do
|
132
|
+
inheritor.mutated = true
|
133
|
+
inheritor.save!
|
134
|
+
inheritor.reload
|
135
|
+
inheritor.mutated?.should be_true
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
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
|
+
describe "#ancestor=" do
|
147
|
+
it "should set an ancestor object" do
|
148
|
+
inheritor.ancestor = ancestor
|
149
|
+
inheritor.ancestor.should eql(ancestor)
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should set a persistent ancestor object" do
|
153
|
+
inheritor.ancestor = ancestor
|
154
|
+
inheritor.save
|
155
|
+
inheritor.reload
|
156
|
+
inheritor.ancestor.should eql(ancestor)
|
157
|
+
end
|
158
|
+
|
159
|
+
it "should set the ancestor's uuid" do
|
160
|
+
inheritor.ancestor = ancestor
|
161
|
+
inheritor.ancestor_uuid.should eql(ancestor.uuid)
|
162
|
+
end
|
163
|
+
|
164
|
+
it "should not fail if ancestor is of a different object type" do
|
165
|
+
invalid_ancestor = Manager.create(:name => "Robin")
|
166
|
+
inheritor.ancestor = invalid_ancestor
|
167
|
+
inheritor.ancestor.should eql(invalid_ancestor)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
describe "#inherit!" do
|
172
|
+
it "should call #inherit_attributes once" do
|
173
|
+
stub(inheritor)._inherited { true }
|
174
|
+
stub(inheritor).inherit_attributes
|
175
|
+
inheritor.ancestor = anna
|
176
|
+
inheritor.inherit!
|
177
|
+
inheritor.should have_received.inherit_attributes.once.with_any_args
|
178
|
+
end
|
179
|
+
|
180
|
+
it "should call #save!" do
|
181
|
+
stub(inheritor).save!
|
182
|
+
inheritor.ancestor = anna
|
183
|
+
inheritor.inherit!
|
184
|
+
inheritor.should have_received.save!
|
185
|
+
end
|
186
|
+
|
187
|
+
context "with mutations" do
|
188
|
+
before { inheritor.update_attributes(:ancestor => anna, :name => "Jenny", :age => 19) }
|
189
|
+
|
190
|
+
it "should keep name and age" do
|
191
|
+
inheritor.inherit!
|
192
|
+
inheritor.name.should eql("Jenny")
|
193
|
+
inheritor.age.should eql(19)
|
194
|
+
end
|
195
|
+
|
196
|
+
it "should override mutated name attribute with option :reset => :name" do
|
197
|
+
inheritor.inherit!(:reset => :name)
|
198
|
+
inheritor.name.should eql("Anna")
|
199
|
+
inheritor.age.should eql(19)
|
200
|
+
end
|
201
|
+
|
202
|
+
it "should override mutated name and age with option :reset => [:name, :age]" do
|
203
|
+
inheritor.inherit!(:reset => [:name, :age])
|
204
|
+
inheritor.name.should eql("Anna")
|
205
|
+
inheritor.age.should eql(35)
|
206
|
+
end
|
207
|
+
|
208
|
+
it "should override all mutations with option :reset => true" do
|
209
|
+
inheritor.inherit!(:reset => true)
|
210
|
+
inheritor.name.should eql("Anna")
|
211
|
+
inheritor.age.should eql(35)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
describe "#inherit_from!" do
|
217
|
+
#let(:ancestor) { Model.create!(:name => "Anna", :age => 35) }
|
218
|
+
|
219
|
+
it "should set ancestor" do
|
220
|
+
inheritor.ancestor.should be_nil
|
221
|
+
inheritor.inherit_from!(ancestor)
|
222
|
+
inheritor.ancestor.should eql(ancestor)
|
223
|
+
end
|
224
|
+
|
225
|
+
it "should call #inherit!" do
|
226
|
+
stub(inheritor).inherit!
|
227
|
+
inheritor.inherit!
|
228
|
+
inheritor.should have_received(:inherit!)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
describe "#inheritors" do
|
233
|
+
it "should return all inheritors" do
|
234
|
+
inheritor1 = Model.create(:ancestor => ancestor)
|
235
|
+
inheritor2 = Model.create(:ancestor => ancestor)
|
236
|
+
ancestor.inheritors.should have(2).inheritors
|
237
|
+
ancestor.inheritors.should include(inheritor1)
|
238
|
+
ancestor.inheritors.should include(inheritor2)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
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)
|
247
|
+
end
|
248
|
+
|
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
|
255
|
+
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
|
263
|
+
end
|
264
|
+
|
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)
|
269
|
+
end
|
270
|
+
|
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)
|
275
|
+
end
|
276
|
+
|
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)
|
282
|
+
end
|
283
|
+
|
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)
|
288
|
+
end
|
289
|
+
|
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)
|
297
|
+
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")
|
305
|
+
end
|
306
|
+
|
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")
|
314
|
+
end
|
315
|
+
|
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)
|
321
|
+
end
|
322
|
+
|
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")
|
327
|
+
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
|
335
|
+
end
|
336
|
+
|
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
|
421
|
+
end
|
422
|
+
|
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
|
471
|
+
end
|
472
|
+
|
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
|
598
|
+
end
|
599
|
+
end
|
600
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "Vidibus::Inheritance::Validators::AncestorValidator" do
|
4
|
+
class ValidModel
|
5
|
+
include ActiveModel::Validations
|
6
|
+
attr_accessor :ancestor
|
7
|
+
validates :ancestor, :ancestor => true
|
8
|
+
end
|
9
|
+
|
10
|
+
class InvalidModel; end
|
11
|
+
|
12
|
+
let(:model) { ValidModel.new }
|
13
|
+
|
14
|
+
it "should be available as ancestor validator" do
|
15
|
+
Model.validators_on(:ancestor).first.should be_a_kind_of(Vidibus::Inheritance::Validators::AncestorValidator)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should validate an ancestor of same class" do
|
19
|
+
model.ancestor = ValidModel.new
|
20
|
+
model.valid?.should be_true
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should add an error, if ancestor is of a different class" do
|
24
|
+
model.ancestor = InvalidModel.new
|
25
|
+
model.valid?.should be_false
|
26
|
+
model.errors[:ancestor].should_not be_blank
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{vidibus-inheritance}
|
8
|
+
s.version = "0.3.6"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Andre Pankratz"]
|
12
|
+
s.date = %q{2010-08-09}
|
13
|
+
s.description = %q{This gem allows inheritance of objects for Rails 3 with Mongoid. It will update all attributes and embedded documents of inheritors when ancestor gets changed.}
|
14
|
+
s.email = %q{andre@vidibus.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc",
|
18
|
+
"TODO"
|
19
|
+
]
|
20
|
+
s.files = [
|
21
|
+
".document",
|
22
|
+
".gitignore",
|
23
|
+
"LICENSE",
|
24
|
+
"README.rdoc",
|
25
|
+
"Rakefile",
|
26
|
+
"TODO",
|
27
|
+
"VERSION",
|
28
|
+
"lib/vidibus-inheritance.rb",
|
29
|
+
"lib/vidibus/inheritance.rb",
|
30
|
+
"lib/vidibus/inheritance/mongoid.rb",
|
31
|
+
"lib/vidibus/inheritance/validators.rb",
|
32
|
+
"lib/vidibus/inheritance/validators/ancestor_validator.rb",
|
33
|
+
"spec/spec.opts",
|
34
|
+
"spec/spec_helper.rb",
|
35
|
+
"spec/vidibus/inheritance/mongoid_spec.rb",
|
36
|
+
"spec/vidibus/inheritance/validators/ancestor_validator_spec.rb",
|
37
|
+
"vidibus-inheritance.gemspec"
|
38
|
+
]
|
39
|
+
s.homepage = %q{http://github.com/vidibus/vidibus-inheritance}
|
40
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
41
|
+
s.require_paths = ["lib"]
|
42
|
+
s.rubygems_version = %q{1.3.7}
|
43
|
+
s.summary = %q{Provides inheritance for models.}
|
44
|
+
s.test_files = [
|
45
|
+
"spec/spec_helper.rb",
|
46
|
+
"spec/vidibus/inheritance/mongoid_spec.rb",
|
47
|
+
"spec/vidibus/inheritance/validators/ancestor_validator_spec.rb"
|
48
|
+
]
|
49
|
+
|
50
|
+
if s.respond_to? :specification_version then
|
51
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
52
|
+
s.specification_version = 3
|
53
|
+
|
54
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
55
|
+
s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
|
56
|
+
s.add_development_dependency(%q<relevance-rcov>, [">= 0"])
|
57
|
+
s.add_development_dependency(%q<mongoid>, ["= 2.0.0.beta.15"])
|
58
|
+
s.add_development_dependency(%q<rr>, [">= 0"])
|
59
|
+
s.add_runtime_dependency(%q<vidibus-core_extensions>, [">= 0"])
|
60
|
+
s.add_runtime_dependency(%q<vidibus-uuid>, [">= 0"])
|
61
|
+
else
|
62
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
63
|
+
s.add_dependency(%q<relevance-rcov>, [">= 0"])
|
64
|
+
s.add_dependency(%q<mongoid>, ["= 2.0.0.beta.15"])
|
65
|
+
s.add_dependency(%q<rr>, [">= 0"])
|
66
|
+
s.add_dependency(%q<vidibus-core_extensions>, [">= 0"])
|
67
|
+
s.add_dependency(%q<vidibus-uuid>, [">= 0"])
|
68
|
+
end
|
69
|
+
else
|
70
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
71
|
+
s.add_dependency(%q<relevance-rcov>, [">= 0"])
|
72
|
+
s.add_dependency(%q<mongoid>, ["= 2.0.0.beta.15"])
|
73
|
+
s.add_dependency(%q<rr>, [">= 0"])
|
74
|
+
s.add_dependency(%q<vidibus-core_extensions>, [">= 0"])
|
75
|
+
s.add_dependency(%q<vidibus-uuid>, [">= 0"])
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|