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

Commit

Permalink
Fix scanning for dashed directories.
Browse files Browse the repository at this point in the history
The FileScanner class wasn't properly scanning for files located in a directory
that used dashes instead of underscores. This in turn would prevent ruby-lint
from analysing itself due to the usage of lib/ruby-lint (instead of
lib/ruby_lint).

With this commit I've also taken the liberty of cleaning up the tests for
FileScanner. These tests now test more specific parts instead of only testing
the scan method.
  • Loading branch information
Yorick Peterse committed Aug 6, 2014
1 parent ccdf3fc commit b8826f7
Show file tree
Hide file tree
Showing 2 changed files with 163 additions and 83 deletions.
82 changes: 47 additions & 35 deletions lib/ruby-lint/file_scanner.rb
Expand Up @@ -60,23 +60,66 @@ def initialize(directories = self.class.default_directories, ignore = [])
# @return [Array]
#
def scan(constant)
@glob_cache ||= directories.empty? ? [] : glob_ruby_files

unless constant_paths_cached?(constant)
build_constant_paths_cache(constant)
end

return @constant_paths_cache[constant]
end

##
# @return [Array]
#
def glob_cache
@glob_cache ||= directories.empty? ? [] : glob_ruby_files
end

##
# @return [Array]
#
def glob_ruby_files
return Dir.glob("{#{directories.join(',')}}/**/*.rb")
end

private
##
# Returns the file path for the given constant.
#
# @example
# constant_to_path('FooBar::Baz') # => "foo_bar/baz.rb"
#
# @param [String] constant
# @return [String]
#
def constant_to_path(constant)
return constant.gsub('::', '/').snake_case + '.rb'
end

##
# Returns a path similar to {#constant_to_path} but using dashes instead of
# underscores for the first directory.
#
# @example
# constant_to_dashed_path('FooBar::Baz') # => "foo-bar/baz.rb"
#
# @see [#constant_to_path]
#
def constant_to_dashed_path(constant)
const_segments = constant.split('::')
path_segments = []

const_segments.each_with_index do |segment, index|
segment = segment.snake_case

# Use dashes for the first segment (= top level directory).
if const_segments.length > 1 and index == 0
segment = segment.gsub('_', '-')
end

path_segments << segment
end

return path_segments.join('/') + '.rb'
end

##
# Searches all the files that could potentially define the given constant
Expand Down Expand Up @@ -119,7 +162,7 @@ def match_globbed_files(segment)
# "bar_foo.rb"). We don't want that.
segment = "/#{segment}"

return @glob_cache.select { |p| p.include?(segment) }
return glob_cache.select { |p| p.include?(segment) }
end

##
Expand All @@ -128,36 +171,5 @@ def match_globbed_files(segment)
def constant_paths_cached?(constant)
return @constant_paths_cache.key?(constant)
end

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

##
# @see #constant_to_path
#
def constant_to_dashed_path(constant)
segments = constant.split('::')
last = segments[-1]
prefix = segments[0..-2].join('/').snake_case.gsub('_', '-')

unless prefix.empty?
prefix += '/'
end

return "#{prefix}#{last.snake_case}.rb"
end

##
# @param [String] segment
# @return [String]
#
def glob_pattern(segment)
return "{#{directories.join(',')}}/**/#{segment}.rb"
end
end # FileScanner
end # RubyLint
164 changes: 116 additions & 48 deletions spec/ruby-lint/file_scanner_spec.rb
Expand Up @@ -6,79 +6,147 @@
@lib_dir = fixture_path('file_scanner/lib')
end

example 'raise when a non enumerable argument is given' do
lambda { RubyLint::FileScanner.new(10) }.should raise_error(TypeError)
end
context '#initialize' do
before do
@scanner = described_class.new
end

example 'set the default directories' do
scanner = RubyLint::FileScanner.new
example 'raise when a non enumerable argument is given' do
lambda { described_class.new(10) }.should raise_error(TypeError)
end

scanner.directories.empty?.should == false
end
example 'set the default directories' do
@scanner.directories.empty?.should == false
end

example 'do not include non existing directories' do
app = File.join(Dir.pwd, 'app')
scanner = RubyLint::FileScanner.new
example 'do not include non existing directories' do
app = File.join(Dir.pwd, 'app')

scanner.directories.include?(app).should == false
@scanner.directories.include?(app).should == false
end
end

example 'glob Ruby source files in a single directory' do
scanner = RubyLint::FileScanner.new([@lib_dir])
context '#glob_ruby_files' do
example 'glob Ruby source files in a single directory' do
scanner = described_class.new([@lib_dir])

scanner.glob_ruby_files.empty?.should == false
end
scanner.glob_ruby_files.empty?.should == false
end

example 'glob Ruby source files in multiple directories' do
scanner = RubyLint::FileScanner.new([@lib_dir, @rails_dir])
example 'glob Ruby source files in multiple directories' do
scanner = described_class.new([@lib_dir, @rails_dir])

scanner.glob_ruby_files.empty?.should == false
scanner.glob_ruby_files.empty?.should == false
end
end

example 'finding a class' do
scanner = RubyLint::FileScanner.new([@lib_dir])
paths = scanner.scan('Example::User')
context '#scan' do
example 'finding a class' do
scanner = described_class.new([@lib_dir])
paths = scanner.scan('Example::User')

paths.should == [fixture_path('file_scanner/lib/example/user.rb')]
end
paths.should == [fixture_path('file_scanner/lib/example/user.rb')]
end

example 'finding a class using dashes for the directory names' do
scanner = RubyLint::FileScanner.new([@lib_dir])
paths = scanner.scan('TestDashes::Foo')
example 'finding a class using dashes for the directory names' do
scanner = described_class.new([@lib_dir])
paths = scanner.scan('TestDashes::Foo')

paths.should == [fixture_path('file_scanner/lib/test-dashes/foo.rb')]
end
paths.should == [fixture_path('file_scanner/lib/test-dashes/foo.rb')]
end

example 'finding a class using a Rails structure' do
scanner = described_class.new([@rails_dir])
paths = scanner.scan('User')

paths.should == [
fixture_path('file_scanner/rails/app/models/user.rb'),
fixture_path('file_scanner/rails/app/models/example/user.rb')
]
end

example 'finding a namespaced class using a Rails structure' do
scanner = described_class.new([@rails_dir])
paths = scanner.scan('Example::User')

paths.should == [
fixture_path('file_scanner/rails/app/models/example/user.rb')
]
end

example 'finding a class using a Rails structure' do
scanner = RubyLint::FileScanner.new([@rails_dir])
paths = scanner.scan('User')
example 'ignoring directories' do
scanner = described_class.new([@lib_dir], [@lib_dir])

paths.should == [
fixture_path('file_scanner/rails/app/models/user.rb'),
fixture_path('file_scanner/rails/app/models/example/user.rb')
]
scanner.scan('Example::User').empty?.should == true
end

example 'do not scan when there are no directories' do
scanner = described_class.new([])

scanner.should_not receive(:glob_ruby_files)

scanner.scan('Foo')
end
end

example 'finding a namespaced class using a Rails structure' do
scanner = RubyLint::FileScanner.new([@rails_dir])
paths = scanner.scan('Example::User')
context '#constant_to_path' do
before do
@scanner = described_class.new([@lib_dir])
end

example 'return the path for a single constant segment' do
@scanner.constant_to_path('Foo').should == 'foo.rb'
end

example 'return the path for two constant segments' do
@scanner.constant_to_path('Foo::Bar').should == 'foo/bar.rb'
end

paths.should == [
fixture_path('file_scanner/rails/app/models/example/user.rb')
]
example 'return the path for three constant segments' do
@scanner.constant_to_path('Foo::Bar::Baz').should == 'foo/bar/baz.rb'
end

example 'snake case the constant names' do
@scanner.constant_to_path('FooBar').should == 'foo_bar.rb'
end
end

example 'ignoring directories' do
scanner = RubyLint::FileScanner.new([@lib_dir], [@lib_dir])
context '#constant_to_dashed_path' do
before do
@scanner = described_class.new([@lib_dir])
end

example 'return the path for a single constant segment' do
@scanner.constant_to_dashed_path('RubyLint').should == 'ruby_lint.rb'
end

scanner.scan('Example::User').empty?.should == true
example 'return the path for two constant segments' do
@scanner.constant_to_dashed_path('RubyLint::FooBar')
.should == 'ruby-lint/foo_bar.rb'
end

example 'return the path for three constant segments' do
@scanner.constant_to_dashed_path('RubyLint::FooBar::BazBaz')
.should == 'ruby-lint/foo_bar/baz_baz.rb'
end
end

example 'do not scan when there are no directories' do
scanner = RubyLint::FileScanner.new([])
context '#build_constant_paths_cache' do
before do
@scanner = described_class.new([@rails_dir])
end

example 'build an empty cache for a missing constant' do
@scanner.build_constant_paths_cache('FoobarDoesNotExist').should be_empty
end

scanner.should_not receive(:glob_ruby_files)
example 'build the cache for a User class' do
cache = @scanner.build_constant_paths_cache('User')

scanner.scan('Foo')
cache.should == [
fixture_path('file_scanner/rails/app/models/user.rb'),
fixture_path('file_scanner/rails/app/models/example/user.rb')
]
end
end
end

0 comments on commit b8826f7

Please sign in to comment.