sinatra-base 1.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. data/.yardopts +4 -0
  2. data/AUTHORS +15 -0
  3. data/CHANGES +524 -1
  4. data/Gemfile +82 -0
  5. data/LICENSE +1 -1
  6. data/README.de.rdoc +2093 -0
  7. data/README.es.rdoc +2091 -0
  8. data/README.fr.rdoc +2116 -0
  9. data/README.hu.rdoc +607 -0
  10. data/README.jp.rdoc +514 -23
  11. data/README.pt-br.rdoc +647 -0
  12. data/README.pt-pt.rdoc +646 -0
  13. data/README.rdoc +1580 -205
  14. data/README.ru.rdoc +2015 -0
  15. data/README.zh.rdoc +1816 -0
  16. data/Rakefile +110 -44
  17. data/examples/chat.rb +61 -0
  18. data/examples/simple.rb +3 -0
  19. data/examples/stream.ru +26 -0
  20. data/lib/sinatra.rb +0 -3
  21. data/lib/sinatra/base.rb +923 -393
  22. data/lib/sinatra/main.rb +9 -7
  23. data/lib/sinatra/showexceptions.rb +37 -4
  24. data/lib/sinatra/version.rb +3 -0
  25. data/sinatra-base.gemspec +15 -91
  26. data/test/base_test.rb +2 -2
  27. data/test/builder_test.rb +32 -2
  28. data/test/coffee_test.rb +92 -0
  29. data/test/contest.rb +62 -28
  30. data/test/creole_test.rb +65 -0
  31. data/test/delegator_test.rb +162 -0
  32. data/test/encoding_test.rb +20 -0
  33. data/test/erb_test.rb +25 -2
  34. data/test/extensions_test.rb +1 -1
  35. data/test/filter_test.rb +226 -8
  36. data/test/haml_test.rb +8 -2
  37. data/test/helper.rb +47 -0
  38. data/test/helpers_test.rb +1287 -80
  39. data/test/integration/app.rb +62 -0
  40. data/test/integration_helper.rb +208 -0
  41. data/test/integration_test.rb +82 -0
  42. data/test/less_test.rb +36 -6
  43. data/test/liquid_test.rb +59 -0
  44. data/test/mapped_error_test.rb +84 -7
  45. data/test/markaby_test.rb +80 -0
  46. data/test/markdown_test.rb +81 -0
  47. data/test/middleware_test.rb +1 -1
  48. data/test/nokogiri_test.rb +69 -0
  49. data/test/rack_test.rb +45 -0
  50. data/test/radius_test.rb +59 -0
  51. data/test/rdoc_test.rb +66 -0
  52. data/test/readme_test.rb +136 -0
  53. data/test/request_test.rb +13 -1
  54. data/test/response_test.rb +21 -2
  55. data/test/result_test.rb +5 -5
  56. data/test/route_added_hook_test.rb +1 -1
  57. data/test/routing_test.rb +328 -13
  58. data/test/sass_test.rb +48 -18
  59. data/test/scss_test.rb +88 -0
  60. data/test/server_test.rb +4 -3
  61. data/test/settings_test.rb +191 -21
  62. data/test/sinatra_test.rb +5 -1
  63. data/test/slim_test.rb +88 -0
  64. data/test/static_test.rb +89 -5
  65. data/test/streaming_test.rb +140 -0
  66. data/test/templates_test.rb +143 -4
  67. data/test/textile_test.rb +65 -0
  68. data/test/views/a/in_a.str +1 -0
  69. data/test/views/ascii.erb +2 -0
  70. data/test/views/b/in_b.str +1 -0
  71. data/test/views/calc.html.erb +1 -0
  72. data/test/views/explicitly_nested.str +1 -0
  73. data/test/views/hello.coffee +1 -0
  74. data/test/views/hello.creole +1 -0
  75. data/test/views/hello.liquid +1 -0
  76. data/test/views/hello.mab +1 -0
  77. data/test/views/hello.md +1 -0
  78. data/test/views/hello.nokogiri +1 -0
  79. data/test/views/hello.radius +1 -0
  80. data/test/views/hello.rdoc +1 -0
  81. data/test/views/hello.sass +1 -1
  82. data/test/views/hello.scss +3 -0
  83. data/test/views/hello.slim +1 -0
  84. data/test/views/hello.str +1 -0
  85. data/test/views/hello.textile +1 -0
  86. data/test/views/hello.yajl +1 -0
  87. data/test/views/layout2.liquid +2 -0
  88. data/test/views/layout2.mab +2 -0
  89. data/test/views/layout2.nokogiri +3 -0
  90. data/test/views/layout2.radius +2 -0
  91. data/test/views/layout2.slim +3 -0
  92. data/test/views/layout2.str +2 -0
  93. data/test/views/nested.str +1 -0
  94. data/test/views/utf8.erb +2 -0
  95. data/test/yajl_test.rb +80 -0
  96. metadata +126 -91
  97. data/lib/sinatra/tilt.rb +0 -746
  98. data/test/erubis_test.rb +0 -82
  99. data/test/views/error.erubis +0 -3
  100. data/test/views/hello.erubis +0 -1
  101. data/test/views/layout2.erubis +0 -2
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/helper'
1
+ require File.expand_path('../helper', __FILE__)
2
2
 
3
3
  class SinatraTest < Test::Unit::TestCase
4
4
  it 'creates a new Sinatra::Base subclass on new' do
@@ -10,4 +10,8 @@ class SinatraTest < Test::Unit::TestCase
10
10
  end
11
11
  assert_same Sinatra::Base, app.superclass
12
12
  end
13
+
14
+ it "responds to #template_cache" do
15
+ assert_kind_of Tilt::Cache, Sinatra::Base.new!.template_cache
16
+ end
13
17
  end
@@ -0,0 +1,88 @@
1
+ require File.expand_path('../helper', __FILE__)
2
+
3
+ begin
4
+ require 'slim'
5
+
6
+ class SlimTest < Test::Unit::TestCase
7
+ def slim_app(&block)
8
+ mock_app {
9
+ set :views, File.dirname(__FILE__) + '/views'
10
+ get '/', &block
11
+ }
12
+ get '/'
13
+ end
14
+
15
+ it 'renders inline slim strings' do
16
+ slim_app { slim "h1 Hiya\n" }
17
+ assert ok?
18
+ assert_equal "<h1>Hiya</h1>", body
19
+ end
20
+
21
+ it 'renders .slim files in views path' do
22
+ slim_app { slim :hello }
23
+ assert ok?
24
+ assert_equal "<h1>Hello From Slim</h1>", body
25
+ end
26
+
27
+ it "renders with inline layouts" do
28
+ mock_app {
29
+ layout { %(h1\n | THIS. IS. \n == yield.upcase ) }
30
+ get('/') { slim 'em Sparta' }
31
+ }
32
+ get '/'
33
+ assert ok?
34
+ assert_equal "<h1>THIS. IS. <EM>SPARTA</EM></h1>", body
35
+ end
36
+
37
+ it "renders with file layouts" do
38
+ slim_app {
39
+ slim '| Hello World', :layout => :layout2
40
+ }
41
+ assert ok?
42
+ assert_equal "<h1>Slim Layout!</h1><p>Hello World</p>", body
43
+ end
44
+
45
+ it "raises error if template not found" do
46
+ mock_app {
47
+ get('/') { slim :no_such_template }
48
+ }
49
+ assert_raise(Errno::ENOENT) { get('/') }
50
+ end
51
+
52
+ HTML4_DOCTYPE = "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">"
53
+
54
+ it "passes slim options to the slim engine" do
55
+ mock_app { get('/') { slim "x foo='bar'", :attr_wrapper => "'" }}
56
+ get '/'
57
+ assert ok?
58
+ assert_body "<x foo='bar'></x>"
59
+ end
60
+
61
+ it "passes default slim options to the slim engine" do
62
+ mock_app do
63
+ set :slim, :attr_wrapper => "'"
64
+ get('/') { slim "x foo='bar'" }
65
+ end
66
+ get '/'
67
+ assert ok?
68
+ assert_body "<x foo='bar'></x>"
69
+ end
70
+
71
+ it "merges the default slim options with the overrides and passes them to the slim engine" do
72
+ mock_app do
73
+ set :slim, :attr_wrapper => "'"
74
+ get('/') { slim "x foo='bar'" }
75
+ get('/other') { slim "x foo='bar'", :attr_wrapper => '"' }
76
+ end
77
+ get '/'
78
+ assert ok?
79
+ assert_body "<x foo='bar'></x>"
80
+ get '/other'
81
+ assert ok?
82
+ assert_body '<x foo="bar"></x>'
83
+ end
84
+ end
85
+
86
+ rescue LoadError
87
+ warn "#{$!.to_s}: skipping slim tests"
88
+ end
@@ -1,10 +1,10 @@
1
- require File.dirname(__FILE__) + '/helper'
1
+ require File.expand_path('../helper', __FILE__)
2
2
 
3
3
  class StaticTest < Test::Unit::TestCase
4
4
  setup do
5
5
  mock_app {
6
6
  set :static, true
7
- set :public, File.dirname(__FILE__)
7
+ set :public_folder, File.dirname(__FILE__)
8
8
  }
9
9
  end
10
10
 
@@ -36,7 +36,6 @@ class StaticTest < Test::Unit::TestCase
36
36
  head "/#{File.basename(__FILE__)}"
37
37
  assert ok?
38
38
  assert_equal '', body
39
- assert_equal File.size(__FILE__).to_s, response['Content-Length']
40
39
  assert response.headers.include?('Last-Modified')
41
40
  end
42
41
 
@@ -66,7 +65,7 @@ class StaticTest < Test::Unit::TestCase
66
65
  end
67
66
 
68
67
  it 'passes to the next handler when the public option is nil' do
69
- @app.set :public, nil
68
+ @app.set :public_folder, nil
70
69
  get "/#{File.basename(__FILE__)}"
71
70
  assert not_found?
72
71
  end
@@ -85,9 +84,94 @@ class StaticTest < Test::Unit::TestCase
85
84
  it '404s when .. path traverses outside of public directory' do
86
85
  mock_app {
87
86
  set :static, true
88
- set :public, File.dirname(__FILE__) + '/data'
87
+ set :public_folder, File.dirname(__FILE__) + '/data'
89
88
  }
90
89
  get "/../#{File.basename(__FILE__)}"
91
90
  assert not_found?
92
91
  end
92
+
93
+ def assert_valid_range(http_range, range, path, file)
94
+ request = Rack::MockRequest.new(@app)
95
+ response = request.get("/#{File.basename(path)}", 'HTTP_RANGE' => http_range)
96
+
97
+ should_be = file[range]
98
+ expected_range = "bytes #{range.begin}-#{range.end}/#{file.length}"
99
+
100
+ assert_equal 206,response.status, "Should be HTTP/1.1 206 Partial content"
101
+ assert_equal should_be.length, response.body.length, "Unexpected response length for #{http_range}"
102
+ assert_equal should_be, response.body, "Unexpected response data for #{http_range}"
103
+ assert_equal should_be.length.to_s, response['Content-Length'], "Incorrect Content-Length for #{http_range}"
104
+ assert_equal expected_range, response['Content-Range'], "Incorrect Content-Range for #{http_range}"
105
+ end
106
+
107
+ it 'handles valid byte ranges correctly' do
108
+ # Use the biggest file in this dir so we can test ranges > 8k bytes. (StaticFile sends in 8k chunks.)
109
+ path = File.dirname(__FILE__) + '/helpers_test.rb' # currently 16k bytes
110
+ file = File.read(path)
111
+ length = file.length
112
+ assert length > 9000, "The test file #{path} is too short (#{length} bytes) to run these tests"
113
+
114
+ [0..0, 42..88, 1234..1234, 100..9000, 0..(length-1), (length-1)..(length-1)].each do |range|
115
+ assert_valid_range("bytes=#{range.begin}-#{range.end}", range, path, file)
116
+ end
117
+
118
+ [0, 100, length-100, length-1].each do |start|
119
+ assert_valid_range("bytes=#{start}-", (start..length-1), path, file)
120
+ end
121
+
122
+ [1, 100, length-100, length-1, length].each do |range_length|
123
+ assert_valid_range("bytes=-#{range_length}", (length-range_length..length-1), path, file)
124
+ end
125
+
126
+ # Some valid ranges that exceed the length of the file:
127
+ assert_valid_range("bytes=100-999999", (100..length-1), path, file)
128
+ assert_valid_range("bytes=100-#{length}", (100..length-1), path, file)
129
+ assert_valid_range("bytes=-#{length}", (0..length-1), path, file)
130
+ assert_valid_range("bytes=-#{length+1}", (0..length-1), path, file)
131
+ assert_valid_range("bytes=-999999", (0..length-1), path, file)
132
+ end
133
+
134
+ it 'correctly ignores syntactically invalid range requests' do
135
+ # ...and also ignores multi-range requests, which aren't supported yet
136
+ ["bytes=45-40", "bytes=IV-LXVI", "octets=10-20", "bytes=-", "bytes=1-2,3-4"].each do |http_range|
137
+ request = Rack::MockRequest.new(@app)
138
+ response = request.get("/#{File.basename(__FILE__)}", 'HTTP_RANGE' => http_range)
139
+
140
+ assert_equal 200,response.status, "Invalid range '#{http_range}' should be ignored"
141
+ assert_equal nil,response['Content-Range'], "Invalid range '#{http_range}' should be ignored"
142
+ end
143
+ end
144
+
145
+ it 'returns error 416 for unsatisfiable range requests' do
146
+ # An unsatisfiable request is one that specifies a start that's at or past the end of the file.
147
+ length = File.read(__FILE__).length
148
+ ["bytes=888888-", "bytes=888888-999999", "bytes=#{length}-#{length}"].each do |http_range|
149
+ request = Rack::MockRequest.new(@app)
150
+ response = request.get("/#{File.basename(__FILE__)}", 'HTTP_RANGE' => http_range)
151
+
152
+ assert_equal 416,response.status, "Unsatisfiable range '#{http_range}' should return 416"
153
+ assert_equal "bytes */#{length}",response['Content-Range'], "416 response should include actual length"
154
+ end
155
+ end
156
+
157
+ it 'does not include static cache control headers by default' do
158
+ env = Rack::MockRequest.env_for("/#{File.basename(__FILE__)}")
159
+ status, headers, body = @app.call(env)
160
+ assert !headers.has_key?('Cache-Control')
161
+ end
162
+
163
+ it 'sets cache control headers on static files if set' do
164
+ @app.set :static_cache_control, :public
165
+ env = Rack::MockRequest.env_for("/#{File.basename(__FILE__)}")
166
+ status, headers, body = @app.call(env)
167
+ assert headers.has_key?('Cache-Control')
168
+ assert_equal headers['Cache-Control'], 'public'
169
+
170
+ @app.set :static_cache_control, [:public, :must_revalidate, {:max_age => 300}]
171
+ env = Rack::MockRequest.env_for("/#{File.basename(__FILE__)}")
172
+ status, headers, body = @app.call(env)
173
+ assert headers.has_key?('Cache-Control')
174
+ assert_equal headers['Cache-Control'], 'public, must-revalidate, max-age=300'
175
+ end
176
+
93
177
  end
@@ -0,0 +1,140 @@
1
+ require File.expand_path('../helper', __FILE__)
2
+
3
+ class StreamingTest < Test::Unit::TestCase
4
+ Stream = Sinatra::Helpers::Stream
5
+
6
+ it 'returns the concatinated body' do
7
+ mock_app do
8
+ get '/' do
9
+ stream do |out|
10
+ out << "Hello" << " "
11
+ out << "World!"
12
+ end
13
+ end
14
+ end
15
+
16
+ get('/')
17
+ assert_body "Hello World!"
18
+ end
19
+
20
+ it 'always yields strings' do
21
+ stream = Stream.new { |out| out << :foo }
22
+ stream.each { |str| assert_equal 'foo', str }
23
+ end
24
+
25
+ it 'postpones body generation' do
26
+ step = 0
27
+
28
+ stream = Stream.new do |out|
29
+ 10.times do
30
+ out << step
31
+ step += 1
32
+ end
33
+ end
34
+
35
+ stream.each do |s|
36
+ assert_equal s, step.to_s
37
+ step += 1
38
+ end
39
+ end
40
+
41
+ it 'calls the callback after it is done' do
42
+ step = 0
43
+ final = 0
44
+ stream = Stream.new { |o| 10.times { step += 1 }}
45
+ stream.callback { final = step }
46
+ stream.each { |str| }
47
+ assert_equal 10, final
48
+ end
49
+
50
+ it 'does not trigger the callback if close is set to :keep_open' do
51
+ step = 0
52
+ final = 0
53
+ stream = Stream.new(Stream, :keep_open) { |o| 10.times { step += 1 } }
54
+ stream.callback { final = step }
55
+ stream.each { |str| }
56
+ assert_equal 0, final
57
+ end
58
+
59
+ it 'allows adding more than one callback' do
60
+ a = b = false
61
+ stream = Stream.new { }
62
+ stream.callback { a = true }
63
+ stream.callback { b = true }
64
+ stream.each { |str| }
65
+ assert a, 'should trigger first callback'
66
+ assert b, 'should trigger second callback'
67
+ end
68
+
69
+ class MockScheduler
70
+ def initialize(*) @schedule, @defer = [], [] end
71
+ def schedule(&block) @schedule << block end
72
+ def defer(&block) @defer << block end
73
+ def schedule!(*) @schedule.pop.call until @schedule.empty? end
74
+ def defer!(*) @defer.pop.call until @defer.empty? end
75
+ end
76
+
77
+ it 'allows dropping in another scheduler' do
78
+ scheduler = MockScheduler.new
79
+ processing = sending = done = false
80
+
81
+ stream = Stream.new(scheduler) do |out|
82
+ processing = true
83
+ out << :foo
84
+ end
85
+
86
+ stream.each { sending = true}
87
+ stream.callback { done = true }
88
+
89
+ scheduler.schedule!
90
+ assert !processing
91
+ assert !sending
92
+ assert !done
93
+
94
+ scheduler.defer!
95
+ assert processing
96
+ assert !sending
97
+ assert !done
98
+
99
+ scheduler.schedule!
100
+ assert sending
101
+ assert done
102
+ end
103
+
104
+ it 'schedules exceptions to be raised on the main thread/event loop/...' do
105
+ scheduler = MockScheduler.new
106
+ Stream.new(scheduler) { fail 'should be caught' }.each { }
107
+ scheduler.defer!
108
+ assert_raise(RuntimeError) { scheduler.schedule! }
109
+ end
110
+
111
+ it 'does not trigger an infinite loop if you call close in a callback' do
112
+ stream = Stream.new { |out| out.callback { out.close }}
113
+ stream.each { |str| }
114
+ end
115
+
116
+ it 'gives access to route specific params' do
117
+ mock_app do
118
+ get('/:name') { stream { |o| o << params[:name] }}
119
+ end
120
+ get '/foo'
121
+ assert_body 'foo'
122
+ end
123
+
124
+ it 'sets up async.close if available' do
125
+ ran = false
126
+ mock_app do
127
+ get('/') do
128
+ close = Object.new
129
+ def close.callback; yield end
130
+ def close.errback; end
131
+ env['async.close'] = close
132
+ stream(:keep_open) do |out|
133
+ out.callback { ran = true }
134
+ end
135
+ end
136
+ end
137
+ get '/'
138
+ assert ran
139
+ end
140
+ end
@@ -1,9 +1,10 @@
1
- require File.dirname(__FILE__) + '/helper'
1
+ # encoding: UTF-8
2
+ require File.expand_path('../helper', __FILE__)
3
+ File.delete(File.dirname(__FILE__) + '/views/layout.test') rescue nil
2
4
 
3
5
  class TestTemplate < Tilt::Template
4
6
  def prepare
5
7
  end
6
- alias compile! prepare # for tilt < 0.7
7
8
 
8
9
  def evaluate(scope, locals={}, &block)
9
10
  inner = block ? block.call : ''
@@ -14,9 +15,11 @@ class TestTemplate < Tilt::Template
14
15
  end
15
16
 
16
17
  class TemplatesTest < Test::Unit::TestCase
17
- 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
18
20
  mock_app(base) {
19
21
  set :views, File.dirname(__FILE__) + '/views'
22
+ set options
20
23
  get '/', &block
21
24
  template(:layout3) { "Layout 3!\n" }
22
25
  }
@@ -77,12 +80,48 @@ class TemplatesTest < Test::Unit::TestCase
77
80
  assert_equal "Layout 3!\nHello World!\n", body
78
81
  end
79
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
+
104
+ it 'is possible to use partials in layouts' do
105
+ render_app do
106
+ settings.layout { "<%= erb 'foo' %><%= yield %>" }
107
+ erb 'bar'
108
+ end
109
+ assert ok?
110
+ assert_equal "foobar", body
111
+ end
112
+
80
113
  it 'loads templates from source file' do
81
114
  mock_app { enable :inline_templates }
82
115
  assert_equal "this is foo\n\n", @app.templates[:foo][0]
83
116
  assert_equal "X\n= yield\nX\n", @app.templates[:layout][0]
84
117
  end
85
118
 
119
+ it 'ignores spaces after names of inline templates' do
120
+ mock_app { enable :inline_templates }
121
+ assert_equal "There's a space after 'bar'!\n\n", @app.templates[:bar][0]
122
+ assert_equal "this is not foo\n\n", @app.templates[:"foo bar"][0]
123
+ end
124
+
86
125
  it 'loads templates from given source file' do
87
126
  mock_app { set :inline_templates, __FILE__ }
88
127
  assert_equal "this is foo\n\n", @app.templates[:foo][0]
@@ -98,8 +137,14 @@ class TemplatesTest < Test::Unit::TestCase
98
137
  assert @app.templates.empty?
99
138
  end
100
139
 
140
+ it 'allows unicode in inline templates' do
141
+ mock_app { set :inline_templates, __FILE__ }
142
+ assert_equal "Den som tror at hemma det är där man bor har aldrig vart hos mig.\n\n",
143
+ @app.templates[:umlaut][0]
144
+ end
145
+
101
146
  it 'loads templates from specified views directory' do
102
- render_app { render :test, :hello, :views => options.views + '/foo' }
147
+ render_app { render :test, :hello, :views => settings.views + '/foo' }
103
148
 
104
149
  assert_equal "from another views directory\n", body
105
150
  end
@@ -128,6 +173,24 @@ class TemplatesTest < Test::Unit::TestCase
128
173
  assert_equal 'bar', body
129
174
  end
130
175
 
176
+ it 'allows setting default content type per template engine' do
177
+ render_app(:str => { :content_type => :txt }) { render :str, 'foo' }
178
+ assert_equal 'text/plain;charset=utf-8', response['Content-Type']
179
+ end
180
+
181
+ it 'setting default content type does not affect other template engines' do
182
+ render_app(:str => { :content_type => :txt }) { render :test, 'foo' }
183
+ assert_equal 'text/html;charset=utf-8', response['Content-Type']
184
+ end
185
+
186
+ it 'setting default content type per template engine does not override content_type' do
187
+ render_app :str => { :content_type => :txt } do
188
+ content_type :html
189
+ render :str, 'foo'
190
+ end
191
+ assert_equal 'text/html;charset=utf-8', response['Content-Type']
192
+ end
193
+
131
194
  it 'uses templates in superclasses before subclasses' do
132
195
  base = Class.new(Sinatra::Base)
133
196
  base.template(:foo) { 'template in superclass' }
@@ -144,6 +207,73 @@ class TemplatesTest < Test::Unit::TestCase
144
207
  assert ok?
145
208
  assert_equal 'template in subclass', body
146
209
  end
210
+
211
+ it "is possible to use a different engine for the layout than for the template itself explicitely" do
212
+ render_app do
213
+ settings.template(:layout) { 'Hello <%= yield %>!' }
214
+ render :str, "<%= 'World' %>", :layout_engine => :erb
215
+ end
216
+ assert_equal "Hello <%= 'World' %>!", body
217
+ end
218
+
219
+ it "is possible to use a different engine for the layout than for the template itself globally" do
220
+ render_app :str => { :layout_engine => :erb } do
221
+ settings.template(:layout) { 'Hello <%= yield %>!' }
222
+ render :str, "<%= 'World' %>"
223
+ end
224
+ assert_equal "Hello <%= 'World' %>!", body
225
+ end
226
+
227
+ it "does not leak the content type to the template" do
228
+ render_app :str => { :layout_engine => :erb } do
229
+ settings.template(:layout) { 'Hello <%= yield %>!' }
230
+ render :str, "<%= 'World' %>", :content_type => :txt
231
+ end
232
+ assert_equal "text/html;charset=utf-8", headers['Content-Type']
233
+ end
234
+
235
+ it "is possible to register another template" do
236
+ Tilt.register "html.erb", Tilt[:erb]
237
+ render_app { render :erb, :calc }
238
+ assert_equal '2', body
239
+ end
240
+
241
+ it "passes scope to the template" do
242
+ mock_app do
243
+ template :scoped do
244
+ 'Hello <%= foo %>'
245
+ end
246
+
247
+ get '/' do
248
+ some_scope = Object.new
249
+ def some_scope.foo() 'World!' end
250
+ erb :scoped, :scope => some_scope
251
+ end
252
+ end
253
+
254
+ get '/'
255
+ assert ok?
256
+ assert_equal 'Hello World!', body
257
+ end
258
+
259
+ it "is possible to use custom logic for finding template files" do
260
+ mock_app do
261
+ set :views, ["a", "b"].map { |d| File.dirname(__FILE__) + '/views/' + d }
262
+ def find_template(views, name, engine, &block)
263
+ Array(views).each { |v| super(v, name, engine, &block) }
264
+ end
265
+
266
+ get('/:name') do
267
+ render :str, params[:name].to_sym
268
+ end
269
+ end
270
+
271
+ get '/in_a'
272
+ assert_body 'Gimme an A!'
273
+
274
+ get '/in_b'
275
+ assert_body 'Gimme a B!'
276
+ end
147
277
  end
148
278
 
149
279
  # __END__ : this is not the real end of the script.
@@ -153,6 +283,15 @@ __END__
153
283
  @@ foo
154
284
  this is foo
155
285
 
286
+ @@ bar
287
+ There's a space after 'bar'!
288
+
289
+ @@ foo bar
290
+ this is not foo
291
+
292
+ @@ umlaut
293
+ Den som tror at hemma det är där man bor har aldrig vart hos mig.
294
+
156
295
  @@ layout
157
296
  X
158
297
  = yield