123456789_123456789_123456789_123456789_123456789_

Class: RuboCop::TargetFinder Private

Do not use. This class is for internal use only.
Relationships & Source Files
Inherits: Object
Defined in: lib/rubocop/target_finder.rb

Overview

This class finds target files to inspect by scanning the directory tree and picking ruby files.

Constant Summary

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Instance Attribute Details

#debug?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/target_finder.rb', line 220

def debug?
  @options[:debug]
end

#fail_fast?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/target_finder.rb', line 224

def fail_fast?
  @options[:fail_fast]
end

#force_exclusion?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/target_finder.rb', line 212

def force_exclusion?
  @options[:force_exclusion]
end

#ignore_parent_exclusion?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/target_finder.rb', line 216

def ignore_parent_exclusion?
  @options[:ignore_parent_exclusion]
end

#stdin?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/target_finder.rb', line 165

def stdin?
  @options.key?(:stdin)
end

Instance Method Details

#all_cops_include (private)

[ GitHub ]

  
# File 'lib/rubocop/target_finder.rb', line 131

def all_cops_include
  @all_cops_include ||= @config_store.for_pwd.for_all_cops['Include'].map(&:to_s)
end

#combined_exclude_glob_patterns(base_dir) (private)

[ GitHub ]

  
# File 'lib/rubocop/target_finder.rb', line 117

def combined_exclude_glob_patterns(base_dir)
  exclude = @config_store.for(base_dir).for_all_cops['Exclude'] || []
  patterns = exclude.select { |pattern| pattern.is_a?(String) && pattern.end_with?('/**/*') }
                    .map { |pattern| pattern.sub("#{base_dir}/", '') }
  "#{base_dir}/{#{patterns.join(',')}}"
end

#configured_include?(file) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/target_finder.rb', line 184

def configured_include?(file)
  @config_store.for_pwd.file_to_include?(file)
end

#find(args, mode) ⇒ Array

Generate a list of target files by expanding globbing patterns (if any). If args is empty, recursively find all Ruby source files under the current directory

Returns:

  • (Array)

    array of file paths

[ GitHub ]

  
# File 'lib/rubocop/target_finder.rb', line 17

def find(args, mode)
  return target_files_in_dir if args.empty?

  files = []

  args.uniq.each do |arg|
    files += if File.directory?(arg)
               target_files_in_dir(arg.chomp(File::SEPARATOR))
             else
               process_explicit_path(arg, mode)
             end
  end

  files.map { |f| File.expand_path(f) }.uniq
end

#find_files(base_dir, flags)

Search for files recursively starting at the given base directory using the given flags that determine how the match is made. Excluded files will be removed later by the caller, but as an optimization find_files removes the top level directories that are excluded in configuration in the normal way (dir/*/).

[ GitHub ]

  
# File 'lib/rubocop/target_finder.rb', line 60

def find_files(base_dir, flags)
  # get all wanted directories first to improve speed of finding all files
  exclude_pattern = combined_exclude_glob_patterns(base_dir)
  dir_flags = flags | File::FNM_PATHNAME | File::FNM_EXTGLOB
  patterns = wanted_dir_patterns(base_dir, exclude_pattern, dir_flags)
  patterns.map! { |dir| File.join(dir, '*') }
  # We need this special case to avoid creating the pattern
  # /**/* which searches the entire file system.
  patterns = [File.join(base_dir, '**/*')] if patterns.empty?

  Dir.glob(patterns, flags).select { |path| FileTest.file?(path) }
end

#hidden_dir?(dir) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/target_finder.rb', line 82

def hidden_dir?(dir)
  basename = File.basename(dir)
  basename.start_with?('.') && basename != '.' && basename != '..'
end

#hidden_file_in_dir?(file, base_dir) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/target_finder.rb', line 87

def hidden_file_in_dir?(file, base_dir)
  base_dir = "#{base_dir}#{File::SEPARATOR}" unless base_dir.end_with?(File::SEPARATOR)
  relative = file.delete_prefix(base_dir)
  relative.start_with?('.') || relative.include?(HIDDEN_PATH_SUBSTRING)
end

#included_file?(file) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/target_finder.rb', line 157

def included_file?(file)
  ruby_file?(file) || configured_include?(file)
end

#order (private)

[ GitHub ]

  
# File 'lib/rubocop/target_finder.rb', line 203

def order
  if fail_fast?
    # Most recently modified file first.
    ->(path) { -Integer(File.mtime(path)) }
  else
    :itself
  end
end

#process_explicit_path(path, mode) (private)

[ GitHub ]

  
# File 'lib/rubocop/target_finder.rb', line 135

def process_explicit_path(path, mode)
  files = path.include?('*') ? Dir[path] : [path]

  if mode == :only_recognized_file_types || force_exclusion?
    files.select! { |file| included_file?(file) }
  end
  files.reject! { |file| FileTest.directory?(file) }

  force_exclusion? ? without_excluded(files) : files
end

#ruby_executable?(file) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/target_finder.rb', line 188

def ruby_executable?(file)
  return false if !File.extname(file).empty? || !File.exist?(file) || File.empty?(file)

  first_line = File.open(file, &:readline)
  /#!.*(#{ruby_interpreters(file).join('|')})/.match?(first_line)
rescue EOFError, ArgumentError => e
  warn("Unprocessable file #{file}: #{e.class}, #{e.message}") if debug?

  false
end

#ruby_extension?(file) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/target_finder.rb', line 169

def ruby_extension?(file)
  ruby_extensions.include?(File.extname(file))
end

#ruby_extensions (private)

[ GitHub ]

  
# File 'lib/rubocop/target_finder.rb', line 173

def ruby_extensions
  @ruby_extensions ||= begin
    ext_patterns = all_cops_include.select { |pattern| pattern.start_with?('**/*.') }
    ext_patterns.map { |pattern| pattern.sub('**/*', '') }
  end
end

#ruby_file?(file) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/target_finder.rb', line 161

def ruby_file?(file)
  stdin? || ruby_extension?(file) || ruby_filename?(file) || ruby_executable?(file)
end

#ruby_filename?(file) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/target_finder.rb', line 180

def ruby_filename?(file)
  ruby_filenames.include?(File.basename(file))
end

#ruby_filenames (private)

[ GitHub ]

  
# File 'lib/rubocop/target_finder.rb', line 124

def ruby_filenames
  @ruby_filenames ||= begin
    file_patterns = all_cops_include.reject { |pattern| pattern.start_with?('**/*.') }
    file_patterns.map { |pattern| pattern.sub('**/', '') }
  end
end

#ruby_interpreters(file) (private)

[ GitHub ]

  
# File 'lib/rubocop/target_finder.rb', line 199

def ruby_interpreters(file)
  @config_store.for(file).for_all_cops['RubyInterpreters']
end

#target_files_in_dir(base_dir = PathUtil.pwd) ⇒ Array

Finds all Ruby source files under the current or other supplied directory. A Ruby source file is defined as a file with the .rb extension or a file with no extension that has a ruby shebang line as its first line. It is possible to specify includes and excludes using the config file, so you can include other Ruby files like Rakefiles and gemspecs.

Parameters:

  • base_dir (defaults to: PathUtil.pwd)

    Root directory under which to search for ruby source files

Returns:

  • (Array)

    Array of filenames

[ GitHub ]

  
# File 'lib/rubocop/target_finder.rb', line 41

def target_files_in_dir(base_dir = PathUtil.pwd)
  # Support Windows: Backslashes from command-line -> forward slashes
  base_dir = base_dir.gsub(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR
  all_files = find_files(base_dir, File::FNM_DOTMATCH)
  base_dir_config = @config_store.for(base_dir)

  target_files = if hidden_dir?(base_dir)
                   all_files.select { |file| ruby_file?(file) }
                 else
                   all_files.select { |file| to_inspect?(file, base_dir, base_dir_config) }
                 end

  target_files.sort_by!(&order)
end

#to_inspect?(file, base_dir, base_dir_config) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/target_finder.rb', line 75

def to_inspect?(file, base_dir, base_dir_config)
  return false if base_dir_config.file_to_exclude?(file)
  return true if !hidden_file_in_dir?(file, base_dir) && ruby_file?(file)

  base_dir_config.file_to_include?(file)
end

#wanted_dir_patterns(base_dir, exclude_pattern, flags) (private)

[ GitHub ]

  
# File 'lib/rubocop/target_finder.rb', line 93

def wanted_dir_patterns(base_dir, exclude_pattern, flags)
  # Escape glob characters in base_dir to avoid unwanted behavior.
  base_dir = base_dir.gsub(/[\\{}\[\]*?]/) do |reserved_glob_character|
    "\\#{reserved_glob_character}"
  end

  dirs = Dir.glob(File.join(base_dir, '*/'), flags)
            .reject do |dir|
              next true if dir.end_with?('/./', '/../')
              next true if File.fnmatch?(exclude_pattern, dir, flags)

              symlink_excluded_or_infinite_loop?(base_dir, dir, exclude_pattern, flags)
            end
  dirs.flat_map { |dir| wanted_dir_patterns(dir, exclude_pattern, flags) }.unshift(base_dir)
end

#without_excluded(files) (private)

[ GitHub ]

  
# File 'lib/rubocop/target_finder.rb', line 146

def without_excluded(files)
  files.reject do |file|
    # When --ignore-parent-exclusion is given, we must look at the configuration associated with
    # the file, but in the default case when --ignore-parent-exclusion is not given, can safely
    # look only at the configuration for the current directory, since it's only the Exclude
    # parameters we're going to check.
    config = @config_store.for(ignore_parent_exclusion? ? file : '.')
    config.file_to_exclude?(file)
  end
end