Class: ActiveRecordGraphExtractor::DependencyResolver

Inherits:
Object
  • Object
show all
Defined in:
lib/activerecord_graph_extractor/dependency_resolver.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(dependency_graph) ⇒ DependencyResolver

Returns a new instance of DependencyResolver.



7
8
9
10
# File 'lib/activerecord_graph_extractor/dependency_resolver.rb', line 7

def initialize(dependency_graph)
  @dependency_graph = dependency_graph
  @resolved_order = []
end

Instance Attribute Details

#dependency_graphObject (readonly)

Returns the value of attribute dependency_graph.



5
6
7
# File 'lib/activerecord_graph_extractor/dependency_resolver.rb', line 5

def dependency_graph
  @dependency_graph
end

#resolved_orderObject (readonly)

Returns the value of attribute resolved_order.



5
6
7
# File 'lib/activerecord_graph_extractor/dependency_resolver.rb', line 5

def resolved_order
  @resolved_order
end

Instance Method Details

#build_creation_order(records_by_model, dependency_graph) ⇒ Object



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/activerecord_graph_extractor/dependency_resolver.rb', line 117

def build_creation_order(records_by_model, dependency_graph)
  grouped_records = group_records_by_dependencies(records_by_model)
  ordered_models = resolve(dependency_graph)
  
  # Create ordered list of [model_name, records] pairs
  ordered_records = []
  
  ordered_models.each do |model_class|
    model_name = model_class.name
    if grouped_records.key?(model_name)
      ordered_records << [model_name, grouped_records[model_name]]
    end
  end
  
  # Add any remaining models not in dependency graph
  grouped_records.each do |model_name, records|
    unless ordered_records.any? { |entry| entry[0] == model_name }
      ordered_records << [model_name, records]
    end
  end
  
  ordered_records
end

#detect_circular_dependencies(dependency_graph) ⇒ Object



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/activerecord_graph_extractor/dependency_resolver.rb', line 99

def detect_circular_dependencies(dependency_graph)
  # Handle different formats
  if dependency_graph.values.first.is_a?(Hash)
    return detect_complex_circular_dependencies(dependency_graph)
  end
  
  # Original boolean detection
  visited = Set.new
  rec_stack = Set.new

  dependency_graph.each_key do |node|
    next if visited.include?(node)
    return true if has_cycle?(node, dependency_graph, visited, rec_stack)
  end

  false
end

#group_by_dependency_levelObject



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/activerecord_graph_extractor/dependency_resolver.rb', line 53

def group_by_dependency_level
  creation_order = resolve_creation_order
  levels = {}
  current_level = 0
  
  creation_order.each do |model_name|
    dependencies = dependency_graph[model_name] || []
    
    if dependencies.empty?
      # No dependencies - can be created first
      levels[current_level] ||= []
      levels[current_level] << model_name
    else
      # Find the maximum level of dependencies
      max_dependency_level = dependencies.map do |dep|
        find_model_level(dep, levels)
      end.max || 0
      
      model_level = max_dependency_level + 1
      levels[model_level] ||= []
      levels[model_level] << model_name
    end
  end
  
  levels
end

#resolve(dependency_graph) ⇒ Object



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/activerecord_graph_extractor/dependency_resolver.rb', line 80

def resolve(dependency_graph)
  # Handle different input formats based on test expectations
  if dependency_graph.values.first.is_a?(Hash)
    # New format: { 'TestOrder' => { 'test_user' => { 'model_class' => 'TestUser' } } }
    return resolve_complex_graph(dependency_graph)
  end
  
  # Original format: { TestOrder => [TestUser] }
  return [] if dependency_graph.empty?

  # Check for circular dependencies
  if detect_circular_dependencies(dependency_graph)
    raise CircularDependencyError, "Circular dependency detected in model relationships"
  end

  # Perform topological sort
  topological_sort(dependency_graph)
end

#resolve_creation_orderObject



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# File 'lib/activerecord_graph_extractor/dependency_resolver.rb', line 12

def resolve_creation_order
  # Create a copy to avoid modifying the original
  graph = dependency_graph.dup
  visited = Set.new
  temp_visited = Set.new
  
  graph.keys.each do |model|
    next if visited.include?(model)
    
    visit_model(model, graph, visited, temp_visited)
  end
  
  # Reverse to get creation order (dependencies first)
  @resolved_order.reverse
end

#resolve_deletion_orderObject



28
29
30
31
# File 'lib/activerecord_graph_extractor/dependency_resolver.rb', line 28

def resolve_deletion_order
  # For deletion, we want the reverse of creation order
  resolve_creation_order.reverse
end

#validate_dependencies(records_data) ⇒ Object



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/activerecord_graph_extractor/dependency_resolver.rb', line 33

def validate_dependencies(records_data)
  missing_dependencies = {}
  
  records_data.each do |model_name, records|
    next unless dependency_graph[model_name]
    
    dependency_graph[model_name].each do |dependency|
      unless records_data.key?(dependency)
        missing_dependencies[model_name] ||= []
        missing_dependencies[model_name] << dependency
      end
    end
  end
  
  return missing_dependencies if missing_dependencies.any?
  
  # Validate foreign key references
  validate_foreign_key_references(records_data)
end