Class: QueryPackwerk::FileCache

Inherits:
Object
  • Object
show all
Extended by:
T::Sig
Defined in:
lib/query_packwerk/file_cache.rb

Overview

Manages caching of file contents, AST nodes, and code analysis results. Provides efficient access to parsed Ruby code, constant references, and source abstractions for performance optimization during violation analysis. Supports both standard and anonymized views of source code patterns.

Constant Summary collapse

RUBY_FILE =
T.let(/\.(rb|rake)\z/, Regexp)

Instance Method Summary collapse

Constructor Details

#initializeFileCache

Returns a new instance of FileCache.



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/query_packwerk/file_cache.rb', line 15

def initialize
  # { file_name => AST }
  @file_ast_cache = T.let({}, T::Hash[String, RuboCop::AST::Node])
  @file_cache = T.let({}, T::Hash[String, String])

  # { [file_name, const_name] => [AST const nodes] }
  @file_const_cache = T.let({}, T::Hash[T::Array[String], T::Array[RuboCop::AST::Node]])

  # { [file_name, const_name] => [AST const nodes with full receiver chains] }
  @full_source_cache = T.let({}, T::Hash[T::Array[String], T::Array[RuboCop::AST::Node]])

  # { [file_name, const_name] => [anonymized sources] }
  @anonymized_source_cache = T.let({}, T::Hash[T::Array[String], T::Array[String]])

  @anonymized_args_cache = T.let({}, T::Hash[String, String])

  @is_active_record_cache = T.let({}, T::Hash[String, String])
  @is_constant_cache = T.let({}, T::Hash[String, String])
end

Instance Method Details

#anonymize_arguments(source) ⇒ Object



111
112
113
114
115
# File 'lib/query_packwerk/file_cache.rb', line 111

def anonymize_arguments(source)
  @anonymized_args_cache[source] ||= RuleRewriter
                                     .rewrite(source)
                                     .delete("\n").squeeze(' ').delete_prefix('::')
end

#get_all_const_occurrences(file_name:, class_name:) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/query_packwerk/file_cache.rb', line 79

def get_all_const_occurrences(file_name:, class_name:)
  const_key = [file_name, class_name]

  return T.must(@file_const_cache[const_key]) if @file_const_cache.key?(const_key)

  absolute_const_node = ast_from(class_name)
  relative_const_node = ast_from(class_name.delete_prefix('::'))

  @file_const_cache[const_key] = get_file_ast(file_name).each_descendant.select do |node|
    next false unless node.const_type?

    node == absolute_const_node || node == relative_const_node
  end
end

#get_file(file_name) ⇒ Object



69
70
71
72
73
74
75
# File 'lib/query_packwerk/file_cache.rb', line 69

def get_file(file_name)
  @file_cache[file_name] ||= if RUBY_FILE.match?(file_name) && File.exist?(file_name)
                               File.read(file_name)
                             else
                               'x'
                             end
end

#get_file_ast(file_name) ⇒ Object



64
65
66
# File 'lib/query_packwerk/file_cache.rb', line 64

def get_file_ast(file_name)
  @file_ast_cache[file_name] ||= ast_from(get_file(file_name))
end

#get_full_anonymous_sources(file_name:, class_name:) ⇒ Object



119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/query_packwerk/file_cache.rb', line 119

def get_full_anonymous_sources(file_name:, class_name:)
  const_key = [file_name, class_name]

  return T.must(@anonymized_source_cache[const_key]) if @anonymized_source_cache.key?(const_key)

  @anonymized_source_cache[const_key] = get_full_sources(
    file_name: file_name,
    class_name: class_name
  ).map do |node|
    get_full_receiver_chain(node)
      .source
      .then { |s| anonymize_arguments(s) }
  end
end

#get_full_sources(file_name:, class_name:) ⇒ Object



96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/query_packwerk/file_cache.rb', line 96

def get_full_sources(file_name:, class_name:)
  const_key = [file_name, class_name]

  return T.must(@full_source_cache[const_key]) if @full_source_cache.key?(const_key)

  @full_source_cache[const_key] = get_all_const_occurrences(
    file_name: file_name,
    class_name: class_name
  ).map do |node|
    get_full_receiver_chain(node)
  end
end

#load!(*file_names, headers: true) ⇒ Object



36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/query_packwerk/file_cache.rb', line 36

def load!(*file_names, headers: true)
  file_count = file_names.size

  warn "Prepopulating AST cache with #{file_count} files: " if headers

  file_names.each do |f|
    get_file_ast(f)
    $stderr.print '.'
  end

  warn '', 'AST cache loaded' if headers
end

#set(cache_name, key:, value:) ⇒ Object



50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/query_packwerk/file_cache.rb', line 50

def set(cache_name, key:, value:)
  case cache_name
  when :is_active_record
    return @is_active_record_cache[key] if @is_active_record_cache.key?(key)

    @is_active_record_cache[key] = value
  when :is_constant
    return @is_constant_cache[key] if @is_constant_cache.key?(key)

    @is_constant_cache[key] = value
  end
end