class Opal::Hike::Index
‘entries` calls are cached for the lifetime of the `Index` object.
file system does not change between `find` calls. All `stat` and
`Index` is an internal cached variant of `Trail`. It assumes the
def build_pattern_for(basename)
Returns a `Regexp` that matches the allowed extensions.
def build_pattern_for(basename) extension_pattern = extensions.map { |e| Regexp.escape(e) }.join('|') /^#{basename}(?:#{extension_pattern})*$/ end
def entries(path)
`~` swap files. Returns an empty `Array` if the directory does
A cached version of `Dir.entries` that filters out `.` files and
def entries(path) @entries[path.to_s] ||= begin pathname = Pathname.new(path) if pathname.directory? pathname.entries.reject { |entry| entry.to_s =~ /^\.|~$|^\#.*\#$/ }.sort else [] end end end
def extract_options!(arguments)
def extract_options!(arguments) arguments.last.is_a?(Hash) ? arguments.pop.dup : {} end
def find(logical_path)
time index and delegates here.
The real implementation of `find`. `Trail#find` generates a one
def find(logical_path) base_path = Pathname.new(@root) logical_path = Pathname.new(logical_path.sub(/^\//, '')) if logical_path.to_s =~ %r{^\.\.?/} find_in_base_path(logical_path, base_path) { |path| return path } else find_in_paths(logical_path) { |path| return path } end nil end
def find_in_base_path(logical_path, base_path, &block)
Finds relative logical path, `../test/test_trail`. Requires a
def find_in_base_path(logical_path, base_path, &block) candidate = base_path.join(logical_path) dirname, basename = candidate.split match(dirname, basename, &block) if paths_contain?(dirname) end
def find_in_paths(logical_path, &block)
def find_in_paths(logical_path, &block) dirname, basename = logical_path.split @pathnames.each do |base_path| match(base_path.join(dirname), basename, &block) end end
def index
def index self end
def initialize(root, paths, extensions)
`Index.new` is an internal method. Instead of constructing it
def initialize(root, paths, extensions) @root = root # Freeze is used here so an error is throw if a mutator method # is called on the array. Mutating `@paths`, `@extensions` # would have unpredictable results. @paths = paths.dup.freeze @extensions = extensions.dup.freeze @pathnames = paths.map { |path| Pathname.new(path) } @stats = {} @entries = {} @patterns = {} end
def match(dirname, basename)
Checks if the path is actually on the file system and performs
def match(dirname, basename) # Potential `entries` syscall matches = entries(dirname) pattern = pattern_for(basename) matches = matches.select { |m| m.to_s =~ pattern } sort_matches(matches, basename).each do |path| pathname = dirname.join(path) # Potential `stat` syscall stat = stat(pathname) # Exclude directories if stat && stat.file? yield pathname.to_s end end end
def paths_contain?(dirname)
def paths_contain?(dirname) paths.any? { |path| dirname.to_s[0, path.length] == path } end
def pattern_for(basename)
def pattern_for(basename) @patterns[basename] ||= build_pattern_for(basename) end
def root
def root @root.to_s end
def sort_matches(matches, basename)
priority. Extensions in the front of the `extensions` carry
Sorts candidate matches by their extension
def sort_matches(matches, basename) matches.sort_by do |match| extnames = match.sub(basename.to_s, '').to_s.scan(/\.[^.]+/) extnames.inject(0) do |sum, ext| index = extensions.index(ext) if index sum + index + 1 else sum end end end end
def stat(path)
A cached version of `File.stat`. Returns nil if the file does
def stat(path) key = path.to_s if @stats.key?(key) @stats[key] elsif File.exist?(path) @stats[key] = File.stat(path) else @stats[key] = nil end end