adamwiggins-sinatra 0.8.9 → 0.10.1

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 (79) hide show
  1. data/AUTHORS +8 -7
  2. data/CHANGES +211 -1
  3. data/LICENSE +1 -1
  4. data/README.rdoc +183 -139
  5. data/Rakefile +20 -81
  6. data/lib/sinatra.rb +5 -1
  7. data/lib/sinatra/base.rb +569 -278
  8. data/lib/sinatra/main.rb +12 -25
  9. data/lib/sinatra/showexceptions.rb +303 -0
  10. data/sinatra.gemspec +20 -44
  11. data/test/base_test.rb +140 -52
  12. data/test/builder_test.rb +14 -17
  13. data/test/contest.rb +64 -0
  14. data/test/erb_test.rb +42 -16
  15. data/test/extensions_test.rb +100 -0
  16. data/test/filter_test.rb +85 -13
  17. data/test/haml_test.rb +39 -21
  18. data/test/helper.rb +76 -0
  19. data/test/helpers_test.rb +219 -84
  20. data/test/mapped_error_test.rb +168 -146
  21. data/test/middleware_test.rb +22 -17
  22. data/test/options_test.rb +323 -54
  23. data/test/render_backtrace_test.rb +145 -0
  24. data/test/request_test.rb +28 -6
  25. data/test/response_test.rb +42 -0
  26. data/test/result_test.rb +27 -21
  27. data/test/route_added_hook_test.rb +59 -0
  28. data/test/routing_test.rb +558 -77
  29. data/test/sass_test.rb +52 -13
  30. data/test/server_test.rb +47 -0
  31. data/test/sinatra_test.rb +3 -5
  32. data/test/static_test.rb +57 -30
  33. data/test/templates_test.rb +74 -25
  34. data/test/views/error.builder +3 -0
  35. data/test/views/error.erb +3 -0
  36. data/test/views/error.haml +3 -0
  37. data/test/views/error.sass +2 -0
  38. data/test/views/foo/hello.test +1 -0
  39. metadata +50 -46
  40. data/compat/app_test.rb +0 -300
  41. data/compat/application_test.rb +0 -334
  42. data/compat/builder_test.rb +0 -101
  43. data/compat/custom_error_test.rb +0 -62
  44. data/compat/erb_test.rb +0 -136
  45. data/compat/events_test.rb +0 -75
  46. data/compat/filter_test.rb +0 -30
  47. data/compat/haml_test.rb +0 -233
  48. data/compat/helper.rb +0 -21
  49. data/compat/mapped_error_test.rb +0 -72
  50. data/compat/pipeline_test.rb +0 -71
  51. data/compat/public/foo.xml +0 -1
  52. data/compat/sass_test.rb +0 -57
  53. data/compat/sessions_test.rb +0 -39
  54. data/compat/streaming_test.rb +0 -121
  55. data/compat/sym_params_test.rb +0 -19
  56. data/compat/template_test.rb +0 -30
  57. data/compat/use_in_file_templates_test.rb +0 -47
  58. data/compat/views/foo.builder +0 -1
  59. data/compat/views/foo.erb +0 -1
  60. data/compat/views/foo.haml +0 -1
  61. data/compat/views/foo.sass +0 -2
  62. data/compat/views/foo_layout.erb +0 -2
  63. data/compat/views/foo_layout.haml +0 -2
  64. data/compat/views/layout_test/foo.builder +0 -1
  65. data/compat/views/layout_test/foo.erb +0 -1
  66. data/compat/views/layout_test/foo.haml +0 -1
  67. data/compat/views/layout_test/foo.sass +0 -2
  68. data/compat/views/layout_test/layout.builder +0 -3
  69. data/compat/views/layout_test/layout.erb +0 -1
  70. data/compat/views/layout_test/layout.haml +0 -1
  71. data/compat/views/layout_test/layout.sass +0 -2
  72. data/compat/views/no_layout/no_layout.builder +0 -1
  73. data/compat/views/no_layout/no_layout.haml +0 -1
  74. data/lib/sinatra/compat.rb +0 -239
  75. data/lib/sinatra/test.rb +0 -112
  76. data/lib/sinatra/test/rspec.rb +0 -2
  77. data/lib/sinatra/test/spec.rb +0 -2
  78. data/lib/sinatra/test/unit.rb +0 -11
  79. data/test/reload_test.rb +0 -65
@@ -1,72 +1,160 @@
1
- require 'test/spec'
2
- require 'sinatra/base'
3
- require 'sinatra/test'
1
+ require File.dirname(__FILE__) + '/helper'
4
2
 
5
- describe 'Sinatra::Base' do
6
- include Sinatra::Test
7
-
8
- it 'includes Rack::Utils' do
9
- Sinatra::Base.should.include Rack::Utils
3
+ class BaseTest < Test::Unit::TestCase
4
+ def test_default
5
+ assert true
10
6
  end
11
7
 
12
- it 'can be used as a Rack application' do
13
- mock_app {
8
+ describe 'Sinatra::Base subclasses' do
9
+ class TestApp < Sinatra::Base
14
10
  get '/' do
15
11
  'Hello World'
16
12
  end
17
- }
18
- @app.should.respond_to :call
13
+ end
14
+
15
+ it 'include Rack::Utils' do
16
+ assert TestApp.included_modules.include?(Rack::Utils)
17
+ end
18
+
19
+ it 'processes requests with #call' do
20
+ assert TestApp.respond_to?(:call)
21
+
22
+ request = Rack::MockRequest.new(TestApp)
23
+ response = request.get('/')
24
+ assert response.ok?
25
+ assert_equal 'Hello World', response.body
26
+ end
19
27
 
20
- request = Rack::MockRequest.new(@app)
21
- response = request.get('/')
22
- response.should.be.ok
23
- response.body.should.equal 'Hello World'
28
+ class TestApp < Sinatra::Base
29
+ get '/state' do
30
+ @foo ||= "new"
31
+ body = "Foo: #{@foo}"
32
+ @foo = 'discard'
33
+ body
34
+ end
35
+ end
36
+
37
+ it 'does not maintain state between requests' do
38
+ request = Rack::MockRequest.new(TestApp)
39
+ 2.times do
40
+ response = request.get('/state')
41
+ assert response.ok?
42
+ assert_equal 'Foo: new', response.body
43
+ end
44
+ end
45
+
46
+ it "passes the subclass to configure blocks" do
47
+ ref = nil
48
+ TestApp.configure { |app| ref = app }
49
+ assert_equal TestApp, ref
50
+ end
51
+
52
+ it "allows the configure block arg to be omitted and does not change context" do
53
+ context = nil
54
+ TestApp.configure { context = self }
55
+ assert_equal self, context
56
+ end
24
57
  end
25
58
 
26
- it 'can be used as Rack middleware' do
27
- app = lambda { |env| [200, {}, ['Goodbye World']] }
28
- mock_middleware =
29
- mock_app {
30
- get '/' do
31
- 'Hello World'
32
- end
33
- get '/goodbye' do
34
- @app.call(request.env)
35
- end
36
- }
37
- middleware = mock_middleware.new(app)
38
- middleware.app.should.be app
59
+ describe "Sinatra::Base as Rack middleware" do
60
+ app = lambda { |env|
61
+ headers = {'X-Downstream' => 'true'}
62
+ headers['X-Route-Missing'] = env['sinatra.route-missing'] || ''
63
+ [210, headers, ['Hello from downstream']] }
64
+
65
+ class TestMiddleware < Sinatra::Base
66
+ end
39
67
 
68
+ it 'creates a middleware that responds to #call with .new' do
69
+ middleware = TestMiddleware.new(app)
70
+ assert middleware.respond_to?(:call)
71
+ end
72
+
73
+ it 'exposes the downstream app' do
74
+ middleware = TestMiddleware.new(app)
75
+ assert_same app, middleware.app
76
+ end
77
+
78
+ class TestMiddleware < Sinatra::Base
79
+ def route_missing
80
+ env['sinatra.route-missing'] = '1'
81
+ super
82
+ end
83
+
84
+ get '/' do
85
+ 'Hello from middleware'
86
+ end
87
+ end
88
+
89
+ middleware = TestMiddleware.new(app)
40
90
  request = Rack::MockRequest.new(middleware)
41
- response = request.get('/')
42
- response.should.be.ok
43
- response.body.should.equal 'Hello World'
44
91
 
45
- response = request.get('/goodbye')
46
- response.should.be.ok
47
- response.body.should.equal 'Goodbye World'
48
- end
92
+ it 'intercepts requests' do
93
+ response = request.get('/')
94
+ assert response.ok?
95
+ assert_equal 'Hello from middleware', response.body
96
+ end
97
+
98
+ it 'automatically forwards requests downstream when no matching route found' do
99
+ response = request.get('/missing')
100
+ assert_equal 210, response.status
101
+ assert_equal 'Hello from downstream', response.body
102
+ end
49
103
 
50
- it 'can take multiple definitions of a route' do
51
- app = mock_app {
52
- user_agent(/Foo/)
53
- get '/foo' do
54
- 'foo'
104
+ it 'calls #route_missing before forwarding downstream' do
105
+ response = request.get('/missing')
106
+ assert_equal '1', response['X-Route-Missing']
107
+ end
108
+
109
+ class TestMiddleware < Sinatra::Base
110
+ get '/low-level-forward' do
111
+ app.call(env)
112
+ end
113
+ end
114
+
115
+ it 'can call the downstream app directly and return result' do
116
+ response = request.get('/low-level-forward')
117
+ assert_equal 210, response.status
118
+ assert_equal 'true', response['X-Downstream']
119
+ assert_equal 'Hello from downstream', response.body
120
+ end
121
+
122
+ class TestMiddleware < Sinatra::Base
123
+ get '/explicit-forward' do
124
+ response['X-Middleware'] = 'true'
125
+ res = forward
126
+ assert_nil res
127
+ assert_equal 210, response.status
128
+ assert_equal 'true', response['X-Downstream']
129
+ assert_equal ['Hello from downstream'], response.body
130
+ 'Hello after explicit forward'
55
131
  end
132
+ end
133
+
134
+ it 'forwards the request downstream and integrates the response into the current context' do
135
+ response = request.get('/explicit-forward')
136
+ assert_equal 210, response.status
137
+ assert_equal 'true', response['X-Downstream']
138
+ assert_equal 'Hello after explicit forward', response.body
139
+ assert_equal '28', response['Content-Length']
140
+ end
141
+
142
+ app_content_length = lambda {|env|
143
+ [200, {'Content-Length' => '16'}, 'From downstream!']}
56
144
 
57
- get '/foo' do
58
- 'not foo'
145
+ class TestMiddlewareContentLength < Sinatra::Base
146
+ get '/forward' do
147
+ res = forward
148
+ 'From after explicit forward!'
59
149
  end
60
- }
150
+ end
61
151
 
62
- request = Rack::MockRequest.new(app)
63
- response = request.get('/foo', 'HTTP_USER_AGENT' => 'Foo')
64
- response.should.be.ok
65
- response.body.should.equal 'foo'
152
+ middleware_content_length = TestMiddlewareContentLength.new(app_content_length)
153
+ request_content_length = Rack::MockRequest.new(middleware_content_length)
66
154
 
67
- request = Rack::MockRequest.new(app)
68
- response = request.get('/foo')
69
- response.should.be.ok
70
- response.body.should.equal 'not foo'
155
+ it "sets content length for last response" do
156
+ response = request_content_length.get('/forward')
157
+ assert_equal '28', response['Content-Length']
158
+ end
71
159
  end
72
160
  end
@@ -1,10 +1,7 @@
1
- require 'test/spec'
2
- require 'sinatra/base'
3
- require 'sinatra/test'
4
-
5
- describe "Builder Templates" do
6
- include Sinatra::Test
1
+ require File.dirname(__FILE__) + '/helper'
2
+ require 'builder'
7
3
 
4
+ class BuilderTest < Test::Unit::TestCase
8
5
  def builder_app(&block)
9
6
  mock_app {
10
7
  set :views, File.dirname(__FILE__) + '/views'
@@ -15,8 +12,8 @@ describe "Builder Templates" do
15
12
 
16
13
  it 'renders inline Builder strings' do
17
14
  builder_app { builder 'xml.instruct!' }
18
- should.be.ok
19
- body.should.equal %{<?xml version="1.0" encoding="UTF-8"?>\n}
15
+ assert ok?
16
+ assert_equal %{<?xml version="1.0" encoding="UTF-8"?>\n}, body
20
17
  end
21
18
 
22
19
  it 'renders inline blocks' do
@@ -26,8 +23,8 @@ describe "Builder Templates" do
26
23
  xml.couple @name
27
24
  end
28
25
  }
29
- should.be.ok
30
- body.should.equal "<couple>Frank &amp; Mary</couple>\n"
26
+ assert ok?
27
+ assert_equal "<couple>Frank &amp; Mary</couple>\n", body
31
28
  end
32
29
 
33
30
  it 'renders .builder files in views path' do
@@ -35,8 +32,8 @@ describe "Builder Templates" do
35
32
  @name = "Blue"
36
33
  builder :hello
37
34
  }
38
- should.be.ok
39
- body.should.equal %(<exclaim>You're my boy, Blue!</exclaim>\n)
35
+ assert ok?
36
+ assert_equal %(<exclaim>You're my boy, Blue!</exclaim>\n), body
40
37
  end
41
38
 
42
39
  it "renders with inline layouts" do
@@ -47,22 +44,22 @@ describe "Builder Templates" do
47
44
  get('/') { builder %(xml.em 'Hello World') }
48
45
  }
49
46
  get '/'
50
- should.be.ok
51
- body.should.equal "<layout>\n<em>Hello World</em>\n</layout>\n"
47
+ assert ok?
48
+ assert_equal "<layout>\n<em>Hello World</em>\n</layout>\n", body
52
49
  end
53
50
 
54
51
  it "renders with file layouts" do
55
52
  builder_app {
56
53
  builder %(xml.em 'Hello World'), :layout => :layout2
57
54
  }
58
- should.be.ok
59
- body.should.equal "<layout>\n<em>Hello World</em>\n</layout>\n"
55
+ assert ok?
56
+ assert_equal "<layout>\n<em>Hello World</em>\n</layout>\n", body
60
57
  end
61
58
 
62
59
  it "raises error if template not found" do
63
60
  mock_app {
64
61
  get('/') { builder :no_such_template }
65
62
  }
66
- lambda { get('/') }.should.raise(Errno::ENOENT)
63
+ assert_raise(Errno::ENOENT) { get('/') }
67
64
  end
68
65
  end
@@ -0,0 +1,64 @@
1
+ require "test/unit"
2
+
3
+ # Test::Unit loads a default test if the suite is empty, and the only
4
+ # purpose of that test is to fail. As having empty contexts is a common
5
+ # practice, we decided to overwrite TestSuite#empty? in order to
6
+ # allow them. Having a failure when no tests have been defined seems
7
+ # counter-intuitive.
8
+ class Test::Unit::TestSuite
9
+ unless method_defined?(:empty?)
10
+ def empty?
11
+ false
12
+ end
13
+ end
14
+ end
15
+
16
+ # We added setup, test and context as class methods, and the instance
17
+ # method setup now iterates on the setup blocks. Note that all setup
18
+ # blocks must be defined with the block syntax. Adding a setup instance
19
+ # method defeats the purpose of this library.
20
+ class Test::Unit::TestCase
21
+ def self.setup(&block)
22
+ setup_blocks << block
23
+ end
24
+
25
+ def setup
26
+ self.class.setup_blocks.each do |block|
27
+ instance_eval(&block)
28
+ end
29
+ end
30
+
31
+ def self.context(name, &block)
32
+ subclass = Class.new(self.superclass)
33
+ subclass.setup_blocks.unshift(*setup_blocks)
34
+ subclass.class_eval(&block)
35
+ const_set(context_name(name), subclass)
36
+ end
37
+
38
+ def self.test(name, &block)
39
+ define_method(test_name(name), &block)
40
+ end
41
+
42
+ class << self
43
+ alias_method :should, :test
44
+ alias_method :describe, :context
45
+ end
46
+
47
+ private
48
+
49
+ def self.setup_blocks
50
+ @setup_blocks ||= []
51
+ end
52
+
53
+ def self.context_name(name)
54
+ "Test#{sanitize_name(name).gsub(/(^| )(\w)/) { $2.upcase }}".to_sym
55
+ end
56
+
57
+ def self.test_name(name)
58
+ "test_#{sanitize_name(name).gsub(/\s+/,'_')}".to_sym
59
+ end
60
+
61
+ def self.sanitize_name(name)
62
+ name.gsub(/\W+/, ' ').strip
63
+ end
64
+ end
@@ -1,10 +1,6 @@
1
- require 'test/spec'
2
- require 'sinatra/base'
3
- require 'sinatra/test'
4
-
5
- describe "ERB Templates" do
6
- include Sinatra::Test
1
+ require File.dirname(__FILE__) + '/helper'
7
2
 
3
+ class ERBTest < Test::Unit::TestCase
8
4
  def erb_app(&block)
9
5
  mock_app {
10
6
  set :views, File.dirname(__FILE__) + '/views'
@@ -15,14 +11,14 @@ describe "ERB Templates" do
15
11
 
16
12
  it 'renders inline ERB strings' do
17
13
  erb_app { erb '<%= 1 + 1 %>' }
18
- should.be.ok
19
- body.should.equal '2'
14
+ assert ok?
15
+ assert_equal '2', body
20
16
  end
21
17
 
22
18
  it 'renders .erb files in views path' do
23
19
  erb_app { erb :hello }
24
- should.be.ok
25
- body.should.equal "Hello World\n"
20
+ assert ok?
21
+ assert_equal "Hello World\n", body
26
22
  end
27
23
 
28
24
  it 'takes a :locals option' do
@@ -30,8 +26,8 @@ describe "ERB Templates" do
30
26
  locals = {:foo => 'Bar'}
31
27
  erb '<%= foo %>', :locals => locals
32
28
  }
33
- should.be.ok
34
- body.should.equal 'Bar'
29
+ assert ok?
30
+ assert_equal 'Bar', body
35
31
  end
36
32
 
37
33
  it "renders with inline layouts" do
@@ -40,16 +36,46 @@ describe "ERB Templates" do
40
36
  get('/') { erb 'Sparta' }
41
37
  }
42
38
  get '/'
43
- should.be.ok
44
- body.should.equal 'THIS. IS. SPARTA!'
39
+ assert ok?
40
+ assert_equal 'THIS. IS. SPARTA!', body
45
41
  end
46
42
 
47
43
  it "renders with file layouts" do
48
44
  erb_app {
49
45
  erb 'Hello World', :layout => :layout2
50
46
  }
51
- should.be.ok
52
- body.should.equal "ERB Layout!\nHello World\n"
47
+ assert ok?
48
+ assert_equal "ERB Layout!\nHello World\n", body
49
+ end
50
+
51
+ it "renders erb with blocks" do
52
+ mock_app {
53
+ def container
54
+ @_out_buf << "THIS."
55
+ yield
56
+ @_out_buf << "SPARTA!"
57
+ end
58
+ def is; "IS." end
59
+ get '/' do
60
+ erb '<% container do %> <%= is %> <% end %>'
61
+ end
62
+ }
63
+ get '/'
64
+ assert ok?
65
+ assert_equal 'THIS. IS. SPARTA!', body
53
66
  end
54
67
 
68
+ it "can be used in a nested fashion for partials and whatnot" do
69
+ mock_app {
70
+ template(:inner) { "<inner><%= 'hi' %></inner>" }
71
+ template(:outer) { "<outer><%= erb :inner %></outer>" }
72
+ get '/' do
73
+ erb :outer
74
+ end
75
+ }
76
+
77
+ get '/'
78
+ assert ok?
79
+ assert_equal '<outer><inner>hi</inner></outer>', body
80
+ end
55
81
  end
@@ -0,0 +1,100 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class ExtensionsTest < Test::Unit::TestCase
4
+ module FooExtensions
5
+ def foo
6
+ end
7
+
8
+ private
9
+ def im_hiding_in_ur_foos
10
+ end
11
+ end
12
+
13
+ module BarExtensions
14
+ def bar
15
+ end
16
+ end
17
+
18
+ module BazExtensions
19
+ def baz
20
+ end
21
+ end
22
+
23
+ module QuuxExtensions
24
+ def quux
25
+ end
26
+ end
27
+
28
+ module PainExtensions
29
+ def foo=(name); end
30
+ def bar?(name); end
31
+ def fizz!(name); end
32
+ end
33
+
34
+ it 'will add the methods to the DSL for the class in which you register them and its subclasses' do
35
+ Sinatra::Base.register FooExtensions
36
+ assert Sinatra::Base.respond_to?(:foo)
37
+
38
+ Sinatra::Default.register BarExtensions
39
+ assert Sinatra::Default.respond_to?(:bar)
40
+ assert Sinatra::Default.respond_to?(:foo)
41
+ assert !Sinatra::Base.respond_to?(:bar)
42
+ end
43
+
44
+ it 'allows extending by passing a block' do
45
+ Sinatra::Base.register {
46
+ def im_in_ur_anonymous_module; end
47
+ }
48
+ assert Sinatra::Base.respond_to?(:im_in_ur_anonymous_module)
49
+ end
50
+
51
+ it 'will make sure any public methods added via Default#register are delegated to Sinatra::Delegator' do
52
+ Sinatra::Default.register FooExtensions
53
+ assert Sinatra::Delegator.private_instance_methods.
54
+ map { |m| m.to_sym }.include?(:foo)
55
+ assert !Sinatra::Delegator.private_instance_methods.
56
+ map { |m| m.to_sym }.include?(:im_hiding_in_ur_foos)
57
+ end
58
+
59
+ it 'will handle special method names' do
60
+ Sinatra::Default.register PainExtensions
61
+ assert Sinatra::Delegator.private_instance_methods.
62
+ map { |m| m.to_sym }.include?(:foo=)
63
+ assert Sinatra::Delegator.private_instance_methods.
64
+ map { |m| m.to_sym }.include?(:bar?)
65
+ assert Sinatra::Delegator.private_instance_methods.
66
+ map { |m| m.to_sym }.include?(:fizz!)
67
+ end
68
+
69
+ it 'will not delegate methods on Base#register' do
70
+ Sinatra::Base.register QuuxExtensions
71
+ assert !Sinatra::Delegator.private_instance_methods.include?("quux")
72
+ end
73
+
74
+ it 'will extend the Sinatra::Default application by default' do
75
+ Sinatra.register BazExtensions
76
+ assert !Sinatra::Base.respond_to?(:baz)
77
+ assert Sinatra::Default.respond_to?(:baz)
78
+ end
79
+
80
+ module BizzleExtension
81
+ def bizzle
82
+ bizzle_option
83
+ end
84
+
85
+ def self.registered(base)
86
+ fail "base should be BizzleApp" unless base == BizzleApp
87
+ fail "base should have already extended BizzleExtension" unless base.respond_to?(:bizzle)
88
+ base.set :bizzle_option, 'bizzle!'
89
+ end
90
+ end
91
+
92
+ class BizzleApp < Sinatra::Base
93
+ end
94
+
95
+ it 'sends .registered to the extension module after extending the class' do
96
+ BizzleApp.register BizzleExtension
97
+ assert_equal 'bizzle!', BizzleApp.bizzle_option
98
+ assert_equal 'bizzle!', BizzleApp.bizzle
99
+ end
100
+ end