diff --git a/lib/closure_tree/numeric_deterministic_ordering.rb b/lib/closure_tree/numeric_deterministic_ordering.rb index ab49f32..95f4146 100644 --- a/lib/closure_tree/numeric_deterministic_ordering.rb +++ b/lib/closure_tree/numeric_deterministic_ordering.rb @@ -30,6 +30,15 @@ def _ct_reorder_children(minimum_sort_order_value = nil) _ct.reorder_with_parent_id(_ct_id, minimum_sort_order_value, scope_conditions) end + def _ct_reorder_prior_parent_or_roots(prior_parent, was_root) + if prior_parent + prior_parent._ct_reorder_children + elsif was_root && !_ct.dont_order_roots + scope_conditions = _ct.scope_values_from_instance(self) + _ct.reorder_with_parent_id(nil, nil, scope_conditions) + end + end + def self_and_descendants_preordered # TODO: raise NotImplementedError if sort_order is not numeric and not null? hierarchy_table = self.class.hierarchy_class.arel_table @@ -130,11 +139,14 @@ def append_child(child_node) end def prepend_child(child_node) + prior_parent = child_node.parent + was_root = prior_parent.nil? && child_node.persisted? child_node.order_value = -1 child_node.parent = self child_node._ct_skip_sort_order_maintenance! if child_node.save _ct_reorder_children + _ct_reorder_prior_parent_or_roots(prior_parent, was_root) child_node.reload else child_node @@ -161,6 +173,7 @@ def add_sibling(sibling, add_after = true) _ct.with_advisory_lock do prior_sibling_parent = sibling.parent + sibling_was_root = prior_sibling_parent.nil? && sibling.persisted? reorder_from_value = if prior_sibling_parent == parent [order_value, sibling.order_value].compact.min else @@ -184,7 +197,7 @@ def add_sibling(sibling, add_after = true) sibling.update_order_value(self_ov) end - prior_sibling_parent.try(:_ct_reorder_children) if prior_sibling_parent != parent + sibling._ct_reorder_prior_parent_or_roots(prior_sibling_parent, sibling_was_root) if prior_sibling_parent != parent sibling end end diff --git a/test/closure_tree/label_order_value_test.rb b/test/closure_tree/label_order_value_test.rb index 968a332..ac286d4 100644 --- a/test/closure_tree/label_order_value_test.rb +++ b/test/closure_tree/label_order_value_test.rb @@ -53,4 +53,69 @@ def setup assert_equal 1, b.order_value assert_equal 2, c.order_value end + + test 'should reorder remaining root nodes when a root node becomes a child via prepend_child' do + node_1 = Label.create(name: 'node_1') + node_2 = Label.create(name: 'node_2') + node_3 = Label.create(name: 'node_3') + node_4 = Label.create(name: 'node_4') + + # Verify initial positions + assert_equal 0, node_1.order_value + assert_equal 1, node_2.order_value + assert_equal 2, node_3.order_value + assert_equal 3, node_4.order_value + + # Move node_2 as a child of node_3 using prepend_child + node_3.prepend_child(node_2) + + # Reload all nodes to get updated positions + node_1.reload + node_2.reload + node_3.reload + node_4.reload + + # Verify node_2 is now a child of node_3 with position 0 + assert_equal node_3.id, node_2._ct_parent_id + assert_equal 0, node_2.order_value + + # Verify remaining root nodes have sequential positions without gaps + assert_equal 0, node_1.order_value + assert_equal 1, node_3.order_value + assert_equal 2, node_4.order_value + end + + test 'should reorder remaining root nodes when a root node becomes a child via add_sibling' do + node_1 = Label.create(name: 'node_1') + node_2 = Label.create(name: 'node_2') + node_3 = Label.create(name: 'node_3') + node_4 = Label.create(name: 'node_4') + + # Create a child under node_3 + child = Label.create(name: 'child', parent: node_3) + + # Verify initial positions + assert_equal 0, node_1.order_value + assert_equal 1, node_2.order_value + assert_equal 2, node_3.order_value + assert_equal 3, node_4.order_value + assert_equal 0, child.order_value + + # Move node_2 as a sibling of child (making it a child of node_3) + child.add_sibling(node_2) + + # Reload all nodes to get updated positions + node_1.reload + node_2.reload + node_3.reload + node_4.reload + + # Verify node_2 is now a child of node_3 + assert_equal node_3.id, node_2._ct_parent_id + + # Verify remaining root nodes have sequential positions without gaps + assert_equal 0, node_1.order_value + assert_equal 1, node_3.order_value + assert_equal 2, node_4.order_value + end end