1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
|
require 'pathname'
require 'hike/extensions'
require 'hike/index'
require 'hike/paths'
module Hike
# `Trail` is the public container class for holding paths and extensions.
class Trail
# `Trail#paths` is a mutable `Paths` collection.
#
# trail = Hike::Trail.new
# trail.paths.push "~/Projects/hike/lib", "~/Projects/hike/test"
#
# The order of the paths is significant. Paths in the beginning of
# the collection will be checked first. In the example above,
# `~/Projects/hike/lib/hike.rb` would shadow the existent of
# `~/Projects/hike/test/hike.rb`.
attr_reader :paths
# `Trail#extensions` is a mutable `Extensions` collection.
#
# trail = Hike::Trail.new
# trail.paths.push "~/Projects/hike/lib"
# trail.extensions.push ".rb"
#
# Extensions allow you to find files by just their name omitting
# their extension. Is similar to Ruby's require mechanism that
# allows you to require files with specifiying `foo.rb`.
attr_reader :extensions
# `Index#aliases` is a mutable `Hash` mapping an extension to
# an `Array` of aliases.
#
# trail = Hike::Trail.new
# trail.paths.push "~/Projects/hike/site"
# trail.aliases['.htm'] = 'html'
# trail.aliases['.xhtml'] = 'html'
# trail.aliases['.php'] = 'html'
#
# Aliases provide a fallback when the primary extension is not
# matched. In the example above, a lookup for "foo.html" will
# check for the existence of "foo.htm", "foo.xhtml", or "foo.php".
attr_reader :aliases
# A Trail accepts an optional root path that defaults to your
# current working directory. Any relative paths added to
# `Trail#paths` will expanded relative to the root.
def initialize(root = ".")
@root = Pathname.new(root).expand_path
@paths = Paths.new(@root)
@extensions = Extensions.new
@aliases = Hash.new { |h, k| h[k] = Extensions.new }
end
# `Trail#root` returns root path as a `String`. This attribute is immutable.
def root
@root.to_s
end
# Prepend `path` to `Paths` collection
def prepend_paths(*paths)
self.paths.unshift(*paths)
end
alias_method :prepend_path, :prepend_paths
# Append `path` to `Paths` collection
def append_paths(*paths)
self.paths.push(*paths)
end
alias_method :append_path, :append_paths
# Remove `path` from `Paths` collection
def remove_path(path)
self.paths.delete(path)
end
# Prepend `extension` to `Extensions` collection
def prepend_extensions(*extensions)
self.extensions.unshift(*extensions)
end
alias_method :prepend_extension, :prepend_extensions
# Append `extension` to `Extensions` collection
def append_extensions(*extensions)
self.extensions.push(*extensions)
end
alias_method :append_extension, :append_extensions
# Remove `extension` from `Extensions` collection
def remove_extension(extension)
self.extensions.delete(extension)
end
# Alias `new_extension` to `old_extension`
def alias_extension(new_extension, old_extension)
aliases[normalize_extension(new_extension)] = normalize_extension(old_extension)
end
# Remove the alias for `extension`
def unalias_extension(extension)
aliases.delete(normalize_extension(extension))
end
# `Trail#find` returns a the expand path for a logical path in the
# path collection.
#
# trail = Hike::Trail.new "~/Projects/hike"
# trail.extensions.push ".rb"
# trail.paths.push "lib", "test"
#
# trail.find "hike/trail"
# # => "~/Projects/hike/lib/hike/trail.rb"
#
# trail.find "test_trail"
# # => "~/Projects/hike/test/test_trail.rb"
#
# `find` accepts multiple fallback logical paths that returns the
# first match.
#
# trail.find "hike", "hike/index"
#
# is equivalent to
#
# trail.find("hike") || trail.find("hike/index")
#
# Though `find` always returns the first match, it is possible
# to iterate over all shadowed matches and fallbacks by supplying
# a block.
#
# trail.find("hike", "hike/index") { |path| warn path }
#
# This allows you to filter your matches by any condition.
#
# trail.find("application") do |path|
# return path if mime_type_for(path) == "text/css"
# end
#
def find(*args, &block)
index.find(*args, &block)
end
# `Trail#index` returns an `Index` object that has the same
# interface as `Trail`. An `Index` is a cached `Trail` object that
# does not update when the file system changes. If you are
# confident that you are not making changes the paths you are
# searching, `index` will avoid excess system calls.
#
# index = trail.index
# index.find "hike/trail"
# index.find "test_trail"
#
def index
Index.new(root, paths, extensions, aliases)
end
# `Trail#entries` is equivalent to `Dir#entries`. It is not
# recommend to use this method for general purposes. It exists for
# parity with `Index#entries`.
def entries(*args)
index.entries(*args)
end
# `Trail#stat` is equivalent to `File#stat`. It is not
# recommend to use this method for general purposes. It exists for
# parity with `Index#stat`.
def stat(*args)
index.stat(*args)
end
private
def normalize_extension(extension)
if extension[/^\./]
extension
else
".#{extension}"
end
end
end
end
|