chrislloyd-eb_nested_set 0.3.2

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.
@@ -0,0 +1,54 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+ require File.dirname(__FILE__) + '/nested_set_behavior'
3
+
4
+ class Directory < ActiveRecord::Base
5
+ acts_as_nested_set :left => :lft, :right => :rgt
6
+
7
+ validates_presence_of :name
8
+ end
9
+
10
+ describe Directory do
11
+
12
+ def invalid_attributes(options = {})
13
+ return { }.merge(options)
14
+ end
15
+
16
+ def valid_attributes(options = {})
17
+ $directory_no = $directory_no ? $directory_no + 1 : 0
18
+ return { :name => "directory#{$directory_no}" }.merge(options)
19
+ end
20
+
21
+ before do
22
+ @model = Directory
23
+ @instance = @model.new
24
+ end
25
+
26
+ it_should_behave_like "all nested set models"
27
+
28
+ it "should throw an error when attempting to assign lft directly" do
29
+ lambda {
30
+ @instance.lft = 42
31
+ }.should raise_error(EvenBetterNestedSet::IllegalAssignmentError)
32
+ @instance.lft.should_not == 42
33
+ end
34
+
35
+ it "should throw an error when attempting to assign rgt directly" do
36
+ lambda {
37
+ @instance.rgt = 42
38
+ }.should raise_error(EvenBetterNestedSet::IllegalAssignmentError)
39
+ @instance.rgt.should_not == 42
40
+ end
41
+
42
+ it "should throw an error when mass assigning to lft" do
43
+ lambda {
44
+ @model.new(valid_attributes(:lft => 1))
45
+ }.should raise_error(EvenBetterNestedSet::IllegalAssignmentError)
46
+ end
47
+
48
+ it "should throw an error when mass assigning to rgt" do
49
+ lambda {
50
+ @model.new(valid_attributes(:rgt => 1))
51
+ }.should raise_error(EvenBetterNestedSet::IllegalAssignmentError)
52
+ end
53
+
54
+ end
@@ -0,0 +1,74 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+ require File.dirname(__FILE__) + '/nested_set_behavior'
3
+
4
+ class Employee < ActiveRecord::Base
5
+ acts_as_nested_set :scope => :company
6
+
7
+ validates_presence_of :name
8
+ end
9
+
10
+ describe Employee, "with nested sets for two different companies" do
11
+ before do
12
+ # Company 1...
13
+ Employee.with_options :company_id => 1 do |c1|
14
+ @c1_1 = c1.create!(:name => "Company 1 - 1")
15
+ @c1_2 = c1.create!(:name => "Company 1 - 2")
16
+
17
+ @c1_11 = c1.create!(:name => "Company 1 - 11", :parent => @c1_1)
18
+ @c1_12 = c1.create!(:name => "Company 1 - 12", :parent => @c1_1)
19
+
20
+ @c1_111 = c1.create!(:name => "Company 1 - 111", :parent => @c1_11)
21
+ end
22
+
23
+ # Company 2...
24
+ Employee.with_options :company_id => 2 do |c2|
25
+ @c2_1 = c2.create!(:name => "Company 2 - 1")
26
+ @c2_11 = c2.create!(:name => "Company 2 - 11", :parent => @c2_1)
27
+ end
28
+ end
29
+
30
+ after do
31
+ Employee.delete_all
32
+ end
33
+
34
+ it "should not allow a new employee in one company to be a child of an employee in the other company, when parent is assigned to" do
35
+ @employee = Employee.create(:company_id => 1, :parent => @c2_11)
36
+ @employee.errors[:parent_id].should_not be_nil
37
+ end
38
+
39
+ it "should not allow a new employee in one company to be a child of an employee in the other company, when parent_id is assigned to" do
40
+ @employee = Employee.create(:company_id => 1, :parent_id => @c2_11.id)
41
+ @employee.errors[:parent_id].should_not be_nil
42
+ end
43
+
44
+ it "should not allow an existing employee in one company to become a child of an employee in the other company, when parent is assigned to" do
45
+ @c1_11.parent = @c2_11
46
+ @c1_11.save
47
+ @c1_11.errors[:parent_id].should_not be_nil
48
+ end
49
+
50
+ it "should not allow an existing employee in one company to become a child of an employee in the other company, when parent_id is assigned to" do
51
+ @c1_11.parent_id = @c2_11.id
52
+ @c1_11.save
53
+ @c1_11.errors[:parent_id].should_not be_nil
54
+ end
55
+
56
+ it "should keep the tree for company 1 and for company 2 entirely disjoint" do
57
+ c1_tree = (@c1_1.family + @c1_2.family).flatten
58
+ c2_tree = @c2_1.family
59
+
60
+ (c1_tree & c2_tree).should be_empty
61
+ end
62
+
63
+ it "should return the correct descendants when retrieving via a database query" do
64
+ @c1_1.descendants.should == [@c1_11, @c1_111, @c1_12]
65
+ @c1_2.descendants.should == []
66
+ @c2_1.descendants.should == [@c2_11]
67
+ end
68
+
69
+ it "should return the correct levels when retrieving via a database query" do
70
+ @c1_1.family.map { |d| d.level }.should == [0, 1, 2, 1]
71
+ @c1_2.family.map { |d| d.level }.should == [0]
72
+ @c2_1.family.map { |d| d.level }.should == [0, 1]
73
+ end
74
+ end
@@ -0,0 +1,586 @@
1
+ describe "all nested set models", :shared => true do
2
+
3
+ describe @model, 'model with acts_as_nested_set' do
4
+
5
+ before do
6
+ @instance = @model.new(valid_attributes)
7
+ end
8
+
9
+ it "should change the parent_id in the database when a parent is assigned" do
10
+ without_changing_the_database do
11
+ @parent = @model.create!(valid_attributes)
12
+
13
+ @instance.parent = @parent
14
+ @instance.save!
15
+ @instance = @model.find(@instance.id)
16
+
17
+ @instance.parent_id.should == @parent.id
18
+ end
19
+ end
20
+
21
+ it "should change the parent_id in the database when a parent_id is assigned" do
22
+ without_changing_the_database do
23
+ @parent = @model.create!(valid_attributes)
24
+
25
+ @instance.parent_id = @parent.id
26
+ @instance.save!
27
+ @instance = @model.find(@instance.id)
28
+
29
+ @instance.parent_id.should == @parent.id
30
+ end
31
+ end
32
+
33
+ describe '#bounds' do
34
+
35
+ it "should return a range, from left to right" do
36
+ without_changing_the_database do
37
+ @instance.save!
38
+ @instance.left.should == 1
39
+ @instance.right.should == 2
40
+ @instance.bounds.should == (1..2)
41
+ end
42
+ end
43
+
44
+ end
45
+
46
+ end
47
+
48
+ describe @model, "with many descendants" do
49
+ before do
50
+ @r1 = @model.create!(valid_attributes)
51
+ @r2 = @model.create!(valid_attributes)
52
+ @r3 = @model.create!(valid_attributes)
53
+
54
+ @r1c1 = @model.create!(valid_attributes(:parent => @r1))
55
+ @r1c2 = @model.create!(valid_attributes(:parent => @r1))
56
+ @r1c3 = @model.create!(valid_attributes(:parent => @r1))
57
+ @r2c1 = @model.create!(valid_attributes(:parent => @r2))
58
+
59
+ @r1c1s1 = @model.create!(valid_attributes(:parent => @r1c1))
60
+ @r1c2s1 = @model.create!(valid_attributes(:parent => @r1c2))
61
+ @r1c2s2 = @model.create!(valid_attributes(:parent => @r1c2))
62
+ @r1c2s3 = @model.create!(valid_attributes(:parent => @r1c2))
63
+
64
+ @r1c2s2m1 = @model.create!(valid_attributes(:parent => @r1c2s2))
65
+ end
66
+
67
+ after do
68
+ @model.delete_all
69
+ end
70
+
71
+ it "should find all root nodes" do
72
+ @model.roots.all.should == [@r1, @r2, @r3]
73
+ end
74
+
75
+ it "should find a root nodes" do
76
+ @model.roots.first.should == @r1
77
+ end
78
+
79
+ it "should maintain the integrity of the tree if a node is deleted" do
80
+ @r1c2.destroy
81
+
82
+ @r1.reload
83
+ @r1c3.reload
84
+
85
+ @r1.bounds.should == (1..8)
86
+ @r1c3.bounds.should == (6..7)
87
+ end
88
+
89
+ it "should maintain the integrity of the tree if a node is moved" do
90
+ @r1c2.parent_id = @r2.id
91
+ @r1c2.save!
92
+
93
+ reload_models(@r1, @r1c3, @r2, @r1c2, @r1c2s1)
94
+
95
+ @r1.bounds.should == (1..8)
96
+ @r1c3.bounds.should == (6..7)
97
+ @r2.bounds.should == (9..22)
98
+ @r1c2.bounds.should == (12..21)
99
+ @r1c2s1.bounds.should == (13..14)
100
+ end
101
+
102
+ it "should change the parent, left and right in the database when a node is moved" do
103
+
104
+ @r1c2.parent_id = @r2.id
105
+
106
+ @r1c2.save!
107
+ @r1c2 = @model.find(@r1c2.id)
108
+
109
+ @r1c2.bounds.should == (12..21)
110
+ @r1c2.parent_id.should == @r2.id
111
+ end
112
+
113
+ it "should maintain the integrity of the tree if a node is moved to a root position" do
114
+ @r1c2.parent_id = nil
115
+ @r1c2.save!
116
+
117
+ reload_models(@r1, @r1c3, @r2, @r1c2, @r1c2s1)
118
+
119
+ @r1.bounds.should == (1..8)
120
+ @r1c3.bounds.should == (6..7)
121
+ @r1c2.bounds.should == (15..24)
122
+ @r1c2s1.bounds.should == (16..17)
123
+ end
124
+
125
+ it "should maintain the integrity of the tree if a node is moved to a root position by assigning a blank string (mass assignment)" do
126
+ @r1c2.parent_id = ""
127
+ @r1c2.save!
128
+
129
+ reload_models(@r1, @r1c3, @r2, @r1c2, @r1c2s1)
130
+
131
+ @r1.bounds.should == (1..8)
132
+ @r1c3.bounds.should == (6..7)
133
+ @r1c2.bounds.should == (15..24)
134
+ @r1c2s1.bounds.should == (16..17)
135
+ end
136
+
137
+ it "should maintain the integrity of the tree if a root is to a non-root position" do
138
+ @r1c2.reload
139
+ @r2.parent_id = @r1c2.id
140
+ @r2.save!
141
+
142
+ reload_models(@r1, @r2, @r2c1, @r1c3, @r3, @r1c2)
143
+
144
+ @r1.bounds.should == (1..22)
145
+ @r1c2.bounds.should == (6..19)
146
+ @r1c3.bounds.should == (20..21)
147
+ @r3.bounds.should == (23..24)
148
+ @r2.bounds.should == (15..18)
149
+ @r2c1.bounds.should == (16..17)
150
+ end
151
+
152
+ it "should maintain the integrity of the tree if a node is moved through the parent association" do
153
+ @r1c2.parent = @r2
154
+ @r1c2.save!
155
+
156
+ reload_models(@r1, @r1c3, @r2, @r1c2, @r1c2s1)
157
+
158
+ @r1.bounds.should == (1..8)
159
+ @r1c3.bounds.should == (6..7)
160
+ @r2.bounds.should == (9..22)
161
+ @r1c2.bounds.should == (12..21)
162
+ @r1c2s1.bounds.should == (13..14)
163
+ end
164
+
165
+ it "should maintain the integrity of the tree if a node is moved to a root position through the parent association" do
166
+ @r1c2.parent = nil
167
+ @r1c2.save!
168
+
169
+ reload_models(@r1, @r1c3, @r2, @r1c2, @r1c2s1)
170
+
171
+ @r1.bounds.should == (1..8)
172
+ @r1c3.bounds.should == (6..7)
173
+ @r1c2.bounds.should == (15..24)
174
+ @r1c2s1.bounds.should == (16..17)
175
+ end
176
+
177
+ it "should maintain the integrity of the tree if a root is to a non-root position through the parent association" do
178
+ @r1c2.reload
179
+ @r2.parent = @r1c2
180
+ @r2.save!
181
+
182
+ reload_models(@r1, @r2, @r2c1, @r1c3, @r3, @r1c2)
183
+
184
+ @r1.bounds.should == (1..22)
185
+ @r1c2.bounds.should == (6..19)
186
+ @r1c3.bounds.should == (20..21)
187
+ @r3.bounds.should == (23..24)
188
+ @r2.bounds.should == (15..18)
189
+ @r2c1.bounds.should == (16..17)
190
+ end
191
+
192
+ it "should be invalid if parent is a descendant" do
193
+ @r2.parent = @r2c1
194
+ @r2.should_not be_valid
195
+ end
196
+
197
+ it "should be invalid if parent is self" do
198
+ @r2.parent = @r2
199
+ @r2.should_not be_valid
200
+ end
201
+
202
+ describe ".nested_set" do
203
+ it "should find all nodes as a nested set and cache that data" do
204
+ roots = @model.nested_set
205
+
206
+ @model.delete_all
207
+
208
+ roots[0].should == @r1
209
+ roots[0].children[0].should == @r1c1
210
+ roots[0].children[0].children[0].should == @r1c1s1
211
+ roots[0].children[1].should == @r1c2
212
+ roots[0].children[1].children[0].should == @r1c2s1
213
+ roots[0].children[1].children[1].should == @r1c2s2
214
+ roots[0].children[1].children[1].children[0].should == @r1c2s2m1
215
+ roots[0].children[1].children[2].should == @r1c2s3
216
+ roots[0].children[2].should == @r1c3
217
+ roots[1].should == @r2
218
+ roots[1].children[0].should == @r2c1
219
+ roots[2].should == @r3
220
+
221
+ roots[1].children[0].parent.should == @r2
222
+ end
223
+ end
224
+
225
+ describe ".find_with_nested_set" do
226
+ it "should find a single node and cache it's descendants" do
227
+ node = @model.find_with_nested_set(@r1c2.id)
228
+
229
+ @model.delete_all
230
+
231
+ node.should == @r1c2
232
+ node.children[0].should == @r1c2s1
233
+ node.children[1].should == @r1c2s2
234
+ node.children[1].children[0].should == @r1c2s2m1
235
+ node.children[2].should == @r1c2s3
236
+ end
237
+
238
+ it "should allow find with conditions" do
239
+ node = @model.find_with_nested_set(:first, :conditions => { :id => @r1c2.id })
240
+
241
+ @model.delete_all
242
+
243
+ node.should == @r1c2
244
+ node.children[0].should == @r1c2s1
245
+ node.children[1].should == @r1c2s2
246
+ node.children[1].children[0].should == @r1c2s2m1
247
+ node.children[2].should == @r1c2s3
248
+ end
249
+
250
+ it "should allow find all with conditions" do
251
+ nodes = @model.find_with_nested_set(:all, :conditions => { :parent_id => @r1.id })
252
+
253
+ @model.delete_all
254
+
255
+ nodes[0].should == @r1c1
256
+ nodes[0].children[0].should == @r1c1s1
257
+ nodes[1].should == @r1c2
258
+ nodes[1].children[0].should == @r1c2s1
259
+ nodes[1].children[1].should == @r1c2s2
260
+ nodes[1].children[1].children[0].should == @r1c2s2m1
261
+ nodes[1].children[2].should == @r1c2s3
262
+ nodes[2].should == @r1c3
263
+ end
264
+ end
265
+
266
+ describe ".sort_nodes_to_nested_set" do
267
+ it "should accept a list of nodes and sort them to a nested set" do
268
+ roots = @model.sort_nodes_to_nested_set(@model.find(:all))
269
+ roots[0].should == @r1
270
+ roots[0].children[0].should == @r1c1
271
+ roots[0].children[0].children[0].should == @r1c1s1
272
+ roots[0].children[1].should == @r1c2
273
+ roots[0].children[1].children[0].should == @r1c2s1
274
+ roots[0].children[1].children[1].should == @r1c2s2
275
+ roots[0].children[1].children[1].children[0].should == @r1c2s2m1
276
+ roots[0].children[1].children[2].should == @r1c2s3
277
+ roots[0].children[2].should == @r1c3
278
+ roots[1].should == @r2
279
+ roots[1].children[0].should == @r2c1
280
+ roots[2].should == @r3
281
+ end
282
+ end
283
+
284
+ describe ".recalculate_nested_set" do
285
+ def values
286
+ @model.find(:all, :order => :id).map { |node| [node.left, node.right] }
287
+ end
288
+
289
+ before do
290
+ @model.find(:all, :order => :id).each do |i|
291
+ i.send(:set_boundaries, rand(1000), rand(1000))
292
+ i.save_without_validation!
293
+ end
294
+ end
295
+
296
+ it "should correctly restore the left and right values for a messed up nested set" do
297
+ @model.recalculate_nested_set
298
+ [@r1, @r2, @r3].each(&:reload)
299
+
300
+ expected = [
301
+ [@r1c1, @r1c2, @r1c3, @r1c1s1, @r1c2s1, @r1c2s2, @r1c2s3, @r1c2s2m1],
302
+ [@r2c1],
303
+ [],
304
+ [@r1c1s1],
305
+ [@r1c2s1, @r1c2s2, @r1c2s3, @r1c2s2m1],
306
+ [],
307
+ [],
308
+ [],
309
+ [],
310
+ [@r1c2s2m1],
311
+ [],
312
+ []
313
+ ]
314
+
315
+ [@r1, @r2, @r3, @r1c1, @r1c2, @r1c3, @r2c1, @r1c1s1, @r1c2s1, @r1c2s2, @r1c2s3, @r1c2s2m1].each_with_index do |node, i|
316
+ node.descendants.find(:all, :order => :id).should == expected[i]
317
+ end
318
+ end
319
+
320
+ it "should leave all records valid after running" do
321
+ @model.recalculate_nested_set
322
+ @model.find(:all).each do |node|
323
+ node.should be_valid
324
+ end
325
+ end
326
+ end
327
+
328
+ describe "#cache_nested_set" do
329
+ it "should cache all descendant nodes so that calls to #children or #parent don't hit the database" do
330
+ @r1c2.cache_nested_set
331
+
332
+ @model.delete_all
333
+
334
+ @r1c2.children[0].should == @r1c2s1
335
+ @r1c2.children[1].should == @r1c2s2
336
+ @r1c2.children[1].children[0].should == @r1c2s2m1
337
+ @r1c2.children[2].should == @r1c2s3
338
+
339
+ @r1c2.children[1].children[0].parent.should == @r1c2s2
340
+ end
341
+ end
342
+
343
+ describe "#parent" do
344
+ it "should find the parent node" do
345
+ @r1c1.parent.should == @r1
346
+ @r1c2s2.parent.should == @r1c2
347
+ @r1c2s2m1.parent.should == @r1c2s2
348
+ end
349
+ end
350
+
351
+ describe "#children" do
352
+ it "should find all nodes that are direct descendants of this one" do
353
+ @r1.children.should == [@r1c1, @r1c2, @r1c3]
354
+ @r1c2s2.children.should == [@r1c2s2m1]
355
+ end
356
+
357
+ it "should allow creation of children" do
358
+ child = @r1c2.children.create!(valid_attributes)
359
+
360
+ child.parent_id.should == @r1c2.id
361
+ child.bounds.should == (15..16)
362
+ end
363
+
364
+ it "should allow addition of children" do
365
+ @r2.children << @r1c2
366
+
367
+ reload_models(@r1, @r1c3, @r2, @r1c2, @r1c2s1)
368
+
369
+ @r1.bounds.should == (1..8)
370
+ @r1c3.bounds.should == (6..7)
371
+ @r2.bounds.should == (9..22)
372
+ @r1c2.bounds.should == (12..21)
373
+ @r1c2s1.bounds.should == (13..14)
374
+ end
375
+ end
376
+
377
+ describe "#patriarch" do
378
+ it "should find the root node that this node descended from" do
379
+ @r1c1.patriarch.should == @r1
380
+ @r1c2s2.patriarch.should == @r1
381
+ @r1c2s2m1.patriarch.should == @r1
382
+ @r2c1.patriarch.should == @r2
383
+ @r1.patriarch.should == @r1
384
+ end
385
+ end
386
+
387
+ describe "#root" do
388
+ it "should find the root node that this node descended from" do
389
+ @r1c1.root.should == @r1
390
+ @r1c2s2.root.should == @r1
391
+ @r1c2s2m1.root.should == @r1
392
+ @r2c1.root.should == @r2
393
+ @r1.root.should == @r1
394
+ end
395
+ end
396
+
397
+ describe "#root?" do
398
+ it "should be true if node doesn't have a parent" do
399
+ @r1.should be_root
400
+ @model.roots.should include(@r1)
401
+ @r1.parent.should be_nil
402
+ end
403
+ end
404
+
405
+ describe "#descendant_of(other_node)" do
406
+ it "should be true if other_node is an ancestor of node" do
407
+ reload_models @r1, @r1c2s2
408
+
409
+ @r1c2s2.should be_descendant_of(@r1)
410
+ @r1c2s2.ancestors.should include(@r1)
411
+ @r1.descendants.should include(@r1c2s2)
412
+ end
413
+ end
414
+
415
+ describe "#generation" do
416
+ it "should find all nodes in the same generation as this one for a root node" do
417
+ @r1.generation.should == [@r1, @r2, @r3]
418
+ end
419
+
420
+ it "should find all nodes in the same generation as this one" do
421
+ @r1c1.generation.should == [@r1c1, @r1c2, @r1c3]
422
+ end
423
+ end
424
+
425
+ describe "#siblings" do
426
+ it "should find all sibling nodes for a root node" do
427
+ @r1.siblings.should == [@r2, @r3]
428
+ end
429
+
430
+ it "should find all sibling nodes for a child node" do
431
+ @r1c1.siblings.should == [@r1c2, @r1c3]
432
+ end
433
+ end
434
+
435
+ describe "#descendants" do
436
+ it "should find all descendants of this node" do
437
+ @r1.descendants.should == [@r1c1, @r1c1s1, @r1c2, @r1c2s1, @r1c2s2, @r1c2s2m1, @r1c2s3, @r1c3]
438
+ end
439
+ end
440
+
441
+ describe "#family" do
442
+ it "should combine self and descendants" do
443
+ @r1.family.should == [@r1, @r1c1, @r1c1s1, @r1c2, @r1c2s1, @r1c2s2, @r1c2s2m1, @r1c2s3, @r1c3]
444
+ end
445
+ end
446
+
447
+ describe "#family_ids" do
448
+ it "should find all ids of the node's nested set" do
449
+ @r1c1.family_ids.should == [@r1c1.id, @r1c1s1.id]
450
+ @r1c2.family_ids.should == [@r1c2.id, @r1c2s1.id, @r1c2s2.id, @r1c2s2m1.id, @r1c2s3.id]
451
+ end
452
+ end
453
+
454
+ describe "#ancestors" do
455
+ it "should return a node's parent and its parent's parents" do
456
+ @r1c2s2m1.ancestors.should == [@r1c2s2, @r1c2, @r1]
457
+ end
458
+ end
459
+
460
+ describe "#lineage" do
461
+ it "should return a node, it's parent and its parent's parents" do
462
+ @r1c2s2m1.lineage.should == [@r1c2s2m1, @r1c2s2, @r1c2, @r1]
463
+ end
464
+ end
465
+
466
+ describe "#level" do
467
+ it "should give the depth from the node to its root" do
468
+ @r1.level.should == 0
469
+ @r1c2.level.should == 1
470
+ @r1c2s2.level.should == 2
471
+ @r1c2s2m1.level.should == 3
472
+ end
473
+ end
474
+
475
+ describe "#kin" do
476
+ it "should find the patriarch and all its descendants" do
477
+ @r1c2s2.kin.should == [@r1, @r1c1, @r1c1s1, @r1c2, @r1c2s1, @r1c2s2, @r1c2s2m1, @r1c2s3, @r1c3]
478
+ end
479
+ end
480
+
481
+ end
482
+
483
+ describe @model, "with acts_as_nested_set" do
484
+
485
+ it "should add a new root node if the parent is not set" do
486
+ without_changing_the_database do
487
+ @instance = @model.create!(valid_attributes)
488
+ @instance.parent_id.should be_nil
489
+
490
+ @instance.bounds.should == (1..2)
491
+ end
492
+ end
493
+
494
+ it "should add a new root node if the parent is not set and there already are some root nodes" do
495
+ without_changing_the_database do
496
+ @model.create!(valid_attributes)
497
+ @model.create!(valid_attributes)
498
+ @instance = @model.create!(valid_attributes)
499
+ @instance.reload
500
+
501
+ @instance.parent_id.should be_nil
502
+ @instance.bounds.should == (5..6)
503
+ end
504
+ end
505
+
506
+ it "should append a child node to a parent" do
507
+ without_changing_the_database do
508
+ @parent = @model.create!(valid_attributes)
509
+ @parent.bounds.should == (1..2)
510
+
511
+ @instance = @model.create!(valid_attributes(:parent => @parent))
512
+
513
+ @parent.reload
514
+
515
+ @instance.parent.should == @parent
516
+
517
+ @instance.bounds.should == (2..3)
518
+ @parent.bounds.should == (1..4)
519
+ end
520
+ end
521
+
522
+ it "should rollback changes if the save is not successfull for some reason" do
523
+ without_changing_the_database do
524
+ @parent = @model.create!(valid_attributes)
525
+ @parent.bounds.should == (1..2)
526
+
527
+ @instance = @model.create(invalid_attributes(:parent => @parent))
528
+ @instance.should be_a_new_record
529
+
530
+ @parent.reload
531
+
532
+ @parent.bounds.should == (1..2)
533
+ end
534
+ end
535
+
536
+ it "should append a child node to a parent and shift other nodes out of the way" do
537
+ without_changing_the_database do
538
+ @root1 = @model.create!(valid_attributes)
539
+ @root2 = @model.create!(valid_attributes)
540
+
541
+ @root1.bounds.should == (1..2)
542
+ @root2.bounds.should == (3..4)
543
+
544
+ @child1 = @model.create!(valid_attributes(:parent => @root1))
545
+ reload_models(@root1, @root2)
546
+
547
+ @root1.bounds.should == (1..4)
548
+ @root2.bounds.should == (5..6)
549
+ @child1.bounds.should == (2..3)
550
+
551
+ @child2 = @model.create!(valid_attributes(:parent => @root1))
552
+ reload_models(@root1, @root2, @child1)
553
+
554
+ @root1.bounds.should == (1..6)
555
+ @root2.bounds.should == (7..8)
556
+ @child1.bounds.should == (2..3)
557
+ @child2.bounds.should == (4..5)
558
+
559
+ @subchild1 = @model.create!(valid_attributes(:parent => @child2))
560
+ reload_models(@root1, @root2, @child1, @child2)
561
+
562
+ @root1.bounds.should == (1..8)
563
+ @root2.bounds.should == (9..10)
564
+ @child1.bounds.should == (2..3)
565
+ @child2.bounds.should == (4..7)
566
+ @subchild1.bounds.should == (5..6)
567
+
568
+ @subchild2 = @model.create!(valid_attributes(:parent => @child1))
569
+ reload_models(@root1, @root2, @child1, @child2, @subchild1)
570
+
571
+ @root1.bounds.should == (1..10)
572
+ @root2.bounds.should == (11..12)
573
+ @child1.bounds.should == (2..5)
574
+ @child2.bounds.should == (6..9)
575
+ @subchild1.bounds.should == (7..8)
576
+ @subchild2.bounds.should == (3..4)
577
+ end
578
+ end
579
+
580
+ end
581
+
582
+ def reload_models(*attrs)
583
+ attrs.each {|m| m.reload }
584
+ end
585
+
586
+ end