class Tilt::Mapping

Tilt::Mapping associates file extensions with template implementations.

mapping = Tilt::Mapping.new
mapping.register(Tilt::RDocTemplate, 'rdoc')
mapping['index.rdoc'] # => Tilt::RDocTemplate
mapping.new('index.rdoc').render

You can use {#register} to register a template class by file extension, {#registered?} to see if a file extension is mapped, {#[]} to lookup template classes, and {#new} to instantiate template objects.

Mapping also supports lazy template implementations. Note that regularly registered template implementations always have preference over lazily registered template implementations. You should use {#register} if you depend on a specific template implementation and {#register_lazy} if there are multiple alternatives.

mapping = Tilt::Mapping.new
mapping.register_lazy('RDiscount::Template', 'rdiscount/template', 'md')
mapping['index.md']
# => RDiscount::Template

{#register_lazy} takes a class name, a filename, and a list of file extensions. When you try to lookup a template name that matches the file extension, Tilt will automatically try to require the filename and constantize the class name.

Unlike {#register}, there can be multiple template implementations registered lazily to the same file extension. Tilt will attempt to load the template implementations in order (registered last would be tried first), returning the first which doesn't raise LoadError.

If all of the registered template implementations fails, Tilt will raise the exception of the first, since that was the most preferred one.

mapping = Tilt::Mapping.new
mapping.register_lazy('Bluecloth::Template', 'bluecloth/template', 'md')
mapping.register_lazy('RDiscount::Template', 'rdiscount/template', 'md')
mapping['index.md']
# => RDiscount::Template

In the previous example we say that RDiscount has a *higher priority* than BlueCloth. Tilt will first try to `require “rdiscount/template”`, falling back to `require “bluecloth/template”`. If none of these are successful, the first error will be raised.

Attributes

lazy_map[R]

@private

template_map[R]

@private

Public Class Methods

new() click to toggle source
# File lib/tilt/mapping.rb, line 52
def initialize
  @template_map = Hash.new
  @lazy_map = Hash.new { |h, k| h[k] = [] }
end

Public Instance Methods

[](file) click to toggle source

Looks up a template class based on file name and/or extension.

@example

mapping['views/hello.erb'] # => Tilt::ERBTemplate
mapping['hello.erb']       # => Tilt::ERBTemplate
mapping['erb']             # => Tilt::ERBTemplate

@return [template class]

# File lib/tilt/mapping.rb, line 150
def [](file)
  _, ext = split(file)
  ext && lookup(ext)
end
Also aliased as: template_for
extensions_for(template_class) click to toggle source

Finds the extensions the template class has been registered under. @param [template class] template_class

# File lib/tilt/mapping.rb, line 181
def extensions_for(template_class)
  res = []
  template_map.each do |ext, klass|
    res << ext if template_class == klass
  end
  lazy_map.each do |ext, choices|
    res << ext if choices.any? { |klass, file| template_class.to_s == klass }
  end
  res
end
initialize_copy(other) click to toggle source

@private

# File lib/tilt/mapping.rb, line 58
def initialize_copy(other)
  @template_map = other.template_map.dup
  @lazy_map = other.lazy_map.dup
end
new(file, line=nil, options={}, &block) click to toggle source

Instantiates a new template class based on the file.

@raise [RuntimeError] if there is no template class registered for the

file name.

@example

mapping.new('index.mt') # => instance of MyEngine::Template

@see Tilt::Template.new

# File lib/tilt/mapping.rb, line 134
def new(file, line=nil, options={}, &block)
  if template_class = self[file]
    template_class.new(file, line, options, &block)
  else
    fail "No template engine registered for #{File.basename(file)}"
  end
end
register(template_class, *extensions) click to toggle source

Registers a template implementation by file extension. There can only be one template implementation per file extension, and this method will override any existing mapping.

@param template_class @param extensions [Array<String>] List of extensions. @return [void]

@example

mapping.register MyEngine::Template, 'mt'
mapping['index.mt'] # => MyEngine::Template
# File lib/tilt/mapping.rb, line 102
def register(template_class, *extensions)
  if template_class.respond_to?(:to_str)
    # Support register(ext, template_class) too
    extensions, template_class = [template_class], extensions[0]
  end

  extensions.each do |ext|
    @template_map[ext.to_s] = template_class
  end
end
register_lazy(class_name, file, *extensions) click to toggle source

Registrers a lazy template implementation by file extension. You can have multiple lazy template implementations defined on the same file extension, in which case the template implementation defined last will be attempted loaded first.

@param class_name [String] Class name of a template class. @param file [String] Filename where the template class is defined. @param extensions [Array<String>] List of extensions. @return [void]

@example

mapping.register_lazy 'MyEngine::Template', 'my_engine/template',  'mt'

defined?(MyEngine::Template) # => false
mapping['index.mt'] # => MyEngine::Template
defined?(MyEngine::Template) # => true
# File lib/tilt/mapping.rb, line 79
def register_lazy(class_name, file, *extensions)
  # Internal API
  if class_name.is_a?(Symbol)
    Tilt.autoload class_name, file
    class_name = "Tilt::#{class_name}"
  end

  extensions.each do |ext|
    @lazy_map[ext].unshift([class_name, file])
  end
end
registered?(ext) click to toggle source

Checks if a file extension is registered (either eagerly or lazily) in this mapping.

@param ext [String] File extension.

@example

mapping.registered?('erb')  # => true
mapping.registered?('nope') # => false
# File lib/tilt/mapping.rb, line 121
def registered?(ext)
  @template_map.has_key?(ext.downcase) or lazy?(ext)
end
template_for(file)
Alias for: []
templates_for(file) click to toggle source

Looks up a list of template classes based on file name. If the file name has multiple extensions, it will return all template classes matching the extensions from the end.

@example

mapping.templates_for('views/index.haml.erb')
# => [Tilt::ERBTemplate, Tilt::HamlTemplate]

@return [Array<template class>]

# File lib/tilt/mapping.rb, line 166
def templates_for(file)
  templates = []

  while true
    prefix, ext = split(file)
    break unless ext
    templates << lookup(ext)
    file = prefix
  end

  templates
end

Private Instance Methods

constant_defined?(name) click to toggle source
# File lib/tilt/mapping.rb, line 257
def constant_defined?(name)
  name.split('::').inject(Object) do |scope, n|
    return false if scope.autoload?(n) # skip autload
    return false unless scope.const_defined?(n)
    scope.const_get(n)
  end
end
lazy?(ext) click to toggle source
# File lib/tilt/mapping.rb, line 194
def lazy?(ext)
  ext = ext.downcase
  @lazy_map.has_key?(ext) && !@lazy_map[ext].empty?
end
lazy_load(pattern) click to toggle source
# File lib/tilt/mapping.rb, line 217
def lazy_load(pattern)
  return unless @lazy_map.has_key?(pattern)

  choices = @lazy_map[pattern]

  # Check if a template class is already present
  choices.each do |class_name, file|
    template_class = constant_defined?(class_name)
    if template_class
      register(template_class, pattern)
      return template_class
    end
  end

  first_failure = nil

  # Load in order
  choices.each do |class_name, file|
    begin
      require file

      if Thread.list.size > 1
        warn "WARN: tilt autoloading '#{file}' in a non thread-safe way; " +
          "explicit require '#{file}' suggested."
      end

      # It's safe to eval() here because constant_defined? will
      # raise NameError on invalid constant names
      template_class = eval(class_name)
    rescue LoadError => ex
      first_failure ||= ex
    else
      register(template_class, pattern)
      return template_class
    end
  end

  raise first_failure if first_failure
end
lookup(ext) click to toggle source
# File lib/tilt/mapping.rb, line 213
def lookup(ext)
  @template_map[ext] || lazy_load(ext)
end
split(file) click to toggle source
# File lib/tilt/mapping.rb, line 199
def split(file)
  pattern = file.to_s.downcase
  full_pattern = pattern.dup

  until registered?(pattern)
    return if pattern.empty?
    pattern = File.basename(pattern)
    pattern.sub!(/^[^.]*\.?/, '')
  end

  prefix_size = full_pattern.size - pattern.size
  [full_pattern[0,prefix_size-1], pattern]
end