Skip to content

Commit

Permalink
Introduce orphan_strategy none
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
kbrock committed Jul 13, 2023
1 parent cb9635c commit 753013a
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 12 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Expand Up @@ -5,6 +5,7 @@ a nice looking [Changelog](http://keepachangelog.com).

## 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)
* 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)
Expand Down Expand Up @@ -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)
- `touch_ancestors` (introduced 2.1, removed by TODO)
* 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>

Expand Down
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -172,6 +172,7 @@ The `has_ancestry` method supports the following options:
:restrict An AncestryException is raised if any children exist
:adopt The orphan subtree is added to the parent of the deleted node
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)
false Do not cache depth (default)
true Cache depth in 'ancestry_depth'
Expand Down
16 changes: 5 additions & 11 deletions lib/ancestry/has_ancestry.rb
Expand Up @@ -60,19 +60,13 @@ def has_ancestry options = {}
after_update :update_descendants_with_new_ancestry, if: :ancestry_changed?

# Apply orphan strategy before destroy
case orphan_strategy
when :rootify
alias_method :apply_orphan_strategy, :apply_orphan_strategy_rootify
when :destroy
alias_method :apply_orphan_strategy, :apply_orphan_strategy_destroy
when :adopt
alias_method :apply_orphan_strategy, :apply_orphan_strategy_adopt
when :restrict
alias_method :apply_orphan_strategy, :apply_orphan_strategy_restrict
else
orphan_strategy_helper = "apply_orphan_strategy_#{orphan_strategy}"
if method_defined?(orphan_strategy_helper)
alias_method :apply_orphan_strategy, orphan_strategy_helper
before_destroy :apply_orphan_strategy
elsif orphan_strategy.to_s != "none"
raise Ancestry::AncestryException.new(I18n.t("ancestry.invalid_orphan_strategy"))
end
before_destroy :apply_orphan_strategy

# Create ancestry column accessor and set to option or default
if options[:cache_depth]
Expand Down
56 changes: 56 additions & 0 deletions test/concerns/orphan_strategies_test.rb
Expand Up @@ -85,6 +85,62 @@ def apply_orphan_strategy
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
AncestryTestDatabase.with_model do |model|
n1 = model.create! #create a root node
Expand Down

0 comments on commit 753013a

Please sign in to comment.