Class: Utils::Finder

Inherits:
Object show all
Includes:
Term::ANSIColor, Tins::Find, Patterns
Defined in:
lib/utils/finder.rb

Overview

A class for finding and searching files with configurable patterns and filters.

This class provides functionality for traversing file systems to locate files based on various criteria including file extensions, directory pruning, and pattern matching. It supports both indexed and direct search approaches to optimize performance when dealing with large codebases or frequently accessed file sets.

Examples:

finder = Utils::Finder.new(args: { l: true }, roots: ['.'])
finder.search

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Patterns

#choose

Constructor Details

#initialize(opts = {}) ⇒ Finder

The initialize method sets up the finder instance with the provided options.

This method configures the finder by processing the input options, including arguments, root directories, and pattern settings. It initializes the pattern matcher based on the specified options and prepares the index for searching.

Parameters:

  • opts (Hash) (defaults to: {})

    the options hash containing configuration settings

Options Hash (opts):

  • :args (Hash)

    the argument options for the finder

  • :roots (Array)

    the root directories to search in

  • :config (Utils::ConfigFile)

    the configuration file object



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/utils/finder.rb', line 35

def initialize(opts = {})
  @args  = opts[:args] || {}
  @roots = discover_roots(opts[:roots])
  @config = opts[:config] || Utils::ConfigFile.new
  if @args[?l] || @args[?L]
    @pattern = nil
  else
    pattern_opts = opts.subhash(:pattern) | {
      :cset  => @args[?a],
      :icase => @args[?i] != ?n,
    }
    @pattern = choose(@args[?p], pattern_opts)
  end
  @paths  = []
  reset_index
end

Instance Attribute Details

#outputObject (readonly)

The output reader method provides access to the output value.

Returns:

  • (Object)

    the output value that was set previously



65
66
67
# File 'lib/utils/finder.rb', line 65

def output
  @output
end

#pathsArray<String> (readonly)

The paths reader method provides access to the array of file paths that have been processed or collected.

This method returns the internal array containing the file paths, allowing external code to read the current set of paths without modifying the original collection.

Returns:

  • (Array<String>)

    an array of file path strings that have been processed or collected



60
61
62
# File 'lib/utils/finder.rb', line 60

def paths
  @paths
end

Instance Method Details

#build_pathsArray

The build_paths method constructs a list of file system paths by traversing the configured root directories.

This method iterates through the specified root directories and collects all file system entries, applying filtering logic to exclude certain directories and files based on configuration settings. It handles both regular files and directories, ensuring that directory entries are properly marked with a trailing slash for distinction. The resulting paths are deduplicated before being returned.

marked by a trailing slash

Returns:

  • (Array)

    an array of file system path strings, with directories



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/utils/finder.rb', line 79

def build_paths
  paths = []

  visit = -> filename {
    s  = filename.stat
    bn = filename.pathname.basename
    if !s ||
        s.directory? && @config.discover.prune?(bn) ||
        s.file? && @config.discover.skip?(bn)
    then
      @args[?v] and warn "Pruning #{filename.inspect}."
      prune
    end
    true
  }
  find(*@roots, visit: visit) do |filename|
    filename.stat.directory? and filename << ?/
    paths << filename
  end
  paths.uniq!
  paths
end

#create_pathsArray

The create_paths method generates and stores path information by building a list of paths, writing them to a secure file, and then returning the list of paths.

to the index file

Returns:

  • (Array)

    an array containing the paths that were built and written



126
127
128
129
130
131
132
# File 'lib/utils/finder.rb', line 126

def create_paths
  paths = build_paths
  File.secure_write(index_path) do |output|
    output.puts paths
  end
  paths
end

#index_pathString

The index_path method generates a unique file path for storing finder results.

This method creates a standardized location in the temporary directory for caching finder path data based on the root directories being processed. It ensures uniqueness by hashing the sorted root paths and uses the current script name as part of the directory structure.

Returns:

  • (String)

    the full file path where finder results should be stored



111
112
113
114
115
116
117
118
# File 'lib/utils/finder.rb', line 111

def index_path
  roots = @roots.map { |r| File.expand_path(r) }.uniq.sort
  filename = "finder-paths-" +
    Digest::MD5.new.update(roots.inspect).hexdigest
  dirname = File.join(Dir.tmpdir, File.basename($0))
  FileUtils.mkdir_p dirname
  File.join(dirname, filename)
end

#load_pathsArray<String>

The load_paths method reads and processes indexed file paths from disk.

This method loads lines from the index file path, removes trailing whitespace, and filters out directory entries if the debug flag is not set. It returns create_paths if the index file is empty or missing, otherwise it returns the processed list of file paths.

Returns:

  • (Array<String>)

    an array of file paths loaded from the index



142
143
144
145
146
147
148
149
150
151
# File 'lib/utils/finder.rb', line 142

memoize method:
def load_paths
  lines = File.readlines(index_path)
  @args[?v] and warn "Loaded index #{index_path.inspect}."
  lines.empty? and raise Errno::ENOENT
  @args[?d] or lines = lines.grep_v(%r{/$})
  lines.map(&:chomp!)
rescue Errno::ENOENT
  return create_paths
end

#reset_indexUtils::Finder

The reset_index method resets the index file by removing it if the reset flag is set or if the index has expired.

This method checks whether the reset argument flag is set or if the index file has expired based on its modification time. If either condition is true, it removes the index file from the filesystem and clears the mize cache. The method then returns the instance itself to allow for method chaining.

Returns:



163
164
165
166
167
168
169
170
171
# File 'lib/utils/finder.rb', line 163

def reset_index
  path = index_path
  if @args[?r] || index_expired?(path)
    @args[?v] and warn "Resetting index #{path.inspect}."
    FileUtils.rm_f path
    mize_cache_clear
  end
  self
end

#search_directlyObject

The search_directly method performs a direct search by building paths and then searching through them.

This method first constructs the list of paths to be searched and then executes the search operation on those paths, returning the results of the search.

Returns:

  • (Object)

    the result of the search operation performed on the built paths



234
235
236
# File 'lib/utils/finder.rb', line 234

def search_directly
  search_paths build_paths
end

#search_indexObject Also known as: search

The search_index method performs a pattern search across previously loaded paths.

This method utilizes the loaded paths from the internal storage to execute a search operation, applying the configured pattern matching criteria to filter and return relevant results based on the current search configuration.



245
246
247
# File 'lib/utils/finder.rb', line 245

def search_index
  search_paths load_paths
end

#search_paths(paths) ⇒ Utils::Finder

The search_paths method processes and filters a collection of file paths based on specified criteria.

This method takes an array of paths and applies filtering based on file extensions and patterns. It handles both fuzzy and regular expression pattern matching, and returns formatted results with optional sorting and limiting of results.

Parameters:

  • paths (Array<String>)

    the collection of file paths to be processed

Returns:



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/utils/finder.rb', line 184

def search_paths(paths)
  suffixes = Array(@args[?I])
  suffixes.full? do |s|
    paths.select! { |path| s.include?(File.extname(path)[1..-1]) }
  end
  paths = paths.map! do |path|
    if @pattern.nil?
      [ [ path.count(?/), path ], path, path ]
    elsif match = @pattern.match(path)
      if FuzzyPattern === @pattern
        current = 0
        marked_path = ''
        score, e = path.size, nil
        for i in 1...match.size
          match[i] or next
          b = match.begin(i)
          e ||= b
          marked_path << path[current...b]
          marked_path << red(path[b, 1])
          score += (b - e) * (path.size - b)
          e = match.end(i)
          current = b + 1
        end
        marked_path << match.post_match
        [ score, path, marked_path ]
      else
        marked_path = path[0...match.begin(0)] <<
          red(path[match.begin(0)...match.end(0)]) <<
          path[match.end(0)..-1]
        [ 0, path, marked_path ]
      end
    end
  end
  paths.compact!
  @paths, @output = paths.sort.transpose.values_at(-2, -1)
  if n = @args[?n]&.to_i
    @paths = @paths&.first(n) || []
    @output = @output&.first(n) || []
  end
  self
end