Object
`Index` is an internal cached variant of `Trail`. It assumes the file system does not change between `find` calls. All `stat` and `entries` calls are cached for the lifetime of the `Index` object.
`Index.new` is an internal method. Instead of constructing it directly, create a `Trail` and call `Trail#index`.
# File lib/hike/index.rb, line 20 20: def initialize(root, paths, extensions, aliases) 21: @root = root 22: 23: # Freeze is used here so an error is throw if a mutator method 24: # is called on the array. Mutating `@paths`, `@extensions`, or 25: # `@aliases` would have unpredictable results. 26: @paths = paths.dup.freeze 27: @extensions = extensions.dup.freeze 28: @aliases = aliases.inject({}) { |h, (k, a)| 29: h[k] = a.dup.freeze; h 30: }.freeze 31: @pathnames = paths.map { |path| Pathname.new(path) } 32: 33: @stats = {} 34: @entries = {} 35: @patterns = {} 36: end
A cached version of `Dir.entries` that filters out `.` files and `~` swap files. Returns an empty `Array` if the directory does not exist.
# File lib/hike/index.rb, line 78 78: def entries(path) 79: key = path.to_s 80: @entries[key] ||= Pathname.new(path).entries.reject { |entry| entry.to_s =~ /^\.|~$|^\#.*\#$/ }.sort 81: rescue Errno::ENOENT 82: @entries[key] = [] 83: end
The real implementation of `find`. `Trail#find` generates a one time index and delegates here.
See `Trail#find` for usage.
# File lib/hike/index.rb, line 52 52: def find(*logical_paths, &block) 53: if block_given? 54: options = extract_options!(logical_paths) 55: base_path = Pathname.new(options[:base_path] || @root) 56: 57: logical_paths.each do |logical_path| 58: logical_path = Pathname.new(logical_path.sub(/^\//, '')) 59: 60: if relative?(logical_path) 61: find_in_base_path(logical_path, base_path, &block) 62: else 63: find_in_paths(logical_path, &block) 64: end 65: end 66: 67: nil 68: else 69: find(*logical_paths) do |path| 70: return path 71: end 72: end 73: end
`Index#index` returns `self` to be compatable with the `Trail` interface.
# File lib/hike/index.rb, line 44 44: def index 45: self 46: end
`Index#root` returns root path as a `String`. This attribute is immutable.
# File lib/hike/index.rb, line 39 39: def root 40: @root.to_s 41: end
A cached version of `File.stat`. Returns nil if the file does not exist.
# File lib/hike/index.rb, line 87 87: def stat(path) 88: key = path.to_s 89: if @stats.key?(key) 90: @stats[key] 91: else 92: begin 93: @stats[key] = File.stat(path) 94: rescue Errno::ENOENT 95: @stats[key] = nil 96: end 97: end 98: end
Returns a `Regexp` that matches the allowed extensions.
pattern_for("index.html") #=> /^index(.html|.htm)(.builder|.erb)*$/
# File lib/hike/index.rb, line 160 160: def build_pattern_for(basename) 161: extname = basename.extname 162: aliases = find_aliases_for(extname) 163: 164: if aliases.any? 165: basename = basename.basename(extname) 166: aliases = [extname] + aliases 167: aliases_pattern = aliases.map { |e| Regexp.escape(e) }.join("|") 168: basename_re = Regexp.escape(basename.to_s) + "(?:#{aliases_pattern})" 169: else 170: basename_re = Regexp.escape(basename.to_s) 171: end 172: 173: extension_pattern = extensions.map { |e| Regexp.escape(e) }.join("|") 174: /^#{basename_re}(?:#{extension_pattern})*$/ 175: end
# File lib/hike/index.rb, line 101 101: def extract_options!(arguments) 102: arguments.last.is_a?(Hash) ? arguments.pop.dup : {} 103: end
# File lib/hike/index.rb, line 197 197: def find_aliases_for(extension) 198: @aliases.inject([]) do |aliases, (key, value)| 199: aliases.push(key) if value == extension 200: aliases 201: end 202: end
Finds relative logical path, `../test/test_trail`. Requires a `base_path` for reference.
# File lib/hike/index.rb, line 119 119: def find_in_base_path(logical_path, base_path, &block) 120: candidate = base_path.join(logical_path) 121: dirname, basename = candidate.split 122: match(dirname, basename, &block) if paths_contain?(dirname) 123: end
Finds logical path across all `paths`
# File lib/hike/index.rb, line 110 110: def find_in_paths(logical_path, &block) 111: dirname, basename = logical_path.split 112: @pathnames.each do |base_path| 113: match(base_path.join(dirname), basename, &block) 114: end 115: end
Checks if the path is actually on the file system and performs any syscalls if necessary.
# File lib/hike/index.rb, line 127 127: def match(dirname, basename) 128: # Potential `entries` syscall 129: matches = entries(dirname) 130: 131: pattern = pattern_for(basename) 132: matches = matches.select { |m| m.to_s =~ pattern } 133: 134: sort_matches(matches, basename).each do |path| 135: pathname = dirname.join(path) 136: 137: # Potential `stat` syscall 138: stat = stat(pathname) 139: 140: # Exclude directories 141: if stat && stat.file? 142: yield pathname.to_s 143: end 144: end 145: end
Returns true if `dirname` is a subdirectory of any of the `paths`
# File lib/hike/index.rb, line 148 148: def paths_contain?(dirname) 149: paths.any? { |path| dirname.to_s[0, path.length] == path } 150: end
Cache results of `build_pattern_for`
# File lib/hike/index.rb, line 153 153: def pattern_for(basename) 154: @patterns[basename] ||= build_pattern_for(basename) 155: end
# File lib/hike/index.rb, line 105 105: def relative?(logical_path) 106: logical_path.to_s =~ /^\.\.?\// 107: end
Sorts candidate matches by their extension priority. Extensions in the front of the `extensions` carry more weight.
# File lib/hike/index.rb, line 180 180: def sort_matches(matches, basename) 181: aliases = find_aliases_for(basename.extname) 182: 183: matches.sort_by do |match| 184: extnames = match.sub(basename.to_s, '').to_s.scan(/\.[^.]+/) 185: extnames.inject(0) do |sum, ext| 186: if i = extensions.index(ext) 187: sum + i + 1 188: elsif i = aliases.index(ext) 189: sum + i + 11 190: else 191: sum 192: end 193: end 194: end 195: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.