Merge branch 'smart-templates'

This commit is contained in:
Konstantin Haase 2010-10-09 17:20:20 +02:00
commit ade0b3b008
17 changed files with 216 additions and 44 deletions

View File

@ -229,7 +229,6 @@ Das buidler gem wird benötigt, um Builder-Templates rendern zu können:
require 'builder'
get '/' do
content_type 'application/xml', :charset => 'utf-8'
builder :index
end
@ -243,7 +242,6 @@ Das haml gem wird benötigt, um SASS-Templates rendern zu können:
require 'sass'
get '/stylesheet.css' do
content_type 'text/css', :charset => 'utf-8'
sass :stylesheet
end
@ -257,7 +255,6 @@ und individuell überschrieben werden.
set :sass, :style => :compact # Standard Sass-Style ist :nested
get '/stylesheet.css' do
content_type 'text/css', :charset => 'utf-8'
sass :stylesheet, :style => :expanded # überschrieben
end
@ -269,7 +266,6 @@ Das haml gem wird benötigt, um SCSS-Templates rendern zu können:
require 'sass'
get '/stylesheet.css' do
content_type 'text/css', :charset => 'utf-8'
scss :stylesheet
end
@ -283,7 +279,6 @@ und individuell überschrieben werden.
set :scss, :style => :compact # Standard Scss-Style ist :nested
get '/stylesheet.css' do
content_type 'text/css', :charset => 'utf-8'
scss :stylesheet, :style => :expanded # überschrieben
end
@ -295,7 +290,6 @@ Das less gem wird benötigt, um Less-Templates rendern zu können:
require 'less'
get '/stylesheet.css' do
content_type 'text/css', :charset => 'utf-8'
less :stylesheet
end
@ -434,7 +428,6 @@ Das coffee-script gem und das `coffee`-Programm werden benötigt, um CoffeScript
require 'coffee-script'
get '/application.js' do
content_type 'text/javascript', :charset => 'utf-8'
coffee :application
end

View File

@ -154,7 +154,6 @@ builderを使うにはbuilderライブラリが必要です:
require 'builder'
get '/' do
content_type 'application/xml', :charset => 'utf-8'
builder :index
end
@ -168,7 +167,6 @@ Sassテンプレートを使うにはsassライブラリが必要です:
require 'sass'
get '/stylesheet.css' do
content_type 'text/css', :charset => 'utf-8'
sass :stylesheet
end
@ -182,7 +180,6 @@ see {Options and Configurations}[http://www.sinatrarb.com/configuration.html],
set :sass, {:style => :compact } # デフォルトのSass styleは :nested
get '/stylesheet.css' do
content_type 'text/css', :charset => 'utf-8'
sass :stylesheet, :sass_options => {:style => :expanded } # 上書き
end

View File

@ -225,7 +225,6 @@ The builder gem/library is required to render builder templates:
require 'builder'
get '/' do
content_type 'application/xml', :charset => 'utf-8'
builder :index
end
@ -239,7 +238,6 @@ The sass gem/library is required to render Sass templates:
require 'sass'
get '/stylesheet.css' do
content_type 'text/css', :charset => 'utf-8'
sass :stylesheet
end
@ -253,7 +251,6 @@ and overridden on an individual basis.
set :sass, :style => :compact # default Sass style is :nested
get '/stylesheet.css' do
content_type 'text/css', :charset => 'utf-8'
sass :stylesheet, :style => :expanded # overridden
end
@ -265,7 +262,6 @@ The sass gem/library is required to render Scss templates:
require 'sass'
get '/stylesheet.css' do
content_type 'text/css', :charset => 'utf-8'
scss :stylesheet
end
@ -279,7 +275,6 @@ and overridden on an individual basis.
set :scss, :style => :compact # default Scss style is :nested
get '/stylesheet.css' do
content_type 'text/css', :charset => 'utf-8'
scss :stylesheet, :style => :expanded # overridden
end
@ -291,7 +286,6 @@ The less gem/library is required to render Less templates:
require 'less'
get '/stylesheet.css' do
content_type 'text/css', :charset => 'utf-8'
less :stylesheet
end
@ -422,7 +416,6 @@ CoffeeScript templates:
require 'coffee-script'
get '/application.js' do
content_type 'text/javascript', :charset => 'utf-8'
coffee :application
end

View File

@ -77,10 +77,12 @@ module Sinatra
# evaluation is deferred until the body is read with #each.
def body(value=nil, &block)
if block_given?
def block.each ; yield call ; end
def block.each; yield(call) end
response.body = block
else
elsif value
response.body = value
else
response.body
end
end
@ -137,6 +139,7 @@ module Sinatra
def content_type(type, params={})
mime_type = mime_type(type)
fail "Unknown media type: %p" % type if mime_type.nil?
params[:charset] ||= defined?(Encoding) ? Encoding.default_external.to_s.downcase : 'utf-8'
if params.any?
params = params.collect { |kv| "%s=%s" % kv }.join(', ')
response['Content-Type'] = [mime_type, params].join(";")
@ -303,6 +306,10 @@ module Sinatra
# :locals A hash with local variables that should be available
# in the template
module Templates
module ContentTyped
attr_accessor :content_type
end
include Tilt::CompileSite
def erb(template, options={}, locals={})
@ -318,21 +325,22 @@ module Sinatra
end
def sass(template, options={}, locals={})
options[:layout] = false
options.merge! :layout => false, :default_content_type => :css
render :sass, template, options, locals
end
def scss(template, options={}, locals={})
options[:layout] = false
options.merge! :layout => false, :default_content_type => :css
render :scss, template, options, locals
end
def less(template, options={}, locals={})
options[:layout] = false
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
options, template = template, nil if template.is_a?(Hash)
template = Proc.new { block } if template.nil?
render :builder, template, options, locals
@ -363,7 +371,7 @@ module Sinatra
end
def coffee(template, options={}, locals={})
options[:layout] = false
options.merge! :layout => false, :default_content_type => :js
render :coffee, template, options, locals
end
@ -374,14 +382,19 @@ module Sinatra
options[:outvar] ||= '@_out_buf'
# extract generic options
locals = options.delete(:locals) || locals || {}
views = options.delete(:views) || settings.views || "./views"
layout = options.delete(:layout)
layout = :layout if layout.nil? || layout == true
locals = options.delete(:locals) || locals || {}
views = options.delete(:views) || settings.views || "./views"
@default_layout = :layout if @default_layout.nil?
layout = options.delete(:layout)
layout = @default_layout if layout.nil? or layout == true
content_type = options.delete(:content_type) || options.delete(:default_content_type)
# compile and render template
template = compile_template(engine, data, options, views)
output = template.render(self, locals, &block)
layout_was = @default_layout
@default_layout = false if layout
template = compile_template(engine, data, options, views)
output = template.render(self, locals, &block)
@default_layout = layout_was
# render layout
if layout
@ -392,6 +405,7 @@ module Sinatra
end
end
output.extend(ContentTyped).content_type = content_type if content_type
output
end
@ -456,8 +470,16 @@ module Sinatra
template_cache.clear if settings.reload_templates
force_encoding(@params)
@response['Content-Type'] = nil
invoke { dispatch! }
invoke { error_block!(response.status) }
unless @response['Content-Type']
if body.respond_to?(:to_ary) and body.first.respond_to? :content_type
content_type body.first.content_type
else
content_type :html
end
end
status, header, body = @response.finish

View File

@ -2,9 +2,10 @@ require File.dirname(__FILE__) + '/helper'
require 'builder'
class BuilderTest < Test::Unit::TestCase
def builder_app(&block)
def builder_app(options = {}, &block)
mock_app {
set :views, File.dirname(__FILE__) + '/views'
set options
get '/', &block
}
get '/'
@ -16,6 +17,29 @@ class BuilderTest < Test::Unit::TestCase
assert_equal %{<?xml version="1.0" encoding="UTF-8"?>\n}, body
end
it 'defaults content type to xml' do
builder_app { builder 'xml.instruct!' }
assert ok?
assert_equal "application/xml;charset=utf-8", response['Content-Type']
end
it 'defaults allows setting content type per route' do
builder_app do
content_type :html
builder 'xml.instruct!'
end
assert ok?
assert_equal "text/html;charset=utf-8", response['Content-Type']
end
it 'defaults allows setting content type globally' do
builder_app(:builder => { :content_type => 'html' }) do
builder 'xml.instruct!'
end
assert ok?
assert_equal "text/html;charset=utf-8", response['Content-Type']
end
it 'renders inline blocks' do
builder_app {
@name = "Frank & Mary"

View File

@ -4,9 +4,10 @@ begin
require 'coffee-script'
class CoffeeTest < Test::Unit::TestCase
def coffee_app(&block)
def coffee_app(options = {}, &block)
mock_app {
set :views, File.dirname(__FILE__) + '/views'
set(options)
get '/', &block
}
get '/'
@ -18,6 +19,29 @@ class CoffeeTest < Test::Unit::TestCase
assert_equal "(function() {\n alert('Aye!');\n})();\n", body
end
it 'defaults content type to javascript' do
coffee_app { coffee "alert 'Aye!'\n" }
assert ok?
assert_equal "application/javascript;charset=utf-8", response['Content-Type']
end
it 'defaults allows setting content type per route' do
coffee_app do
content_type :html
coffee "alert 'Aye!'\n"
end
assert ok?
assert_equal "text/html;charset=utf-8", response['Content-Type']
end
it 'defaults allows setting content type globally' do
coffee_app(:coffee => { :content_type => 'html' }) do
coffee "alert 'Aye!'\n"
end
assert ok?
assert_equal "text/html;charset=utf-8", response['Content-Type']
end
it 'renders .coffee files in views path' do
coffee_app { coffee :hello }
assert ok?

View File

@ -1,4 +1,5 @@
ENV['RACK_ENV'] = 'test'
Encoding.default_external = "UTF-8" if defined? Encoding
begin
require 'rack'

View File

@ -291,21 +291,21 @@ class HelpersTest < Test::Unit::TestCase
}
get '/'
assert_equal 'text/plain', response['Content-Type']
assert_equal 'text/plain;charset=utf-8', response['Content-Type']
assert_equal 'Hello World', body
end
it 'takes media type parameters (like charset=)' do
mock_app {
get '/' do
content_type 'text/html', :charset => 'utf-8'
content_type 'text/html', :charset => 'latin1'
"<h1>Hello, World</h1>"
end
}
get '/'
assert ok?
assert_equal 'text/html;charset=utf-8', response['Content-Type']
assert_equal 'text/html;charset=latin1', response['Content-Type']
assert_equal "<h1>Hello, World</h1>", body
end
@ -320,7 +320,7 @@ class HelpersTest < Test::Unit::TestCase
get '/foo.xml'
assert ok?
assert_equal 'application/foo', response['Content-Type']
assert_equal 'application/foo;charset=utf-8', response['Content-Type']
assert_equal 'I AM FOO', body
end
@ -366,19 +366,19 @@ class HelpersTest < Test::Unit::TestCase
it 'sets the Content-Type response header if a mime-type can be located' do
send_file_app
get '/file.txt'
assert_equal 'text/plain', response['Content-Type']
assert_equal 'text/plain;charset=utf-8', response['Content-Type']
end
it 'sets the Content-Type response header if type option is set to a file extesion' do
send_file_app :type => 'html'
get '/file.txt'
assert_equal 'text/html', response['Content-Type']
assert_equal 'text/html;charset=utf-8', response['Content-Type']
end
it 'sets the Content-Type response header if type option is set to a mime type' do
send_file_app :type => 'application/octet-stream'
get '/file.txt'
assert_equal 'application/octet-stream', response['Content-Type']
assert_equal 'application/octet-stream;charset=utf-8', response['Content-Type']
end
it 'sets the Content-Length response header' do

View File

@ -2,20 +2,44 @@ require File.dirname(__FILE__) + '/helper'
require 'less'
class LessTest < Test::Unit::TestCase
def less_app(&block)
def less_app(options = {}, &block)
mock_app {
set :views, File.dirname(__FILE__) + '/views'
set options
get '/', &block
}
get '/'
end
it 'renders inline Less strings' do
less_app { less "@white_color: #fff; #main { background-color: @white_color }"}
less_app { less "@white_color: #fff; #main { background-color: @white_color }" }
assert ok?
assert_equal "#main { background-color: #ffffff; }\n", body
end
it 'defaults content type to css' do
less_app { less "@white_color: #fff; #main { background-color: @white_color }" }
assert ok?
assert_equal "text/css;charset=utf-8", response['Content-Type']
end
it 'defaults allows setting content type per route' do
less_app do
content_type :html
less "@white_color: #fff; #main { background-color: @white_color }"
end
assert ok?
assert_equal "text/html;charset=utf-8", response['Content-Type']
end
it 'defaults allows setting content type globally' do
less_app(:less => { :content_type => 'html' }) do
less "@white_color: #fff; #main { background-color: @white_color }"
end
assert ok?
assert_equal "text/html;charset=utf-8", response['Content-Type']
end
it 'renders .less files in views path' do
less_app { less :hello }
assert ok?

View File

@ -80,7 +80,7 @@ class RoutingTest < Test::Unit::TestCase
get '/foo'
assert_equal 404, status
assert_equal 'text/html', response["Content-Type"]
assert_equal 'text/html;charset=utf-8', response["Content-Type"]
assert_equal "<h1>Not Found</h1>", response.body
end

View File

@ -4,9 +4,10 @@ begin
require 'sass'
class SassTest < Test::Unit::TestCase
def sass_app(&block)
def sass_app(options = {}, &block)
mock_app {
set :views, File.dirname(__FILE__) + '/views'
set options
get '/', &block
}
get '/'
@ -18,6 +19,29 @@ class SassTest < Test::Unit::TestCase
assert_equal "#sass {\n background-color: white; }\n", body
end
it 'defaults content type to css' do
sass_app { sass "#sass\n :background-color white\n" }
assert ok?
assert_equal "text/css;charset=utf-8", response['Content-Type']
end
it 'defaults allows setting content type per route' do
sass_app do
content_type :html
sass "#sass\n :background-color white\n"
end
assert ok?
assert_equal "text/html;charset=utf-8", response['Content-Type']
end
it 'defaults allows setting content type globally' do
sass_app(:sass => { :content_type => 'html' }) do
sass "#sass\n :background-color white\n"
end
assert ok?
assert_equal "text/html;charset=utf-8", response['Content-Type']
end
it 'renders .sass files in views path' do
sass_app { sass :hello }
assert ok?

View File

@ -4,9 +4,10 @@ begin
require 'sass'
class ScssTest < Test::Unit::TestCase
def scss_app(&block)
def scss_app(options = {}, &block)
mock_app {
set :views, File.dirname(__FILE__) + '/views'
set options
get '/', &block
}
get '/'
@ -18,6 +19,29 @@ class ScssTest < Test::Unit::TestCase
assert_equal "#scss {\n background-color: white; }\n", body
end
it 'defaults content type to css' do
scss_app { scss "#scss {\n background-color: white; }\n" }
assert ok?
assert_equal "text/css;charset=utf-8", response['Content-Type']
end
it 'defaults allows setting content type per route' do
scss_app do
content_type :html
scss "#scss {\n background-color: white; }\n"
end
assert ok?
assert_equal "text/html;charset=utf-8", response['Content-Type']
end
it 'defaults allows setting content type globally' do
scss_app(:scss => { :content_type => 'html' }) do
scss "#scss {\n background-color: white; }\n"
end
assert ok?
assert_equal "text/html;charset=utf-8", response['Content-Type']
end
it 'renders .scss files in views path' do
scss_app { scss :hello }
assert ok?

View File

@ -15,9 +15,11 @@ class TestTemplate < Tilt::Template
end
class TemplatesTest < Test::Unit::TestCase
def render_app(base=Sinatra::Base, &block)
def render_app(base=Sinatra::Base, options = {}, &block)
base, options = Sinatra::Base, base if base.is_a? Hash
mock_app(base) {
set :views, File.dirname(__FILE__) + '/views'
set options
get '/', &block
template(:layout3) { "Layout 3!\n" }
}
@ -78,6 +80,27 @@ class TemplatesTest < Test::Unit::TestCase
assert_equal "Layout 3!\nHello World!\n", body
end
it 'avoids wrapping layouts around nested templates' do
render_app { render :str, :nested, :layout => :layout2 }
assert ok?
assert_equal "<h1>String Layout!</h1>\n<content><h1>Hello From String</h1></content>", body
end
it 'allows explicitly wrapping layouts around nested templates' do
render_app { render :str, :explicitly_nested, :layout => :layout2 }
assert ok?
assert_equal "<h1>String Layout!</h1>\n<content><h1>String Layout!</h1>\n<h1>Hello From String</h1></content>", body
end
it 'two independent render calls do not disable layouts' do
render_app do
render :str, :explicitly_nested, :layout => :layout2
render :str, :nested, :layout => :layout2
end
assert ok?
assert_equal "<h1>String Layout!</h1>\n<content><h1>Hello From String</h1></content>", body
end
it 'loads templates from source file' do
mock_app { enable :inline_templates }
assert_equal "this is foo\n\n", @app.templates[:foo][0]
@ -135,6 +158,24 @@ class TemplatesTest < Test::Unit::TestCase
assert_equal 'bar', body
end
it 'allows setting default content type per template engine' do
render_app(:str => { :content_type => :txt }) { render :str, 'foo' }
assert_equal 'text/plain;charset=utf-8', response['Content-Type']
end
it 'setting default content type does not affect other template engines' do
render_app(:str => { :content_type => :txt }) { render :test, 'foo' }
assert_equal 'text/html;charset=utf-8', response['Content-Type']
end
it 'setting default content type per template engine does not override content_type' do
render_app :str => { :content_type => :txt } do
content_type :html
render :str, 'foo'
end
assert_equal 'text/html;charset=utf-8', response['Content-Type']
end
it 'uses templates in superclasses before subclasses' do
base = Class.new(Sinatra::Base)
base.template(:foo) { 'template in superclass' }

View File

@ -0,0 +1 @@
<content>#{render :str, :hello, :layout => :layout2}</content>

1
test/views/hello.str Normal file
View File

@ -0,0 +1 @@
<h1>Hello From String</h1>

2
test/views/layout2.str Normal file
View File

@ -0,0 +1,2 @@
<h1>String Layout!</h1>
#{yield}

1
test/views/nested.str Normal file
View File

@ -0,0 +1 @@
<content>#{render :str, :hello}</content>