module I18n::Tasks::Scanners::Files # Finds the files in the specified search paths with support for exclusion / inclusion patterns. # # @since 0.9.0 class FileFinder include I18n::Tasks::Logging # @param paths [Array] {Find.find}-compatible paths to traverse, # absolute or relative to the working directory. # @param only [Array, nil] {File.fnmatch}-compatible patterns files to include. # Files not matching any of the inclusion patterns will be excluded. # @param exclude [Arry] {File.fnmatch}-compatible patterns of files to exclude. # Files matching any of the exclusion patterns will be excluded even if they match an inclusion pattern. def initialize(paths: ['.'], only: nil, exclude: []) raise 'paths argument is required' if paths.nil? @paths = paths @include = only @exclude = exclude || [] end # Traverse the paths and yield the matching ones. # # @yield [path] # @yieldparam path [String] the path of the found file. # @return [Array] def traverse_files find_files.map { |path| yield path } end # @return [Array] found files def find_files results = [] paths = @paths.select { |p| File.exist?(p) } if paths.empty? log_warn "None of the search.paths exist #{@paths.inspect}" else Find.find(*paths) do |path| is_dir = File.directory?(path) hidden = File.basename(path).start_with?('.') && !%w(. ./).include?(path) not_incl = @include && !path_fnmatch_any?(path, @include) excl = path_fnmatch_any?(path, @exclude) if is_dir || hidden || not_incl || excl Find.prune if is_dir && (hidden || excl) else results << path end end end results end private # @param path [String] # @param globs [Array] # @return [Boolean] def path_fnmatch_any?(path, globs) globs.any? { |glob| File.fnmatch(glob, path) } end end end