Skip to content
This repository has been archived by the owner on Mar 16, 2023. It is now read-only.


Cache results from FileScanner#scan.
Browse files Browse the repository at this point in the history
Before this up to 2 calls to Dir.glob could be made every time FileScanner#scan
was called. For an average file referring to a set of constants this could
introduce significant overhead.

This commit changes the workings of FileScanner#scan so that it does two things

1. All possible files are globbed once and then stored.
2. The results of FileScanner#scan are cached per constant path.

In terms of numbers, before this commit the script `benchmark/bootup.rb` would
report an average exection time of around 1,347707 seconds. This commit reduces
that to around 0,903891 seconds, a difference of around 440 miliseconds.

Although the decrease might not seem like much, especially for Ruby scripts, it
means that it becomes more attractive to use ruby-lint in an editor without
having to worry about it locking up the entire editor. For example, when using
Syntastic for Vim in the above scenario this would mean that Vim, since it
doesn't run plugins in an asynchronous manner, would be locked up for almost
half a second *at least*.

Benchmarks were performed on a standard Thinkpad T520 with a 5400 RPM HDD
running Linux 3.11.6. Numbers may vary for those who use an SSD.

See #61 for more information.
  • Loading branch information
Yorick Peterse committed Nov 14, 2013
1 parent d1fbb6e commit 47ab299
Showing 1 changed file with 44 additions and 11 deletions.
55 changes: 44 additions & 11 deletions lib/ruby-lint/file_scanner.rb
Expand Up @@ -23,6 +23,13 @@ def initialize(directories = [Dir.pwd], ignore = [])

@directories = directories
@ignore = ignore || []

# Hash that will contain the matching file paths for a given constant.
@constant_paths_cache = {}

# Globbing all files at once and then comparing those results is faster
# than running a Dir.glob for every call to #scan.
@glob_cache = Dir.glob("#{directories.join(',')}/**/*.rb")

Expand All @@ -34,41 +41,67 @@ def initialize(directories = [Dir.pwd], ignore = [])
# @return [Array]
def scan(constant)
segment = constant_to_path(constant)
paths = Dir.glob(glob_pattern(segment))
unless constant_paths_cached?(constant)

return @constant_paths_cache[constant]


# Searches all the files that could potentially define the given constant
# and caches them.
# @param [String] constant
def build_constant_paths_cache(constant)
paths = match_globbed_files(constant_to_path(constant))

# Lets see if we can find anything when using dashes for the directory
# names instead of underscores.
if paths.empty?
segment = constant_to_dashed_path(constant)
paths = Dir.glob(glob_pattern(segment))
paths = match_globbed_files(constant_to_dashed_path(constant))
end! { |path| File.expand_path(path) }! { |p| File.expand_path(p) }

ignore.each do |pattern|
paths.reject! do |path|

# Ensure that the order is from top-level -> deeply nested files instead
# of a random order.
# Ensure that the order is from top-level -> deeply nested files
# instead of a random order.
paths.sort! do |left, right|
left.length <=> right.length

return paths
@constant_paths_cache[constant] = paths

# @return [Array]
def match_globbed_files(segment)
return { |p| p.include?(segment) }

# @return [TrueClass|FalseClass]
def constant_paths_cached?(constant)
return @constant_paths_cache.key?(constant)

# @param [String] constant
# @return [String]
def constant_to_path(constant)
return constant.gsub('::', '/').snake_case
return constant.gsub('::', '/').snake_case + '.rb'

Expand All @@ -79,7 +112,7 @@ def constant_to_dashed_path(constant)
last = segments[-1]
path = segments[0..-2].join('/').snake_case.gsub('_', '-')

return "#{path}/#{last.snake_case}"
return "#{path}/#{last.snake_case}.rb"

Expand Down

0 comments on commit 47ab299

Please sign in to comment.