sinatra-base 1.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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