class Roger::Resolver

The resolver is here to resolve urls to paths and sometimes vice-versa

Constants

EXTENSION_MAP

Maps output extensions to template extensions to find source files.

Attributes

load_paths[R]

Public Class Methods

new(paths) click to toggle source
# File lib/roger/resolver.rb, line 31
def initialize(paths)
  raise ArgumentError, "Resolver base path can't be nil" if paths.nil?

  # Convert to paths
  @load_paths = [paths].flatten.map { |p| Pathname.new(p) }
end

Public Instance Methods

find_template(url, options = {}) click to toggle source

@param [String] url The url to resolve to a path @param [Hash] options Options

@option options [String] :prefer The preferred template extension. When searching for

templates, the preferred template extension defines what file type we're requesting
when we ask for a file without an extension
# File lib/roger/resolver.rb, line 44
def find_template(url, options = {})
  options = {
    prefer: "html"
  }.update(options)

  orig_path, _qs, _anch = strip_query_string_and_anchor(url.to_s)

  output = nil

  load_paths.find do |load_path|
    path = File.join(load_path, orig_path)
    output = find_template_path(path, options)
  end

  output
end
Also aliased as: url_to_path
path_to_url(path, relative_to = nil) click to toggle source

Convert a disk path on file to an url

# File lib/roger/resolver.rb, line 63
def path_to_url(path, relative_to = nil)
  # Find the parent path we're in
  path = Pathname.new(path).realpath
  base = load_paths.find { |lp| path.to_s =~ /\A#{Regexp.escape(lp.realpath.to_s)}/ }

  path = path.relative_path_from(base).cleanpath

  if relative_to
    relative_path_to_url(path, relative_to, base).to_s
  else
    "/#{path}"
  end
end
strip_query_string_and_anchor(url) click to toggle source
# File lib/roger/resolver.rb, line 94
def strip_query_string_and_anchor(url)
  url = url.dup

  # Strip off anchors
  anchor = nil
  url.gsub!(/(#.+)\Z/) do |r|
    anchor = r
    ""
  end

  # Strip off query strings
  query = nil
  url.gsub!(/(\?.+)\Z/) do |r|
    query = r
    ""
  end

  [url, query, anchor]
end
url_to_path(url, options = {})
Alias for: find_template
url_to_relative_url(url, relative_to_path) click to toggle source
# File lib/roger/resolver.rb, line 77
def url_to_relative_url(url, relative_to_path)
  # Skip if the url doesn't start with a / (but not with //)
  return false unless url =~ %r{\A/[^/]}

  path, qs, anch = strip_query_string_and_anchor(url)

  # Get disk path
  if true_path = url_to_path(path, exact_match: true)
    path = path_to_url(true_path, relative_to_path)
    path += qs if qs
    path += anch if anch
    path
  else
    false
  end
end

Protected Instance Methods

filter_files(files, path, path_without_extension, template_extensions) click to toggle source

Filter a list of files to see wether or not we can process them. Will take into account that the longest match with path will be the first result.

# File lib/roger/resolver.rb, line 150
def filter_files(files, path, path_without_extension, template_extensions)
  results = []

  files.each do |file|
    match = if file.start_with?(path)
              path
            else
              path_without_extension
            end

    processable_extensions = file[(match.length + 1)..-1].split(".")

    # All processable_extensions must be processable
    # by a template_extension
    next unless (processable_extensions - template_extensions).empty?

    if file.start_with?(path)
      # The whole path is found in the filename, not just
      # the path without the extension.
      # it must have priority over all else
      results.unshift(file)
    else
      results.push(file)
    end
  end
  results
end
find_template_path(name, options = {}) click to toggle source

Finds the template path for “name”

# File lib/roger/resolver.rb, line 117
def find_template_path(name, options = {})
  options = {
    prefer: "html", # Prefer a template with extension
  }.update(options)

  path = sanitize_name(name, options[:prefer])

  # Exact match
  return Pathname.new(path) if File.exist?(path)

  # Split extension and path
  path_extension, path_without_extension = split_path(path)

  # Get possible output extensions for path_extension
  template_extensions = template_extensions_for_output(path_extension, options[:prefer])

  # Let's look at the disk to see what files we've got
  files = Dir.glob(path_without_extension + ".*")

  results = filter_files(files, path, path_without_extension, template_extensions)

  if !results[0]
    # No results found, but maybe there is a directory
    # with the same name and it contains an index.XYZ
    find_template_path(File.join(name, "index")) if File.directory?(name)
  else
    Pathname.new(results[0])
  end
end
relative_path_to_url(path, relative_to, base) click to toggle source
# File lib/roger/resolver.rb, line 216
def relative_path_to_url(path, relative_to, base)
  relative_to = Pathname.new(File.dirname(relative_to.to_s))

  # If relative_to is an absolute path
  if relative_to.absolute?
    relative_to = relative_to.relative_path_from(base).cleanpath
  end

  Pathname.new("/" + path.to_s).relative_path_from(Pathname.new("/" + relative_to.to_s))
end
sanitize_name(name, prefer = nil) click to toggle source

Check if the name is a directory and append index Append preferred extension or html if it doesn't have one yet

# File lib/roger/resolver.rb, line 180
def sanitize_name(name, prefer = nil)
  path = name.to_s

  # Check if we haven't got an extension
  # we'll assume you're looking for prefer or "html" otherwise
  path += ".#{prefer || 'html'}" unless File.basename(path).include?(".")

  path
end
split_path(path) click to toggle source

Split path in to extension an path without extension

# File lib/roger/resolver.rb, line 191
def split_path(path)
  path = path.to_s
  extension = File.extname(path)[1..-1] || ""
  path_without_extension = path.sub(/\.#{Regexp.escape(extension)}\Z/, "")
  [extension, path_without_extension]
end
template_extensions_for_output(ext, prefer = nil) click to toggle source
# File lib/roger/resolver.rb, line 198
def template_extensions_for_output(ext, prefer = nil)
  template_extensions = []

  # The preferred template_extension is first
  template_extensions += prefer.to_s.split(".") if prefer

  # Any exact template matches for extension
  template_extensions += EXTENSION_MAP[ext] if EXTENSION_MAP[ext]

  # Any generic templates
  template_extensions += EXTENSION_MAP[nil]

  # Myself to pass extension matching later on
  template_extensions += [ext]

  template_extensions
end