Class: DatabaseConsistency::Checkers::MissingIndexFindByChecker::FindByCollector

Inherits:
Prism::Visitor
  • Object
show all
Defined in:
lib/database_consistency/checkers/column_checkers/missing_index_find_by_checker.rb

Overview

Prism AST visitor that collects ALL find_by calls from a source file into a results hash. Key: [model_name, column_name] – model_name is derived from the explicit receiver or the lexical class/module scope for bare calls. Bare calls outside any class are ignored. Value: “file:line” location of the first matching call.

Handles:

- find_by_<col>(<value>) / Model.find_by_<col>!  (dynamic finder)
- find_by(col: <value>) / Model.find_by col:     (symbol-key hash)
- find_by("col" => <value>)                      (string-key hash)

Defined only when Prism is available (Ruby 3.3+).

Constant Summary collapse

DYNAMIC_FINDER_RE =

Matches the full column name from a dynamic finder method name. e.g. find_by_email -> “email”, find_by_first_name -> “first_name” Multi-column patterns like find_by_name_and_email extract “name_and_email” which won’t match any single-column name, so there are no false positives.

/\Afind_by_(.+?)!?\z/.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(file) ⇒ FindByCollector

Returns a new instance of FindByCollector.



97
98
99
100
101
102
# File 'lib/database_consistency/checkers/column_checkers/missing_index_find_by_checker.rb', line 97

def initialize(file)
  super()
  @file = file
  @results = {}
  @scope_stack = []
end

Instance Attribute Details

#resultsObject (readonly)

Returns the value of attribute results.



95
96
97
# File 'lib/database_consistency/checkers/column_checkers/missing_index_find_by_checker.rb', line 95

def results
  @results
end

Instance Method Details

#visit_call_node(node) ⇒ Object



118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/database_consistency/checkers/column_checkers/missing_index_find_by_checker.rb', line 118

def visit_call_node(node)
  name = node.name.to_s
  if (match = DYNAMIC_FINDER_RE.match(name))
    model_key = receiver_to_model_key(node.receiver)
    store(model_key, match[1], node) unless model_key == :skip
  elsif name == 'find_by' && node.arguments
    col = single_hash_column(node.arguments)
    model_key = receiver_to_model_key(node.receiver)
    store(model_key, col, node) if col && model_key != :skip
  end
  super
end

#visit_class_node(node) ⇒ Object



104
105
106
107
108
109
# File 'lib/database_consistency/checkers/column_checkers/missing_index_find_by_checker.rb', line 104

def visit_class_node(node)
  @scope_stack.push(constant_path_name(node.constant_path))
  super
ensure
  @scope_stack.pop
end

#visit_module_node(node) ⇒ Object



111
112
113
114
115
116
# File 'lib/database_consistency/checkers/column_checkers/missing_index_find_by_checker.rb', line 111

def visit_module_node(node)
  @scope_stack.push(constant_path_name(node.constant_path))
  super
ensure
  @scope_stack.pop
end