123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179 |
- 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
|