Class: RuboCop::ResultCache Private

Inherits:
Object
  • Object
show all
Defined in:
lib/rubocop/result_cache.rb

Overview

This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.

Provides functionality for caching RuboCop runs.

Constant Summary collapse

NON_CHANGING =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

%i[color format formatters out debug display_time fail_level
fix_layout autocorrect safe_autocorrect autocorrect_all
cache fail_fast stdin parallel].freeze
DL_EXTENSIONS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

::RbConfig::CONFIG
.values_at('DLEXT', 'DLEXT2')
.reject { |ext| !ext || ext.empty? }
.map    { |ext| ".#{ext}" }
.freeze

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(file, team, options, config_store, cache_root_override = nil) ⇒ ResultCache

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a new instance of ResultCache.



108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/rubocop/result_cache.rb', line 108

def initialize(file, team, options, config_store, cache_root_override = nil)
  cache_root_override ||= options[:cache_root] if options[:cache_root]
  rubocop_cache_dir = ResultCache.cache_root(config_store, cache_root_override)
  @allow_symlinks_in_cache_location =
    ResultCache.allow_symlinks_in_cache_location?(config_store)
  @path = File.join(rubocop_cache_dir,
                    self.class.source_checksum,
                    context_checksum(team, options),
                    file_checksum(file, config_store))
  @cached_data = CachedData.new(file)
  @debug = options[:debug]
end

Class Attribute Details

.inhibit_cleanupObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



191
192
193
# File 'lib/rubocop/result_cache.rb', line 191

def inhibit_cleanup
  @inhibit_cleanup
end

.rubocop_required_featuresObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



45
46
47
# File 'lib/rubocop/result_cache.rb', line 45

def rubocop_required_features
  @rubocop_required_features
end

Instance Attribute Details

#pathObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



106
107
108
# File 'lib/rubocop/result_cache.rb', line 106

def path
  @path
end

Class Method Details

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


98
99
100
# File 'lib/rubocop/result_cache.rb', line 98

def self.allow_symlinks_in_cache_location?(config_store)
  config_store.for_pwd.for_all_cops['AllowSymlinksInCacheRootDirectory']
end

.cache_root(config_store, cache_root_override = nil) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



88
89
90
91
92
93
94
95
96
# File 'lib/rubocop/result_cache.rb', line 88

def self.cache_root(config_store, cache_root_override = nil)
  return @cache_root if @cache_root && !cache_root_override

  result = CacheConfig.root_dir do
    cache_root_override || config_store.for_pwd.for_all_cops['CacheRootDirectory']
  end
  @cache_root = result unless cache_root_override
  result
end

.cleanup(config_store, verbose, cache_root_override = nil) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Remove old files so that the cache doesn’t grow too big. When the threshold MaxFilesInCache has been exceeded, the oldest 50% of all the files in the cache are removed. The reason for removing so much is that removing should be done relatively seldom, since there is a slight risk that some other RuboCop process was just about to read the file, when there’s parallel execution and the cache is shared.



28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/rubocop/result_cache.rb', line 28

def self.cleanup(config_store, verbose, cache_root_override = nil)
  return if inhibit_cleanup # OPTIMIZE: For faster testing
  return unless config_store.for_pwd.for_all_cops['MaxFilesInCache']

  rubocop_cache_dir = cache_root(config_store, cache_root_override)
  return unless File.exist?(rubocop_cache_dir)

  # We know the cache entries are 3 level deep, so globing
  # for `*/*/*` only returns files.
  files = Dir[File.join(rubocop_cache_dir, '*/*/*')]
  return unless requires_file_removal?(files.length, config_store)

  remove_oldest_files(files, rubocop_cache_dir, verbose)
end

.relevant_options_digest(options) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Return a hash of the options given at invocation, minus the ones that have no effect on which offenses and disabled line ranges are found, and thus don’t affect caching.



211
212
213
214
215
216
217
# File 'lib/rubocop/result_cache.rb', line 211

def relevant_options_digest(options)
  @relevant_options_digest ||= {}
  @relevant_options_digest[options] ||= begin
    options = options.reject { |key, _| NON_CHANGING.include?(key) }
    options.to_s.gsub(/[^a-z]+/i, '_')
  end
end

.reset_config_cacheObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



102
103
104
# File 'lib/rubocop/result_cache.rb', line 102

def self.reset_config_cache
  @cache_root = nil
end

.source_checksumObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

The checksum of the RuboCop program running the inspection.



194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/rubocop/result_cache.rb', line 194

def source_checksum
  @source_checksum ||= begin
    digest = Digest::SHA1.new
    rubocop_extra_features
      .select { |path| File.file?(path) }
      .sort!
      .each do |path|
        digest << digest(path)
      end
    digest << RuboCop::Version::STRING << RuboCop::AST::Version::STRING
    digest.hexdigest
  end
end

Instance Method Details

#debug?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


121
122
123
# File 'lib/rubocop/result_cache.rb', line 121

def debug?
  @debug
end

#loadObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



129
130
131
132
# File 'lib/rubocop/result_cache.rb', line 129

def load
  puts "Loading cache from #{@path}" if debug?
  @cached_data.from_json(File.read(@path, encoding: Encoding::UTF_8))
end

#save(offenses) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/rubocop/result_cache.rb', line 134

def save(offenses)
  dir = File.dirname(@path)

  begin
    FileUtils.mkdir_p(dir)
  rescue Errno::EACCES, Errno::EROFS => e
    warn "Couldn't create cache directory. Continuing without cache.\n  #{e.message}"
    return
  end

  preliminary_path = "#{@path}_#{rand(1_000_000_000)}"
  # RuboCop must be in control of where its cached data is stored. A
  # symbolic link anywhere in the cache directory tree can be an
  # indication that a symlink attack is being waged.
  return if symlink_protection_triggered?(dir)

  File.open(preliminary_path, 'w', encoding: Encoding::UTF_8) do |f|
    f.write(@cached_data.to_json(offenses))
  end
  # The preliminary path is used so that if there are multiple RuboCop
  # processes trying to save data for the same inspected file
  # simultaneously, the only problem we run in to is a competition who gets
  # to write to the final file. The contents are the same, so no corruption
  # of data should occur.
  FileUtils.mv(preliminary_path, @path)
end

#valid?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


125
126
127
# File 'lib/rubocop/result_cache.rb', line 125

def valid?
  File.exist?(@path)
end