mongoid-tree-rational 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|