nobrainer-tree 0.0.1

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,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