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)

pattern_for("index.html") #=> /^index(.html|.htm)(.builder|.erb)*$/

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)

not exist.
`~` 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)

See `Trail#find` for usage.

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)

`base_path` for reference.
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)

Finds logical path across all `paths`
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

`Index#index` returns `self` to be compatable with the `Trail` interface.
def index
  self
end

def initialize(root, paths, extensions)

directly, create a `Trail` and call `Trail#index`.
`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)

any syscalls if necessary.
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)

Returns true if `dirname` is a subdirectory of any of the `paths`
def paths_contain?(dirname)
  paths.any? { |path| dirname.to_s[0, path.length] == path }
end

def pattern_for(basename)

Cache results of `build_pattern_for`
def pattern_for(basename)
  @patterns[basename] ||= build_pattern_for(basename)
end

def root

`Index#root` returns root path as a `String`. This attribute is immutable.
def root
  @root.to_s
end

def sort_matches(matches, basename)

more weight.
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)

not exist.
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