Class: LcpRuby::Tasks::DestroyOrderResolver
- Inherits:
-
Object
- Object
- LcpRuby::Tasks::DestroyOrderResolver
- Includes:
- TSort
- Defined in:
- lib/lcp_ruby/tasks/destroy_order_resolver.rb
Overview
Computes a destroy order for ‘lcp_ruby:reset_data` (#18) using a topological sort over `belongs_to` dependencies. Children come first, parents last, so `.destroy_all` cascades correctly without tripping `dependent: :restrict_with_error` on the parent side.
Self-edges (tree-style ‘belongs_to :parent`) are skipped — the destroy_all on a tree-enabled model handles its own children via the AR `dependent:` cascade.
Cycles between distinct models (rare but possible: A.belongs_to :b AND B.belongs_to :a) raise ‘TSort::Cyclic`; the rake task catches that and falls back to alphabetical order, logging a warning.
Instance Method Summary collapse
-
#destroy_order ⇒ Object
Returns model definitions in destroy order: children first, parents last.
-
#initialize(model_definitions) ⇒ DestroyOrderResolver
constructor
A new instance of DestroyOrderResolver.
- #tsort_each_child(node, &block) ⇒ Object
- #tsort_each_node(&block) ⇒ Object
Constructor Details
#initialize(model_definitions) ⇒ DestroyOrderResolver
Returns a new instance of DestroyOrderResolver.
22 23 24 25 26 27 28 29 30 31 32 33 |
# File 'lib/lcp_ruby/tasks/destroy_order_resolver.rb', line 22 def initialize(model_definitions) # Skip models with no AR class to destroy: virtual, abstract, # api-backed. bind_to is intentionally INCLUDED here so the host AR # class is reachable as a dependency target — the rake task itself # decides whether to call destroy_all on it. @defs = model_definitions.values.reject do |m| m.respond_to?(:virtual?) && m.virtual? || m.respond_to?(:abstract?) && m.abstract? || m.respond_to?(:api_model?) && m.api_model? end @by_name = @defs.each_with_object({}) { |d, h| h[d.name] = d } end |
Instance Method Details
#destroy_order ⇒ Object
Returns model definitions in destroy order: children first, parents last. Self-edges (tree FKs) are excluded from the graph; AR’s cascade handles them at runtime.
38 39 40 |
# File 'lib/lcp_ruby/tasks/destroy_order_resolver.rb', line 38 def destroy_order tsort.reverse end |
#tsort_each_child(node, &block) ⇒ Object
46 47 48 49 50 51 52 53 54 |
# File 'lib/lcp_ruby/tasks/destroy_order_resolver.rb', line 46 def tsort_each_child(node, &block) node.belongs_to_fk_map.values.each do |assoc| target = @by_name[assoc.target_model] # Skip missing targets (likely a polymorphic belongs_to or a # bind_to where the host class isn't tracked here) and self-edges. next if target.nil? || target == node yield target end end |
#tsort_each_node(&block) ⇒ Object
42 43 44 |
# File 'lib/lcp_ruby/tasks/destroy_order_resolver.rb', line 42 def tsort_each_node(&block) @defs.each(&block) end |