Skip to content

Commit f3c77e3

Browse files
authored
Merge pull request #661 from kbrock/drop_orphans
Add option on how to handle orphans when arranging nodes
2 parents 46a9a9d + 51dc03a commit f3c77e3

File tree

2 files changed

+74
-4
lines changed

2 files changed

+74
-4
lines changed

lib/ancestry/class_methods.rb

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,29 @@ def arrange(options = {})
4040
# arranges array of nodes to a hierarchical hash
4141
#
4242
# @param nodes [Array[Node]] nodes to be arranged
43+
# @param orphan_strategy [Symbol] :rootify or :destroy (default: :rootify)
4344
# @returns Hash{Node => {Node => {}, Node => {}}}
4445
# If a node's parent is not included, the node will be included as if it is a top level node
45-
def arrange_nodes(nodes)
46+
def arrange_nodes(nodes, orphan_strategy: :rootify)
4647
node_ids = Set.new(nodes.map(&:id))
4748
index = Hash.new { |h, k| h[k] = {} }
4849

4950
nodes.each_with_object({}) do |node, arranged|
50-
children = index[node.id]
51-
index[node.parent_id][node] = children
52-
arranged[node] = children unless node_ids.include?(node.parent_id)
51+
index[node.parent_id][node] = children = index[node.id]
52+
if node.parent_id.nil?
53+
arranged[node] = children
54+
elsif !node_ids.include?(node.parent_id)
55+
case orphan_strategy
56+
when :destroy
57+
# All children are destroyed as well (default)
58+
when :adopt
59+
raise ArgumentError, "Not Implemented"
60+
when :rootify
61+
arranged[node] = children
62+
when :restrict
63+
raise Ancestry::AncestryException, I18n.t("ancestry.cannot_delete_descendants")
64+
end
65+
end
5366
end
5467
end
5568

test/concerns/arrangement_test.rb

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,4 +234,61 @@ def test_arrange_partial
234234
assert_equal([n5.id, n3.id], model.arrange_nodes([n5, n3]).keys.map(&:id))
235235
end
236236
end
237+
238+
def test_arrange_nodes_orphan_strategy_rootify
239+
AncestryTestDatabase.with_model do |model|
240+
# - n1
241+
# - n2
242+
# - n3
243+
# - n4
244+
n1 = model.create!
245+
n2 = model.create!(parent: n1)
246+
n3 = model.create!(parent: n2)
247+
n4 = model.create!(parent: n1)
248+
249+
# n2 and n4's parent (n1) is missing, so they become roots
250+
result = model.arrange_nodes([n2, n3, n4], orphan_strategy: :rootify)
251+
assert_equal({n2 => {n3 => {}}, n4 => {}}, result)
252+
end
253+
end
254+
255+
def test_arrange_nodes_orphan_strategy_destroy
256+
AncestryTestDatabase.with_model do |model|
257+
# - n1
258+
# - n2
259+
# - n3
260+
# - n4
261+
n1 = model.create!
262+
n2 = model.create!(parent: n1)
263+
n3 = model.create!(parent: n2)
264+
n4 = model.create!(parent: n1)
265+
266+
# n2 and n4's parent (n1) is missing, so they and their children are dropped
267+
result = model.arrange_nodes([n2, n3, n4], orphan_strategy: :destroy)
268+
assert_equal({}, result)
269+
end
270+
end
271+
272+
def test_arrange_nodes_orphan_strategy_restrict
273+
AncestryTestDatabase.with_model do |model|
274+
n1 = model.create!
275+
n2 = model.create!(parent: n1)
276+
277+
# n2's parent (n1) is missing, so restrict raises
278+
assert_raises(Ancestry::AncestryException) do
279+
model.arrange_nodes([n2], orphan_strategy: :restrict)
280+
end
281+
end
282+
end
283+
284+
def test_arrange_nodes_orphan_strategy_rootify_is_default
285+
AncestryTestDatabase.with_model do |model|
286+
n1 = model.create!
287+
n2 = model.create!(parent: n1)
288+
289+
# default behavior: orphans become roots
290+
result = model.arrange_nodes([n2])
291+
assert_equal({n2 => {}}, result)
292+
end
293+
end
237294
end

0 commit comments

Comments
 (0)