Class: RuboCop::ResultCache

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

Overview

Provides functionality for caching rubocop runs.

Constant Summary collapse

NON_CHANGING =
[:color, :format, :formatters, :out, :debug, :fail_level,
:cache, :fail_fast, :stdin].freeze

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

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

Returns a new instance of ResultCache.



57
58
59
60
61
62
63
# File 'lib/rubocop/result_cache.rb', line 57

def initialize(file, options, config_store, cache_root = nil)
  cache_root ||= ResultCache.cache_root(config_store)
  @path = File.join(cache_root, rubocop_checksum,
                    relevant_options_digest(options),
                    file_checksum(file, config_store))
  @cached_data = CachedData.new(file)
end

Class Attribute Details

.inhibit_cleanupObject

Returns the value of attribute inhibit_cleanup.



116
117
118
# File 'lib/rubocop/result_cache.rb', line 116

def inhibit_cleanup
  @inhibit_cleanup
end

.source_checksumObject

Returns the value of attribute source_checksum.



116
117
118
# File 'lib/rubocop/result_cache.rb', line 116

def source_checksum
  @source_checksum
end

Class Method Details

.cache_root(config_store) ⇒ Object



47
48
49
50
51
52
53
54
55
# File 'lib/rubocop/result_cache.rb', line 47

def self.cache_root(config_store)
  root = config_store.for('.')['AllCops']['CacheRootDirectory']
  if root == '/tmp'
    tmpdir = File.realpath(Dir.tmpdir)
    # Include user ID in the path to make sure the user has write access.
    root = File.join(tmpdir, Process.uid.to_s)
  end
  File.join(root, 'rubocop_cache')
end

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

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 cleaning 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.



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
# File 'lib/rubocop/result_cache.rb', line 21

def self.cleanup(config_store, verbose, cache_root = nil)
  return if inhibit_cleanup # OPTIMIZE: For faster testing
  cache_root ||= cache_root(config_store)
  return unless File.exist?(cache_root)

  files, dirs = Find.find(cache_root).partition { |path| File.file?(path) }
  if files.length > config_store.for('.')['AllCops']['MaxFilesInCache'] &&
     files.length > 1
    # Add 1 to half the number of files, so that we remove the file if
    # there's only 1 left.
    remove_count = 1 + files.length / 2
    if verbose
      puts "Removing the #{remove_count} oldest files from #{cache_root}"
    end
    sorted = files.sort_by { |path| File.mtime(path) }
    begin
      File.delete(*sorted[0, remove_count])
      dirs.each { |dir| Dir.rmdir(dir) if Dir["#{dir}/*"].empty? }
    rescue Errno::ENOENT
      # This can happen if parallel RuboCop invocations try to remove the
      # same files. No problem.
      puts $ERROR_INFO if verbose
    end
  end
end

Instance Method Details

#loadObject



69
70
71
# File 'lib/rubocop/result_cache.rb', line 69

def load
  @cached_data.from_json(IO.binread(@path))
end

#save(offenses) ⇒ Object



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/rubocop/result_cache.rb', line 73

def save(offenses)
  dir = File.dirname(@path)
  FileUtils.mkdir_p(dir)
  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 is an indication
  # that a symlink attack is being waged.
  return if any_symlink?(dir)

  File.open(preliminary_path, 'wb') 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

Returns:

  • (Boolean)


65
66
67
# File 'lib/rubocop/result_cache.rb', line 65

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