nobrainer-tree 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,344 @@
1
+ require 'spec_helper'
2
+
3
+ describe NoBrainer::Tree::Ordering do
4
+
5
+ subject { OrderedNode }
6
+
7
+ it "should store position as an Integer with a default of nil" do
8
+ f = OrderedNode.fields[:position]
9
+ expect(f).not_to be_nil
10
+ expect(f[:type]).to eq(Integer)
11
+ expect(f[:default]).not_to be
12
+ end
13
+
14
+ describe 'when saved' do
15
+ before(:each) do
16
+ setup_tree <<-ENDTREE
17
+ - root:
18
+ - child:
19
+ - subchild:
20
+ - subsubchild
21
+ - other_root:
22
+ - other_child
23
+ - another_child
24
+ ENDTREE
25
+ end
26
+
27
+ it "should assign a default position of 0 to each node without a sibling" do
28
+ expect(node(:child).position).to eq(0)
29
+ expect(node(:subchild).position).to eq(0)
30
+ expect(node(:subsubchild).position).to eq(0)
31
+ end
32
+
33
+ it "should place siblings at the end of the list by default" do
34
+ expect(node(:root).position).to eq(0)
35
+ expect(node(:other_root).position).to eq(1)
36
+ expect(node(:other_child).position).to eq(0)
37
+ expect(node(:another_child).position).to eq(1)
38
+ end
39
+
40
+ it "should move a node to the end of a list when it is moved to a new parent" do
41
+ other_root = node(:other_root)
42
+ child = node(:child)
43
+ expect(child.position).to eq(0)
44
+ child.parent = other_root
45
+ child.save
46
+ expect(child.position).to eq(2)
47
+ end
48
+
49
+ it "should correctly reposition siblings when one of them is removed" do
50
+ node(:other_child).destroy
51
+ expect(node!(:another_child).position).to eq(0)
52
+ end
53
+
54
+ it "should correctly reposition siblings when one of them is added to another parent" do
55
+ node(:other_child).parent = node(:root)
56
+ node(:other_child).save
57
+ expect(node!(:another_child).position).to eq(0)
58
+ end
59
+
60
+ it "should correctly reposition siblings when the parent is changed" do
61
+ other_child = node(:other_child)
62
+ other_child.parent = node(:root)
63
+ other_child.save
64
+ expect(node!(:another_child).position).to eq(0)
65
+ end
66
+
67
+ it "should not reposition siblings when it's not yet saved" do
68
+ new_node = OrderedNode.new(:name => 'new')
69
+ new_node.parent = node(:root)
70
+ expect(new_node).not_to receive(:reposition_former_siblings)
71
+ new_node.save
72
+ end
73
+ end
74
+
75
+ describe 'destroy strategies' do
76
+ before(:each) do
77
+ setup_tree <<-ENDTREE
78
+ - root:
79
+ - child:
80
+ - subchild
81
+ - other_child
82
+ - other_root
83
+ ENDTREE
84
+ end
85
+
86
+ describe ':move_children_to_parent' do
87
+ it "should set its childen's parent_id to the documents parent_id" do
88
+ node!(:child).move_children_to_parent
89
+ expect(node!(:child)).to be_leaf
90
+ expect(node!(:root).children.to_a).to eq([node(:child), node(:other_child), node(:subchild)])
91
+ end
92
+ end
93
+ end
94
+
95
+ describe 'utility methods' do
96
+ before(:each) do
97
+ setup_tree <<-ENDTREE
98
+ - first_root:
99
+ - first_child_of_first_root
100
+ - second_child_of_first_root
101
+ - second_root
102
+ - third_root
103
+ ENDTREE
104
+ end
105
+
106
+ describe '#lower_siblings' do
107
+ it "should return a collection of siblings lower on the list" do
108
+ node(:second_child_of_first_root).reload
109
+ expect(node(:first_root).lower_siblings.to_a).to eq([node(:second_root), node(:third_root)])
110
+ expect(node(:second_root).lower_siblings.to_a).to eq([node(:third_root)])
111
+ expect(node(:third_root).lower_siblings.to_a).to eq([])
112
+ expect(node(:first_child_of_first_root).lower_siblings.to_a).to eq([node(:second_child_of_first_root)])
113
+ expect(node(:second_child_of_first_root).lower_siblings.to_a).to eq([])
114
+ end
115
+ end
116
+
117
+ describe '#higher_siblings' do
118
+ it "should return a collection of siblings lower on the list" do
119
+ expect(node(:first_root).higher_siblings.to_a).to eq([])
120
+ expect(node(:second_root).higher_siblings.to_a).to eq([node(:first_root)])
121
+ expect(node(:third_root).higher_siblings.to_a).to eq([node(:first_root), node(:second_root)])
122
+ expect(node(:first_child_of_first_root).higher_siblings.to_a).to eq([])
123
+ expect(node(:second_child_of_first_root).higher_siblings.to_a).to eq([node(:first_child_of_first_root)])
124
+ end
125
+ end
126
+
127
+ describe '#at_top?' do
128
+ it "should return true when the node is first in the list" do
129
+ expect(node(:first_root)).to be_at_top
130
+ expect(node(:first_child_of_first_root)).to be_at_top
131
+ end
132
+
133
+ it "should return false when the node is not first in the list" do
134
+ expect(node(:second_root)).not_to be_at_top
135
+ expect(node(:third_root)).not_to be_at_top
136
+ expect(node(:second_child_of_first_root)).not_to be_at_top
137
+ end
138
+ end
139
+
140
+ describe '#at_bottom?' do
141
+ it "should return true when the node is last in the list" do
142
+ expect(node(:third_root)).to be_at_bottom
143
+ expect(node(:second_child_of_first_root)).to be_at_bottom
144
+ end
145
+
146
+ it "should return false when the node is not last in the list" do
147
+ expect(node(:first_root)).not_to be_at_bottom
148
+ expect(node(:second_root)).not_to be_at_bottom
149
+ expect(node(:first_child_of_first_root)).not_to be_at_bottom
150
+ end
151
+ end
152
+
153
+ describe '#last_sibling_in_list' do
154
+ it "should return the last sibling in the list containing the current sibling" do
155
+ expect(node(:first_root).last_sibling_in_list).to eq(node(:third_root))
156
+ expect(node(:second_root).last_sibling_in_list).to eq(node(:third_root))
157
+ expect(node(:third_root).last_sibling_in_list).to eq(node(:third_root))
158
+ end
159
+ end
160
+
161
+ describe '#first_sibling_in_list' do
162
+ it "should return the first sibling in the list containing the current sibling" do
163
+ expect(node(:first_root).first_sibling_in_list).to eq(node(:first_root))
164
+ expect(node(:second_root).first_sibling_in_list).to eq(node(:first_root))
165
+ expect(node(:third_root).first_sibling_in_list).to eq(node(:first_root))
166
+ end
167
+ end
168
+
169
+ describe '#ancestors' do
170
+ it "should be returned in the correct order" do
171
+ setup_tree <<-ENDTREE
172
+ - root:
173
+ - level_1_a
174
+ - level_1_b:
175
+ - level_2_a:
176
+ - leaf
177
+ ENDTREE
178
+
179
+ expect(node!(:leaf).ancestors.to_a).to eq([node(:root), node(:level_1_b), node(:level_2_a)])
180
+ end
181
+
182
+ it "should return the ancestors in correct order even after rearranging" do
183
+ setup_tree <<-ENDTREE
184
+ - root:
185
+ - child:
186
+ - subchild
187
+ ENDTREE
188
+
189
+
190
+ child = node(:child); child.parent = nil; child.save
191
+ root = node(:root); root.parent = node(:child); root.save
192
+ subchild = node(:subchild); subchild.parent = root; subchild.save
193
+
194
+ expect(subchild.ancestors.to_a).to eq([child, root])
195
+ end
196
+ end
197
+ end
198
+
199
+ describe 'moving nodes around' do
200
+ before(:each) do
201
+ setup_tree <<-ENDTREE
202
+ - first_root:
203
+ - first_child_of_first_root
204
+ - second_child_of_first_root
205
+ - second_root:
206
+ - first_child_of_second_root
207
+ - third_root:
208
+ - first
209
+ - second
210
+ - third
211
+ ENDTREE
212
+ end
213
+
214
+ describe '#move_below' do
215
+ it 'should fix positions within the current list when moving an sibling away from its current parent' do
216
+ node_to_move = node!(:first_child_of_first_root)
217
+ node_to_move.move_below(node!(:first_child_of_second_root))
218
+ expect(node!(:second_child_of_first_root).position).to eq(0)
219
+ end
220
+
221
+ it 'should work when moving to a different parent' do
222
+ node_to_move = node(:first_child_of_first_root)
223
+ new_parent = node(:second_root)
224
+ node_to_move.move_below(node(:first_child_of_second_root))
225
+ node_to_move.reload
226
+ expect(node_to_move).to be_at_bottom
227
+ expect(node(:first_child_of_second_root)).to be_at_top
228
+ end
229
+
230
+ it 'should be able to move the first node below the second node' do
231
+ first_node = node(:first_root)
232
+ second_node = node(:second_root)
233
+ first_node.move_below(second_node)
234
+ first_node.reload
235
+ second_node.reload
236
+ expect(second_node).to be_at_top
237
+ expect(first_node.higher_siblings.to_a).to eq([second_node])
238
+ end
239
+
240
+ it 'should be able to move the last node below the first node' do
241
+ first_node = node(:first_root)
242
+ last_node = node(:third_root)
243
+ last_node.move_below(first_node)
244
+ first_node.reload
245
+ last_node.reload
246
+ expect(last_node).not_to be_at_bottom
247
+ expect(node(:second_root)).to be_at_bottom
248
+ expect(last_node.higher_siblings.to_a).to eq([first_node])
249
+ end
250
+ end
251
+
252
+ describe '#move_above' do
253
+ it 'should fix positions within the current list when moving an sibling away from its current parent' do
254
+ node_to_move = node!(:first_child_of_first_root)
255
+ node_to_move.move_above(node!(:first_child_of_second_root))
256
+ expect(node!(:second_child_of_first_root).position).to eq(0)
257
+ end
258
+
259
+ it 'should work when moving to a different parent' do
260
+ node_to_move = node(:first_child_of_first_root)
261
+ new_parent = node(:second_root)
262
+ node_to_move.move_above(node(:first_child_of_second_root))
263
+ node_to_move.reload
264
+ expect(node_to_move).to be_at_top
265
+ expect(node(:first_child_of_second_root)).to be_at_bottom
266
+ end
267
+
268
+ it 'should be able to move the last node above the second node' do
269
+ last_node = node(:third_root)
270
+ second_node = node(:second_root)
271
+ last_node.move_above(second_node)
272
+ last_node.reload
273
+ second_node.reload
274
+ expect(second_node).to be_at_bottom
275
+ expect(last_node.higher_siblings.to_a).to eq([node(:first_root)])
276
+ end
277
+
278
+ it 'should be able to move the first node above the last node' do
279
+ first_node = node(:first_root)
280
+ last_node = node(:third_root)
281
+ first_node.move_above(last_node)
282
+ first_node.reload
283
+ last_node.reload
284
+ expect(node(:second_root)).to be_at_top
285
+ expect(first_node.higher_siblings.to_a).to eq([node(:second_root)])
286
+ end
287
+ end
288
+
289
+ describe "#move_to_top" do
290
+ it "should return true when attempting to move the first sibling" do
291
+ expect(node(:first_root).move_to_top).to eq(true)
292
+ expect(node(:first_child_of_first_root).move_to_top).to eq(true)
293
+ end
294
+
295
+ it "should be able to move the last sibling to the top" do
296
+ first_node = node(:first_root)
297
+ last_node = node(:third_root)
298
+ last_node.move_to_top
299
+ first_node.reload
300
+ expect(last_node).to be_at_top
301
+ expect(first_node).not_to be_at_top
302
+ expect(first_node.higher_siblings.to_a).to eq([last_node])
303
+ expect(last_node.lower_siblings.to_a).to eq([first_node, node(:second_root)])
304
+ end
305
+ end
306
+
307
+ describe "#move_to_bottom" do
308
+ it "should return true when attempting to move the last sibling" do
309
+ expect(node(:third_root).move_to_bottom).to eq(true)
310
+ expect(node(:second_child_of_first_root).move_to_bottom).to eq(true)
311
+ end
312
+
313
+ it "should be able to move the first sibling to the bottom" do
314
+ first_node = node(:first_root)
315
+ middle_node = node(:second_root)
316
+ last_node = node(:third_root)
317
+ first_node.move_to_bottom
318
+ middle_node.reload
319
+ last_node.reload
320
+ expect(first_node).not_to be_at_top
321
+ expect(first_node).to be_at_bottom
322
+ expect(last_node).not_to be_at_bottom
323
+ expect(last_node).not_to be_at_top
324
+ expect(middle_node).to be_at_top
325
+ expect(first_node.lower_siblings.to_a).to eq([])
326
+ expect(last_node.higher_siblings.to_a).to eq([middle_node])
327
+ end
328
+ end
329
+
330
+ describe "#move_up" do
331
+ it "should correctly move nodes up" do
332
+ node!(:third).move_up
333
+ expect(node!(:third_root).children).to eq([node(:first), node(:third), node(:second)])
334
+ end
335
+ end
336
+
337
+ describe "#move_down" do
338
+ it "should correctly move nodes down" do
339
+ node!(:first).move_down
340
+ expect(node!(:third_root).children).to eq([node(:second), node(:first), node(:third)])
341
+ end
342
+ end
343
+ end # moving nodes around
344
+ end # NoBrainer::Tree::Ordering
@@ -0,0 +1,178 @@
1
+ require 'spec_helper'
2
+
3
+ describe NoBrainer::Tree::Traversal do
4
+
5
+ subject { OrderedNode }
6
+
7
+ describe '#traverse' do
8
+
9
+ subject { Node.new }
10
+
11
+ [:depth_first, :breadth_first].each do |method|
12
+ it "should support #{method} traversal" do
13
+ expect { subject.traverse(method) {} }.to_not raise_error
14
+ end
15
+ end
16
+
17
+ it "should complain about unsupported traversal methods" do
18
+ expect { subject.traverse('non_existing') {} }.to raise_error
19
+ end
20
+
21
+ it "should default to depth_first traversal" do
22
+ expect(subject).to receive(:depth_first_traversal)
23
+ subject.traverse {}
24
+ end
25
+ end
26
+
27
+ describe 'depth first traversal' do
28
+
29
+ describe 'with unmodified tree' do
30
+ before do
31
+ setup_tree <<-ENDTREE
32
+ node1:
33
+ - node2:
34
+ - node3
35
+ - node4:
36
+ - node5
37
+ - node6
38
+ - node7
39
+ ENDTREE
40
+ end
41
+
42
+ it "should traverse correctly" do
43
+ result = []
44
+ node!(:node1).traverse(:depth_first) { |node| result << node }
45
+ expect(result.collect { |n| n.name.to_sym }).to eq([:node1, :node2, :node3, :node4, :node5, :node6, :node7])
46
+ end
47
+
48
+ it "should return and array containing the results of the block for each node" do
49
+ result = node!(:node1).traverse(:depth_first) { |n| n.name.to_sym }
50
+ expect(result).to eq([:node1, :node2, :node3, :node4, :node5, :node6, :node7])
51
+ end
52
+ end
53
+
54
+ describe 'with merged trees' do
55
+ before do
56
+ setup_tree <<-ENDTREE
57
+ - node4:
58
+ - node5
59
+ - node6:
60
+ - node7
61
+
62
+ - node1:
63
+ - node2:
64
+ - node3
65
+ ENDTREE
66
+
67
+ node!(:node4).parent = node!(:node1)
68
+ node(:node4).save
69
+ end
70
+
71
+ it "should traverse correctly" do
72
+ result = node(:node1).traverse(:depth_first) { |n| n.name.to_sym }
73
+ expect(result).to eq([:node1, :node2, :node3, :node4, :node5, :node6, :node7])
74
+ end
75
+ end
76
+
77
+ describe 'with reordered nodes' do
78
+
79
+ before do
80
+ setup_tree <<-ENDTREE
81
+ node1:
82
+ - node2:
83
+ - node3
84
+ - node4:
85
+ - node6
86
+ - node5
87
+ - node7
88
+ ENDTREE
89
+
90
+ node(:node5).move_above(node(:node6))
91
+ end
92
+
93
+ it 'should iterate through the nodes in the correct order' do
94
+ result = []
95
+ node!(:node1).traverse(:depth_first) { |node| result << node }
96
+ expect(result.collect { |n| n.name.to_sym }).to eq([:node1, :node2, :node3, :node4, :node5, :node6, :node7])
97
+ end
98
+
99
+ it 'should return the nodes in the correct order' do
100
+ result = node!(:node1).traverse(:depth_first)
101
+ expect(result.collect { |n| n.name.to_sym }).to eq([:node1, :node2, :node3, :node4, :node5, :node6, :node7])
102
+ end
103
+
104
+ end
105
+
106
+ end
107
+
108
+ describe 'breadth first traversal' do
109
+
110
+ before do
111
+ setup_tree <<-ENDTREE
112
+ node1:
113
+ - node2:
114
+ - node5
115
+ - node3:
116
+ - node6
117
+ - node7
118
+ - node4
119
+ ENDTREE
120
+ end
121
+
122
+ it "should traverse correctly" do
123
+ result = []
124
+ node!(:node1).traverse(:breadth_first) { |n| result << n }
125
+ expect(result.collect { |n| n.name.to_sym }).to eq([:node1, :node2, :node3, :node4, :node5, :node6, :node7])
126
+ end
127
+
128
+ it "should return and array containing the results of the block for each node" do
129
+ result = node!(:node1).traverse(:breadth_first) { |n| n.name.to_sym }
130
+ expect(result).to eq([:node1, :node2, :node3, :node4, :node5, :node6, :node7])
131
+ end
132
+
133
+ end
134
+
135
+ describe '.traverse' do
136
+ before :each do
137
+ setup_tree <<-ENDTREE
138
+ - root1
139
+ - root2
140
+ ENDTREE
141
+
142
+ @root1 = node(:root1)
143
+ @root2 = node(:root2)
144
+
145
+ allow(Node).to receive(:roots).and_return [@root1, @root2]
146
+ end
147
+
148
+ it 'should grab each root' do
149
+ expect(Node).to receive(:roots).and_return []
150
+
151
+ expect(Node.traverse).to eq([])
152
+ end
153
+
154
+ it 'should default the "type" arg to :depth_first' do
155
+ expect(@root1).to receive(:traverse).with(:depth_first).and_return([])
156
+ expect(@root2).to receive(:traverse).with(:depth_first).and_return([])
157
+
158
+ expect(Node.traverse).to eq([])
159
+ end
160
+
161
+ it 'should traverse each root' do
162
+ expect(@root1).to receive(:traverse).and_return([1, 2])
163
+ expect(@root2).to receive(:traverse).and_return([3, 4])
164
+
165
+ expect(Node.traverse).to eq([1, 2, 3, 4])
166
+ end
167
+
168
+ describe 'when the "type" arg is :breadth_first' do
169
+
170
+ it 'should traverse breadth-first' do
171
+ expect(@root1).to receive(:traverse).with(:breadth_first).and_return([])
172
+ expect(@root2).to receive(:traverse).with(:breadth_first).and_return([])
173
+
174
+ Node.traverse :breadth_first
175
+ end
176
+ end
177
+ end
178
+ end