sinatra-base 1.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.
- data/AUTHORS +43 -0
- data/CHANGES +511 -0
- data/LICENSE +22 -0
- data/README.jp.rdoc +552 -0
- data/README.rdoc +636 -0
- data/Rakefile +116 -0
- data/lib/sinatra.rb +7 -0
- data/lib/sinatra/base.rb +1167 -0
- data/lib/sinatra/images/404.png +0 -0
- data/lib/sinatra/images/500.png +0 -0
- data/lib/sinatra/main.rb +28 -0
- data/lib/sinatra/showexceptions.rb +307 -0
- data/lib/sinatra/tilt.rb +746 -0
- data/sinatra-base.gemspec +94 -0
- data/test/base_test.rb +160 -0
- data/test/builder_test.rb +65 -0
- data/test/contest.rb +64 -0
- data/test/erb_test.rb +81 -0
- data/test/erubis_test.rb +82 -0
- data/test/extensions_test.rb +100 -0
- data/test/filter_test.rb +221 -0
- data/test/haml_test.rb +95 -0
- data/test/helper.rb +76 -0
- data/test/helpers_test.rb +582 -0
- data/test/less_test.rb +37 -0
- data/test/mapped_error_test.rb +197 -0
- data/test/middleware_test.rb +68 -0
- data/test/public/favicon.ico +0 -0
- data/test/request_test.rb +33 -0
- data/test/response_test.rb +42 -0
- data/test/result_test.rb +98 -0
- data/test/route_added_hook_test.rb +59 -0
- data/test/routing_test.rb +860 -0
- data/test/sass_test.rb +85 -0
- data/test/server_test.rb +47 -0
- data/test/settings_test.rb +368 -0
- data/test/sinatra_test.rb +13 -0
- data/test/static_test.rb +93 -0
- data/test/templates_test.rb +159 -0
- data/test/views/error.builder +3 -0
- data/test/views/error.erb +3 -0
- data/test/views/error.erubis +3 -0
- data/test/views/error.haml +3 -0
- data/test/views/error.sass +2 -0
- data/test/views/foo/hello.test +1 -0
- data/test/views/hello.builder +1 -0
- data/test/views/hello.erb +1 -0
- data/test/views/hello.erubis +1 -0
- data/test/views/hello.haml +1 -0
- data/test/views/hello.less +5 -0
- data/test/views/hello.sass +2 -0
- data/test/views/hello.test +1 -0
- data/test/views/layout2.builder +3 -0
- data/test/views/layout2.erb +2 -0
- data/test/views/layout2.erubis +2 -0
- data/test/views/layout2.haml +2 -0
- data/test/views/layout2.test +1 -0
- metadata +257 -0
data/test/less_test.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
require 'less'
|
3
|
+
|
4
|
+
class LessTest < Test::Unit::TestCase
|
5
|
+
def less_app(&block)
|
6
|
+
mock_app {
|
7
|
+
set :views, File.dirname(__FILE__) + '/views'
|
8
|
+
get '/', &block
|
9
|
+
}
|
10
|
+
get '/'
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'renders inline Less strings' do
|
14
|
+
less_app { less "@white_color: #fff; #main { background-color: @white_color }"}
|
15
|
+
assert ok?
|
16
|
+
assert_equal "#main { background-color: #ffffff; }\n", body
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'renders .less files in views path' do
|
20
|
+
less_app { less :hello }
|
21
|
+
assert ok?
|
22
|
+
assert_equal "#main { background-color: #ffffff; }\n", body
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'ignores the layout option' do
|
26
|
+
less_app { less :hello, :layout => :layout2 }
|
27
|
+
assert ok?
|
28
|
+
assert_equal "#main { background-color: #ffffff; }\n", body
|
29
|
+
end
|
30
|
+
|
31
|
+
it "raises error if template not found" do
|
32
|
+
mock_app {
|
33
|
+
get('/') { less :no_such_template }
|
34
|
+
}
|
35
|
+
assert_raise(Errno::ENOENT) { get('/') }
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
class FooError < RuntimeError
|
4
|
+
end
|
5
|
+
|
6
|
+
class FooNotFound < Sinatra::NotFound
|
7
|
+
end
|
8
|
+
|
9
|
+
class MappedErrorTest < Test::Unit::TestCase
|
10
|
+
def test_default
|
11
|
+
assert true
|
12
|
+
end
|
13
|
+
|
14
|
+
describe 'Exception Mappings' do
|
15
|
+
it 'invokes handlers registered with ::error when raised' do
|
16
|
+
mock_app {
|
17
|
+
set :raise_errors, false
|
18
|
+
error(FooError) { 'Foo!' }
|
19
|
+
get '/' do
|
20
|
+
raise FooError
|
21
|
+
end
|
22
|
+
}
|
23
|
+
get '/'
|
24
|
+
assert_equal 500, status
|
25
|
+
assert_equal 'Foo!', body
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'uses the Exception handler if no matching handler found' do
|
29
|
+
mock_app {
|
30
|
+
set :raise_errors, false
|
31
|
+
error(Exception) { 'Exception!' }
|
32
|
+
get '/' do
|
33
|
+
raise FooError
|
34
|
+
end
|
35
|
+
}
|
36
|
+
|
37
|
+
get '/'
|
38
|
+
assert_equal 500, status
|
39
|
+
assert_equal 'Exception!', body
|
40
|
+
end
|
41
|
+
|
42
|
+
it "sets env['sinatra.error'] to the rescued exception" do
|
43
|
+
mock_app {
|
44
|
+
set :raise_errors, false
|
45
|
+
error(FooError) {
|
46
|
+
assert env.include?('sinatra.error')
|
47
|
+
assert env['sinatra.error'].kind_of?(FooError)
|
48
|
+
'looks good'
|
49
|
+
}
|
50
|
+
get '/' do
|
51
|
+
raise FooError
|
52
|
+
end
|
53
|
+
}
|
54
|
+
get '/'
|
55
|
+
assert_equal 'looks good', body
|
56
|
+
end
|
57
|
+
|
58
|
+
it "raises errors from the app when raise_errors set and no handler defined" do
|
59
|
+
mock_app {
|
60
|
+
set :raise_errors, true
|
61
|
+
get '/' do
|
62
|
+
raise FooError
|
63
|
+
end
|
64
|
+
}
|
65
|
+
assert_raise(FooError) { get '/' }
|
66
|
+
end
|
67
|
+
|
68
|
+
it "calls error handlers before raising errors even when raise_errors is set" do
|
69
|
+
mock_app {
|
70
|
+
set :raise_errors, true
|
71
|
+
error(FooError) { "she's there." }
|
72
|
+
get '/' do
|
73
|
+
raise FooError
|
74
|
+
end
|
75
|
+
}
|
76
|
+
assert_nothing_raised { get '/' }
|
77
|
+
assert_equal 500, status
|
78
|
+
end
|
79
|
+
|
80
|
+
it "never raises Sinatra::NotFound beyond the application" do
|
81
|
+
mock_app {
|
82
|
+
set :raise_errors, true
|
83
|
+
get '/' do
|
84
|
+
raise Sinatra::NotFound
|
85
|
+
end
|
86
|
+
}
|
87
|
+
assert_nothing_raised { get '/' }
|
88
|
+
assert_equal 404, status
|
89
|
+
end
|
90
|
+
|
91
|
+
it "cascades for subclasses of Sinatra::NotFound" do
|
92
|
+
mock_app {
|
93
|
+
set :raise_errors, true
|
94
|
+
error(FooNotFound) { "foo! not found." }
|
95
|
+
get '/' do
|
96
|
+
raise FooNotFound
|
97
|
+
end
|
98
|
+
}
|
99
|
+
assert_nothing_raised { get '/' }
|
100
|
+
assert_equal 404, status
|
101
|
+
assert_equal 'foo! not found.', body
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'has a not_found method for backwards compatibility' do
|
105
|
+
mock_app {
|
106
|
+
not_found do
|
107
|
+
"Lost, are we?"
|
108
|
+
end
|
109
|
+
}
|
110
|
+
|
111
|
+
get '/test'
|
112
|
+
assert_equal 404, status
|
113
|
+
assert_equal "Lost, are we?", body
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'inherits error mappings from base class' do
|
117
|
+
base = Class.new(Sinatra::Base)
|
118
|
+
base.error(FooError) { 'base class' }
|
119
|
+
|
120
|
+
mock_app(base) {
|
121
|
+
set :raise_errors, false
|
122
|
+
get '/' do
|
123
|
+
raise FooError
|
124
|
+
end
|
125
|
+
}
|
126
|
+
|
127
|
+
get '/'
|
128
|
+
assert_equal 'base class', body
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'overrides error mappings in base class' do
|
132
|
+
base = Class.new(Sinatra::Base)
|
133
|
+
base.error(FooError) { 'base class' }
|
134
|
+
|
135
|
+
mock_app(base) {
|
136
|
+
set :raise_errors, false
|
137
|
+
error(FooError) { 'subclass' }
|
138
|
+
get '/' do
|
139
|
+
raise FooError
|
140
|
+
end
|
141
|
+
}
|
142
|
+
|
143
|
+
get '/'
|
144
|
+
assert_equal 'subclass', body
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
describe 'Custom Error Pages' do
|
149
|
+
it 'allows numeric status code mappings to be registered with ::error' do
|
150
|
+
mock_app {
|
151
|
+
set :raise_errors, false
|
152
|
+
error(500) { 'Foo!' }
|
153
|
+
get '/' do
|
154
|
+
[500, {}, 'Internal Foo Error']
|
155
|
+
end
|
156
|
+
}
|
157
|
+
get '/'
|
158
|
+
assert_equal 500, status
|
159
|
+
assert_equal 'Foo!', body
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'allows ranges of status code mappings to be registered with :error' do
|
163
|
+
mock_app {
|
164
|
+
set :raise_errors, false
|
165
|
+
error(500..550) { "Error: #{response.status}" }
|
166
|
+
get '/' do
|
167
|
+
[507, {}, 'A very special error']
|
168
|
+
end
|
169
|
+
}
|
170
|
+
get '/'
|
171
|
+
assert_equal 507, status
|
172
|
+
assert_equal 'Error: 507', body
|
173
|
+
end
|
174
|
+
|
175
|
+
class FooError < RuntimeError
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'runs after exception mappings and overwrites body' do
|
179
|
+
mock_app {
|
180
|
+
set :raise_errors, false
|
181
|
+
error FooError do
|
182
|
+
response.status = 502
|
183
|
+
'from exception mapping'
|
184
|
+
end
|
185
|
+
error(500) { 'from 500 handler' }
|
186
|
+
error(502) { 'from custom error page' }
|
187
|
+
|
188
|
+
get '/' do
|
189
|
+
raise FooError
|
190
|
+
end
|
191
|
+
}
|
192
|
+
get '/'
|
193
|
+
assert_equal 502, status
|
194
|
+
assert_equal 'from custom error page', body
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
class MiddlewareTest < Test::Unit::TestCase
|
4
|
+
setup do
|
5
|
+
@app = mock_app(Sinatra::Application) {
|
6
|
+
get '/*' do
|
7
|
+
response.headers['X-Tests'] = env['test.ran'].
|
8
|
+
map { |n| n.split('::').last }.
|
9
|
+
join(', ')
|
10
|
+
env['PATH_INFO']
|
11
|
+
end
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
class MockMiddleware < Struct.new(:app)
|
16
|
+
def call(env)
|
17
|
+
(env['test.ran'] ||= []) << self.class.to_s
|
18
|
+
app.call(env)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class UpcaseMiddleware < MockMiddleware
|
23
|
+
def call(env)
|
24
|
+
env['PATH_INFO'] = env['PATH_INFO'].upcase
|
25
|
+
super
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
it "is added with Sinatra::Application.use" do
|
30
|
+
@app.use UpcaseMiddleware
|
31
|
+
get '/hello-world'
|
32
|
+
assert ok?
|
33
|
+
assert_equal '/HELLO-WORLD', body
|
34
|
+
end
|
35
|
+
|
36
|
+
class DowncaseMiddleware < MockMiddleware
|
37
|
+
def call(env)
|
38
|
+
env['PATH_INFO'] = env['PATH_INFO'].downcase
|
39
|
+
super
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
it "runs in the order defined" do
|
44
|
+
@app.use UpcaseMiddleware
|
45
|
+
@app.use DowncaseMiddleware
|
46
|
+
get '/Foo'
|
47
|
+
assert_equal "/foo", body
|
48
|
+
assert_equal "UpcaseMiddleware, DowncaseMiddleware", response['X-Tests']
|
49
|
+
end
|
50
|
+
|
51
|
+
it "resets the prebuilt pipeline when new middleware is added" do
|
52
|
+
@app.use UpcaseMiddleware
|
53
|
+
get '/Foo'
|
54
|
+
assert_equal "/FOO", body
|
55
|
+
@app.use DowncaseMiddleware
|
56
|
+
get '/Foo'
|
57
|
+
assert_equal '/foo', body
|
58
|
+
assert_equal "UpcaseMiddleware, DowncaseMiddleware", response['X-Tests']
|
59
|
+
end
|
60
|
+
|
61
|
+
it "works when app is used as middleware" do
|
62
|
+
@app.use UpcaseMiddleware
|
63
|
+
@app = @app.new
|
64
|
+
get '/Foo'
|
65
|
+
assert_equal "/FOO", body
|
66
|
+
assert_equal "UpcaseMiddleware", response['X-Tests']
|
67
|
+
end
|
68
|
+
end
|
File without changes
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
class RequestTest < Test::Unit::TestCase
|
4
|
+
it 'responds to #user_agent' do
|
5
|
+
request = Sinatra::Request.new({'HTTP_USER_AGENT' => 'Test'})
|
6
|
+
assert request.respond_to?(:user_agent)
|
7
|
+
assert_equal 'Test', request.user_agent
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'parses POST params when Content-Type is form-dataish' do
|
11
|
+
request = Sinatra::Request.new(
|
12
|
+
'REQUEST_METHOD' => 'PUT',
|
13
|
+
'CONTENT_TYPE' => 'application/x-www-form-urlencoded',
|
14
|
+
'rack.input' => StringIO.new('foo=bar')
|
15
|
+
)
|
16
|
+
assert_equal 'bar', request.params['foo']
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'is secure when the url scheme is https' do
|
20
|
+
request = Sinatra::Request.new('rack.url_scheme' => 'https')
|
21
|
+
assert request.secure?
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'is not secure when the url scheme is http' do
|
25
|
+
request = Sinatra::Request.new('rack.url_scheme' => 'http')
|
26
|
+
assert !request.secure?
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'respects X-Forwarded-Proto header for proxied SSL' do
|
30
|
+
request = Sinatra::Request.new('HTTP_X_FORWARDED_PROTO' => 'https')
|
31
|
+
assert request.secure?
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require File.dirname(__FILE__) + '/helper'
|
4
|
+
|
5
|
+
class ResponseTest < Test::Unit::TestCase
|
6
|
+
setup do
|
7
|
+
@response = Sinatra::Response.new
|
8
|
+
end
|
9
|
+
|
10
|
+
it "initializes with 200, text/html, and empty body" do
|
11
|
+
assert_equal 200, @response.status
|
12
|
+
assert_equal 'text/html', @response['Content-Type']
|
13
|
+
assert_equal [], @response.body
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'uses case insensitive headers' do
|
17
|
+
@response['content-type'] = 'application/foo'
|
18
|
+
assert_equal 'application/foo', @response['Content-Type']
|
19
|
+
assert_equal 'application/foo', @response['CONTENT-TYPE']
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'writes to body' do
|
23
|
+
@response.body = 'Hello'
|
24
|
+
@response.write ' World'
|
25
|
+
assert_equal 'Hello World', @response.body
|
26
|
+
end
|
27
|
+
|
28
|
+
[204, 304].each do |status_code|
|
29
|
+
it "removes the Content-Type header and body when response status is #{status_code}" do
|
30
|
+
@response.status = status_code
|
31
|
+
@response.body = ['Hello World']
|
32
|
+
assert_equal [status_code, {}, []], @response.finish
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'Calculates the Content-Length using the bytesize of the body' do
|
37
|
+
@response.body = ['Hello', 'World!', '✈']
|
38
|
+
status, headers, body = @response.finish
|
39
|
+
assert_equal '14', headers['Content-Length']
|
40
|
+
assert_equal @response.body, body
|
41
|
+
end
|
42
|
+
end
|
data/test/result_test.rb
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
class ResultTest < Test::Unit::TestCase
|
4
|
+
it "sets response.body when result is a String" do
|
5
|
+
mock_app {
|
6
|
+
get '/' do
|
7
|
+
'Hello World'
|
8
|
+
end
|
9
|
+
}
|
10
|
+
|
11
|
+
get '/'
|
12
|
+
assert ok?
|
13
|
+
assert_equal 'Hello World', body
|
14
|
+
end
|
15
|
+
|
16
|
+
it "sets response.body when result is an Array of Strings" do
|
17
|
+
mock_app {
|
18
|
+
get '/' do
|
19
|
+
['Hello', 'World']
|
20
|
+
end
|
21
|
+
}
|
22
|
+
|
23
|
+
get '/'
|
24
|
+
assert ok?
|
25
|
+
assert_equal 'HelloWorld', body
|
26
|
+
end
|
27
|
+
|
28
|
+
it "sets response.body when result responds to #each" do
|
29
|
+
mock_app {
|
30
|
+
get '/' do
|
31
|
+
res = lambda { 'Hello World' }
|
32
|
+
def res.each ; yield call ; end
|
33
|
+
res
|
34
|
+
end
|
35
|
+
}
|
36
|
+
|
37
|
+
get '/'
|
38
|
+
assert ok?
|
39
|
+
assert_equal 'Hello World', body
|
40
|
+
end
|
41
|
+
|
42
|
+
it "sets response.body to [] when result is nil" do
|
43
|
+
mock_app {
|
44
|
+
get '/' do
|
45
|
+
nil
|
46
|
+
end
|
47
|
+
}
|
48
|
+
|
49
|
+
get '/'
|
50
|
+
assert ok?
|
51
|
+
assert_equal '', body
|
52
|
+
end
|
53
|
+
|
54
|
+
it "sets status, headers, and body when result is a Rack response tuple" do
|
55
|
+
mock_app {
|
56
|
+
get '/' do
|
57
|
+
[205, {'Content-Type' => 'foo/bar'}, 'Hello World']
|
58
|
+
end
|
59
|
+
}
|
60
|
+
|
61
|
+
get '/'
|
62
|
+
assert_equal 205, status
|
63
|
+
assert_equal 'foo/bar', response['Content-Type']
|
64
|
+
assert_equal 'Hello World', body
|
65
|
+
end
|
66
|
+
|
67
|
+
it "sets status and body when result is a two-tuple" do
|
68
|
+
mock_app {
|
69
|
+
get '/' do
|
70
|
+
[409, 'formula of']
|
71
|
+
end
|
72
|
+
}
|
73
|
+
|
74
|
+
get '/'
|
75
|
+
assert_equal 409, status
|
76
|
+
assert_equal 'formula of', body
|
77
|
+
end
|
78
|
+
|
79
|
+
it "raises a TypeError when result is a non two or three tuple Array" do
|
80
|
+
mock_app {
|
81
|
+
get '/' do
|
82
|
+
[409, 'formula of', 'something else', 'even more']
|
83
|
+
end
|
84
|
+
}
|
85
|
+
|
86
|
+
assert_raise(TypeError) { get '/' }
|
87
|
+
end
|
88
|
+
|
89
|
+
it "sets status when result is a Fixnum status code" do
|
90
|
+
mock_app {
|
91
|
+
get('/') { 205 }
|
92
|
+
}
|
93
|
+
|
94
|
+
get '/'
|
95
|
+
assert_equal 205, status
|
96
|
+
assert_equal '', body
|
97
|
+
end
|
98
|
+
end
|