mirror of
https://github.com/sinatra/sinatra
synced 2023-03-27 23:18:01 -04:00
Upgrade Tilt to 0.4
This commit is contained in:
parent
20f6d0a156
commit
f6aaadf082
1 changed files with 141 additions and 26 deletions
|
@ -1,5 +1,5 @@
|
||||||
module Tilt
|
module Tilt
|
||||||
VERSION = '0.3'
|
VERSION = '0.4'
|
||||||
|
|
||||||
@template_mappings = {}
|
@template_mappings = {}
|
||||||
|
|
||||||
|
@ -67,6 +67,9 @@ module Tilt
|
||||||
# default, template data is read from the file specified. When a block
|
# default, template data is read from the file specified. When a block
|
||||||
# is given, it should read template data and return as a String. When
|
# is given, it should read template data and return as a String. When
|
||||||
# file is nil, a block is required.
|
# file is nil, a block is required.
|
||||||
|
#
|
||||||
|
# The #initialize_engine method is called if this is the very first
|
||||||
|
# time this template subclass has been initialized.
|
||||||
def initialize(file=nil, line=1, options={}, &block)
|
def initialize(file=nil, line=1, options={}, &block)
|
||||||
raise ArgumentError, "file or block required" if file.nil? && block.nil?
|
raise ArgumentError, "file or block required" if file.nil? && block.nil?
|
||||||
options, line = line, 1 if line.is_a?(Hash)
|
options, line = line, 1 if line.is_a?(Hash)
|
||||||
|
@ -74,7 +77,21 @@ module Tilt
|
||||||
@line = line || 1
|
@line = line || 1
|
||||||
@options = options || {}
|
@options = options || {}
|
||||||
@reader = block || lambda { |t| File.read(file) }
|
@reader = block || lambda { |t| File.read(file) }
|
||||||
|
|
||||||
|
if !self.class.engine_initialized
|
||||||
|
initialize_engine
|
||||||
|
self.class.engine_initialized = true
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Called once and only once for each template subclass the first time
|
||||||
|
# the template class is initialized. This should be used to require the
|
||||||
|
# underlying template library and perform any initial setup.
|
||||||
|
def initialize_engine
|
||||||
|
end
|
||||||
|
@engine_initialized = false
|
||||||
|
class << self ; attr_accessor :engine_initialized ; end
|
||||||
|
|
||||||
|
|
||||||
# Load template source and compile the template. The template is
|
# Load template source and compile the template. The template is
|
||||||
# loaded and compiled the first time this method is called; subsequent
|
# loaded and compiled the first time this method is called; subsequent
|
||||||
|
@ -150,14 +167,19 @@ module Tilt
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Extremely simple template cache implementation.
|
# Extremely simple template cache implementation. Calling applications
|
||||||
|
# create a Tilt::Cache instance and use #fetch with any set of hashable
|
||||||
|
# arguments (such as those to Tilt.new):
|
||||||
|
# cache = Tilt::Cache.new
|
||||||
|
# cache.fetch(path, line, options) { Tilt.new(path, line, options) }
|
||||||
|
#
|
||||||
|
# Subsequent invocations return the already compiled template object.
|
||||||
class Cache
|
class Cache
|
||||||
def initialize
|
def initialize
|
||||||
@cache = {}
|
@cache = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch(*key)
|
def fetch(*key)
|
||||||
key = key.map { |part| part.to_s }.join(":")
|
|
||||||
@cache[key] ||= yield
|
@cache[key] ||= yield
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -187,8 +209,11 @@ module Tilt
|
||||||
# ERB template implementation. See:
|
# ERB template implementation. See:
|
||||||
# http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/classes/ERB.html
|
# http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/classes/ERB.html
|
||||||
class ERBTemplate < Template
|
class ERBTemplate < Template
|
||||||
|
def initialize_engine
|
||||||
|
require_template_library 'erb' unless defined? ::ERB
|
||||||
|
end
|
||||||
|
|
||||||
def compile!
|
def compile!
|
||||||
require_template_library 'erb' unless defined?(::ERB)
|
|
||||||
@engine = ::ERB.new(data, options[:safe], options[:trim], '@_out_buf')
|
@engine = ::ERB.new(data, options[:safe], options[:trim], '@_out_buf')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -229,11 +254,25 @@ module Tilt
|
||||||
# Erubis template implementation. See:
|
# Erubis template implementation. See:
|
||||||
# http://www.kuwata-lab.com/erubis/
|
# http://www.kuwata-lab.com/erubis/
|
||||||
class ErubisTemplate < ERBTemplate
|
class ErubisTemplate < ERBTemplate
|
||||||
|
def initialize_engine
|
||||||
|
require_template_library 'erubis' unless defined? ::Erubis
|
||||||
|
end
|
||||||
|
|
||||||
def compile!
|
def compile!
|
||||||
require_template_library 'erubis' unless defined?(::Erubis)
|
|
||||||
Erubis::Eruby.class_eval(%Q{def add_preamble(src) src << "@_out_buf = _buf = '';" end})
|
Erubis::Eruby.class_eval(%Q{def add_preamble(src) src << "@_out_buf = _buf = '';" end})
|
||||||
@engine = ::Erubis::Eruby.new(data, options)
|
@engine = ::Erubis::Eruby.new(data, options)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Erubis doesn't have ERB's line-off-by-one under 1.9 problem. Override
|
||||||
|
# and adjust back.
|
||||||
|
if RUBY_VERSION >= '1.9.0'
|
||||||
|
def local_assignment_code(locals)
|
||||||
|
source, offset = super
|
||||||
|
[source, offset - 1]
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
register 'erubis', ErubisTemplate
|
register 'erubis', ErubisTemplate
|
||||||
|
|
||||||
|
@ -241,8 +280,11 @@ module Tilt
|
||||||
# Haml template implementation. See:
|
# Haml template implementation. See:
|
||||||
# http://haml.hamptoncatlin.com/
|
# http://haml.hamptoncatlin.com/
|
||||||
class HamlTemplate < Template
|
class HamlTemplate < Template
|
||||||
|
def initialize_engine
|
||||||
|
require_template_library 'haml' unless defined? ::Haml::Engine
|
||||||
|
end
|
||||||
|
|
||||||
def compile!
|
def compile!
|
||||||
require_template_library 'haml' unless defined?(::Haml::Engine)
|
|
||||||
@engine = ::Haml::Engine.new(data, haml_options)
|
@engine = ::Haml::Engine.new(data, haml_options)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -263,8 +305,11 @@ module Tilt
|
||||||
#
|
#
|
||||||
# Sass templates do not support object scopes, locals, or yield.
|
# Sass templates do not support object scopes, locals, or yield.
|
||||||
class SassTemplate < Template
|
class SassTemplate < Template
|
||||||
|
def initialize_engine
|
||||||
|
require_template_library 'sass' unless defined? ::Sass::Engine
|
||||||
|
end
|
||||||
|
|
||||||
def compile!
|
def compile!
|
||||||
require_template_library 'sass' unless defined?(::Sass::Engine)
|
|
||||||
@engine = ::Sass::Engine.new(data, sass_options)
|
@engine = ::Sass::Engine.new(data, sass_options)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -283,10 +328,13 @@ module Tilt
|
||||||
# Builder template implementation. See:
|
# Builder template implementation. See:
|
||||||
# http://builder.rubyforge.org/
|
# http://builder.rubyforge.org/
|
||||||
class BuilderTemplate < Template
|
class BuilderTemplate < Template
|
||||||
def compile!
|
def initialize_engine
|
||||||
require_template_library 'builder' unless defined?(::Builder)
|
require_template_library 'builder' unless defined?(::Builder)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def compile!
|
||||||
|
end
|
||||||
|
|
||||||
def evaluate(scope, locals, &block)
|
def evaluate(scope, locals, &block)
|
||||||
xml = ::Builder::XmlMarkup.new(:indent => 2)
|
xml = ::Builder::XmlMarkup.new(:indent => 2)
|
||||||
if data.respond_to?(:to_str)
|
if data.respond_to?(:to_str)
|
||||||
|
@ -308,29 +356,57 @@ module Tilt
|
||||||
# Liquid template implementation. See:
|
# Liquid template implementation. See:
|
||||||
# http://liquid.rubyforge.org/
|
# http://liquid.rubyforge.org/
|
||||||
#
|
#
|
||||||
# LiquidTemplate does not support scopes or yield blocks.
|
# Liquid is designed to be a *safe* template system and threfore
|
||||||
|
# does not provide direct access to execuatable scopes. In order to
|
||||||
|
# support a +scope+, the +scope+ must be able to represent itself
|
||||||
|
# as a hash by responding to #to_h. If the +scope+ does not respond
|
||||||
|
# to #to_h it will be ignored.
|
||||||
|
#
|
||||||
|
# LiquidTemplate does not support yield blocks.
|
||||||
#
|
#
|
||||||
# It's suggested that your program require 'liquid' at load
|
# It's suggested that your program require 'liquid' at load
|
||||||
# time when using this template engine.
|
# time when using this template engine.
|
||||||
class LiquidTemplate < Template
|
class LiquidTemplate < Template
|
||||||
|
def initialize_engine
|
||||||
|
require_template_library 'liquid' unless defined? ::Liquid::Template
|
||||||
|
end
|
||||||
|
|
||||||
def compile!
|
def compile!
|
||||||
require_template_library 'liquid' unless defined?(::Liquid::Template)
|
|
||||||
@engine = ::Liquid::Template.parse(data)
|
@engine = ::Liquid::Template.parse(data)
|
||||||
end
|
end
|
||||||
|
|
||||||
def evaluate(scope, locals, &block)
|
def evaluate(scope, locals, &block)
|
||||||
locals = locals.inject({}) { |hash,(k,v)| hash[k.to_s] = v ; hash }
|
locals = locals.inject({}){ |h,(k,v)| h[k.to_s] = v ; h }
|
||||||
|
if scope.respond_to?(:to_h)
|
||||||
|
scope = scope.to_h.inject({}){ |h,(k,v)| h[k.to_s] = v ; h }
|
||||||
|
locals = scope.merge(locals)
|
||||||
|
end
|
||||||
|
# TODO: Is it possible to lazy yield ?
|
||||||
|
locals['yield'] = block.nil? ? '' : yield
|
||||||
|
locals['content'] = block.nil? ? '' : yield
|
||||||
@engine.render(locals)
|
@engine.render(locals)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
register 'liquid', LiquidTemplate
|
register 'liquid', LiquidTemplate
|
||||||
|
|
||||||
|
|
||||||
# Discount Markdown implementation.
|
# Discount Markdown implementation. See:
|
||||||
|
# http://github.com/rtomayko/rdiscount
|
||||||
|
#
|
||||||
|
# RDiscount is a simple text filter. It does not support +scope+ or
|
||||||
|
# +locals+. The +:smart+ and +:filter_html+ options may be set true
|
||||||
|
# to enable those flags on the underlying RDiscount object.
|
||||||
class RDiscountTemplate < Template
|
class RDiscountTemplate < Template
|
||||||
|
def flags
|
||||||
|
[:smart, :filter_html].select { |flag| options[flag] }
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize_engine
|
||||||
|
require_template_library 'rdiscount' unless defined? ::RDiscount
|
||||||
|
end
|
||||||
|
|
||||||
def compile!
|
def compile!
|
||||||
require_template_library 'rdiscount' unless defined?(::RDiscount)
|
@engine = RDiscount.new(data, *flags)
|
||||||
@engine = RDiscount.new(data)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def evaluate(scope, locals, &block)
|
def evaluate(scope, locals, &block)
|
||||||
|
@ -338,9 +414,28 @@ module Tilt
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
register 'markdown', RDiscountTemplate
|
register 'markdown', RDiscountTemplate
|
||||||
|
register 'mkd', RDiscountTemplate
|
||||||
register 'md', RDiscountTemplate
|
register 'md', RDiscountTemplate
|
||||||
|
|
||||||
|
|
||||||
|
# RedCloth implementation. See:
|
||||||
|
# http://redcloth.org/
|
||||||
|
class RedClothTemplate < Template
|
||||||
|
def initialize_engine
|
||||||
|
require_template_library 'redcloth' unless defined? ::RedCloth
|
||||||
|
end
|
||||||
|
|
||||||
|
def compile!
|
||||||
|
@engine = RedCloth.new(data)
|
||||||
|
end
|
||||||
|
|
||||||
|
def evaluate(scope, locals, &block)
|
||||||
|
@engine.to_html
|
||||||
|
end
|
||||||
|
end
|
||||||
|
register 'textile', RedClothTemplate
|
||||||
|
|
||||||
|
|
||||||
# Mustache is written and maintained by Chris Wanstrath. See:
|
# Mustache is written and maintained by Chris Wanstrath. See:
|
||||||
# http://github.com/defunkt/mustache
|
# http://github.com/defunkt/mustache
|
||||||
#
|
#
|
||||||
|
@ -350,17 +445,13 @@ module Tilt
|
||||||
class MustacheTemplate < Template
|
class MustacheTemplate < Template
|
||||||
attr_reader :engine
|
attr_reader :engine
|
||||||
|
|
||||||
# Locates and compiles the Mustache object used to create new views. The
|
def initialize_engine
|
||||||
|
require_template_library 'mustache' unless defined? ::Mustache
|
||||||
|
end
|
||||||
|
|
||||||
def compile!
|
def compile!
|
||||||
require_template_library 'mustache' unless defined?(::Mustache)
|
|
||||||
|
|
||||||
# Set the Mustache view namespace if we can
|
|
||||||
Mustache.view_namespace = options[:namespace]
|
Mustache.view_namespace = options[:namespace]
|
||||||
|
|
||||||
# Figure out which Mustache class to use.
|
|
||||||
@engine = options[:view] || Mustache.view_class(name)
|
@engine = options[:view] || Mustache.view_class(name)
|
||||||
|
|
||||||
# set options on the view class
|
|
||||||
options.each do |key, value|
|
options.each do |key, value|
|
||||||
next if %w[view namespace mustaches].include?(key.to_s)
|
next if %w[view namespace mustaches].include?(key.to_s)
|
||||||
@engine.send("#{key}=", value) if @engine.respond_to? "#{key}="
|
@engine.send("#{key}=", value) if @engine.respond_to? "#{key}="
|
||||||
|
@ -368,20 +459,19 @@ module Tilt
|
||||||
end
|
end
|
||||||
|
|
||||||
def evaluate(scope=nil, locals={}, &block)
|
def evaluate(scope=nil, locals={}, &block)
|
||||||
# Create a new instance for playing with
|
|
||||||
instance = @engine.new
|
instance = @engine.new
|
||||||
|
|
||||||
# Copy instance variables from scope to the view
|
# copy instance variables from scope to the view
|
||||||
scope.instance_variables.each do |name|
|
scope.instance_variables.each do |name|
|
||||||
instance.instance_variable_set(name, scope.instance_variable_get(name))
|
instance.instance_variable_set(name, scope.instance_variable_get(name))
|
||||||
end
|
end
|
||||||
|
|
||||||
# Locals get added to the view's context
|
# locals get added to the view's context
|
||||||
locals.each do |local, value|
|
locals.each do |local, value|
|
||||||
instance[local] = value
|
instance[local] = value
|
||||||
end
|
end
|
||||||
|
|
||||||
# If we're passed a block it's a subview. Sticking it in yield
|
# if we're passed a block it's a subview. Sticking it in yield
|
||||||
# lets us use {{yield}} in layout.html to render the actual page.
|
# lets us use {{yield}} in layout.html to render the actual page.
|
||||||
instance[:yield] = block.call if block
|
instance[:yield] = block.call if block
|
||||||
|
|
||||||
|
@ -391,4 +481,29 @@ module Tilt
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
register 'mustache', MustacheTemplate
|
register 'mustache', MustacheTemplate
|
||||||
|
|
||||||
|
# RDoc template. See:
|
||||||
|
# http://rdoc.rubyforge.org/
|
||||||
|
#
|
||||||
|
# It's suggested that your program require 'rdoc/markup' and
|
||||||
|
# 'rdoc/markup/to_html' at load time when using this template
|
||||||
|
# engine.
|
||||||
|
class RDocTemplate < Template
|
||||||
|
def initialize_engine
|
||||||
|
unless defined?(::RDoc::Markup)
|
||||||
|
require_template_library 'rdoc/markup'
|
||||||
|
require_template_library 'rdoc/markup/to_html'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def compile!
|
||||||
|
markup = RDoc::Markup::ToHtml.new
|
||||||
|
@engine = markup.convert(data)
|
||||||
|
end
|
||||||
|
|
||||||
|
def evaluate(scope, locals, &block)
|
||||||
|
@engine.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
register 'rdoc', RDocTemplate
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue