Skip to content

Commit

Permalink
Template#initialize_engine - called first use of template class
Browse files Browse the repository at this point in the history
  • Loading branch information
rtomayko committed Nov 12, 2009
1 parent 2f2aa38 commit 54a8604
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 30 deletions.
86 changes: 59 additions & 27 deletions lib/tilt.rb
Expand Up @@ -67,14 +67,31 @@ class Template
# 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
# 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)
raise ArgumentError, "file or block required" if file.nil? && block.nil?
options, line = line, 1 if line.is_a?(Hash)
@file = file
@line = line || 1
@options = options || {}
@reader = block || lambda { |t| File.read(file) }

if !self.class.engine_initialized
initialize_engine
self.class.engine_initialized = true
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
# loaded and compiled the first time this method is called; subsequent
Expand Down Expand Up @@ -192,8 +209,11 @@ def initialize(*args)
@template_procs = {}
end

def initialize_engine
require_template_library 'erb' unless defined? ::ERB
end

def compile!
require_template_library 'erb' unless defined?(::ERB)
@engine = ::ERB.new(data, options[:safe], options[:trim], '@_out_buf')
end

Expand Down Expand Up @@ -238,8 +258,11 @@ def local_assignment_code(locals)
# Erubis template implementation. See:
# http://www.kuwata-lab.com/erubis/
class ErubisTemplate < ERBTemplate
def initialize_engine
require_template_library 'erubis' unless defined? ::Erubis
end

def compile!
require_template_library 'erubis' unless defined?(::Erubis)
Erubis::Eruby.class_eval(%Q{def add_preamble(src) src << "@_out_buf = _buf = '';" end})
@engine = ::Erubis::Eruby.new(data, options)
end
Expand All @@ -250,8 +273,11 @@ def compile!
# Haml template implementation. See:
# http://haml.hamptoncatlin.com/
class HamlTemplate < Template
def initialize_engine
require_template_library 'haml' unless defined? ::Haml::Engine
end

def compile!
require_template_library 'haml' unless defined?(::Haml::Engine)
@engine = ::Haml::Engine.new(data, haml_options)
end

Expand All @@ -272,8 +298,11 @@ def haml_options
#
# Sass templates do not support object scopes, locals, or yield.
class SassTemplate < Template
def initialize_engine
require_template_library 'sass' unless defined? ::Sass::Engine
end

def compile!
require_template_library 'sass' unless defined?(::Sass::Engine)
@engine = ::Sass::Engine.new(data, sass_options)
end

Expand All @@ -292,10 +321,13 @@ def sass_options
# Builder template implementation. See:
# http://builder.rubyforge.org/
class BuilderTemplate < Template
def compile!
def initialize_engine
require_template_library 'builder' unless defined?(::Builder)
end

def compile!
end

def evaluate(scope, locals, &block)
xml = ::Builder::XmlMarkup.new(:indent => 2)
if data.respond_to?(:to_str)
Expand Down Expand Up @@ -328,8 +360,11 @@ def template_source
# It's suggested that your program require 'liquid' at load
# time when using this template engine.
class LiquidTemplate < Template
def initialize_engine
require_template_library 'liquid' unless defined? ::Liquid::Template
end

def compile!
require_template_library 'liquid' unless defined?(::Liquid::Template)
@engine = ::Liquid::Template.parse(data)
end

Expand Down Expand Up @@ -358,8 +393,11 @@ def flags
[:smart, :filter_html].select { |flag| options[flag] }
end

def initialize_engine
require_template_library 'rdiscount' unless defined? ::RDiscount
end

def compile!
require_template_library 'rdiscount' unless defined?(::RDiscount)
@engine = RDiscount.new(data, *flags)
end

Expand All @@ -381,38 +419,33 @@ def evaluate(scope, locals, &block)
class MustacheTemplate < Template
attr_reader :engine

# Locates and compiles the Mustache object used to create new views. The
def compile!
require_template_library 'mustache' unless defined?(::Mustache)
def initialize_engine
require_template_library 'mustache' unless defined? ::Mustache
end

# Set the Mustache view namespace if we can
def compile!
Mustache.view_namespace = options[:namespace]

# Figure out which Mustache class to use.
@engine = options[:view] || Mustache.view_class(name)

# set options on the view class
options.each do |key, value|
next if %w[view namespace mustaches].include?(key.to_s)
@engine.send("#{key}=", value) if @engine.respond_to? "#{key}="
end
end

def evaluate(scope=nil, locals={}, &block)
# Create a new instance for playing with
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|
instance.instance_variable_set(name, scope.instance_variable_get(name))
end

# Locals get added to the view's context
# locals get added to the view's context
locals.each do |local, value|
instance[local] = value
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.
instance[:yield] = block.call if block

Expand All @@ -426,18 +459,18 @@ def evaluate(scope=nil, locals={}, &block)
# RDoc template. See:
# http://rdoc.rubyforge.org/
#
# It's suggested that your program require:
#
# require 'rdoc/markup'
# require 'rdoc/markup/to_html'
#
# at load time when using this template engine.
# 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 compile!
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
Expand All @@ -447,5 +480,4 @@ def evaluate(scope, locals, &block)
end
end
register 'rdoc', RDocTemplate

end
32 changes: 29 additions & 3 deletions test/spec_tilt_template.rb
Expand Up @@ -44,9 +44,30 @@
}.should.not.raise
end

it "raises NotImplementedError when #compile! not defined" do
inst = Tilt::Template.new { |template| "Hello World!" }
lambda { inst.render }.should.raise NotImplementedError
class InitializingMockTemplate < Tilt::Template
@@initialized_count = 0
def self.initialized_count
@@initialized_count
end

def initialize_engine
@@initialized_count += 1
end

def compile!
end
end

it "calls #initialize_engine the very first time " do
InitializingMockTemplate.engine_initialized.should.be.nil
InitializingMockTemplate.initialized_count.should.equal 0

InitializingMockTemplate.new { "Hello World!" }
InitializingMockTemplate.engine_initialized.should.equal true
InitializingMockTemplate.initialized_count.should.equal 1

InitializingMockTemplate.new { "Hello World!" }
InitializingMockTemplate.initialized_count.should.equal 1
end

class CompilingMockTemplate < Tilt::Template
Expand All @@ -57,6 +78,11 @@ def compile!
def compiled? ; @compiled ; end
end

it "raises NotImplementedError when #compile! not defined" do
inst = Tilt::Template.new { |template| "Hello World!" }
lambda { inst.render }.should.raise NotImplementedError
end

it "raises NotImplementedError when #evaluate or #template_source not defined" do
inst = CompilingMockTemplate.new { |t| "Hello World!" }
lambda { inst.render }.should.raise NotImplementedError
Expand Down

0 comments on commit 54a8604

Please sign in to comment.