Class: LcpRuby::Tasks::DestroyOrderResolver

Inherits:
Object
  • Object
show all
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

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_orderObject

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