1
0
Fork 0
mirror of https://github.com/sinatra/sinatra synced 2023-03-27 23:18:01 -04:00

Sets default content type according to template engine used instead of just text/html.

It does so by including a Mixin into the the returned string offering a content_type method. Therefore all of the following examples produce the expected results:

    # text/html
    get('/') do
      haml :index
    end

    # text/css
    get('/') do
      sass :index
    end

    # text/css
    get('/') do
      haml :index
      sass :index
    end

    # text/html
    get('/') do
      haml '= sass :index'
    end

It also allows setting the default content type for a template engine:

    set :builder, :content_type => :html

Tests and README adjustments (all languages) included.
This commit is contained in:
Konstantin Haase 2010-09-19 17:57:48 +02:00
parent e5e00471fe
commit 1d676f41f8
13 changed files with 180 additions and 38 deletions

View file

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

View file

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

View file

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

View file

@ -77,10 +77,12 @@ module Sinatra
# evaluation is deferred until the body is read with #each. # evaluation is deferred until the body is read with #each.
def body(value=nil, &block) def body(value=nil, &block)
if block_given? if block_given?
def block.each ; yield call ; end def block.each; yield(call) end
response.body = block response.body = block
else elsif value
response.body = value response.body = value
else
response.body
end end
end end
@ -137,6 +139,7 @@ module Sinatra
def content_type(type, params={}) def content_type(type, params={})
mime_type = mime_type(type) mime_type = mime_type(type)
fail "Unknown media type: %p" % type if mime_type.nil? 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? if params.any?
params = params.collect { |kv| "%s=%s" % kv }.join(', ') params = params.collect { |kv| "%s=%s" % kv }.join(', ')
response['Content-Type'] = [mime_type, params].join(";") response['Content-Type'] = [mime_type, params].join(";")
@ -300,6 +303,10 @@ module Sinatra
# :locals A hash with local variables that should be available # :locals A hash with local variables that should be available
# in the template # in the template
module Templates module Templates
module ContentTyped
attr_accessor :content_type
end
include Tilt::CompileSite include Tilt::CompileSite
def erb(template, options={}, locals={}) def erb(template, options={}, locals={})
@ -315,21 +322,22 @@ module Sinatra
end end
def sass(template, options={}, locals={}) def sass(template, options={}, locals={})
options[:layout] = false options.merge! :layout => false, :default_content_type => :css
render :sass, template, options, locals render :sass, template, options, locals
end end
def scss(template, options={}, locals={}) def scss(template, options={}, locals={})
options[:layout] = false options.merge! :layout => false, :default_content_type => :css
render :scss, template, options, locals render :scss, template, options, locals
end end
def less(template, options={}, locals={}) def less(template, options={}, locals={})
options[:layout] = false options.merge! :layout => false, :default_content_type => :css
render :less, template, options, locals render :less, template, options, locals
end end
def builder(template=nil, options={}, locals={}, &block) def builder(template=nil, options={}, locals={}, &block)
options[:default_content_type] = :xml
options, template = template, nil if template.is_a?(Hash) options, template = template, nil if template.is_a?(Hash)
template = Proc.new { block } if template.nil? template = Proc.new { block } if template.nil?
render :builder, template, options, locals render :builder, template, options, locals
@ -360,7 +368,7 @@ module Sinatra
end end
def coffee(template, options={}, locals={}) def coffee(template, options={}, locals={})
options[:layout] = false options.merge! :layout => false, :default_content_type => :js
render :coffee, template, options, locals render :coffee, template, options, locals
end end
@ -376,6 +384,7 @@ module Sinatra
@default_layout = :layout if @default_layout.nil? @default_layout = :layout if @default_layout.nil?
layout = options.delete(:layout) layout = options.delete(:layout)
layout = @default_layout if layout.nil? or layout == true layout = @default_layout if layout.nil? or layout == true
content_type = options.delete(:content_type) || options.delete(:default_content_type)
# compile and render template # compile and render template
layout_was = @default_layout layout_was = @default_layout
@ -393,6 +402,7 @@ module Sinatra
end end
end end
output.extend(ContentTyped).content_type = content_type if content_type
output output
end end
@ -457,8 +467,16 @@ module Sinatra
template_cache.clear if settings.reload_templates template_cache.clear if settings.reload_templates
force_encoding(@params) force_encoding(@params)
@response['Content-Type'] = nil
invoke { dispatch! } invoke { dispatch! }
invoke { error_block!(response.status) } 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 status, header, body = @response.finish

View file

@ -2,9 +2,10 @@ require File.dirname(__FILE__) + '/helper'
require 'builder' require 'builder'
class BuilderTest < Test::Unit::TestCase class BuilderTest < Test::Unit::TestCase
def builder_app(&block) def builder_app(options = {}, &block)
mock_app { mock_app {
set :views, File.dirname(__FILE__) + '/views' set :views, File.dirname(__FILE__) + '/views'
set options
get '/', &block get '/', &block
} }
get '/' get '/'
@ -16,6 +17,29 @@ class BuilderTest < Test::Unit::TestCase
assert_equal %{<?xml version="1.0" encoding="UTF-8"?>\n}, body assert_equal %{<?xml version="1.0" encoding="UTF-8"?>\n}, body
end 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 it 'renders inline blocks' do
builder_app { builder_app {
@name = "Frank & Mary" @name = "Frank & Mary"

View file

@ -4,9 +4,10 @@ begin
require 'coffee-script' require 'coffee-script'
class CoffeeTest < Test::Unit::TestCase class CoffeeTest < Test::Unit::TestCase
def coffee_app(&block) def coffee_app(options = {}, &block)
mock_app { mock_app {
set :views, File.dirname(__FILE__) + '/views' set :views, File.dirname(__FILE__) + '/views'
set(options)
get '/', &block get '/', &block
} }
get '/' get '/'
@ -18,6 +19,29 @@ class CoffeeTest < Test::Unit::TestCase
assert_equal "(function() {\n alert('Aye!');\n})();\n", body assert_equal "(function() {\n alert('Aye!');\n})();\n", body
end 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 it 'renders .coffee files in views path' do
coffee_app { coffee :hello } coffee_app { coffee :hello }
assert ok? assert ok?

View file

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

View file

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

View file

@ -2,20 +2,44 @@ require File.dirname(__FILE__) + '/helper'
require 'less' require 'less'
class LessTest < Test::Unit::TestCase class LessTest < Test::Unit::TestCase
def less_app(&block) def less_app(options = {}, &block)
mock_app { mock_app {
set :views, File.dirname(__FILE__) + '/views' set :views, File.dirname(__FILE__) + '/views'
set options
get '/', &block get '/', &block
} }
get '/' get '/'
end end
it 'renders inline Less strings' do 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 ok?
assert_equal "#main { background-color: #ffffff; }\n", body assert_equal "#main { background-color: #ffffff; }\n", body
end 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 it 'renders .less files in views path' do
less_app { less :hello } less_app { less :hello }
assert ok? assert ok?

View file

@ -80,7 +80,7 @@ class RoutingTest < Test::Unit::TestCase
get '/foo' get '/foo'
assert_equal 404, status 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 assert_equal "<h1>Not Found</h1>", response.body
end end

View file

@ -4,9 +4,10 @@ begin
require 'sass' require 'sass'
class SassTest < Test::Unit::TestCase class SassTest < Test::Unit::TestCase
def sass_app(&block) def sass_app(options = {}, &block)
mock_app { mock_app {
set :views, File.dirname(__FILE__) + '/views' set :views, File.dirname(__FILE__) + '/views'
set options
get '/', &block get '/', &block
} }
get '/' get '/'
@ -18,6 +19,29 @@ class SassTest < Test::Unit::TestCase
assert_equal "#sass {\n background-color: white; }\n", body assert_equal "#sass {\n background-color: white; }\n", body
end 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 it 'renders .sass files in views path' do
sass_app { sass :hello } sass_app { sass :hello }
assert ok? assert ok?

View file

@ -4,9 +4,10 @@ begin
require 'sass' require 'sass'
class ScssTest < Test::Unit::TestCase class ScssTest < Test::Unit::TestCase
def scss_app(&block) def scss_app(options = {}, &block)
mock_app { mock_app {
set :views, File.dirname(__FILE__) + '/views' set :views, File.dirname(__FILE__) + '/views'
set options
get '/', &block get '/', &block
} }
get '/' get '/'
@ -18,6 +19,29 @@ class ScssTest < Test::Unit::TestCase
assert_equal "#scss {\n background-color: white; }\n", body assert_equal "#scss {\n background-color: white; }\n", body
end 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 it 'renders .scss files in views path' do
scss_app { scss :hello } scss_app { scss :hello }
assert ok? assert ok?

View file

@ -15,9 +15,11 @@ class TestTemplate < Tilt::Template
end end
class TemplatesTest < Test::Unit::TestCase 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) { mock_app(base) {
set :views, File.dirname(__FILE__) + '/views' set :views, File.dirname(__FILE__) + '/views'
set options
get '/', &block get '/', &block
template(:layout3) { "Layout 3!\n" } template(:layout3) { "Layout 3!\n" }
} }
@ -156,6 +158,24 @@ class TemplatesTest < Test::Unit::TestCase
assert_equal 'bar', body assert_equal 'bar', body
end 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 it 'uses templates in superclasses before subclasses' do
base = Class.new(Sinatra::Base) base = Class.new(Sinatra::Base)
base.template(:foo) { 'template in superclass' } base.template(:foo) { 'template in superclass' }