Created
February 1, 2012 05:21
-
-
Save ericgj/1715273 to your computer and use it in GitHub Desktop.
Templating ripped out of Sinatra
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Note: ripped out of Sinatra, minus any `setting` calls, and disabling inline templates | |
# Template rendering methods. Each method takes the name of a template | |
# to render as a Symbol and returns a String with the rendered output, | |
# as well as an optional hash with additional options. | |
# | |
# `template` is either the name or path of the template as symbol | |
# (Use `:'subdir/myview'` for views in subdirectories), or a string | |
# that will be rendered. | |
# | |
# Possible options are: | |
# :content_type The content type to use, same arguments as content_type. | |
# :layout If set to false, no layout is rendered, otherwise | |
# the specified layout is used (Ignored for `sass` and `less`) | |
# :layout_engine Engine to use for rendering the layout. | |
# :locals A hash with local variables that should be available | |
# in the template | |
# :scope If set, template is evaluate with the binding of the given | |
# object rather than the application instance. | |
# :views Views directory to use. | |
module Templates | |
def template_cache; @template_cache ||= Tilt::Cache.new; end | |
module ContentTyped | |
attr_accessor :content_type | |
end | |
def initialize | |
super | |
@default_layout = :layout | |
end | |
def erb(template, options={}, locals={}) | |
render :erb, template, options, locals | |
end | |
def haml(template, options={}, locals={}) | |
render :haml, template, options, locals | |
end | |
def sass(template, options={}, locals={}) | |
options.merge! :layout => false, :default_content_type => :css | |
render :sass, template, options, locals | |
end | |
def scss(template, options={}, locals={}) | |
options.merge! :layout => false, :default_content_type => :css | |
render :scss, template, options, locals | |
end | |
def less(template, options={}, locals={}) | |
options.merge! :layout => false, :default_content_type => :css | |
render :less, template, options, locals | |
end | |
def builder(template=nil, options={}, locals={}, &block) | |
options[:default_content_type] = :xml | |
render_ruby(:builder, template, options, locals, &block) | |
end | |
def liquid(template, options={}, locals={}) | |
render :liquid, template, options, locals | |
end | |
def markdown(template, options={}, locals={}) | |
render :markdown, template, options, locals | |
end | |
def textile(template, options={}, locals={}) | |
render :textile, template, options, locals | |
end | |
def rdoc(template, options={}, locals={}) | |
render :rdoc, template, options, locals | |
end | |
def radius(template, options={}, locals={}) | |
render :radius, template, options, locals | |
end | |
def markaby(template=nil, options={}, locals={}, &block) | |
render_ruby(:mab, template, options, locals, &block) | |
end | |
def coffee(template, options={}, locals={}) | |
options.merge! :layout => false, :default_content_type => :js | |
render :coffee, template, options, locals | |
end | |
def nokogiri(template=nil, options={}, locals={}, &block) | |
options[:default_content_type] = :xml | |
render_ruby(:nokogiri, template, options, locals, &block) | |
end | |
def slim(template, options={}, locals={}) | |
render :slim, template, options, locals | |
end | |
def creole(template, options={}, locals={}) | |
render :creole, template, options, locals | |
end | |
# Calls the given block for every possible template file in views, | |
# named name.ext, where ext is registered on engine. | |
def find_template(views, name, engine) | |
yield ::File.join(views, "#{name}.#{@preferred_extension}") | |
Tilt.mappings.each do |ext, engines| | |
next unless ext != @preferred_extension and engines.include? engine | |
yield ::File.join(views, "#{name}.#{ext}") | |
end | |
end | |
private | |
# logic shared between builder and nokogiri | |
def render_ruby(engine, template, options={}, locals={}, &block) | |
options, template = template, nil if template.is_a?(Hash) | |
template = Proc.new { block } if template.nil? | |
render engine, template, options, locals | |
end | |
def render(engine, data, options={}, locals={}, &block) | |
# merge app-level options | |
options[:outvar] ||= '@_out_buf' | |
# extract generic options | |
locals = options.delete(:locals) || {} | |
views = options.delete(:views) || "./views" | |
layout = options.delete(:layout) | |
eat_errors = layout.nil? | |
layout = @default_layout if layout.nil? or layout == true | |
content_type = options.delete(:content_type) || options.delete(:default_content_type) | |
layout_engine = options.delete(:layout_engine) || engine | |
scope = options.delete(:scope) || self | |
# compile and render template | |
layout_was = @default_layout | |
@default_layout = false | |
template = compile_template(engine, data, options, views) | |
output = template.render(scope, locals, &block) | |
@default_layout = layout_was | |
# render layout | |
if layout | |
options = options.merge(:views => views, :layout => false, :eat_errors => eat_errors, :scope => scope) | |
catch(:layout_missing) { return render(layout_engine, layout, options, locals) { output } } | |
end | |
output.extend(ContentTyped).content_type = content_type if content_type | |
output | |
end | |
def compile_template(engine, data, options, views) | |
eat_errors = options.delete :eat_errors | |
template_cache.fetch engine, data, options do | |
template = Tilt[engine] | |
raise "Template engine not found: #{engine}" if template.nil? | |
case data | |
when Symbol | |
path = nil | |
found = false | |
@preferred_extension = engine.to_s | |
find_template(views, data, template) do |file| | |
path ||= file # keep the initial path rather than the last one | |
if found = File.exists?(file) | |
path = file | |
break | |
end | |
end | |
throw :layout_missing if eat_errors and not found | |
template.new(path, 1, options) | |
else | |
raise ArgumentError, "Sorry, don't know how to render #{data.inspect}." | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment