mongoid-ancestry-fixes 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,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