Skip to content
This repository

Smart Templates #67

Closed
wants to merge 2 commits into from

2 participants

Konstantin Haase Ryan Sobol
Konstantin Haase
Owner

Imporved templates a bit:

  • Template engines now set default content type (i.e. using sass will default to css). Content types can still be set explicitly and all corner cases should be covered
  • Nested templates don't use implicit layouts anymore. Eases the use of rendering methods for partials.

Those are two separate features, but applying the first without the latter to master results in a merge conflict that does not need to be resolved in case we decide to merge both.

Tests are, of course, included.

added some commits September 19, 2010
Konstantin Haase Skip implicit layouts for nested templates.
That way the following will produce valid HTML:

@@ layout
!!!
= yield

@@ content
%html
  %head= haml :head
  %body= haml :body

That way using render methods for partials is a lot easier.
Tests included.
e5e0047
Konstantin Haase Sets default content type according to template engine used instead o…
…f 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.
1d676f4
Ryan Sobol

Huh? Is this really the best way of adding a content_type accessor to a String instance (I presume) ? +1 clever points at least. :P

Owner

It avoids tons of edge cases (what if you nest different template engines, what if you call haml just for fun and return something different instead). Even though it doesn't seem so at first glance, this is rather noninvasive and OO. By extending output with a mixin we avoid monkey-patching plus get better documentation. But if you have another approach in mind, I'd love to wrap my head around it.

Your rationale seems sound, although I'm still wrestling how we get "better documentation" with this technique. I guess it makes sense if you're referring exclusively to Sinatra contributors. I might have gone with a different technique, encapsulating output and it's meta-data in a first-class object. But this surely would have lead to more code, more tests, more potential bugs, and more time. Your technique is quick and precise, which is essential for the next release.

BTW - Konstantin, you've done a f*cking amazing job with organizing this 1.1 release. I'm really excited about smart templates. When I upgrade Sinatra in my project, I'm going to delete so much code! And my users are going to love having more template language choices! I hope you don't mind a little peer-reviewing from me.

Owner

Thanks. No, it's fine, welcomed even. The reason for not creating a own class is that we had to cover every possibility where such a class could be handed to rack, including using it for streaming (wrapping it in another project) or with tools like async-sinatra, as Rack expects strings. Returning something that just behaves like a string would violate the Rack spec. With documentation I mean as opposed to monkey-patching the string directly. The mixin can actually show up in generated documentation (not that it matters much without comments, but it's a start). If you got more feedback/discussion, keep it coming.

It's no question that compatibility with the Rack spec is paramount (for all Ruby web frameworks). And I would never advocate violating that. Using the technique of a wrapper class, as I've hinted, would require more work to ensure compatibility with Rack (and with async-sinatra for that matter). Right now, I don't think it's worth the time investment.

As far as improving Sinatra's documentation, I wish I had more time to pitch in. :( It's a good thing the source is so readable already. :P I've actually added quite a few techniques into my own toolbox from periodically perusing the source.

This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 2 unique commits by 1 author.

Sep 27, 2010
Konstantin Haase Skip implicit layouts for nested templates.
That way the following will produce valid HTML:

@@ layout
!!!
= yield

@@ content
%html
  %head= haml :head
  %body= haml :body

That way using render methods for partials is a lot easier.
Tests included.
e5e0047
Konstantin Haase Sets default content type according to template engine used instead o…
…f 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.
1d676f4
This page is out of date. Refresh to see the latest.
7  README.de.rdoc
Source Rendered
@@ -229,7 +229,6 @@ Das buidler gem wird benötigt um Builder-Templates rendern zu können:
229 229
   require 'builder'
230 230
 
231 231
   get '/' do
232  
-    content_type 'application/xml', :charset => 'utf-8'
233 232
     builder :index
234 233
   end
235 234
 
@@ -243,7 +242,6 @@ Das haml gem wird benötigt um SASS-Templates rendern zu können:
243 242
   require 'sass'
244 243
 
245 244
   get '/stylesheet.css' do
246  
-    content_type 'text/css', :charset => 'utf-8'
247 245
     sass :stylesheet
248 246
   end
249 247
 
@@ -257,7 +255,6 @@ und individuell überschrieben werden.
257 255
   set :sass, :style => :compact # Standard Sass-Style ist :nested
258 256
 
259 257
   get '/stylesheet.css' do
260  
-    content_type 'text/css', :charset => 'utf-8'
261 258
     sass :stylesheet, :style => :expanded # überschrieben
262 259
   end
263 260
 
@@ -269,7 +266,6 @@ Das haml gem wird benötigt um SCSS-Templates rendern zu können:
269 266
   require 'sass'
270 267
 
271 268
   get '/stylesheet.css' do
272  
-    content_type 'text/css', :charset => 'utf-8'
273 269
     scss :stylesheet
274 270
   end
275 271
 
@@ -283,7 +279,6 @@ und individuell überschrieben werden.
283 279
   set :scss, :style => :compact # Standard Scss-Style ist :nested
284 280
 
285 281
   get '/stylesheet.css' do
286  
-    content_type 'text/css', :charset => 'utf-8'
287 282
     scss :stylesheet, :style => :expanded # überschrieben
288 283
   end
289 284
 
@@ -295,7 +290,6 @@ Das less gem wird benötigt um Less-Templates rendern zu können:
295 290
   require 'less'
296 291
 
297 292
   get '/stylesheet.css' do
298  
-    content_type 'text/css', :charset => 'utf-8'
299 293
     less :stylesheet
300 294
   end
301 295
 
@@ -434,7 +428,6 @@ Das coffee-script gem und das `coffee`-Programm werden benötigt um CoffeScript-
434 428
   require 'coffee-script'
435 429
 
436 430
   get '/application.js' do
437  
-    content_type 'text/javascript', :charset => 'utf-8'
438 431
     coffee :application
439 432
   end
440 433
 
3  README.jp.rdoc
Source Rendered
@@ -154,7 +154,6 @@ builderを使うにはbuilderライブラリが必要です:
154 154
   require 'builder'
155 155
 
156 156
   get '/' do
157  
-    content_type 'application/xml', :charset => 'utf-8'
158 157
     builder :index
159 158
   end
160 159
 
@@ -168,7 +167,6 @@ Sassテンプレートを使うにはsassライブラリが必要です:
168 167
   require 'sass'
169 168
 
170 169
   get '/stylesheet.css' do
171  
-    content_type 'text/css', :charset => 'utf-8'
172 170
     sass :stylesheet
173 171
   end
174 172
 
@@ -182,7 +180,6 @@ see {Options and Configurations}[http://www.sinatrarb.com/configuration.html],
182 180
   set :sass, {:style => :compact } # デフォルトのSass styleは :nested
183 181
 
184 182
   get '/stylesheet.css' do
185  
-    content_type 'text/css', :charset => 'utf-8'
186 183
     sass :stylesheet, :sass_options => {:style => :expanded } # 上書き
187 184
   end
188 185
 
7  README.rdoc
Source Rendered
@@ -225,7 +225,6 @@ The builder gem/library is required to render builder templates:
225 225
   require 'builder'
226 226
 
227 227
   get '/' do
228  
-    content_type 'application/xml', :charset => 'utf-8'
229 228
     builder :index
230 229
   end
231 230
 
@@ -239,7 +238,6 @@ The sass gem/library is required to render Sass templates:
239 238
   require 'sass'
240 239
 
241 240
   get '/stylesheet.css' do
242  
-    content_type 'text/css', :charset => 'utf-8'
243 241
     sass :stylesheet
244 242
   end
245 243
 
@@ -253,7 +251,6 @@ and overridden on an individual basis.
253 251
   set :sass, :style => :compact # default Sass style is :nested
254 252
 
255 253
   get '/stylesheet.css' do
256  
-    content_type 'text/css', :charset => 'utf-8'
257 254
     sass :stylesheet, :style => :expanded # overridden
258 255
   end
259 256
 
@@ -265,7 +262,6 @@ The sass gem/library is required to render Scss templates:
265 262
   require 'sass'
266 263
 
267 264
   get '/stylesheet.css' do
268  
-    content_type 'text/css', :charset => 'utf-8'
269 265
     scss :stylesheet
270 266
   end
271 267
 
@@ -279,7 +275,6 @@ and overridden on an individual basis.
279 275
   set :scss, :style => :compact # default Scss style is :nested
280 276
 
281 277
   get '/stylesheet.css' do
282  
-    content_type 'text/css', :charset => 'utf-8'
283 278
     scss :stylesheet, :style => :expanded # overridden
284 279
   end
285 280
 
@@ -291,7 +286,6 @@ The less gem/library is required to render Less templates:
291 286
   require 'less'
292 287
 
293 288
   get '/stylesheet.css' do
294  
-    content_type 'text/css', :charset => 'utf-8'
295 289
     less :stylesheet
296 290
   end
297 291
 
@@ -422,7 +416,6 @@ CoffeeScript templates:
422 416
   require 'coffee-script'
423 417
 
424 418
   get '/application.js' do
425  
-    content_type 'text/javascript', :charset => 'utf-8'
426 419
     coffee :application
427 420
   end
428 421
 
46  lib/sinatra/base.rb
@@ -77,10 +77,12 @@ def status(value=nil)
77 77
     # evaluation is deferred until the body is read with #each.
78 78
     def body(value=nil, &block)
79 79
       if block_given?
80  
-        def block.each ; yield call ; end
  80
+        def block.each; yield(call) end
81 81
         response.body = block
82  
-      else
  82
+      elsif value
83 83
         response.body = value
  84
+      else
  85
+        response.body
84 86
       end
85 87
     end
86 88
 
@@ -137,6 +139,7 @@ def mime_type(type)
137 139
     def content_type(type, params={})
138 140
       mime_type = mime_type(type)
139 141
       fail "Unknown media type: %p" % type if mime_type.nil?
  142
+      params[:charset] ||= defined?(Encoding) ? Encoding.default_external.to_s.downcase : 'utf-8'
140 143
       if params.any?
141 144
         params = params.collect { |kv| "%s=%s" % kv }.join(', ')
142 145
         response['Content-Type'] = [mime_type, params].join(";")
@@ -300,6 +303,10 @@ def back ; request.referer ; end
300 303
   #   :locals       A hash with local variables that should be available
301 304
   #                 in the template
302 305
   module Templates
  306
+    module ContentTyped
  307
+      attr_accessor :content_type
  308
+    end
  309
+
303 310
     include Tilt::CompileSite
304 311
 
305 312
     def erb(template, options={}, locals={})
@@ -315,21 +322,22 @@ def haml(template, options={}, locals={})
315 322
     end
316 323
 
317 324
     def sass(template, options={}, locals={})
318  
-      options[:layout] = false
  325
+      options.merge! :layout => false, :default_content_type => :css
319 326
       render :sass, template, options, locals
320 327
     end
321 328
 
322 329
     def scss(template, options={}, locals={})
323  
-      options[:layout] = false
  330
+      options.merge! :layout => false, :default_content_type => :css
324 331
       render :scss, template, options, locals
325 332
     end
326 333
 
327 334
     def less(template, options={}, locals={})
328  
-      options[:layout] = false
  335
+      options.merge! :layout => false, :default_content_type => :css
329 336
       render :less, template, options, locals
330 337
     end
331 338
 
332 339
     def builder(template=nil, options={}, locals={}, &block)
  340
+      options[:default_content_type] = :xml
333 341
       options, template = template, nil if template.is_a?(Hash)
334 342
       template = Proc.new { block } if template.nil?
335 343
       render :builder, template, options, locals
@@ -360,7 +368,7 @@ def markaby(template, options={}, locals={})
360 368
     end
361 369
 
362 370
     def coffee(template, options={}, locals={})
363  
-      options[:layout] = false
  371
+      options.merge! :layout => false, :default_content_type => :js
364 372
       render :coffee, template, options, locals
365 373
     end
366 374
 
@@ -371,14 +379,19 @@ def render(engine, data, options={}, locals={}, &block)
371 379
       options[:outvar] ||= '@_out_buf'
372 380
 
373 381
       # extract generic options
374  
-      locals = options.delete(:locals) || locals || {}
375  
-      views = options.delete(:views) || settings.views || "./views"
376  
-      layout = options.delete(:layout)
377  
-      layout = :layout if layout.nil? || layout == true
  382
+      locals          = options.delete(:locals) || locals         || {}
  383
+      views           = options.delete(:views)  || settings.views || "./views"
  384
+      @default_layout = :layout if @default_layout.nil?
  385
+      layout          = options.delete(:layout)
  386
+      layout          = @default_layout if layout.nil? or layout == true
  387
+      content_type    = options.delete(:content_type) || options.delete(:default_content_type)
378 388
 
379 389
       # compile and render template
380  
-      template = compile_template(engine, data, options, views)
381  
-      output = template.render(self, locals, &block)
  390
+      layout_was      = @default_layout
  391
+      @default_layout = false if layout
  392
+      template        = compile_template(engine, data, options, views)
  393
+      output          = template.render(self, locals, &block)
  394
+      @default_layout = layout_was
382 395
 
383 396
       # render layout
384 397
       if layout
@@ -389,6 +402,7 @@ def render(engine, data, options={}, locals={}, &block)
389 402
         end
390 403
       end
391 404
 
  405
+      output.extend(ContentTyped).content_type = content_type if content_type
392 406
       output
393 407
     end
394 408
 
@@ -453,8 +467,16 @@ def call!(env) # :nodoc:
453 467
       template_cache.clear if settings.reload_templates
454 468
       force_encoding(@params)
455 469
 
  470
+      @response['Content-Type'] = nil
456 471
       invoke { dispatch! }
457 472
       invoke { error_block!(response.status) }
  473
+      unless @response['Content-Type']
  474
+        if body.respond_to?(:to_ary) and body.first.respond_to? :content_type
  475
+          content_type body.first.content_type
  476
+        else
  477
+          content_type :html
  478
+        end
  479
+      end
458 480
 
459 481
       status, header, body = @response.finish
460 482
 
26  test/builder_test.rb
@@ -2,9 +2,10 @@
2 2
 require 'builder'
3 3
 
4 4
 class BuilderTest < Test::Unit::TestCase
5  
-  def builder_app(&block)
  5
+  def builder_app(options = {}, &block)
6 6
     mock_app {
7 7
       set :views, File.dirname(__FILE__) + '/views'
  8
+      set options
8 9
       get '/', &block
9 10
     }
10 11
     get '/'
@@ -16,6 +17,29 @@ def builder_app(&block)
16 17
     assert_equal %{<?xml version="1.0" encoding="UTF-8"?>\n}, body
17 18
   end
18 19
 
  20
+  it 'defaults content type to xml' do
  21
+    builder_app { builder 'xml.instruct!' }
  22
+    assert ok?
  23
+    assert_equal "application/xml;charset=utf-8", response['Content-Type']
  24
+  end
  25
+
  26
+  it 'defaults allows setting content type per route' do
  27
+    builder_app do
  28
+      content_type :html
  29
+      builder 'xml.instruct!'
  30
+    end
  31
+    assert ok?
  32
+    assert_equal "text/html;charset=utf-8", response['Content-Type']
  33
+  end
  34
+
  35
+  it 'defaults allows setting content type globally' do
  36
+    builder_app(:builder => { :content_type => 'html' }) do
  37
+      builder 'xml.instruct!'
  38
+    end
  39
+    assert ok?
  40
+    assert_equal "text/html;charset=utf-8", response['Content-Type']
  41
+  end
  42
+
19 43
   it 'renders inline blocks' do
20 44
     builder_app {
21 45
       @name = "Frank & Mary"
26  test/coffee_test.rb
@@ -4,9 +4,10 @@
4 4
 require 'coffee-script'
5 5
 
6 6
 class CoffeeTest < Test::Unit::TestCase
7  
-  def coffee_app(&block)
  7
+  def coffee_app(options = {}, &block)
8 8
     mock_app {
9 9
       set :views, File.dirname(__FILE__) + '/views'
  10
+      set(options)
10 11
       get '/', &block
11 12
     }
12 13
     get '/'
@@ -18,6 +19,29 @@ def coffee_app(&block)
18 19
     assert_equal "(function() {\n  alert('Aye!');\n})();\n", body
19 20
   end
20 21
 
  22
+  it 'defaults content type to javascript' do
  23
+    coffee_app { coffee "alert 'Aye!'\n" }
  24
+    assert ok?
  25
+    assert_equal "application/javascript;charset=utf-8", response['Content-Type']
  26
+  end
  27
+
  28
+  it 'defaults allows setting content type per route' do
  29
+    coffee_app do
  30
+      content_type :html
  31
+      coffee "alert 'Aye!'\n"
  32
+    end
  33
+    assert ok?
  34
+    assert_equal "text/html;charset=utf-8", response['Content-Type']
  35
+  end
  36
+
  37
+  it 'defaults allows setting content type globally' do
  38
+    coffee_app(:coffee => { :content_type => 'html' }) do
  39
+      coffee "alert 'Aye!'\n"
  40
+    end
  41
+    assert ok?
  42
+    assert_equal "text/html;charset=utf-8", response['Content-Type']
  43
+  end
  44
+
21 45
   it 'renders .coffee files in views path' do
22 46
     coffee_app { coffee :hello }
23 47
     assert ok?
1  test/helper.rb
... ...
@@ -1,4 +1,5 @@
1 1
 ENV['RACK_ENV'] = 'test'
  2
+Encoding.default_external = "UTF-8" if defined? Encoding
2 3
 
3 4
 begin
4 5
   require 'rack'
14  test/helpers_test.rb
@@ -291,21 +291,21 @@ def test_default
291 291
       }
292 292
 
293 293
       get '/'
294  
-      assert_equal 'text/plain', response['Content-Type']
  294
+      assert_equal 'text/plain;charset=utf-8', response['Content-Type']
295 295
       assert_equal 'Hello World', body
296 296
     end
297 297
 
298 298
     it 'takes media type parameters (like charset=)' do
299 299
       mock_app {
300 300
         get '/' do
301  
-          content_type 'text/html', :charset => 'utf-8'
  301
+          content_type 'text/html', :charset => 'latin1'
302 302
           "<h1>Hello, World</h1>"
303 303
         end
304 304
       }
305 305
 
306 306
       get '/'
307 307
       assert ok?
308  
-      assert_equal 'text/html;charset=utf-8', response['Content-Type']
  308
+      assert_equal 'text/html;charset=latin1', response['Content-Type']
309 309
       assert_equal "<h1>Hello, World</h1>", body
310 310
     end
311 311
 
@@ -320,7 +320,7 @@ def test_default
320 320
 
321 321
       get '/foo.xml'
322 322
       assert ok?
323  
-      assert_equal 'application/foo', response['Content-Type']
  323
+      assert_equal 'application/foo;charset=utf-8', response['Content-Type']
324 324
       assert_equal 'I AM FOO', body
325 325
     end
326 326
 
@@ -366,19 +366,19 @@ def send_file_app(opts={})
366 366
     it 'sets the Content-Type response header if a mime-type can be located' do
367 367
       send_file_app
368 368
       get '/file.txt'
369  
-      assert_equal 'text/plain', response['Content-Type']
  369
+      assert_equal 'text/plain;charset=utf-8', response['Content-Type']
370 370
     end
371 371
 
372 372
     it 'sets the Content-Type response header if type option is set to a file extesion' do
373 373
       send_file_app :type => 'html'
374 374
       get '/file.txt'
375  
-      assert_equal 'text/html', response['Content-Type']
  375
+      assert_equal 'text/html;charset=utf-8', response['Content-Type']
376 376
     end
377 377
 
378 378
     it 'sets the Content-Type response header if type option is set to a mime type' do
379 379
       send_file_app :type => 'application/octet-stream'
380 380
       get '/file.txt'
381  
-      assert_equal 'application/octet-stream', response['Content-Type']
  381
+      assert_equal 'application/octet-stream;charset=utf-8', response['Content-Type']
382 382
     end
383 383
 
384 384
     it 'sets the Content-Length response header' do
28  test/less_test.rb
@@ -2,20 +2,44 @@
2 2
 require 'less'
3 3
 
4 4
 class LessTest < Test::Unit::TestCase
5  
-  def less_app(&block)
  5
+  def less_app(options = {}, &block)
6 6
     mock_app {
7 7
       set :views, File.dirname(__FILE__) + '/views'
  8
+      set options
8 9
       get '/', &block
9 10
     }
10 11
     get '/'
11 12
   end
12 13
 
13 14
   it 'renders inline Less strings' do
14  
-    less_app { less "@white_color: #fff; #main { background-color: @white_color }"}
  15
+    less_app { less "@white_color: #fff; #main { background-color: @white_color }" }
15 16
     assert ok?
16 17
     assert_equal "#main { background-color: #ffffff; }\n", body
17 18
   end
18 19
 
  20
+  it 'defaults content type to css' do
  21
+    less_app { less "@white_color: #fff; #main { background-color: @white_color }" }
  22
+    assert ok?
  23
+    assert_equal "text/css;charset=utf-8", response['Content-Type']
  24
+  end
  25
+
  26
+  it 'defaults allows setting content type per route' do
  27
+    less_app do
  28
+      content_type :html
  29
+      less "@white_color: #fff; #main { background-color: @white_color }"
  30
+    end
  31
+    assert ok?
  32
+    assert_equal "text/html;charset=utf-8", response['Content-Type']
  33
+  end
  34
+
  35
+  it 'defaults allows setting content type globally' do
  36
+    less_app(:less => { :content_type => 'html' }) do
  37
+      less "@white_color: #fff; #main { background-color: @white_color }"
  38
+    end
  39
+    assert ok?
  40
+    assert_equal "text/html;charset=utf-8", response['Content-Type']
  41
+  end
  42
+
19 43
   it 'renders .less files in views path' do
20 44
     less_app { less :hello }
21 45
     assert ok?
2  test/routing_test.rb
@@ -80,7 +80,7 @@ class RoutingTest < Test::Unit::TestCase
80 80
 
81 81
     get '/foo'
82 82
     assert_equal 404, status
83  
-    assert_equal 'text/html', response["Content-Type"]
  83
+    assert_equal 'text/html;charset=utf-8', response["Content-Type"]
84 84
     assert_equal "<h1>Not Found</h1>", response.body
85 85
   end
86 86
 
26  test/sass_test.rb
@@ -4,9 +4,10 @@
4 4
 require 'sass'
5 5
 
6 6
 class SassTest < Test::Unit::TestCase
7  
-  def sass_app(&block)
  7
+  def sass_app(options = {}, &block)
8 8
     mock_app {
9 9
       set :views, File.dirname(__FILE__) + '/views'
  10
+      set options
10 11
       get '/', &block
11 12
     }
12 13
     get '/'
@@ -18,6 +19,29 @@ def sass_app(&block)
18 19
     assert_equal "#sass {\n  background-color: white; }\n", body
19 20
   end
20 21
 
  22
+  it 'defaults content type to css' do
  23
+    sass_app { sass "#sass\n  :background-color white\n" }
  24
+    assert ok?
  25
+    assert_equal "text/css;charset=utf-8", response['Content-Type']
  26
+  end
  27
+
  28
+  it 'defaults allows setting content type per route' do
  29
+    sass_app do
  30
+      content_type :html
  31
+      sass "#sass\n  :background-color white\n"
  32
+    end
  33
+    assert ok?
  34
+    assert_equal "text/html;charset=utf-8", response['Content-Type']
  35
+  end
  36
+
  37
+  it 'defaults allows setting content type globally' do
  38
+    sass_app(:sass => { :content_type => 'html' }) do
  39
+      sass "#sass\n  :background-color white\n"
  40
+    end
  41
+    assert ok?
  42
+    assert_equal "text/html;charset=utf-8", response['Content-Type']
  43
+  end
  44
+
21 45
   it 'renders .sass files in views path' do
22 46
     sass_app { sass :hello }
23 47
     assert ok?
26  test/scss_test.rb
@@ -4,9 +4,10 @@
4 4
 require 'sass'
5 5
 
6 6
 class ScssTest < Test::Unit::TestCase
7  
-  def scss_app(&block)
  7
+  def scss_app(options = {}, &block)
8 8
     mock_app {
9 9
       set :views, File.dirname(__FILE__) + '/views'
  10
+      set options
10 11
       get '/', &block
11 12
     }
12 13
     get '/'
@@ -18,6 +19,29 @@ def scss_app(&block)
18 19
     assert_equal "#scss {\n  background-color: white; }\n", body
19 20
   end
20 21
 
  22
+  it 'defaults content type to css' do
  23
+    scss_app { scss "#scss {\n  background-color: white; }\n" }
  24
+    assert ok?
  25
+    assert_equal "text/css;charset=utf-8", response['Content-Type']
  26
+  end
  27
+
  28
+  it 'defaults allows setting content type per route' do
  29
+    scss_app do
  30
+      content_type :html
  31
+      scss "#scss {\n  background-color: white; }\n"
  32
+    end
  33
+    assert ok?
  34
+    assert_equal "text/html;charset=utf-8", response['Content-Type']
  35
+  end
  36
+
  37
+  it 'defaults allows setting content type globally' do
  38
+    scss_app(:scss => { :content_type => 'html' }) do
  39
+      scss "#scss {\n  background-color: white; }\n"
  40
+    end
  41
+    assert ok?
  42
+    assert_equal "text/html;charset=utf-8", response['Content-Type']
  43
+  end
  44
+
21 45
   it 'renders .scss files in views path' do
22 46
     scss_app { scss :hello }
23 47
     assert ok?
43  test/templates_test.rb
@@ -15,9 +15,11 @@ def evaluate(scope, locals={}, &block)
15 15
 end
16 16
 
17 17
 class TemplatesTest < Test::Unit::TestCase
18  
-  def render_app(base=Sinatra::Base, &block)
  18
+  def render_app(base=Sinatra::Base, options = {}, &block)
  19
+    base, options = Sinatra::Base, base if base.is_a? Hash
19 20
     mock_app(base) {
20 21
       set :views, File.dirname(__FILE__) + '/views'
  22
+      set options
21 23
       get '/', &block
22 24
       template(:layout3) { "Layout 3!\n" }
23 25
     }
@@ -78,6 +80,27 @@ def with_default_layout
78 80
     assert_equal "Layout 3!\nHello World!\n", body
79 81
   end
80 82
 
  83
+  it 'avoids wrapping layouts around nested templates' do
  84
+    render_app { render :str, :nested, :layout => :layout2 }
  85
+    assert ok?
  86
+    assert_equal "<h1>String Layout!</h1>\n<content><h1>Hello From String</h1></content>", body
  87
+  end
  88
+
  89
+  it 'allows explicitly wrapping layouts around nested templates' do
  90
+    render_app { render :str, :explicitly_nested, :layout => :layout2 }
  91
+    assert ok?
  92
+    assert_equal "<h1>String Layout!</h1>\n<content><h1>String Layout!</h1>\n<h1>Hello From String</h1></content>", body
  93
+  end
  94
+
  95
+  it 'two independent render calls do not disable layouts' do
  96
+    render_app do
  97
+      render :str, :explicitly_nested, :layout => :layout2
  98
+      render :str, :nested, :layout => :layout2
  99
+    end
  100
+    assert ok?
  101
+    assert_equal "<h1>String Layout!</h1>\n<content><h1>Hello From String</h1></content>", body
  102
+  end
  103
+
81 104
   it 'loads templates from source file' do
82 105
     mock_app { enable :inline_templates }
83 106
     assert_equal "this is foo\n\n", @app.templates[:foo][0]
@@ -135,6 +158,24 @@ def with_default_layout
135 158
     assert_equal 'bar', body
136 159
   end
137 160
 
  161
+  it 'allows setting default content type per template engine' do
  162
+    render_app(:str => { :content_type => :txt }) { render :str, 'foo' }
  163
+    assert_equal 'text/plain;charset=utf-8', response['Content-Type']
  164
+  end
  165
+
  166
+  it 'setting default content type does not affect other template engines' do
  167
+    render_app(:str => { :content_type => :txt }) { render :test, 'foo' }
  168
+    assert_equal 'text/html;charset=utf-8', response['Content-Type']
  169
+  end
  170
+
  171
+  it 'setting default content type per template engine does not override content_type' do
  172
+    render_app :str => { :content_type => :txt } do
  173
+      content_type :html
  174
+      render :str, 'foo'
  175
+    end
  176
+    assert_equal 'text/html;charset=utf-8', response['Content-Type']
  177
+  end
  178
+
138 179
   it 'uses templates in superclasses before subclasses' do
139 180
     base = Class.new(Sinatra::Base)
140 181
     base.template(:foo) { 'template in superclass' }
1  test/views/explicitly_nested.str
... ...
@@ -0,0 +1 @@
  1
+<content>#{render :str, :hello, :layout => :layout2}</content>
1  test/views/hello.str
... ...
@@ -0,0 +1 @@
  1
+<h1>Hello From String</h1>
2  test/views/layout2.str
... ...
@@ -0,0 +1,2 @@
  1
+<h1>String Layout!</h1>
  2
+#{yield}
1  test/views/nested.str
... ...
@@ -0,0 +1 @@
  1
+<content>#{render :str, :hello}</content>
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.