Introduce orphan_strategy none

Many people override apply_orphan_strategy and either leave it blank or call custom methods from there.

Introducing `orphan_strategy: :none` to allows developers to skip default orphan handling.
From here a developer can add `before_destroy` with what ever logic is desired.

It is possible to introduce a custom orphan strategy with a mixin/concern,
but it doesn't seem to save any code and is not the best interface with too many nuances.
Leaving in the code for now but not promoting to a supported feature yet.
This commit is contained in:
Keenan Brock 2023-07-13 09:32:07 -04:00
parent cb9635cc3a
commit 753013a1bb
No known key found for this signature in database
GPG Key ID: DDF03448455882FC
4 changed files with 64 additions and 12 deletions

View File

@ -5,6 +5,7 @@ a nice looking [Changelog](http://keepachangelog.com).
## Version [HEAD] <sub><sup>Unreleased</sub></sup> ## Version [HEAD] <sub><sup>Unreleased</sub></sup>
* Introduce `orphan_strategy: :none` [#658](https://github.com/stefankroes/ancestry/pull/658)
* Introduce `rebuild_counter_cache!` to reset counter caches. [#663](https://github.com/stefankroes/ancestry/pull/663) [#668](https://github.com/stefankroes/ancestry/pull/668) (thx @RongRongTeng) * Introduce `rebuild_counter_cache!` to reset counter caches. [#663](https://github.com/stefankroes/ancestry/pull/663) [#668](https://github.com/stefankroes/ancestry/pull/668) (thx @RongRongTeng)
* Documentation fixes [#664](https://github.com/stefankroes/ancestry/pull/664) [#667](https://github.com/stefankroes/ancestry/pull/667) (thx @motokikando, @onerinas) * Documentation fixes [#664](https://github.com/stefankroes/ancestry/pull/664) [#667](https://github.com/stefankroes/ancestry/pull/667) (thx @motokikando, @onerinas)
* Introduce `build_cache_depth_sql!`, a sql alternative to `build_cache_depth` [#654](https://github.com/stefankroes/ancestry/pull/654) * Introduce `build_cache_depth_sql!`, a sql alternative to `build_cache_depth` [#654](https://github.com/stefankroes/ancestry/pull/654)
@ -41,7 +42,7 @@ jobs. If you need to do this in the ui, please use `cache_depth`.
- `ancestry_primary_key_format` (introduced 4.3.0, removed by #649) - `ancestry_primary_key_format` (introduced 4.3.0, removed by #649)
- `touch_ancestors` (introduced 2.1, removed by TODO) - `touch_ancestors` (introduced 2.1, removed by TODO)
* These are seen as internal and may go away: * These are seen as internal and may go away:
- `apply_orphan_strategy` (TODO: use `orphan_strategy => none` and define `before_destory`) - `apply_orphan_strategy` Please use `orphan_strategy: :none` and a custom `before_destory` instead.
## Version [4.3.3] <sub><sup>2023-04-01</sub></sup> ## Version [4.3.3] <sub><sup>2023-04-01</sub></sup>

View File

@ -172,6 +172,7 @@ The `has_ancestry` method supports the following options:
:restrict An AncestryException is raised if any children exist :restrict An AncestryException is raised if any children exist
:adopt The orphan subtree is added to the parent of the deleted node :adopt The orphan subtree is added to the parent of the deleted node
If the deleted node is Root, then rootify the orphan subtree If the deleted node is Root, then rootify the orphan subtree
:none skip this logic. (add your own `before_destroy`)
:cache_depth Cache the depth of each node: (See Depth Cache section) :cache_depth Cache the depth of each node: (See Depth Cache section)
false Do not cache depth (default) false Do not cache depth (default)
true Cache depth in 'ancestry_depth' true Cache depth in 'ancestry_depth'

View File

@ -60,19 +60,13 @@ module Ancestry
after_update :update_descendants_with_new_ancestry, if: :ancestry_changed? after_update :update_descendants_with_new_ancestry, if: :ancestry_changed?
# Apply orphan strategy before destroy # Apply orphan strategy before destroy
case orphan_strategy orphan_strategy_helper = "apply_orphan_strategy_#{orphan_strategy}"
when :rootify if method_defined?(orphan_strategy_helper)
alias_method :apply_orphan_strategy, :apply_orphan_strategy_rootify alias_method :apply_orphan_strategy, orphan_strategy_helper
when :destroy before_destroy :apply_orphan_strategy
alias_method :apply_orphan_strategy, :apply_orphan_strategy_destroy elsif orphan_strategy.to_s != "none"
when :adopt
alias_method :apply_orphan_strategy, :apply_orphan_strategy_adopt
when :restrict
alias_method :apply_orphan_strategy, :apply_orphan_strategy_restrict
else
raise Ancestry::AncestryException.new(I18n.t("ancestry.invalid_orphan_strategy")) raise Ancestry::AncestryException.new(I18n.t("ancestry.invalid_orphan_strategy"))
end end
before_destroy :apply_orphan_strategy
# Create ancestry column accessor and set to option or default # Create ancestry column accessor and set to option or default
if options[:cache_depth] if options[:cache_depth]

View File

@ -85,6 +85,62 @@ class OphanStrategiesTest < ActiveSupport::TestCase
end end
end end
def test_apply_orphan_strategy_none
AncestryTestDatabase.with_model orphan_strategy: :none do |model, roots|
root = model.create!
child = model.create!(:parent => root)
model.class_eval do
def apply_orphan_strategy
raise "this should not be called"
end
end
assert_difference 'model.count', -1 do
root.destroy
end
# this record should still exist
assert child.reload.root_id == root.id
end
end
def test_apply_orphan_strategy_custom
AncestryTestDatabase.with_model orphan_strategy: :none do |model|
model.class_eval do
before_destroy :apply_orphan_strategy_abc
def apply_orphan_strategy_abc
apply_orphan_strategy_destroy
end
end
root = model.create!
3.times { root.children.create! }
model.create! # a node that is not affected
assert_difference 'model.count', -4 do
root.destroy
end
end
end
# Not supported. Keeping around to explore for future uses.
def test_apply_orphan_strategy_custom_unsupported
AncestryTestDatabase.with_model skip_ancestry: true do |model|
model.class_eval do
# needs to be defined before calling has_ancestry
def apply_orphan_strategy_abc
apply_orphan_strategy_destroy
end
has_ancestry orphan_strategy: :abc, ancestry_column: AncestryTestDatabase.ancestry_column
end
root = model.create!
3.times { root.children.create! }
model.create! # a node that is not affected
assert_difference 'model.count', -4 do
root.destroy
end
end
end
def test_basic_delete def test_basic_delete
AncestryTestDatabase.with_model do |model| AncestryTestDatabase.with_model do |model|
n1 = model.create! #create a root node n1 = model.create! #create a root node