diff --git a/README.de.rdoc b/README.de.rdoc index 7b7e9eee..bd4dd2eb 100644 --- a/README.de.rdoc +++ b/README.de.rdoc @@ -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 diff --git a/README.jp.rdoc b/README.jp.rdoc index ad8250a1..7cf6bbe3 100644 --- a/README.jp.rdoc +++ b/README.jp.rdoc @@ -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 diff --git a/README.rdoc b/README.rdoc index d7562884..0117c3d4 100644 --- a/README.rdoc +++ b/README.rdoc @@ -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 diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index 609b7f28..b33c85db 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -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 diff --git a/test/builder_test.rb b/test/builder_test.rb index 04ab3a58..92a0ffe3 100644 --- a/test/builder_test.rb +++ b/test/builder_test.rb @@ -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 %{\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" diff --git a/test/coffee_test.rb b/test/coffee_test.rb index c71f9d25..f9677ae7 100644 --- a/test/coffee_test.rb +++ b/test/coffee_test.rb @@ -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? diff --git a/test/helper.rb b/test/helper.rb index 70699295..2220ebca 100644 --- a/test/helper.rb +++ b/test/helper.rb @@ -1,4 +1,5 @@ ENV['RACK_ENV'] = 'test' +Encoding.default_external = "UTF-8" if defined? Encoding begin require 'rack' diff --git a/test/helpers_test.rb b/test/helpers_test.rb index b87a2e7f..b0b6d076 100644 --- a/test/helpers_test.rb +++ b/test/helpers_test.rb @@ -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' "

Hello, World

" 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 "

Hello, World

", 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 diff --git a/test/less_test.rb b/test/less_test.rb index f6cd72fd..ef9ac158 100644 --- a/test/less_test.rb +++ b/test/less_test.rb @@ -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? diff --git a/test/routing_test.rb b/test/routing_test.rb index 956f5b64..85961a55 100644 --- a/test/routing_test.rb +++ b/test/routing_test.rb @@ -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 "

Not Found

", response.body end diff --git a/test/sass_test.rb b/test/sass_test.rb index 596e3c45..e1a24329 100644 --- a/test/sass_test.rb +++ b/test/sass_test.rb @@ -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? diff --git a/test/scss_test.rb b/test/scss_test.rb index 7fccb8f2..d1df4919 100644 --- a/test/scss_test.rb +++ b/test/scss_test.rb @@ -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? diff --git a/test/templates_test.rb b/test/templates_test.rb index 3eb7fd34..48f0ec65 100644 --- a/test/templates_test.rb +++ b/test/templates_test.rb @@ -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 "

String Layout!

\n

Hello From String

", body + end + + it 'allows explicitly wrapping layouts around nested templates' do + render_app { render :str, :explicitly_nested, :layout => :layout2 } + assert ok? + assert_equal "

String Layout!

\n

String Layout!

\n

Hello From String

", 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 "

String Layout!

\n

Hello From String

", 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' } diff --git a/test/views/explicitly_nested.str b/test/views/explicitly_nested.str new file mode 100644 index 00000000..a3b0875b --- /dev/null +++ b/test/views/explicitly_nested.str @@ -0,0 +1 @@ +#{render :str, :hello, :layout => :layout2} \ No newline at end of file diff --git a/test/views/hello.str b/test/views/hello.str new file mode 100644 index 00000000..6c121b37 --- /dev/null +++ b/test/views/hello.str @@ -0,0 +1 @@ +

Hello From String

\ No newline at end of file diff --git a/test/views/layout2.str b/test/views/layout2.str new file mode 100644 index 00000000..c6391811 --- /dev/null +++ b/test/views/layout2.str @@ -0,0 +1,2 @@ +

String Layout!

+#{yield} \ No newline at end of file diff --git a/test/views/nested.str b/test/views/nested.str new file mode 100644 index 00000000..603038a4 --- /dev/null +++ b/test/views/nested.str @@ -0,0 +1 @@ +#{render :str, :hello} \ No newline at end of file