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.
- data/.rspec +2 -0
- data/.travis.yml +12 -0
- data/Gemfile +23 -0
- data/Guardfile +6 -0
- data/LICENSE +21 -0
- data/README.md +287 -0
- data/Rakefile +63 -0
- data/VERSION +1 -0
- data/lib/mongoid/locale/en.yml +11 -0
- data/lib/mongoid/locale/nb.yml +8 -0
- data/lib/mongoid/tree.rb +443 -0
- data/lib/mongoid/tree/ordering.rb +236 -0
- data/lib/mongoid/tree/rational_numbering.rb +805 -0
- data/lib/mongoid/tree/traversal.rb +122 -0
- data/mongoid-tree-rational.gemspec +103 -0
- data/spec/mongoid/tree/ordering_spec.rb +342 -0
- data/spec/mongoid/tree/rational_numbering_spec.rb +765 -0
- data/spec/mongoid/tree/traversal_spec.rb +177 -0
- data/spec/mongoid/tree_spec.rb +444 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/support/macros/tree_macros.rb +53 -0
- data/spec/support/models/node.rb +47 -0
- metadata +320 -0
@@ -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
|