1
0
Fork 0
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:
Ryan Tomayko 2009-12-13 11:45:28 -08:00
parent 20f6d0a156
commit f6aaadf082

View file

@ -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,8 +77,22 @@ 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
# calls are no-ops. # calls are no-ops.
@ -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