mongoid-tree-rational 0.1.0

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,177 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mongoid::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(:node1).children << node(:node4)
68
+ end
69
+
70
+ it "should traverse correctly" do
71
+ result = node(:node1).traverse(:depth_first) { |n| n.name.to_sym }
72
+ expect(result).to eq([:node1, :node2, :node3, :node4, :node5, :node6, :node7])
73
+ end
74
+ end
75
+
76
+ describe 'with reordered nodes' do
77
+
78
+ before do
79
+ setup_tree <<-ENDTREE
80
+ node1:
81
+ - node2:
82
+ - node3
83
+ - node4:
84
+ - node6
85
+ - node5
86
+ - node7
87
+ ENDTREE
88
+
89
+ node(:node5).move_above(node(:node6))
90
+ end
91
+
92
+ it 'should iterate through the nodes in the correct order' do
93
+ result = []
94
+ node(:node1).traverse(:depth_first) { |node| result << node }
95
+ expect(result.collect { |n| n.name.to_sym }).to eq([:node1, :node2, :node3, :node4, :node5, :node6, :node7])
96
+ end
97
+
98
+ it 'should return the nodes in the correct order' do
99
+ result = node(:node1).traverse(:depth_first)
100
+ expect(result.collect { |n| n.name.to_sym }).to eq([:node1, :node2, :node3, :node4, :node5, :node6, :node7])
101
+ end
102
+
103
+ end
104
+
105
+ end
106
+
107
+ describe 'breadth first traversal' do
108
+
109
+ before do
110
+ setup_tree <<-ENDTREE
111
+ node1:
112
+ - node2:
113
+ - node5
114
+ - node3:
115
+ - node6
116
+ - node7
117
+ - node4
118
+ ENDTREE
119
+ end
120
+
121
+ it "should traverse correctly" do
122
+ result = []
123
+ node(:node1).traverse(:breadth_first) { |n| result << n }
124
+ expect(result.collect { |n| n.name.to_sym }).to eq([:node1, :node2, :node3, :node4, :node5, :node6, :node7])
125
+ end
126
+
127
+ it "should return and array containing the results of the block for each node" do
128
+ result = node(:node1).traverse(:breadth_first) { |n| n.name.to_sym }
129
+ expect(result).to eq([:node1, :node2, :node3, :node4, :node5, :node6, :node7])
130
+ end
131
+
132
+ end
133
+
134
+ describe '.traverse' do
135
+ before :each do
136
+ setup_tree <<-ENDTREE
137
+ - root1
138
+ - root2
139
+ ENDTREE
140
+
141
+ @root1 = node(:root1)
142
+ @root2 = node(:root2)
143
+
144
+ Node.stub(:roots).and_return [@root1, @root2]
145
+ end
146
+
147
+ it 'should grab each root' do
148
+ expect(Node).to receive(:roots).and_return []
149
+
150
+ expect(Node.traverse).to eq([])
151
+ end
152
+
153
+ it 'should default the "type" arg to :depth_first' do
154
+ expect(@root1).to receive(:traverse).with(:depth_first).and_return([])
155
+ expect(@root2).to receive(:traverse).with(:depth_first).and_return([])
156
+
157
+ expect(Node.traverse).to eq([])
158
+ end
159
+
160
+ it 'should traverse each root' do
161
+ expect(@root1).to receive(:traverse).and_return([1, 2])
162
+ expect(@root2).to receive(:traverse).and_return([3, 4])
163
+
164
+ expect(Node.traverse).to eq([1, 2, 3, 4])
165
+ end
166
+
167
+ describe 'when the "type" arg is :breadth_first' do
168
+
169
+ it 'should traverse breadth-first' do
170
+ expect(@root1).to receive(:traverse).with(:breadth_first).and_return([])
171
+ expect(@root2).to receive(:traverse).with(:breadth_first).and_return([])
172
+
173
+ Node.traverse :breadth_first
174
+ end
175
+ end
176
+ end
177
+ end
@@ -0,0 +1,444 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mongoid::Tree do
4
+
5
+ subject { Node }
6
+
7
+ it "should reference many children as inverse of parent with index" do
8
+ a = Node.reflect_on_association(:children)
9
+ expect(a).to be
10
+ expect(a.macro).to eq(:has_many)
11
+ expect(a.class_name).to eq('Node')
12
+ expect(a.foreign_key).to eq('parent_id')
13
+ expect(Node.index_options).to have_key(:parent_id => 1)
14
+ end
15
+
16
+ it "should be referenced in one parent as inverse of children" do
17
+ a = Node.reflect_on_association(:parent)
18
+ expect(a).to be
19
+ expect(a.macro).to eq(:belongs_to)
20
+ expect(a.class_name).to eq('Node')
21
+ expect(a.inverse_of).to eq(:children)
22
+ end
23
+
24
+ it "should store parent_ids as Array with [] as default with index" do
25
+ f = Node.fields['parent_ids']
26
+ expect(f).to be
27
+ expect(f.options[:type]).to eq(Array)
28
+ expect(f.options[:default]).to eq([])
29
+ expect(Node.index_options).to have_key(:parent_ids => 1)
30
+ end
31
+
32
+ describe 'when new' do
33
+ it "should not require a saved parent when adding children" do
34
+ root = Node.new(:name => 'root'); child = Node.new(:name => 'child')
35
+ expect { root.children << child; root.save! }.to_not raise_error
36
+ expect(child).to be_persisted
37
+ end
38
+
39
+ it "should not be saved when parent is not saved" do
40
+ root = Node.new(:name => 'root'); child = Node.new(:name => 'child')
41
+ expect(child).not_to receive(:save)
42
+ root.children << child
43
+ end
44
+
45
+ it "should save its unsaved children" do
46
+ root = Node.new(:name => 'root'); child = Node.new(:name => 'child')
47
+ root.children << child
48
+ expect(child).to receive(:save)
49
+ root.save
50
+ end
51
+ end
52
+
53
+ describe 'when saved' do
54
+
55
+ before(:each) do
56
+ setup_tree <<-ENDTREE
57
+ - root:
58
+ - child:
59
+ - subchild:
60
+ - subsubchild
61
+ - other_root:
62
+ - other_child
63
+ ENDTREE
64
+ end
65
+
66
+ it "should set the child's parent_id when added to parent's children" do
67
+ root = Node.create; child = Node.create
68
+ root.children << child
69
+ expect(child.parent).to eq(root)
70
+ expect(child.parent_id).to eq(root.id)
71
+ end
72
+
73
+ it "should set the child's parent_id parent is set on child" do
74
+ root = Node.create; child = Node.create
75
+ child.parent = root
76
+ expect(child.parent).to eq(root)
77
+ expect(child.parent_id).to eq(root.id)
78
+ end
79
+
80
+ it "should rebuild its parent_ids" do
81
+ root = Node.create; child = Node.create
82
+ root.children << child
83
+ expect(child.parent_ids).to eq([root.id])
84
+ end
85
+
86
+ it "should rebuild its children's parent_ids when its own parent_ids changed" do
87
+ other_root = node(:other_root); child = node(:child); subchild = node(:subchild);
88
+ other_root.children << child
89
+ subchild.reload # To get the updated version
90
+ expect(subchild.parent_ids).to eq([other_root.id, child.id])
91
+ end
92
+
93
+ it "should correctly rebuild its descendants' parent_ids when moved into an other subtree" do
94
+ subchild = node(:subchild); subsubchild = node(:subsubchild); other_child = node(:other_child)
95
+ other_child.children << subchild
96
+ subsubchild.reload
97
+ expect(subsubchild.parent_ids).to eq([node(:other_root).id, other_child.id, subchild.id])
98
+ end
99
+
100
+ it "should rebuild its children's parent_ids when its own parent_id is removed" do
101
+ c = node(:child)
102
+ c.parent_id = nil
103
+ c.save
104
+ expect(node(:subchild).parent_ids).to eq([node(:child).id])
105
+ end
106
+
107
+ it "should not rebuild its children's parent_ids when it's not required" do
108
+ root = node(:root)
109
+ expect(root).not_to receive(:rearrange_children)
110
+ root.save
111
+ end
112
+
113
+ it "should prevent cycles" do
114
+ child = node(:child)
115
+ child.parent = node(:subchild)
116
+ expect(child).not_to be_valid
117
+ expect(child.errors[:parent_id]).not_to be_nil
118
+ end
119
+
120
+ it "should save its children when added" do
121
+ new_child = Node.new(:name => 'new_child')
122
+ node(:root).children << new_child
123
+ expect(new_child).to be_persisted
124
+ end
125
+ end
126
+
127
+ describe 'when subclassed' do
128
+
129
+ before(:each) do
130
+ setup_tree <<-ENDTREE
131
+ - root:
132
+ - child:
133
+ - subchild
134
+ - other_child
135
+ - other_root
136
+ ENDTREE
137
+ end
138
+
139
+ it "should allow to store any subclass within the tree" do
140
+ subclassed = SubclassedNode.create!(:name => 'subclassed_subchild')
141
+ node(:child).children << subclassed
142
+ expect(subclassed.root).to eq(node(:root))
143
+ end
144
+
145
+ end
146
+
147
+ describe 'destroy strategies' do
148
+
149
+ before(:each) do
150
+ setup_tree <<-ENDTREE
151
+ - root:
152
+ - child:
153
+ - subchild
154
+ - other_child
155
+ - other_root
156
+ ENDTREE
157
+ end
158
+
159
+ describe ':nullify_children' do
160
+ it "should set its children's parent_id to null" do
161
+ node(:root).nullify_children
162
+ expect(node(:child)).to be_root
163
+ expect(node(:subchild).reload).not_to be_descendant_of node(:root)
164
+ end
165
+ end
166
+
167
+ describe ':move_children_to_parent' do
168
+ it "should set its childen's parent_id to the documents parent_id" do
169
+ node(:child).move_children_to_parent
170
+ expect(node(:child)).to be_leaf
171
+ expect(node(:root).children.to_a).to match_array([node(:child), node(:other_child), node(:subchild)])
172
+ end
173
+
174
+ it "should be able to handle a missing parent" do
175
+ node(:root).delete
176
+ expect { node(:child).move_children_to_parent }.to_not raise_error
177
+ end
178
+ end
179
+
180
+ describe ':destroy_children' do
181
+ it "should destroy all children" do
182
+ root = node(:root)
183
+ expect(root.children).to receive(:destroy_all)
184
+ root.destroy_children
185
+ end
186
+ end
187
+
188
+ describe ':delete_descendants' do
189
+ it "should delete all descendants" do
190
+ root = node(:root)
191
+ expect(Node).to receive(:delete_all).with(:conditions => { :parent_ids => root.id })
192
+ root.delete_descendants
193
+ end
194
+ end
195
+
196
+ end
197
+
198
+ describe 'utility methods' do
199
+
200
+ before(:each) do
201
+ setup_tree <<-ENDTREE
202
+ - root:
203
+ - child:
204
+ - subchild
205
+ - other_child
206
+ - other_root
207
+ ENDTREE
208
+ end
209
+
210
+ describe '.root' do
211
+ it "should return the first root document" do
212
+ expect(Node.root).to eq(node(:root))
213
+ end
214
+ end
215
+
216
+ describe '.roots' do
217
+ it "should return all root documents" do
218
+ expect(Node.roots.to_a).to eq([node(:root), node(:other_root)])
219
+ end
220
+ end
221
+
222
+ describe '.leaves' do
223
+ it "should return all leaf documents" do
224
+ expect(Node.leaves.to_a).to match_array([node(:subchild), node(:other_child), node(:other_root)])
225
+ end
226
+ end
227
+
228
+ describe '#root?' do
229
+ it "should return true for root documents" do
230
+ expect(node(:root)).to be_root
231
+ end
232
+
233
+ it "should return false for non-root documents" do
234
+ expect(node(:child)).not_to be_root
235
+ end
236
+ end
237
+
238
+ describe '#leaf?' do
239
+ it "should return true for leaf documents" do
240
+ expect(node(:subchild)).to be_leaf
241
+ expect(node(:other_child)).to be_leaf
242
+ expect(Node.new).to be_leaf
243
+ end
244
+
245
+ it "should return false for non-leaf documents" do
246
+ expect(node(:child)).not_to be_leaf
247
+ expect(node(:root)).not_to be_leaf
248
+ end
249
+ end
250
+
251
+ describe '#depth' do
252
+ it "should return the depth of this document" do
253
+ expect(node(:root).depth).to eq(0)
254
+ expect(node(:child).depth).to eq(1)
255
+ expect(node(:subchild).depth).to eq(2)
256
+ end
257
+ end
258
+
259
+ describe '#root' do
260
+ it "should return the root for this document" do
261
+ expect(node(:subchild).root).to eq(node(:root))
262
+ end
263
+
264
+ it "should return itself when there is no root" do
265
+ new_node = Node.new
266
+ expect(new_node.root).to be(new_node)
267
+ end
268
+
269
+ it "should return it root when it's not saved yet" do
270
+ root = Node.new(:name => 'root')
271
+ new_node = Node.new(:name => 'child')
272
+ new_node.parent = root
273
+ expect(new_node.root).to be(root)
274
+ end
275
+ end
276
+
277
+ describe 'ancestors' do
278
+ describe '#ancestors' do
279
+ it "should return the documents ancestors" do
280
+ expect(node(:subchild).ancestors.to_a).to eq([node(:root), node(:child)])
281
+ end
282
+
283
+ it "should return the ancestors in correct order even after rearranging" do
284
+ setup_tree <<-ENDTREE
285
+ - root:
286
+ - child:
287
+ - subchild
288
+ ENDTREE
289
+
290
+ child = node(:child); child.parent = nil; child.save!
291
+ root = node(:root); root.parent = node(:child); root.save!
292
+ subchild = node(:subchild); subchild.parent = root; subchild.save!
293
+
294
+ expect(subchild.ancestors.to_a).to eq([child, root])
295
+ end
296
+
297
+ it 'should return nothing when there are no ancestors' do
298
+ root = Node.new(:name => 'root')
299
+ expect(root.ancestors).to be_empty
300
+ end
301
+
302
+ it 'should allow chaning of other `or`-criterias' do
303
+ setup_tree <<-ENDTREE
304
+ - root:
305
+ - child:
306
+ - subchild:
307
+ - subsubchild
308
+ ENDTREE
309
+
310
+ filtered_ancestors = node(:subsubchild).ancestors.or(
311
+ { :name => 'child' },
312
+ { :name => 'subchild' }
313
+ )
314
+
315
+ expect(filtered_ancestors.to_a).to eq([node(:child), node(:subchild)])
316
+ end
317
+ end
318
+
319
+ describe '#ancestors_and_self' do
320
+ it "should return the documents ancestors and itself" do
321
+ expect(node(:subchild).ancestors_and_self.to_a).to eq([node(:root), node(:child), node(:subchild)])
322
+ end
323
+ end
324
+
325
+ describe '#ancestor_of?' do
326
+ it "should return true for ancestors" do
327
+ expect(node(:child)).to be_ancestor_of(node(:subchild))
328
+ end
329
+
330
+ it "should return false for non-ancestors" do
331
+ expect(node(:other_child)).not_to be_ancestor_of(node(:subchild))
332
+ end
333
+ end
334
+ end
335
+
336
+ describe 'descendants' do
337
+ describe '#descendants' do
338
+ it "should return the documents descendants" do
339
+ expect(node(:root).descendants.to_a).to match_array([node(:child), node(:other_child), node(:subchild)])
340
+ end
341
+ end
342
+
343
+ describe '#descendants_and_self' do
344
+ it "should return the documents descendants and itself" do
345
+ expect(node(:root).descendants_and_self.to_a).to match_array([node(:root), node(:child), node(:other_child), node(:subchild)])
346
+ end
347
+ end
348
+
349
+ describe '#descendant_of?' do
350
+ it "should return true for descendants" do
351
+ subchild = node(:subchild)
352
+ expect(subchild).to be_descendant_of(node(:child))
353
+ expect(subchild).to be_descendant_of(node(:root))
354
+ end
355
+
356
+ it "should return false for non-descendants" do
357
+ expect(node(:subchild)).not_to be_descendant_of(node(:other_child))
358
+ end
359
+ end
360
+ end
361
+
362
+ describe 'siblings' do
363
+ describe '#siblings' do
364
+ it "should return the documents siblings" do
365
+ expect(node(:child).siblings.to_a).to eq([node(:other_child)])
366
+ end
367
+ end
368
+
369
+ describe '#siblings_and_self' do
370
+ it "should return the documents siblings and itself" do
371
+ expect(node(:child).siblings_and_self).to be_kind_of(Mongoid::Criteria)
372
+ expect(node(:child).siblings_and_self.to_a).to eq([node(:child), node(:other_child)])
373
+ end
374
+ end
375
+
376
+ describe '#sibling_of?' do
377
+ it "should return true for siblings" do
378
+ expect(node(:child)).to be_sibling_of(node(:other_child))
379
+ end
380
+
381
+ it "should return false for non-siblings" do
382
+ expect(node(:root)).not_to be_sibling_of(node(:other_child))
383
+ end
384
+ end
385
+ end
386
+
387
+ describe '#leaves' do
388
+ it "should return this documents leaves" do
389
+ expect(node(:root).leaves.to_a).to match_array([node(:other_child), node(:subchild)])
390
+ end
391
+ end
392
+
393
+ end
394
+
395
+ describe 'callbacks' do
396
+
397
+ after(:each) do
398
+ Node.reset_callbacks(:rearrange)
399
+ end
400
+
401
+ it "should provide a before_rearrange callback" do
402
+ expect(Node).to respond_to :before_rearrange
403
+ end
404
+
405
+ it "should provida an after_rearrange callback" do
406
+ expect(Node).to respond_to :after_rearrange
407
+ end
408
+
409
+ describe 'before rearrange callback' do
410
+
411
+ it "should be called before the document is rearranged" do
412
+ Node.before_rearrange :callback
413
+ node = Node.new
414
+ expect(node).to receive(:callback).ordered
415
+ expect(node).to receive(:rearrange).ordered
416
+ node.save
417
+ end
418
+
419
+ end
420
+
421
+ describe 'after rearrange callback' do
422
+
423
+ it "should be called after the document is rearranged" do
424
+ Node.after_rearrange :callback
425
+ node = Node.new
426
+ expect(node).to receive(:rearrange).ordered
427
+ expect(node).to receive(:callback).ordered
428
+ node.save
429
+ end
430
+
431
+ end
432
+
433
+ describe 'cascading to embedded documents' do
434
+
435
+ it 'should not raise a NoMethodError' do
436
+ node = NodeWithEmbeddedDocument.new
437
+ document = node.build_embedded_document
438
+ expect { node.save }.to_not raise_error
439
+ end
440
+
441
+ end
442
+
443
+ end
444
+ end