mongoid-ancestry-fixes 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,300 @@
1
+ require 'spec_helper'
2
+
3
+ describe MongoidAncestry do
4
+
5
+ subject { MongoidAncestry }
6
+
7
+ it "should have scopes" do
8
+ subject.with_model :depth => 3, :width => 3 do |model, roots|
9
+ # Roots assertion
10
+ model.roots.all.to_a.should eql(roots.map(&:first))
11
+
12
+ model.all.each do |test_node|
13
+ # Assertions for ancestors_of named scope
14
+ model.ancestors_of(test_node).all.should == test_node.ancestors.all
15
+ model.ancestors_of(test_node.id).all.to_a.should eql(test_node.ancestors.all.to_a)
16
+ # Assertions for children_of named scope
17
+ model.children_of(test_node).all.to_a.should eql(test_node.children.all.to_a)
18
+ model.children_of(test_node.id).all.to_a.should eql(test_node.children.all.to_a)
19
+ # Assertions for descendants_of named scope
20
+ model.descendants_of(test_node).all.should == (test_node.descendants.all)
21
+ model.descendants_of(test_node.id).all.to_a.should eql(test_node.descendants.all.to_a)
22
+ # Assertions for subtree_of named scope
23
+ model.subtree_of(test_node).all.to_a.should eql(test_node.subtree.all.to_a)
24
+ model.subtree_of(test_node.id).all.to_a.should eql(test_node.subtree.all.to_a)
25
+ # Assertions for siblings_of named scope
26
+ model.siblings_of(test_node).all.to_a.should eql(test_node.siblings.all.to_a)
27
+ model.siblings_of(test_node.id).all.to_a.should eql(test_node.siblings.all.to_a)
28
+ end
29
+ end
30
+ end
31
+
32
+ it "should be arranged" do
33
+ subject.with_model :depth => 3, :width => 3 do |model, roots|
34
+ id_sorter = Proc.new {|a, b|; a.to_param <=> b.to_param }
35
+ arranged_nodes = model.arrange
36
+ arranged_nodes.size.should eql(3)
37
+ arranged_nodes.each do |node, children|
38
+ children.keys.sort(&id_sorter).should eql(node.children.sort(&id_sorter))
39
+ children.each do |node, children|
40
+ children.keys.sort(&id_sorter).should eql(node.children.sort(&id_sorter))
41
+ children.each do |node, children|
42
+ children.size.should eql(0)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ it "should have arrange order option" do
50
+ subject.with_model :width => 3, :depth => 3 do |model, roots|
51
+ descending_nodes_lvl0 = model.arrange :order => [:_id, :desc]
52
+ ascending_nodes_lvl0 = model.arrange :order => [:_id, :asc]
53
+
54
+ descending_nodes_lvl0.keys.zip(ascending_nodes_lvl0.keys.reverse).each do |descending_node, ascending_node|
55
+ ascending_node.should eql(descending_node)
56
+ descending_nodes_lvl1 = descending_nodes_lvl0[descending_node]
57
+ ascending_nodes_lvl1 = ascending_nodes_lvl0[ascending_node]
58
+ descending_nodes_lvl1.keys.zip(ascending_nodes_lvl1.keys.reverse).each do |descending_node, ascending_node|
59
+ ascending_node.should eql(descending_node)
60
+ descending_nodes_lvl2 = descending_nodes_lvl1[descending_node]
61
+ ascending_nodes_lvl2 = ascending_nodes_lvl1[ascending_node]
62
+ descending_nodes_lvl2.keys.zip(ascending_nodes_lvl2.keys.reverse).each do |descending_node, ascending_node|
63
+ ascending_node.should eql(descending_node)
64
+ descending_nodes_lvl3 = descending_nodes_lvl2[descending_node]
65
+ ascending_nodes_lvl3 = ascending_nodes_lvl2[ascending_node]
66
+ descending_nodes_lvl3.keys.zip(ascending_nodes_lvl3.keys.reverse).each do |descending_node, ascending_node|
67
+ ascending_node.should eql(descending_node)
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ it "should have valid orphan rootify strategy" do
76
+ subject.with_model :depth => 3, :width => 3 do |model, roots|
77
+ model.orphan_strategy = :rootify
78
+ root = roots.first.first
79
+ children = root.children.all
80
+ root.destroy
81
+ children.each do |child|
82
+ child.reload
83
+ child.is_root?.should be_true
84
+ child.children.size.should eql(3)
85
+ end
86
+ end
87
+ end
88
+
89
+ it "should have valid orphan destroy strategy" do
90
+ subject.with_model :depth => 3, :width => 3 do |model, roots|
91
+ model.orphan_strategy = :destroy
92
+ root = roots.first.first
93
+ expect { root.destroy }.to change(model, :count).by(-root.subtree.size)
94
+ node = model.roots.first.children.first
95
+ expect { node.destroy }.to change(model, :count).by(-node.subtree.size)
96
+ end
97
+ end
98
+
99
+ it "should have valid orphan restrict strategy" do
100
+ subject.with_model :depth => 3, :width => 3 do |model, roots|
101
+ model.orphan_strategy = :restrict
102
+ root = roots.first.first
103
+ expect { root.destroy }.to raise_error Mongoid::Ancestry::Error
104
+ expect { root.children.first.children.first.destroy }.to_not raise_error Mongoid::Ancestry::Error
105
+ end
106
+ end
107
+
108
+ it "should check that there are no errors on a valid tree" do
109
+ subject.with_model :width => 3, :depth => 3 do |model, roots|
110
+ expect { model.check_ancestry_integrity! }.to_not raise_error(Mongoid::Ancestry::Error)
111
+ model.check_ancestry_integrity!(:report => :list).size.should eql(0)
112
+ end
113
+ end
114
+
115
+ it "should check detection of invalid format for ancestry field" do
116
+ subject.with_model :width => 3, :depth => 3 do |model, roots|
117
+ roots.first.first.update_attribute model.ancestry_field, 'invalid_ancestry'
118
+ expect { model.check_ancestry_integrity! }.to raise_error(Mongoid::Ancestry::IntegrityError)
119
+ model.check_ancestry_integrity!(:report => :list).size.should eql(1)
120
+ end
121
+ end
122
+
123
+ it "should check detection of non-existent ancestor" do
124
+ subject.with_model :width => 3, :depth => 3 do |model, roots|
125
+ node = roots.first.first
126
+ node.without_ancestry_callbacks do
127
+ node.update_attribute model.ancestry_field, 35
128
+ end
129
+ expect { model.check_ancestry_integrity! }.to raise_error(Mongoid::Ancestry::IntegrityError)
130
+ model.check_ancestry_integrity!(:report => :list).size.should eql(1)
131
+ end
132
+ end
133
+
134
+ it "should check detection of cyclic ancestry" do
135
+ subject.with_model :width => 3, :depth => 3 do |model, roots|
136
+ node = roots.first.first
137
+ node.update_attribute model.ancestry_field, node.id
138
+ expect { model.check_ancestry_integrity! }.to raise_error(Mongoid::Ancestry::IntegrityError)
139
+ model.check_ancestry_integrity!(:report => :list).size.should eql(1)
140
+ end
141
+ end
142
+
143
+ it "should check detection of conflicting parent id" do
144
+ subject.with_model do |model|
145
+ model.destroy_all
146
+ model.create!(model.ancestry_field => model.create!(model.ancestry_field => model.create!(model.ancestry_field => nil).id).id)
147
+ expect { model.check_ancestry_integrity! }.to raise_error(Mongoid::Ancestry::IntegrityError)
148
+ model.check_ancestry_integrity!(:report => :list).size.should eql(1)
149
+ end
150
+ end
151
+
152
+ def assert_integrity_restoration model
153
+ expect { model.check_ancestry_integrity! }.to raise_error(Mongoid::Ancestry::IntegrityError)
154
+ model.restore_ancestry_integrity!
155
+ expect { model.check_ancestry_integrity! }.to_not raise_error(Mongoid::Ancestry::IntegrityError)
156
+ end
157
+
158
+ it "should check that integrity is restored for invalid format for ancestry field" do
159
+ subject.with_model :width => 3, :depth => 3 do |model, roots|
160
+ roots.first.first.update_attribute model.ancestry_field, 'invalid_ancestry'
161
+ assert_integrity_restoration model
162
+ end
163
+ end
164
+
165
+ it "should check that integrity is restored for non-existent ancestor" do
166
+ subject.with_model :width => 3, :depth => 3 do |model, roots|
167
+ roots.first.first.update_attribute model.ancestry_field, 35
168
+ assert_integrity_restoration model
169
+ end
170
+ end
171
+
172
+ it "should check that integrity is restored for cyclic ancestry" do
173
+ subject.with_model :width => 3, :depth => 3 do |model, roots|
174
+ node = roots.first.first
175
+ node.update_attribute model.ancestry_field, node.id
176
+ assert_integrity_restoration model
177
+ end
178
+ end
179
+
180
+ it "should check that integrity is restored for conflicting parent id" do
181
+ subject.with_model do |model|
182
+ model.destroy_all
183
+ model.create!(model.ancestry_field => model.create!(model.ancestry_field => model.create!(model.ancestry_field => nil).id).id)
184
+ assert_integrity_restoration model
185
+ end
186
+ end
187
+
188
+ it "should create node through scope" do
189
+ subject.with_model do |model|
190
+ node = model.create!
191
+ child = node.children.create # doesn't pass with .create!
192
+ child.parent.should eql(node)
193
+
194
+ other_child = child.siblings.create # doesn't pass with .create!
195
+ other_child.parent.should eql(node)
196
+
197
+ grandchild = model.children_of(child).build # doesn't pass with .new
198
+ grandchild.save
199
+ grandchild.parent.should eql(child)
200
+
201
+ other_grandchild = model.siblings_of(grandchild).build # doesn't pass with .new
202
+ other_grandchild.save!
203
+ other_grandchild.parent.should eql(child)
204
+ end
205
+ end
206
+
207
+ it "should have depth scopes" do
208
+ subject.with_model :depth => 4, :width => 2, :cache_depth => true do |model, roots|
209
+ model.before_depth(2).all? { |node| node.depth < 2 }.should be_true
210
+ model.to_depth(2).all? { |node| node.depth <= 2 }.should be_true
211
+ model.at_depth(2).all? { |node| node.depth == 2 }.should be_true
212
+ model.from_depth(2).all? { |node| node.depth >= 2 }.should be_true
213
+ model.after_depth(2).all? { |node| node.depth > 2 }.should be_true
214
+ end
215
+ end
216
+
217
+ it "should raise error on invalid scopes" do
218
+ subject.with_model do |model|
219
+ expect { model.before_depth(1) } .to raise_error(Mongoid::Ancestry::Error)
220
+ expect { model.to_depth(1) } .to raise_error(Mongoid::Ancestry::Error)
221
+ expect { model.at_depth(1) } .to raise_error(Mongoid::Ancestry::Error)
222
+ expect { model.from_depth(1) } .to raise_error(Mongoid::Ancestry::Error)
223
+ expect { model.after_depth(1) } .to raise_error(Mongoid::Ancestry::Error)
224
+ end
225
+ end
226
+
227
+ it "should raise error on invalid has_ancestry options" do
228
+ subject.with_model do |model|
229
+ expect { model.has_ancestry :this_option_doesnt_exist => 42 }.to raise_error(Mongoid::Ancestry::Error)
230
+ expect { model.has_ancestry :not_a_hash }.to raise_error(Mongoid::Ancestry::Error)
231
+ end
232
+ end
233
+
234
+ it "should build ancestry from parent ids" do
235
+ subject.with_model :skip_ancestry => true, :extra_columns => {:parent_id => :integer} do |model|
236
+ [model.create!].each do |parent1|
237
+ (Array.new(5) { model.create :parent_id => parent1.id }).each do |parent2|
238
+ (Array.new(5) { model.create :parent_id => parent2.id }).each do |parent3|
239
+ (Array.new(5) { model.create :parent_id => parent3.id })
240
+ end
241
+ end
242
+ end
243
+
244
+ # Assert all nodes where created
245
+ model.count.should eql((0..3).map { |n| 5 ** n }.sum)
246
+ end
247
+
248
+ subject.with_model do |model|
249
+
250
+ model.build_ancestry_from_parent_ids!
251
+
252
+ # Assert ancestry integrity
253
+ model.check_ancestry_integrity!
254
+
255
+ roots = model.roots.all
256
+ ## Assert single root node
257
+ roots.size.should eql(1)
258
+
259
+ ## Assert it has 5 children
260
+ roots.each do |parent|
261
+ parent.children.count.should eql(5)
262
+ parent.children.each do |parent|
263
+ parent.children.count.should eql(5)
264
+ parent.children.each do |parent|
265
+ parent.children.count.should eql(5)
266
+ parent.children.each do |parent|
267
+ parent.children.count.should eql(0)
268
+ end
269
+ end
270
+ end
271
+ end
272
+ end
273
+ end
274
+
275
+ it "should rebuild depth cache" do
276
+ subject.with_model :depth => 3, :width => 3, :cache_depth => true, :depth_cache_field => :depth_cache do |model, roots|
277
+ model.update_all(:depth_cache => nil)
278
+
279
+ # Assert cache was emptied correctly
280
+ model.all.each do |test_node|
281
+ test_node.depth_cache.should eql(nil)
282
+ end
283
+
284
+ # Rebuild cache
285
+ model.rebuild_depth_cache!
286
+
287
+ # Assert cache was rebuild correctly
288
+ model.all.each do |test_node|
289
+ test_node.depth_cache.should eql(test_node.depth)
290
+ end
291
+ end
292
+ end
293
+
294
+ it "should raise exception when rebuilding depth cache for model without depth caching" do
295
+ subject.with_model do |model|
296
+ expect { model.rebuild_depth_cache! }.to raise_error(Mongoid::Ancestry::Error)
297
+ end
298
+ end
299
+
300
+ end
@@ -0,0 +1,241 @@
1
+ require 'spec_helper'
2
+
3
+ describe MongoidAncestry do
4
+
5
+ subject { MongoidAncestry }
6
+
7
+ it "should have tree navigation" do
8
+ subject.with_model :depth => 3, :width => 3 do |model, roots|
9
+ roots.each do |lvl0_node, lvl0_children|
10
+ # Ancestors assertions
11
+ lvl0_node.ancestor_ids.should eql([])
12
+ lvl0_node.ancestors.to_a.should eql([])
13
+ lvl0_node.path_ids.should eql([lvl0_node.id])
14
+ lvl0_node.path.to_a.should eql([lvl0_node])
15
+ lvl0_node.depth.should eql(0)
16
+ # Parent assertions
17
+ lvl0_node.parent_id.should be_nil
18
+ lvl0_node.parent.should be_nil
19
+ # Root assertions
20
+ lvl0_node.root_id.should eql(lvl0_node.id)
21
+ lvl0_node.root.should eql(lvl0_node)
22
+ lvl0_node.is_root?.should be_true
23
+ # Children assertions
24
+ lvl0_node.child_ids.should eql(lvl0_children.map(&:first).map(&:id))
25
+ lvl0_node.children.to_a.should eql(lvl0_children.map(&:first))
26
+ lvl0_node.has_children?.should be_true
27
+ lvl0_node.is_childless?.should be_false
28
+ # Siblings assertions
29
+ lvl0_node.sibling_ids.should eql(roots.map(&:first).map(&:id))
30
+ lvl0_node.siblings.to_a.should eql(roots.map(&:first))
31
+ lvl0_node.has_siblings?.should be_true
32
+ lvl0_node.is_only_child?.should be_false
33
+ # Descendants assertions
34
+ descendants = model.all.find_all do |node|
35
+ node.ancestor_ids.include?(lvl0_node.id)
36
+ end
37
+ lvl0_node.descendant_ids.should eql(descendants.map(&:id))
38
+ lvl0_node.descendants.to_a.should eql(descendants)
39
+ lvl0_node.subtree.to_a.should eql([lvl0_node] + descendants)
40
+
41
+ lvl0_children.each do |lvl1_node, lvl1_children|
42
+ # Ancestors assertions
43
+ lvl1_node.ancestor_ids.should eql([lvl0_node.id])
44
+ lvl1_node.ancestors.to_a.should eql([lvl0_node])
45
+ lvl1_node.path_ids.should eql([lvl0_node.id, lvl1_node.id])
46
+ lvl1_node.path.to_a.should eql([lvl0_node, lvl1_node])
47
+ lvl1_node.depth.should eql(1)
48
+ # Parent assertions
49
+ lvl1_node.parent_id.should eql(lvl0_node.id)
50
+ lvl1_node.parent.should eql(lvl0_node)
51
+ # Root assertions
52
+ lvl1_node.root_id.should eql(lvl0_node.id)
53
+ lvl1_node.root.should eql(lvl0_node)
54
+ lvl1_node.is_root?.should be_false
55
+ # Children assertions
56
+ lvl1_node.child_ids.should eql(lvl1_children.map(&:first).map(&:id))
57
+ lvl1_node.children.to_a.should eql(lvl1_children.map(&:first))
58
+ lvl1_node.has_children?.should be_true
59
+ lvl1_node.is_childless?.should be_false
60
+ # Siblings assertions
61
+ lvl1_node.sibling_ids.should eql(lvl0_children.map(&:first).map(&:id))
62
+ lvl1_node.siblings.to_a.should eql(lvl0_children.map(&:first))
63
+ lvl1_node.has_siblings?.should be_true
64
+ lvl1_node.is_only_child?.should be_false
65
+ # Descendants assertions
66
+ descendants = model.all.find_all do |node|
67
+ node.ancestor_ids.include? lvl1_node.id
68
+ end
69
+
70
+ lvl1_node.descendant_ids.should eql(descendants.map(&:id))
71
+ lvl1_node.descendants.to_a.should eql(descendants)
72
+ lvl1_node.subtree.to_a.should eql([lvl1_node] + descendants)
73
+
74
+ lvl1_children.each do |lvl2_node, lvl2_children|
75
+ # Ancestors assertions
76
+ lvl2_node.ancestor_ids.should eql([lvl0_node.id, lvl1_node.id])
77
+ lvl2_node.ancestors.to_a.should eql([lvl0_node, lvl1_node])
78
+ lvl2_node.path_ids.should eql([lvl0_node.id, lvl1_node.id, lvl2_node.id])
79
+ lvl2_node.path.to_a.should eql([lvl0_node, lvl1_node, lvl2_node])
80
+ lvl2_node.depth.should eql(2)
81
+ # Parent assertions
82
+ lvl2_node.parent_id.should eql(lvl1_node.id)
83
+ lvl2_node.parent.should eql(lvl1_node)
84
+ # Root assertions
85
+ lvl2_node.root_id.should eql(lvl0_node.id)
86
+ lvl2_node.root.should eql(lvl0_node)
87
+ lvl2_node.is_root?.should be_false
88
+ # Children assertions
89
+ lvl2_node.child_ids.should eql([])
90
+ lvl2_node.children.to_a.should eql([])
91
+ lvl2_node.has_children?.should be_false
92
+ lvl2_node.is_childless?.should be_true
93
+ # Siblings assertions
94
+ lvl2_node.sibling_ids.should eql(lvl1_children.map(&:first).map(&:id))
95
+ lvl2_node.siblings.to_a.should eql(lvl1_children.map(&:first))
96
+ lvl2_node.has_siblings?.should be_true
97
+ lvl2_node.is_only_child?.should be_false
98
+ # Descendants assertions
99
+ descendants = model.all.find_all do |node|
100
+ node.ancestor_ids.include? lvl2_node.id
101
+ end
102
+ lvl2_node.descendant_ids.should eql(descendants.map(&:id))
103
+ lvl2_node.descendants.to_a.should eql(descendants)
104
+ lvl2_node.subtree.to_a.should eql([lvl2_node] + descendants)
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
110
+
111
+ it "should validate ancestry field" do
112
+ subject.with_model do |model|
113
+ node = model.create
114
+ ['3', '10/2', '1/4/30', nil].each do |value|
115
+ node.send :write_attribute, model.ancestry_field, value
116
+ node.should be_valid
117
+ node.errors[model.ancestry_field].blank?.should be_true
118
+ end
119
+ ['1/3/', '/2/3', 'A/b', '-34', '/54'].each do |value|
120
+ node.send :write_attribute, model.ancestry_field, value
121
+ node.should_not be_valid
122
+ node.errors[model.ancestry_field].blank?.should be_false
123
+ end
124
+ end
125
+ end
126
+
127
+ it "should move descendants with node" do
128
+ subject.with_model :depth => 3, :width => 3 do |model, roots|
129
+ root1, root2, root3 = roots.map(&:first)
130
+
131
+ descendants = root1.descendants.asc(:_id).map(&:to_param)
132
+ expect {
133
+ root1.parent = root2
134
+ root1.save!
135
+ root1.descendants.asc(:_id).map(&:to_param).should eql(descendants)
136
+ }.to change(root2.descendants, 'size').by(root1.subtree.size)
137
+
138
+ descendants = root2.descendants.asc(:_id).map(&:to_param)
139
+ expect {
140
+ root2.parent = root3
141
+ root2.save!
142
+ root2.descendants.asc(:_id).map(&:to_param).should eql(descendants)
143
+ }.to change(root3.descendants, 'size').by(root2.subtree.size)
144
+
145
+ descendants = root1.descendants.asc(:_id).map(&:to_param)
146
+ expect {
147
+ expect {
148
+ root1.parent = nil
149
+ root1.save!
150
+ root1.descendants.asc(:_id).map(&:to_param).should eql(descendants)
151
+ }.to change(root3.descendants, 'size').by(-root1.subtree.size)
152
+ }.to change(root2.descendants, 'size').by(-root1.subtree.size)
153
+ end
154
+ end
155
+
156
+ it "should validate ancestry exclude self" do
157
+ subject.with_model do |model|
158
+ parent = model.create!
159
+ child = parent.children.create
160
+ expect { parent.update_attributes! :parent => child }.to raise_error(Mongoid::Errors::Validations)
161
+ end
162
+ end
163
+
164
+ it "should have depth caching" do
165
+ subject.with_model :depth => 3, :width => 3, :cache_depth => true, :depth_cache_field => :depth_cache do |model, roots|
166
+ roots.each do |lvl0_node, lvl0_children|
167
+ lvl0_node.depth_cache.should eql(0)
168
+ lvl0_children.each do |lvl1_node, lvl1_children|
169
+ lvl1_node.depth_cache.should eql(1)
170
+ lvl1_children.each do |lvl2_node, lvl2_children|
171
+ lvl2_node.depth_cache.should eql(2)
172
+ end
173
+ end
174
+ end
175
+ end
176
+ end
177
+
178
+ it "should have descendants with depth constraints" do
179
+ subject.with_model :depth => 4, :width => 4, :cache_depth => true do |model, roots|
180
+ model.roots.first.descendants(:before_depth => 2).count.should eql(4)
181
+ model.roots.first.descendants(:to_depth => 2).count.should eql(20)
182
+ model.roots.first.descendants(:at_depth => 2).count.should eql(16)
183
+ model.roots.first.descendants(:from_depth => 2).count.should eql(80)
184
+ model.roots.first.descendants(:after_depth => 2).count.should eql(64)
185
+ end
186
+ end
187
+
188
+ it "should have subtree with depth constraints" do
189
+ subject.with_model :depth => 4, :width => 4, :cache_depth => true do |model, roots|
190
+ model.roots.first.subtree(:before_depth => 2).count.should eql(5)
191
+ model.roots.first.subtree(:to_depth => 2).count.should eql(21)
192
+ model.roots.first.subtree(:at_depth => 2).count.should eql(16)
193
+ model.roots.first.subtree(:from_depth => 2).count.should eql(80)
194
+ model.roots.first.subtree(:after_depth => 2).count.should eql(64)
195
+ end
196
+ end
197
+
198
+ it "should have ancestors with depth constraints" do
199
+ subject.with_model :cache_depth => true do |model|
200
+ node1 = model.create!
201
+ node2 = node1.children.create
202
+ node3 = node2.children.create
203
+ node4 = node3.children.create
204
+ node5 = node4.children.create
205
+ leaf = node5.children.create
206
+
207
+ leaf.ancestors(:before_depth => -2).to_a.should eql([node1, node2, node3])
208
+ leaf.ancestors(:to_depth => -2).to_a.should eql([node1, node2, node3, node4])
209
+ leaf.ancestors(:at_depth => -2).to_a.should eql([node4])
210
+ leaf.ancestors(:from_depth => -2).to_a.should eql([node4, node5])
211
+ leaf.ancestors(:after_depth => -2).to_a.should eql([node5])
212
+ end
213
+ end
214
+
215
+ it "should have path with depth constraints" do
216
+ subject.with_model :cache_depth => true do |model|
217
+ node1 = model.create!
218
+ node2 = node1.children.create
219
+ node3 = node2.children.create
220
+ node4 = node3.children.create
221
+ node5 = node4.children.create
222
+ leaf = node5.children.create
223
+
224
+ leaf.path(:before_depth => -2).to_a.should eql([node1, node2, node3])
225
+ leaf.path(:to_depth => -2).to_a.should eql([node1, node2, node3, node4])
226
+ leaf.path(:at_depth => -2).to_a.should eql([node4])
227
+ leaf.path(:from_depth => -2).to_a.should eql([node4, node5, leaf])
228
+ leaf.path(:after_depth => -2).to_a.should eql([node5, leaf])
229
+ end
230
+ end
231
+
232
+ it "should raise exception on unknown depth field" do
233
+ subject.with_model :cache_depth => true do |model|
234
+ expect {
235
+ model.create!.subtree(:this_is_not_a_valid_depth_option => 42)
236
+ }.to raise_error(Mongoid::Ancestry::Error)
237
+ end
238
+ end
239
+
240
+
241
+ end
@@ -0,0 +1,22 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'mongoid'
5
+ require 'rspec'
6
+
7
+ require 'mongoid-ancestry'
8
+
9
+ Mongoid.configure do |config|
10
+ logger = Logger.new('log/test.log')
11
+ config.master = Mongo::Connection.new('localhost', 27017,
12
+ :logger => logger).db('ancestry_test')
13
+ config.logger = logger
14
+ end
15
+
16
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
17
+
18
+ RSpec.configure do |config|
19
+ config.after :each do
20
+ Mongoid.master.collections.reject { |c| c.name =~ /^system\./ }.each(&:drop)
21
+ end
22
+ end
@@ -0,0 +1,40 @@
1
+ class MongoidAncestry
2
+
3
+ def self.with_model options = {}
4
+ depth = options.delete(:depth) || 0
5
+ width = options.delete(:width) || 0
6
+ extra_columns = options.delete(:extra_columns)
7
+ skip_ancestry = options.delete(:skip_ancestry)
8
+
9
+ begin
10
+ model = Class.new
11
+ (class << model; self; end).send :define_method, :model_name do; Struct.new(:human, :underscore, :i18n_key).new 'TestNode', 'test_node', 'key'; end
12
+ const_set 'TestNode', model
13
+ TestNode.send(:include, Mongoid::Document)
14
+ TestNode.send(:include, Mongoid::Ancestry) unless skip_ancestry
15
+
16
+ extra_columns.each do |name, type|
17
+ TestNode.send :field, name, :type => type.to_s.capitalize.constantize
18
+ end unless extra_columns.nil?
19
+
20
+ TestNode.has_ancestry options unless skip_ancestry
21
+
22
+ if depth > 0
23
+ yield TestNode, create_test_nodes(TestNode, depth, width)
24
+ else
25
+ yield TestNode
26
+ end
27
+ ensure
28
+ remove_const "TestNode"
29
+ end
30
+ end
31
+
32
+ def self.create_test_nodes model, depth, width, parent = nil
33
+ unless depth == 0
34
+ Array.new width do
35
+ node = model.create!(:parent => parent)
36
+ [node, create_test_nodes(model, depth - 1, width, node)]
37
+ end
38
+ else; []; end
39
+ end
40
+ end