Class: ActiveRecordGraphExtractor::RelationshipAnalyzer

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

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config = ActiveRecordGraphExtractor.configuration) ⇒ RelationshipAnalyzer

Returns a new instance of RelationshipAnalyzer.



7
8
9
10
11
12
# File 'lib/activerecord_graph_extractor/relationship_analyzer.rb', line 7

def initialize(config = ActiveRecordGraphExtractor.configuration)
  @config = config
  @visited_models = Set.new
  @circular_paths = []
  @primary_connection = ActiveRecord::Base.connection
end

Instance Attribute Details

#circular_pathsObject (readonly)

Returns the value of attribute circular_paths.



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

def circular_paths
  @circular_paths
end

#configObject (readonly)

Returns the value of attribute config.



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

def config
  @config
end

#visited_modelsObject (readonly)

Returns the value of attribute visited_models.



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

def visited_models
  @visited_models
end

Instance Method Details

#analyze_model(model_class) ⇒ Object



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/activerecord_graph_extractor/relationship_analyzer.rb', line 14

def analyze_model(model_class)
  # Validate that this is an ActiveRecord model
  unless model_class.respond_to?(:reflect_on_all_associations)
    raise ExtractionError, "#{model_class} is not an ActiveRecord model"
  end

  relationships = {}

  model_class.reflect_on_all_associations.each do |association|
    begin
      next if association.klass.nil?

      relationship_name = association.name.to_s
      next unless config.relationship_included?(relationship_name)

      # Check if the associated model uses the primary database
      associated_model = association.klass
      model_name = associated_model.name
      
      # Skip if model is excluded
      next unless config.model_included?(model_name)
      
      # Skip if the associated model doesn't use the primary database (if enabled)
      if config.skip_non_primary_database_models
        next unless uses_primary_database?(associated_model)
      end

      relationships[relationship_name] = {
        'type' => association.macro.to_s,
        'model_class' => associated_model,
        'model_name' => model_name,
        'foreign_key' => association.foreign_key,
        'polymorphic' => association.options[:polymorphic] || false,
        'optional' => association.options[:optional] || false
      }
    rescue NameError => e
      if config.skip_missing_models
        next
      else
        raise e
      end
    rescue StandardError => e
      # Skip associations that cause database connection errors
      if e.message.include?('Unknown database') || e.message.include?('database connection')
        next
      else
        raise e unless config.skip_missing_models
      end
    end
  end

  filter_relationships(relationships)
end

#analyze_models(model_classes) ⇒ Object



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/activerecord_graph_extractor/relationship_analyzer.rb', line 68

def analyze_models(model_classes)
  # Validate input
  unless model_classes.is_a?(Array)
    raise ExtractionError, "Expected an array of model classes"
  end

  # Handle empty array
  return {} if model_classes.empty?

  # Validate each model class
  model_classes.each do |model_class|
    unless model_class.respond_to?(:reflect_on_all_associations)
      raise ExtractionError, "#{model_class} is not an ActiveRecord model"
    end
  end

  relationships = {}

  model_classes.each do |model_class|
    model_name = model_class.name
    relationships[model_name] = analyze_model(model_class)
  end

  relationships
end

#build_dependency_graph(models) ⇒ Object



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/activerecord_graph_extractor/relationship_analyzer.rb', line 108

def build_dependency_graph(models)
  dependency_graph = {}

  models.each do |model_class|
    dependencies = []
    relationships = analyze_model(model_class)

    relationships.each do |name, info|
      if info['type'] == 'belongs_to' && !info['optional']
        dependencies << info['model_class'] unless dependencies.include?(info['model_class'])
      end
    end

    dependency_graph[model_class] = dependencies.uniq
  end

  dependency_graph
end

#circular_reference?(model_name, visited) ⇒ Boolean

Returns:

  • (Boolean)


127
128
129
# File 'lib/activerecord_graph_extractor/relationship_analyzer.rb', line 127

def circular_reference?(model_name, visited)
  visited.include?(model_name)
end

#get_relationship_info(record, relationship_name) ⇒ Object



94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/activerecord_graph_extractor/relationship_analyzer.rb', line 94

def get_relationship_info(record, relationship_name)
  relationships = analyze_model(record.class)
  relationship = relationships[relationship_name.to_s]
  
  return nil unless relationship

  # Convert to symbols for consistency with expected API
  {
    model_class: relationship['model_class'],
    model_name: relationship['model_name'],
    type: relationship['type'].to_sym
  }
end