sinatra 1.3.6 → 1.4.0.a

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sinatra might be problematic. Click here for more details.

Files changed (71) hide show
  1. data/CHANGES +96 -22
  2. data/Gemfile +11 -3
  3. data/README.de.md +2590 -0
  4. data/README.es.rdoc +66 -38
  5. data/README.fr.md +2630 -0
  6. data/README.hu.rdoc +3 -2
  7. data/README.jp.rdoc +16 -3
  8. data/README.ko.rdoc +11 -5
  9. data/README.md +2699 -0
  10. data/README.pt-br.rdoc +152 -21
  11. data/README.pt-pt.rdoc +3 -2
  12. data/README.ru.md +2724 -0
  13. data/README.zh.rdoc +3 -3
  14. data/Rakefile +3 -4
  15. data/examples/chat.rb +3 -3
  16. data/lib/sinatra/base.rb +433 -247
  17. data/lib/sinatra/main.rb +4 -2
  18. data/lib/sinatra/showexceptions.rb +6 -1
  19. data/lib/sinatra/version.rb +1 -1
  20. data/test/base_test.rb +21 -9
  21. data/test/builder_test.rb +15 -19
  22. data/test/coffee_test.rb +4 -6
  23. data/test/compile_test.rb +154 -0
  24. data/test/contest.rb +4 -6
  25. data/test/creole_test.rb +5 -5
  26. data/test/delegator_test.rb +1 -3
  27. data/test/erb_test.rb +32 -20
  28. data/test/extensions_test.rb +1 -3
  29. data/test/filter_test.rb +65 -56
  30. data/test/haml_test.rb +34 -26
  31. data/test/helpers_test.rb +331 -221
  32. data/test/integration_helper.rb +8 -0
  33. data/test/integration_test.rb +3 -1
  34. data/test/less_test.rb +10 -8
  35. data/test/liquid_test.rb +22 -4
  36. data/test/mapped_error_test.rb +122 -96
  37. data/test/markaby_test.rb +5 -5
  38. data/test/markdown_test.rb +5 -5
  39. data/test/middleware_test.rb +3 -3
  40. data/test/nokogiri_test.rb +4 -6
  41. data/test/rabl_test.rb +89 -0
  42. data/test/radius_test.rb +4 -4
  43. data/test/rdoc_test.rb +7 -7
  44. data/test/readme_test.rb +14 -30
  45. data/test/request_test.rb +15 -0
  46. data/test/response_test.rb +3 -4
  47. data/test/result_test.rb +11 -33
  48. data/test/route_added_hook_test.rb +10 -10
  49. data/test/routing_test.rb +123 -1
  50. data/test/sass_test.rb +26 -26
  51. data/test/scss_test.rb +16 -16
  52. data/test/server_test.rb +2 -2
  53. data/test/settings_test.rb +48 -4
  54. data/test/sinatra_test.rb +2 -7
  55. data/test/slim_test.rb +37 -23
  56. data/test/static_test.rb +56 -15
  57. data/test/streaming_test.rb +11 -2
  58. data/test/templates_test.rb +117 -45
  59. data/test/textile_test.rb +9 -9
  60. data/test/views/hello.rabl +2 -0
  61. data/test/views/hello.wlang +1 -0
  62. data/test/views/hello.yajl +1 -0
  63. data/test/views/layout2.rabl +3 -0
  64. data/test/views/layout2.wlang +2 -0
  65. data/test/wlang_test.rb +87 -0
  66. data/test/yajl_test.rb +86 -0
  67. metadata +27 -17
  68. data/README.de.rdoc +0 -2097
  69. data/README.fr.rdoc +0 -2036
  70. data/README.rdoc +0 -2017
  71. data/README.ru.rdoc +0 -1785
@@ -14,7 +14,7 @@ module Sinatra
14
14
  require 'optparse'
15
15
  OptionParser.new { |op|
16
16
  op.on('-p port', 'set the port (default is 4567)') { |val| set :port, Integer(val) }
17
- op.on('-o addr', 'set the host (default is 0.0.0.0)') { |val| set :bind, val }
17
+ op.on('-o addr', "set the host (default is #{bind})") { |val| set :bind, val }
18
18
  op.on('-e env', 'set the environment (default is development)') { |val| set :environment, val.to_sym }
19
19
  op.on('-s server', 'specify rack server/handler (default is thin)') { |val| set :server, val }
20
20
  op.on('-x', 'turn on the mutex lock (default is off)') { set :lock, true }
@@ -25,4 +25,6 @@ module Sinatra
25
25
  at_exit { Application.run! if $!.nil? && Application.run? }
26
26
  end
27
27
 
28
- include Sinatra::Delegator
28
+ # include would include the module in Object
29
+ # extend only extends the `main` object
30
+ extend Sinatra::Delegator
@@ -22,7 +22,7 @@ module Sinatra
22
22
  rescue Exception => e
23
23
  errors, env["rack.errors"] = env["rack.errors"], @@eats_errors
24
24
 
25
- if respond_to?(:prefers_plain_text?) and prefers_plain_text?(env)
25
+ if prefers_plain_text?(env)
26
26
  content_type = "text/plain"
27
27
  body = [dump_exception(e)]
28
28
  else
@@ -40,6 +40,11 @@ module Sinatra
40
40
 
41
41
  private
42
42
 
43
+ def prefers_plain_text?(env)
44
+ !(Request.new(env).preferred_type("text/plain","text/html") == "text/html") &&
45
+ [/curl/].index{|item| item =~ env["HTTP_USER_AGENT"]}
46
+ end
47
+
43
48
  def frame_class(frame)
44
49
  if frame.filename =~ /lib\/sinatra.*\.rb/
45
50
  "framework"
@@ -1,3 +1,3 @@
1
1
  module Sinatra
2
- VERSION = '1.3.6'
2
+ VERSION = '1.4.0.a'
3
3
  end
@@ -7,9 +7,7 @@ class BaseTest < Test::Unit::TestCase
7
7
 
8
8
  describe 'Sinatra::Base subclasses' do
9
9
  class TestApp < Sinatra::Base
10
- get '/' do
11
- 'Hello World'
12
- end
10
+ get('/') { 'Hello World' }
13
11
  end
14
12
 
15
13
  it 'include Rack::Utils' do
@@ -56,6 +54,24 @@ class BaseTest < Test::Unit::TestCase
56
54
  end
57
55
  end
58
56
 
57
+ describe "Sinatra::Base#new" do
58
+ it 'returns a wrapper' do
59
+ assert_equal Sinatra::Wrapper, Sinatra::Base.new.class
60
+ end
61
+
62
+ it 'implements a nice inspect' do
63
+ assert_equal '#<Sinatra::Base app_file=nil>', Sinatra::Base.new.inspect
64
+ end
65
+
66
+ it 'exposes settings' do
67
+ assert_equal Sinatra::Base.settings, Sinatra::Base.new.settings
68
+ end
69
+
70
+ it 'expses helpers' do
71
+ assert_equal 'image/jpeg', Sinatra::Base.new.helpers.mime_type(:jpg)
72
+ end
73
+ end
74
+
59
75
  describe "Sinatra::Base as Rack middleware" do
60
76
  app = lambda { |env|
61
77
  headers = {'X-Downstream' => 'true'}
@@ -81,9 +97,7 @@ class BaseTest < Test::Unit::TestCase
81
97
  super
82
98
  end
83
99
 
84
- get '/' do
85
- 'Hello from middleware'
86
- end
100
+ get('/') { 'Hello from middleware' }
87
101
  end
88
102
 
89
103
  middleware = TestMiddleware.new(app)
@@ -107,9 +121,7 @@ class BaseTest < Test::Unit::TestCase
107
121
  end
108
122
 
109
123
  class TestMiddleware < Sinatra::Base
110
- get '/low-level-forward' do
111
- app.call(env)
112
- end
124
+ get('/low-level-forward') { app.call(env) }
113
125
  end
114
126
 
115
127
  it 'can call the downstream app directly and return result' do
@@ -5,11 +5,11 @@ require 'builder'
5
5
 
6
6
  class BuilderTest < Test::Unit::TestCase
7
7
  def builder_app(options = {}, &block)
8
- mock_app {
8
+ mock_app do
9
9
  set :views, File.dirname(__FILE__) + '/views'
10
10
  set options
11
- get '/', &block
12
- }
11
+ get('/', &block)
12
+ end
13
13
  get '/'
14
14
  end
15
15
 
@@ -43,49 +43,45 @@ class BuilderTest < Test::Unit::TestCase
43
43
  end
44
44
 
45
45
  it 'renders inline blocks' do
46
- builder_app {
46
+ builder_app do
47
47
  @name = "Frank & Mary"
48
- builder do |xml|
49
- xml.couple @name
50
- end
51
- }
48
+ builder { |xml| xml.couple @name }
49
+ end
52
50
  assert ok?
53
51
  assert_equal "<couple>Frank &amp; Mary</couple>\n", body
54
52
  end
55
53
 
56
54
  it 'renders .builder files in views path' do
57
- builder_app {
55
+ builder_app do
58
56
  @name = "Blue"
59
57
  builder :hello
60
- }
58
+ end
61
59
  assert ok?
62
60
  assert_equal %(<exclaim>You're my boy, Blue!</exclaim>\n), body
63
61
  end
64
62
 
65
63
  it "renders with inline layouts" do
66
- mock_app {
67
- layout do
68
- %(xml.layout { xml << yield })
69
- end
64
+ mock_app do
65
+ layout { %(xml.layout { xml << yield }) }
70
66
  get('/') { builder %(xml.em 'Hello World') }
71
- }
67
+ end
72
68
  get '/'
73
69
  assert ok?
74
70
  assert_equal "<layout>\n<em>Hello World</em>\n</layout>\n", body
75
71
  end
76
72
 
77
73
  it "renders with file layouts" do
78
- builder_app {
74
+ builder_app do
79
75
  builder %(xml.em 'Hello World'), :layout => :layout2
80
- }
76
+ end
81
77
  assert ok?
82
78
  assert_equal "<layout>\n<em>Hello World</em>\n</layout>\n", body
83
79
  end
84
80
 
85
81
  it "raises error if template not found" do
86
- mock_app {
82
+ mock_app do
87
83
  get('/') { builder :no_such_template }
88
- }
84
+ end
89
85
  assert_raise(Errno::ENOENT) { get('/') }
90
86
  end
91
87
  end
@@ -12,11 +12,11 @@ end
12
12
 
13
13
  class CoffeeTest < Test::Unit::TestCase
14
14
  def coffee_app(options = {}, &block)
15
- mock_app {
15
+ mock_app do
16
16
  set :views, File.dirname(__FILE__) + '/views'
17
17
  set(options)
18
- get '/', &block
19
- }
18
+ get('/', &block)
19
+ end
20
20
  get '/'
21
21
  end
22
22
 
@@ -77,9 +77,7 @@ class CoffeeTest < Test::Unit::TestCase
77
77
  it "passes default coffee options to the coffee engine" do
78
78
  mock_app do
79
79
  set :coffee, :no_wrap => true # default coffee style is :nested
80
- get '/' do
81
- coffee "alert 'Aye!'\n"
82
- end
80
+ get('/') { coffee "alert 'Aye!'\n" }
83
81
  end
84
82
  get '/'
85
83
  assert ok?
@@ -0,0 +1,154 @@
1
+ # I like coding: UTF-8
2
+ require File.expand_path('../helper', __FILE__)
3
+
4
+ class CompileTest < Test::Unit::TestCase
5
+
6
+ def self.converts pattern, expected_regexp
7
+ it "generates #{expected_regexp.source} from #{pattern}" do
8
+ compiled, _ = compiled pattern
9
+ assert_equal expected_regexp, compiled
10
+ end
11
+ end
12
+ def self.parses pattern, example, expected_params
13
+ it "parses #{example} with #{pattern} into params #{expected_params}" do
14
+ compiled, keys = compiled pattern
15
+ match = compiled.match(example)
16
+ fail %Q{"#{example}" does not parse on pattern "#{pattern}".} unless match
17
+
18
+ # Aggregate e.g. multiple splat values into one array.
19
+ #
20
+ params = keys.zip(match.captures).reduce({}) do |hash, mapping|
21
+ key, value = mapping
22
+ hash[key] = if existing = hash[key]
23
+ existing.respond_to?(:to_ary) ? existing << value : [existing, value]
24
+ else
25
+ value
26
+ end
27
+ hash
28
+ end
29
+
30
+ assert_equal(expected_params, params)
31
+ end
32
+ end
33
+ def self.fails pattern, example
34
+ it "does not parse #{example} with #{pattern}" do
35
+ compiled, _ = compiled pattern
36
+ match = compiled.match(example)
37
+ fail %Q{"#{pattern}" does parse "#{example}" but it should fail} if match
38
+ end
39
+ end
40
+ def compiled pattern
41
+ app ||= mock_app {}
42
+ compiled, keys = app.send(:compile, pattern)
43
+ [compiled, keys]
44
+ end
45
+
46
+ converts "/", %r{\A/\z}
47
+ parses "/", "/", {}
48
+
49
+ converts "/foo", %r{\A/foo\z}
50
+ parses "/foo", "/foo", {}
51
+
52
+ converts "/:foo", %r{\A/([^/?#]+)\z}
53
+ parses "/:foo", "/foo", "foo" => "foo"
54
+ parses "/:foo", "/foo.bar", "foo" => "foo.bar"
55
+ parses "/:foo", "/foo%2Fbar", "foo" => "foo%2Fbar"
56
+ parses "/:foo", "/%0Afoo", "foo" => "%0Afoo"
57
+ fails "/:foo", "/foo?"
58
+ fails "/:foo", "/foo/bar"
59
+ fails "/:foo", "/"
60
+ fails "/:foo", "/foo/"
61
+
62
+ converts "/föö", %r{\A/f%[Cc]3%[Bb]6%[Cc]3%[Bb]6\z}
63
+ parses "/föö", "/f%C3%B6%C3%B6", {}
64
+
65
+ converts "/:foo/:bar", %r{\A/([^/?#]+)/([^/?#]+)\z}
66
+ parses "/:foo/:bar", "/foo/bar", "foo" => "foo", "bar" => "bar"
67
+
68
+ converts "/hello/:person", %r{\A/hello/([^/?#]+)\z}
69
+ parses "/hello/:person", "/hello/Frank", "person" => "Frank"
70
+
71
+ converts "/?:foo?/?:bar?", %r{\A/?([^/?#]+)?/?([^/?#]+)?\z}
72
+ parses "/?:foo?/?:bar?", "/hello/world", "foo" => "hello", "bar" => "world"
73
+ parses "/?:foo?/?:bar?", "/hello", "foo" => "hello", "bar" => nil
74
+ parses "/?:foo?/?:bar?", "/", "foo" => nil, "bar" => nil
75
+ parses "/?:foo?/?:bar?", "", "foo" => nil, "bar" => nil
76
+
77
+ converts "/*", %r{\A/(.*?)\z}
78
+ parses "/*", "/", "splat" => ""
79
+ parses "/*", "/foo", "splat" => "foo"
80
+ parses "/*", "/foo/bar", "splat" => "foo/bar"
81
+
82
+ converts "/:foo/*", %r{\A/([^/?#]+)/(.*?)\z}
83
+ parses "/:foo/*", "/foo/bar/baz", "foo" => "foo", "splat" => "bar/baz"
84
+
85
+ converts "/:foo/:bar", %r{\A/([^/?#]+)/([^/?#]+)\z}
86
+ parses "/:foo/:bar", "/user@example.com/name", "foo" => "user@example.com", "bar" => "name"
87
+
88
+ converts "/test$/", %r{\A/test(?:\$|%24)/\z}
89
+ parses "/test$/", "/test$/", {}
90
+
91
+ converts "/te+st/", %r{\A/te(?:\+|%2[Bb])st/\z}
92
+ parses "/te+st/", "/te+st/", {}
93
+ fails "/te+st/", "/test/"
94
+ fails "/te+st/", "/teeest/"
95
+
96
+ converts "/test(bar)/", %r{\A/test(?:\(|%28)bar(?:\)|%29)/\z}
97
+ parses "/test(bar)/", "/test(bar)/", {}
98
+
99
+ converts "/path with spaces", %r{\A/path(?:%20|(?:\+|%2[Bb]))with(?:%20|(?:\+|%2[Bb]))spaces\z}
100
+ parses "/path with spaces", "/path%20with%20spaces", {}
101
+ parses "/path with spaces", "/path%2Bwith%2Bspaces", {}
102
+ parses "/path with spaces", "/path+with+spaces", {}
103
+
104
+ converts "/foo&bar", %r{\A/foo(?:&|%26)bar\z}
105
+ parses "/foo&bar", "/foo&bar", {}
106
+
107
+ converts "/:foo/*", %r{\A/([^/?#]+)/(.*?)\z}
108
+ parses "/:foo/*", "/hello%20world/how%20are%20you", "foo" => "hello%20world", "splat" => "how%20are%20you"
109
+
110
+ converts "/*/foo/*/*", %r{\A/(.*?)/foo/(.*?)/(.*?)\z}
111
+ parses "/*/foo/*/*", "/bar/foo/bling/baz/boom", "splat" => ["bar", "bling", "baz/boom"]
112
+ fails "/*/foo/*/*", "/bar/foo/baz"
113
+
114
+ converts "/test.bar", %r{\A/test(?:\.|%2[Ee])bar\z}
115
+ parses "/test.bar", "/test.bar", {}
116
+ fails "/test.bar", "/test0bar"
117
+
118
+ converts "/:file.:ext", %r{\A/((?:[^\./?#%]|(?:%[^2].|%[2][^Ee]))+)(?:\.|%2[Ee])((?:[^\./?#%]|(?:%[^2].|%[2][^Ee]))+)\z}
119
+ parses "/:file.:ext", "/pony.jpg", "file" => "pony", "ext" => "jpg"
120
+ parses "/:file.:ext", "/pony%2Ejpg", "file" => "pony", "ext" => "jpg"
121
+ fails "/:file.:ext", "/.jpg"
122
+
123
+ converts "/:name.?:format?", %r{\A/((?:[^\./?#%]|(?:%[^2].|%[2][^Ee]))+)(?:\.|%2[Ee])?((?:[^\./?#%]|(?:%[^2].|%[2][^Ee]))+)?\z}
124
+ parses "/:name.?:format?", "/foo", "name" => "foo", "format" => nil
125
+ parses "/:name.?:format?", "/foo.bar", "name" => "foo", "format" => "bar"
126
+ parses "/:name.?:format?", "/foo%2Ebar", "name" => "foo", "format" => "bar"
127
+ fails "/:name.?:format?", "/.bar"
128
+
129
+ converts "/:user@?:host?", %r{\A/((?:[^@/?#%]|(?:%[^4].|%[4][^0]))+)(?:@|%40)?((?:[^@/?#%]|(?:%[^4].|%[4][^0]))+)?\z}
130
+ parses "/:user@?:host?", "/foo@bar", "user" => "foo", "host" => "bar"
131
+ parses "/:user@?:host?", "/foo.foo@bar", "user" => "foo.foo", "host" => "bar"
132
+ parses "/:user@?:host?", "/foo@bar.bar", "user" => "foo", "host" => "bar.bar"
133
+
134
+ # From https://gist.github.com/2154980#gistcomment-169469.
135
+ #
136
+ # converts "/:name(.:format)?", %r{\A/([^\.%2E/?#]+)(?:\(|%28)(?:\.|%2E)([^\.%2E/?#]+)(?:\)|%29)?\z}
137
+ # parses "/:name(.:format)?", "/foo", "name" => "foo", "format" => nil
138
+ # parses "/:name(.:format)?", "/foo.bar", "name" => "foo", "format" => "bar"
139
+ fails "/:name(.:format)?", "/foo."
140
+
141
+ parses "/:id/test.bar", "/3/test.bar", {"id" => "3"}
142
+ parses "/:id/test.bar", "/2/test.bar", {"id" => "2"}
143
+ parses "/:id/test.bar", "/2E/test.bar", {"id" => "2E"}
144
+ parses "/:id/test.bar", "/2e/test.bar", {"id" => "2e"}
145
+ fails "/:id/test.bar", "/%2E/test.bar"
146
+
147
+ parses "/:file.:ext", "/pony%2ejpg", "file" => "pony", "ext" => "jpg"
148
+ parses "/:file.:ext", "/pony%E6%AD%A3%2Ejpg", "file" => "pony%E6%AD%A3", "ext" => "jpg"
149
+ parses "/:file.:ext", "/pony%e6%ad%a3%2ejpg", "file" => "pony%e6%ad%a3", "ext" => "jpg"
150
+ parses "/:file.:ext", "/pony正%2Ejpg", "file" => "pony正", "ext" => "jpg"
151
+ parses "/:file.:ext", "/pony正%2ejpg", "file" => "pony正", "ext" => "jpg"
152
+ fails "/:file.:ext", "/pony正..jpg"
153
+ fails "/:file.:ext", "/pony正.%2ejpg"
154
+ end
@@ -1,15 +1,15 @@
1
1
  # Copyright (c) 2009 Damian Janowski and Michel Martens for Citrusbyte
2
- #
2
+ #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  # of this software and associated documentation files (the "Software"), to deal
5
5
  # in the Software without restriction, including without limitation the rights
6
6
  # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
7
  # copies of the Software, and to permit persons to whom the Software is
8
8
  # furnished to do so, subject to the following conditions:
9
- #
9
+ #
10
10
  # The above copyright notice and this permission notice shall be included in
11
11
  # all copies or substantial portions of the Software.
12
- #
12
+ #
13
13
  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
14
  # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
15
  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -82,9 +82,7 @@ private
82
82
  end
83
83
 
84
84
  def self.test_name(name)
85
- name = "test_#{sanitize_name(name).gsub(/\s+/,'_')}_0"
86
- name = name.succ while method_defined? name
87
- name.to_sym
85
+ "test_#{sanitize_name(name).gsub(/\s+/,'_')}".to_sym
88
86
  end
89
87
 
90
88
  def self.sanitize_name(name)
@@ -7,7 +7,7 @@ class CreoleTest < Test::Unit::TestCase
7
7
  def creole_app(&block)
8
8
  mock_app do
9
9
  set :views, File.dirname(__FILE__) + '/views'
10
- get '/', &block
10
+ get('/', &block)
11
11
  end
12
12
  get '/'
13
13
  end
@@ -40,7 +40,9 @@ class CreoleTest < Test::Unit::TestCase
40
40
  end
41
41
 
42
42
  it "renders with file layouts" do
43
- creole_app { creole 'Hello World', :layout => :layout2, :layout_engine => :erb }
43
+ creole_app do
44
+ creole 'Hello World', :layout => :layout2, :layout_engine => :erb
45
+ end
44
46
  assert ok?
45
47
  assert_body "ERB Layout!\n<p>Hello World</p>"
46
48
  end
@@ -49,9 +51,7 @@ class CreoleTest < Test::Unit::TestCase
49
51
  mock_app do
50
52
  template(:inner) { "hi" }
51
53
  template(:outer) { "<outer><%= creole :inner %></outer>" }
52
- get '/' do
53
- erb :outer
54
- end
54
+ get('/') { erb :outer }
55
55
  end
56
56
 
57
57
  get '/'
@@ -63,9 +63,7 @@ class DelegatorTest < Test::Unit::TestCase
63
63
  %w[get put post delete options patch].each do |verb|
64
64
  it "delegates #{verb} correctly" do
65
65
  delegation_app do
66
- send verb, '/hello' do
67
- 'Hello World'
68
- end
66
+ send(verb, '/hello') { 'Hello World' }
69
67
  end
70
68
 
71
69
  request = Rack::MockRequest.new(@app)
@@ -11,10 +11,10 @@ class ERBTest < Test::Unit::TestCase
11
11
  end
12
12
 
13
13
  def erb_app(&block)
14
- mock_app {
14
+ mock_app do
15
15
  set :views, File.dirname(__FILE__) + '/views'
16
- get '/', &block
17
- }
16
+ get('/', &block)
17
+ end
18
18
  get '/'
19
19
  end
20
20
 
@@ -35,62 +35,74 @@ class ERBTest < Test::Unit::TestCase
35
35
  end
36
36
 
37
37
  it 'takes a :locals option' do
38
- erb_app {
38
+ erb_app do
39
39
  locals = {:foo => 'Bar'}
40
40
  erb '<%= foo %>', :locals => locals
41
- }
41
+ end
42
42
  assert ok?
43
43
  assert_equal 'Bar', body
44
44
  end
45
45
 
46
46
  it "renders with inline layouts" do
47
- mock_app {
47
+ mock_app do
48
48
  layout { 'THIS. IS. <%= yield.upcase %>!' }
49
49
  get('/') { erb 'Sparta' }
50
- }
50
+ end
51
51
  get '/'
52
52
  assert ok?
53
53
  assert_equal 'THIS. IS. SPARTA!', body
54
54
  end
55
55
 
56
56
  it "renders with file layouts" do
57
- erb_app {
58
- erb 'Hello World', :layout => :layout2
59
- }
57
+ erb_app { erb 'Hello World', :layout => :layout2 }
60
58
  assert ok?
61
59
  assert_body "ERB Layout!\nHello World"
62
60
  end
63
61
 
64
62
  it "renders erb with blocks" do
65
- mock_app {
63
+ mock_app do
66
64
  def container
67
65
  @_out_buf << "THIS."
68
66
  yield
69
67
  @_out_buf << "SPARTA!"
70
68
  end
71
69
  def is; "IS." end
72
- get '/' do
73
- erb '<% container do %> <%= is %> <% end %>'
74
- end
75
- }
70
+ get('/') { erb '<% container do %> <%= is %> <% end %>' }
71
+ end
76
72
  get '/'
77
73
  assert ok?
78
74
  assert_equal 'THIS. IS. SPARTA!', body
79
75
  end
80
76
 
81
77
  it "can be used in a nested fashion for partials and whatnot" do
82
- mock_app {
78
+ mock_app do
83
79
  template(:inner) { "<inner><%= 'hi' %></inner>" }
84
80
  template(:outer) { "<outer><%= erb :inner %></outer>" }
85
- get '/' do
86
- erb :outer
87
- end
88
- }
81
+ get('/') { erb :outer }
82
+ end
89
83
 
90
84
  get '/'
91
85
  assert ok?
92
86
  assert_equal '<outer><inner>hi</inner></outer>', body
93
87
  end
88
+
89
+ it "can rendere truly nested layouts by accepting a layout and a block with the contents" do
90
+ mock_app do
91
+ template(:main_outer_layout) { "<h1>Title</h1>\n<%= yield %>" }
92
+ template(:an_inner_layout) { "<h2>Subtitle</h2>\n<%= yield %>" }
93
+ template(:a_page) { "<p>Contents.</p>\n" }
94
+ get('/') do
95
+ erb :main_outer_layout, :layout => false do
96
+ erb :an_inner_layout do
97
+ erb :a_page
98
+ end
99
+ end
100
+ end
101
+ end
102
+ get '/'
103
+ assert ok?
104
+ assert_body "<h1>Title</h1>\n<h2>Subtitle</h2>\n<p>Contents.</p>\n"
105
+ end
94
106
  end
95
107
 
96
108