Sane template options [#191]

* The options hash now takes the :views, :layout, and :locals
  options but also any template-specific options. The generic
  options are removed before calling the template specific render
  method.

* The haml ":options" and ":haml" options are deprecated. These
  should be merged in directly with the options hash.

* The sass ":sass" option is deprecated. Merge directly with the
  options hash instead.

* All template engines have an app-level option named the same as
  their engine (erb, haml, sass, etc.). This must be a hash and is
  merged with the options passed to the render method.

* The :views_directory option is deprecated; renamed :views.
This commit is contained in:
Ryan Tomayko 2009-03-27 10:00:33 -07:00
parent 5b2dfebbf0
commit 49adaa5362
6 changed files with 73 additions and 67 deletions

View File

@ -52,6 +52,16 @@ context "Sass" do
body.should.equal "#sass {\n background_color: #FFF; }\n"
end
it "passes :sass option to the Sass engine" do
get '/' do
sass "#sass\n :background-color #FFF\n :color #000\n", :sass => {:style => :compact}
end
get_it '/'
should.be.ok
body.should.equal "#sass { background-color: #FFF; color: #000; }\n"
end
end
end

View File

@ -232,53 +232,60 @@ module Sinatra
# :locals A hash with local variables that should be available
# in the template
module Templates
def erb(template, options={})
def erb(template, options={}, locals={})
require 'erb' unless defined? ::ERB
render :erb, template, options
render :erb, template, options, locals
end
def haml(template, options={})
def haml(template, options={}, locals={})
require 'haml' unless defined? ::Haml::Engine
opts = options[:haml_options] || options.delete(:options) || {}
opts = self.class.haml.merge(opts) if self.class.respond_to?(:haml)
options[:haml_options] = opts
render :haml, template, options
render :haml, template, options, locals
end
def sass(template, options={}, &block)
def sass(template, options={}, locals={})
require 'sass' unless defined? ::Sass::Engine
opts = options[:sass_options] || options.delete(:sass) || {}
opts = self.class.sass.merge(opts) if self.class.respond_to?(:sass)
options.merge! :layout => false, :sass_options => opts
render :sass, template, options
options[:layout] = false
render :sass, template, options, locals
end
def builder(template=nil, options={}, &block)
def builder(template=nil, options={}, locals={}, &block)
require 'builder' unless defined? ::Builder
options, template = template, nil if template.is_a?(Hash)
template = lambda { block } if template.nil?
render :builder, template, options
render :builder, template, options, locals
end
private
def render(engine, template, options={}) #:nodoc:
data = lookup_template(engine, template, options)
output = __send__("render_#{engine}", template, data, options)
layout, data = lookup_layout(engine, options)
if layout
__send__("render_#{engine}", layout, data, options) { output }
def render(engine, template, options={}, locals={})
# merge app-level options
options = self.class.send(engine).merge(options) if self.class.respond_to?(engine)
# extract generic options
layout = options.delete(:layout)
layout = :layout if layout.nil? || layout == true
views = options.delete(:views) || self.class.views || "./views"
locals = options.delete(:locals) || locals || {}
# render template
data = lookup_template(engine, template, views)
output = __send__("render_#{engine}", template, data, options, locals)
# render layout
if layout && data = lookup_layout(engine, layout, views)
__send__("render_#{engine}", layout, data, options, {}) { output }
else
output
end
end
def lookup_template(engine, template, options={})
def lookup_template(engine, template, views_dir)
case template
when Symbol
if cached = self.class.templates[template]
lookup_template(engine, cached, options)
lookup_template(engine, cached, views_dir)
else
::File.read(template_path(engine, template, options))
path = ::File.join(views_dir, "#{template}.#{engine}")
::File.read(path)
end
when Proc
template.call
@ -289,28 +296,17 @@ module Sinatra
end
end
def lookup_layout(engine, options)
return if options[:layout] == false
options.delete(:layout) if options[:layout] == true
template = options[:layout] || :layout
data = lookup_template(engine, template, options)
[template, data]
def lookup_layout(engine, template, views_dir)
lookup_template(engine, template, views_dir)
rescue Errno::ENOENT
nil
end
def template_path(engine, template, options={})
views_dir =
options[:views_directory] || self.options.views || "./views"
"#{views_dir}/#{template}.#{engine}"
end
def render_erb(template, data, options, &block)
def render_erb(template, data, options, locals, &block)
original_out_buf = @_out_buf
data = data.call if data.kind_of? Proc
instance = ::ERB.new(data, nil, nil, '@_out_buf')
locals = options[:locals] || {}
locals_assigns = locals.to_a.collect { |k,v| "#{k} = locals[:#{k}]" }
src = "#{locals_assigns.join("\n")}\n#{instance.src}"
@ -319,18 +315,17 @@ module Sinatra
result
end
def render_haml(template, data, options, &block)
engine = ::Haml::Engine.new(data, options[:haml_options] || {})
engine.render(self, options[:locals] || {}, &block)
def render_haml(template, data, options, locals, &block)
::Haml::Engine.new(data, options).render(self, locals, &block)
end
def render_sass(template, data, options, &block)
engine = ::Sass::Engine.new(data, options[:sass_options] || {})
engine.render
def render_sass(template, data, options, locals, &block)
::Sass::Engine.new(data, options).render
end
def render_builder(template, data, options, &block)
xml = ::Builder::XmlMarkup.new(:indent => 2)
def render_builder(template, data, options, locals, &block)
options = { :indent => 2 }.merge(options)
xml = ::Builder::XmlMarkup.new(options)
if data.respond_to?(:to_str)
eval data.to_str, binding, '<BUILDER>', 1
elsif data.kind_of?(Proc)

View File

@ -115,6 +115,22 @@ module Sinatra
halt data
end
# The :views_directory, :options, :haml, and :sass options are deprecated.
def render(engine, template, options={}, locals={}, &bk)
if options.key?(:views_directory)
sinatra_warn "The :views_directory option is deprecated; use :views instead."
options[:views] = options.delete(:views_directory)
end
[:options, engine.to_sym].each do |key|
if options.key?(key)
sinatra_warn "Passing :#{key} => {} to #{engine} is deprecated; " +
"merge options directly into hash instead."
options.merge! options.delete(key)
end
end
super(engine, template, options, locals, &bk)
end
# Throwing halt with a Symbol and the to_result convention are
# deprecated. Override the invoke method to detect those types of return
# values.

View File

@ -49,18 +49,12 @@ describe "HAML Templates" do
it "passes HAML options to the Haml engine" do
mock_app {
get '/' do
haml "!!!\n%h1 Hello World", :haml_options => {:format => :html5}
end
get '/backwards_compatible' do
haml "!!!\n%h1 Hello World", :options => {:format => :html4}
haml "!!!\n%h1 Hello World", :format => :html5
end
}
get '/'
assert ok?
assert_equal "<!DOCTYPE html>\n<h1>Hello World</h1>\n", body
get '/backwards_compatible'
assert ok?
assert_match(/^<!DOCTYPE html PUBLIC (.*) HTML 4.01/, body)
end
it "passes default HAML options to the Haml engine" do
@ -82,7 +76,7 @@ describe "HAML Templates" do
haml "!!!\n%h1{:class => :header} Hello World"
end
get '/html4' do
haml "!!!\n%h1{:class => 'header'} Hello World", :haml_options => {:format => :html4}
haml "!!!\n%h1{:class => 'header'} Hello World", :format => :html4
end
}
get '/'

View File

@ -36,15 +36,7 @@ describe "Sass Templates" do
it "passes SASS options to the Sass engine" do
sass_app {
sass "#sass\n :background-color #FFF\n :color #000\n", :sass_options => {:style => :compact}
}
assert ok?
assert_equal "#sass { background-color: #FFF; color: #000; }\n", body
end
it "passes backwards compatible SASS options to the Sass engine" do
sass_app {
sass "#sass\n :background-color #FFF\n :color #000\n", :sass => {:style => :compact}
sass "#sass\n :background-color #FFF\n :color #000\n", :style => :compact
}
assert ok?
assert_equal "#sass { background-color: #FFF; color: #000; }\n", body
@ -52,7 +44,7 @@ describe "Sass Templates" do
it "passes default SASS options to the Sass engine" do
mock_app {
set :sass, {:style => :compact } # default Sass style is :nested
set :sass, {:style => :compact} # default Sass style is :nested
get '/' do
sass "#sass\n :background-color #FFF\n :color #000\n"
end
@ -69,10 +61,10 @@ describe "Sass Templates" do
sass "#sass\n background-color: #FFF\n color: #000\n"
end
get '/raised' do
sass "#sass\n :background-color #FFF\n :color #000\n", :sass_options => {:style => :expanded } # retains global attribute_syntax settings
sass "#sass\n :background-color #FFF\n :color #000\n", :style => :expanded # retains global attribute_syntax settings
end
get '/expanded_normal' do
sass "#sass\n :background-color #FFF\n :color #000\n", :sass_options => {:style => :expanded, :attribute_syntax => :normal }
sass "#sass\n :background-color #FFF\n :color #000\n", :style => :expanded, :attribute_syntax => :normal
end
}
get '/'
@ -82,6 +74,5 @@ describe "Sass Templates" do
get '/expanded_normal'
assert ok?
assert_equal "#sass {\n background-color: #FFF;\n color: #000;\n}\n", body
end
end

View File

@ -3,7 +3,7 @@ require File.dirname(__FILE__) + '/helper'
describe 'Templating' do
def render_app(&block)
mock_app {
def render_test(template, data, options, &block)
def render_test(template, data, options, locals, &block)
inner = block ? block.call : ''
data + inner
end