sinatra 0.9.1.1 → 0.9.2
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.
- data/CHANGES +81 -0
- data/LICENSE +1 -1
- data/README.rdoc +63 -23
- data/Rakefile +25 -23
- data/compat/haml_test.rb +7 -4
- data/compat/helper.rb +3 -0
- data/compat/sass_test.rb +10 -0
- data/lib/sinatra/base.rb +240 -148
- data/lib/sinatra/compat.rb +16 -0
- data/lib/sinatra/main.rb +2 -14
- data/lib/sinatra/showexceptions.rb +303 -0
- data/lib/sinatra/test.rb +2 -0
- data/sinatra.gemspec +13 -4
- data/test/base_test.rb +123 -93
- data/test/builder_test.rb +2 -1
- data/test/contest.rb +64 -0
- data/test/erb_test.rb +1 -1
- data/test/extensions_test.rb +17 -1
- data/test/filter_test.rb +1 -1
- data/test/haml_test.rb +25 -3
- data/test/helper.rb +43 -48
- data/test/helpers_test.rb +446 -440
- data/test/mapped_error_test.rb +138 -143
- data/test/middleware_test.rb +2 -2
- data/test/options_test.rb +241 -248
- data/test/render_backtrace_test.rb +145 -0
- data/test/request_test.rb +1 -1
- data/test/response_test.rb +2 -2
- data/test/result_test.rb +1 -1
- data/test/route_added_hook_test.rb +59 -0
- data/test/routing_test.rb +94 -22
- data/test/sass_test.rb +44 -1
- data/test/server_test.rb +5 -3
- data/test/sinatra_test.rb +1 -1
- data/test/static_test.rb +2 -2
- data/test/templates_test.rb +16 -4
- data/test/test_test.rb +15 -7
- data/test/views/error.builder +3 -0
- data/test/views/error.erb +3 -0
- data/test/views/error.haml +3 -0
- data/test/views/error.sass +2 -0
- metadata +32 -4
- data/test/reload_test.rb +0 -68
data/lib/sinatra/compat.rb
CHANGED
@@ -115,6 +115,22 @@ module Sinatra
|
|
115
115
|
halt data
|
116
116
|
end
|
117
117
|
|
118
|
+
# The :views_directory, :options, :haml, and :sass options are deprecated.
|
119
|
+
def render(engine, template, options={}, locals={}, &bk)
|
120
|
+
if options.key?(:views_directory)
|
121
|
+
sinatra_warn "The :views_directory option is deprecated; use :views instead."
|
122
|
+
options[:views] = options.delete(:views_directory)
|
123
|
+
end
|
124
|
+
[:options, engine.to_sym].each do |key|
|
125
|
+
if options.key?(key)
|
126
|
+
sinatra_warn "Passing :#{key} => {} to #{engine} is deprecated; " +
|
127
|
+
"merge options directly into hash instead."
|
128
|
+
options.merge! options.delete(key)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
super(engine, template, options, locals, &bk)
|
132
|
+
end
|
133
|
+
|
118
134
|
# Throwing halt with a Symbol and the to_result convention are
|
119
135
|
# deprecated. Override the invoke method to detect those types of return
|
120
136
|
# values.
|
data/lib/sinatra/main.rb
CHANGED
@@ -6,26 +6,14 @@ module Sinatra
|
|
6
6
|
# we assume that the first file that requires 'sinatra' is the
|
7
7
|
# app_file. all other path related options are calculated based
|
8
8
|
# on this path by default.
|
9
|
-
set :app_file,
|
10
|
-
ignore = [
|
11
|
-
/lib\/sinatra.*\.rb$/, # all sinatra code
|
12
|
-
/\(.*\)/, # generated code
|
13
|
-
/custom_require\.rb$/ # rubygems require hacks
|
14
|
-
]
|
15
|
-
path =
|
16
|
-
caller.map{ |line| line.split(/:\d/, 2).first }.find do |file|
|
17
|
-
next if ignore.any? { |pattern| file =~ pattern }
|
18
|
-
file
|
19
|
-
end
|
20
|
-
path || $0
|
21
|
-
}.call
|
9
|
+
set :app_file, caller_files.first || $0
|
22
10
|
|
23
11
|
set :run, Proc.new { $0 == app_file }
|
24
12
|
|
25
13
|
if run? && ARGV.any?
|
26
14
|
require 'optparse'
|
27
15
|
OptionParser.new { |op|
|
28
|
-
op.on('-x') { set :
|
16
|
+
op.on('-x') { set :lock, true }
|
29
17
|
op.on('-e env') { |val| set :environment, val.to_sym }
|
30
18
|
op.on('-s server') { |val| set :server, val }
|
31
19
|
op.on('-p port') { |val| set :port, val.to_i }
|
@@ -0,0 +1,303 @@
|
|
1
|
+
require 'rack/showexceptions'
|
2
|
+
|
3
|
+
module Sinatra
|
4
|
+
class ShowExceptions < Rack::ShowExceptions
|
5
|
+
def initialize(app)
|
6
|
+
@app = app
|
7
|
+
@template = ERB.new(TEMPLATE)
|
8
|
+
end
|
9
|
+
|
10
|
+
def frame_class(frame)
|
11
|
+
if frame.filename =~ /lib\/sinatra.*\.rb/
|
12
|
+
"framework"
|
13
|
+
elsif (defined?(Gem) && frame.filename.include?(Gem.dir)) ||
|
14
|
+
frame.filename =~ /\/bin\/(\w+)$/
|
15
|
+
"system"
|
16
|
+
else
|
17
|
+
"app"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
TEMPLATE = <<HTML
|
22
|
+
<!DOCTYPE html>
|
23
|
+
<html>
|
24
|
+
<head>
|
25
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
26
|
+
<title><%=h exception.class %> at <%=h path %></title>
|
27
|
+
|
28
|
+
<script type="text/javascript">
|
29
|
+
//<!--
|
30
|
+
function toggle(id) {
|
31
|
+
var pre = document.getElementById("pre-" + id);
|
32
|
+
var post = document.getElementById("post-" + id);
|
33
|
+
var context = document.getElementById("context-" + id);
|
34
|
+
|
35
|
+
if (pre.style.display == 'block') {
|
36
|
+
pre.style.display = 'none';
|
37
|
+
post.style.display = 'none';
|
38
|
+
context.style.background = "none";
|
39
|
+
} else {
|
40
|
+
pre.style.display = 'block';
|
41
|
+
post.style.display = 'block';
|
42
|
+
context.style.background = "#fffed9";
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
46
|
+
function toggleBacktrace(){
|
47
|
+
var bt = document.getElementById("backtrace");
|
48
|
+
var toggler = document.getElementById("expando");
|
49
|
+
|
50
|
+
if (bt.className == 'condensed') {
|
51
|
+
bt.className = 'expanded';
|
52
|
+
toggler.innerHTML = "(condense)";
|
53
|
+
} else {
|
54
|
+
bt.className = 'condensed';
|
55
|
+
toggler.innerHTML = "(expand)";
|
56
|
+
}
|
57
|
+
}
|
58
|
+
//-->
|
59
|
+
</script>
|
60
|
+
|
61
|
+
<style type="text/css" media="screen">
|
62
|
+
* {margin: 0; padding: 0; border: 0; outline: 0;}
|
63
|
+
div.clear {clear: both;}
|
64
|
+
body {background: #EEEEEE; margin: 0; padding: 0;
|
65
|
+
font-family: 'Lucida Grande', 'Lucida Sans Unicode',
|
66
|
+
'Garuda';}
|
67
|
+
code {font-family: 'Lucida Console', monospace;
|
68
|
+
font-size: 12px;}
|
69
|
+
li {height: 18px;}
|
70
|
+
ul {list-style: none; margin: 0; padding: 0;}
|
71
|
+
ol:hover {cursor: pointer;}
|
72
|
+
ol li {white-space: pre;}
|
73
|
+
#explanation {font-size: 12px; color: #666666;
|
74
|
+
margin: 20px 0 0 100px;}
|
75
|
+
/* WRAP */
|
76
|
+
#wrap {width: 860px; background: #FFFFFF; margin: 0 auto;
|
77
|
+
padding: 30px 50px 20px 50px;
|
78
|
+
border-left: 1px solid #DDDDDD;
|
79
|
+
border-right: 1px solid #DDDDDD;}
|
80
|
+
/* HEADER */
|
81
|
+
#header {margin: 0 auto 25px auto;}
|
82
|
+
#header img {float: left;}
|
83
|
+
#header #summary {float: left; margin: 12px 0 0 20px; width:520px;
|
84
|
+
font-family: 'Lucida Grande', 'Lucida Sans Unicode';}
|
85
|
+
h1 {margin: 0; font-size: 36px; color: #981919;}
|
86
|
+
h2 {margin: 0; font-size: 22px; color: #333333;}
|
87
|
+
#header ul {margin: 0; font-size: 12px; color: #666666;}
|
88
|
+
#header ul li strong{color: #444444;}
|
89
|
+
#header ul li {display: inline; padding: 0 10px;}
|
90
|
+
#header ul li.first {padding-left: 0;}
|
91
|
+
#header ul li.last {border: 0; padding-right: 0;}
|
92
|
+
/* BODY */
|
93
|
+
#backtrace,
|
94
|
+
#get,
|
95
|
+
#post,
|
96
|
+
#cookies,
|
97
|
+
#rack {width: 860px; margin: 0 auto 10px auto;}
|
98
|
+
p#nav {float: right; font-size: 14px;}
|
99
|
+
/* BACKTRACE */
|
100
|
+
a#expando {float: left; padding-left: 5px; color: #666666;
|
101
|
+
font-size: 14px; text-decoration: none; cursor: pointer;}
|
102
|
+
a#expando:hover {text-decoration: underline;}
|
103
|
+
h3 {float: left; width: 100px; margin-bottom: 10px;
|
104
|
+
color: #981919; font-size: 14px; font-weight: bold;}
|
105
|
+
#nav a {color: #666666; text-decoration: none; padding: 0 5px;}
|
106
|
+
#backtrace li.frame-info {background: #f7f7f7; padding-left: 10px;
|
107
|
+
font-size: 12px; color: #333333;}
|
108
|
+
#backtrace ul {list-style-position: outside; border: 1px solid #E9E9E9;
|
109
|
+
border-bottom: 0;}
|
110
|
+
#backtrace ol {width: 808px; margin-left: 50px;
|
111
|
+
font: 10px 'Lucida Console', monospace; color: #666666;}
|
112
|
+
#backtrace ol li {border: 0; border-left: 1px solid #E9E9E9;
|
113
|
+
padding: 2px 0;}
|
114
|
+
#backtrace ol code {font-size: 10px; color: #555555; padding-left: 5px;}
|
115
|
+
#backtrace-ul li {border-bottom: 1px solid #E9E9E9; height: auto;
|
116
|
+
padding: 3px 0;}
|
117
|
+
#backtrace-ul .code {padding: 6px 0 4px 0;}
|
118
|
+
#backtrace.condensed .system,
|
119
|
+
#backtrace.condensed .framework {display:none;}
|
120
|
+
/* REQUEST DATA */
|
121
|
+
p.no-data {padding-top: 2px; font-size: 12px; color: #666666;}
|
122
|
+
table.req {width: 760px; text-align: left; font-size: 12px;
|
123
|
+
color: #666666; padding: 0; border-spacing: 0;
|
124
|
+
border: 1px solid #EEEEEE; border-bottom: 0;
|
125
|
+
border-left: 0;}
|
126
|
+
table.req tr th {padding: 2px 10px; font-weight: bold;
|
127
|
+
background: #F7F7F7; border-bottom: 1px solid #EEEEEE;
|
128
|
+
border-left: 1px solid #EEEEEE;}
|
129
|
+
table.req tr td {padding: 2px 20px 2px 10px;
|
130
|
+
border-bottom: 1px solid #EEEEEE;
|
131
|
+
border-left: 1px solid #EEEEEE;}
|
132
|
+
/* HIDE PRE/POST CODE AT START */
|
133
|
+
.pre-context,
|
134
|
+
.post-context {display: none;}
|
135
|
+
</style>
|
136
|
+
</head>
|
137
|
+
<body>
|
138
|
+
<div id="wrap">
|
139
|
+
<div id="header">
|
140
|
+
<img src="/__sinatra__/500.png" alt="application error" />
|
141
|
+
<div id="summary">
|
142
|
+
<h1><strong><%=h exception.class %></strong> at <strong><%=h path %>
|
143
|
+
</strong></h1>
|
144
|
+
<h2><%=h exception.message %></h2>
|
145
|
+
<ul>
|
146
|
+
<li class="first"><strong>file:</strong> <code>
|
147
|
+
<%=h frames.first.filename.split("/").last %></code></li>
|
148
|
+
<li><strong>location:</strong> <code><%=h frames.first.function %>
|
149
|
+
</code></li>
|
150
|
+
<li class="last"><strong>line:
|
151
|
+
</strong> <%=h frames.first.lineno %></li>
|
152
|
+
</ul>
|
153
|
+
</div>
|
154
|
+
<div class="clear"></div>
|
155
|
+
</div>
|
156
|
+
|
157
|
+
<div id="backtrace" class='condensed'>
|
158
|
+
<h3>BACKTRACE</h3>
|
159
|
+
<p><a href="#" id="expando"
|
160
|
+
onclick="toggleBacktrace(); return false">(expand)</a></p>
|
161
|
+
<p id="nav"><strong>JUMP TO:</strong>
|
162
|
+
<a href="#get-info">GET</a>
|
163
|
+
<a href="#post-info">POST</a>
|
164
|
+
<a href="#cookie-info">COOKIES</a>
|
165
|
+
<a href="#env-info">ENV</a>
|
166
|
+
</p>
|
167
|
+
<div class="clear"></div>
|
168
|
+
|
169
|
+
<ul id="backtrace-ul">
|
170
|
+
|
171
|
+
<% id = 1 %>
|
172
|
+
<% frames.each do |frame| %>
|
173
|
+
<% if frame.context_line && frame.context_line != "#" %>
|
174
|
+
|
175
|
+
<li class="frame-info <%= frame_class(frame) %>">
|
176
|
+
<code><%=h frame.filename %></code> in
|
177
|
+
<code><strong><%=h frame.function %></strong></code>
|
178
|
+
</li>
|
179
|
+
|
180
|
+
<li class="code <%= frame_class(frame) %>">
|
181
|
+
<% if frame.pre_context %>
|
182
|
+
<ol start="<%=h frame.pre_context_lineno + 1 %>"
|
183
|
+
class="pre-context" id="pre-<%= id %>"
|
184
|
+
onclick="toggle(<%= id %>);">
|
185
|
+
<% frame.pre_context.each do |line| %>
|
186
|
+
<li class="pre-context-line"><code><%=h line %></code></li>
|
187
|
+
<% end %>
|
188
|
+
</ol>
|
189
|
+
<% end %>
|
190
|
+
|
191
|
+
<ol start="<%= frame.lineno %>" class="context" id="<%= id %>"
|
192
|
+
onclick="toggle(<%= id %>);">
|
193
|
+
<li class="context-line" id="context-<%= id %>"><code><%=
|
194
|
+
h frame.context_line %></code></li>
|
195
|
+
</ol>
|
196
|
+
|
197
|
+
<% if frame.post_context %>
|
198
|
+
<ol start="<%=h frame.lineno + 1 %>" class="post-context"
|
199
|
+
id="post-<%= id %>" onclick="toggle(<%= id %>);">
|
200
|
+
<% frame.post_context.each do |line| %>
|
201
|
+
<li class="post-context-line"><code><%=h line %></code></li>
|
202
|
+
<% end %>
|
203
|
+
</ol>
|
204
|
+
<% end %>
|
205
|
+
<div class="clear"></div>
|
206
|
+
</li>
|
207
|
+
|
208
|
+
<% end %>
|
209
|
+
|
210
|
+
<% id += 1 %>
|
211
|
+
<% end %>
|
212
|
+
|
213
|
+
</ul>
|
214
|
+
</div> <!-- /BACKTRACE -->
|
215
|
+
|
216
|
+
<div id="get">
|
217
|
+
<h3 id="get-info">GET</h3>
|
218
|
+
<% unless req.GET.empty? %>
|
219
|
+
<table class="req">
|
220
|
+
<tr>
|
221
|
+
<th>Variable</th>
|
222
|
+
<th>Value</th>
|
223
|
+
</tr>
|
224
|
+
<% req.GET.sort_by { |k, v| k.to_s }.each { |key, val| %>
|
225
|
+
<tr>
|
226
|
+
<td><%=h key %></td>
|
227
|
+
<td class="code"><div><%=h val.inspect %></div></td>
|
228
|
+
</tr>
|
229
|
+
<% } %>
|
230
|
+
</table>
|
231
|
+
<% else %>
|
232
|
+
<p class="no-data">No GET data.</p>
|
233
|
+
<% end %>
|
234
|
+
<div class="clear"></div>
|
235
|
+
</div> <!-- /GET -->
|
236
|
+
|
237
|
+
<div id="post">
|
238
|
+
<h3 id="post-info">POST</h3>
|
239
|
+
<% unless req.POST.empty? %>
|
240
|
+
<table class="req">
|
241
|
+
<tr>
|
242
|
+
<th>Variable</th>
|
243
|
+
<th>Value</th>
|
244
|
+
</tr>
|
245
|
+
<% req.POST.sort_by { |k, v| k.to_s }.each { |key, val| %>
|
246
|
+
<tr>
|
247
|
+
<td><%=h key %></td>
|
248
|
+
<td class="code"><div><%=h val.inspect %></div></td>
|
249
|
+
</tr>
|
250
|
+
<% } %>
|
251
|
+
</table>
|
252
|
+
<% else %>
|
253
|
+
<p class="no-data">No POST data.</p>
|
254
|
+
<% end %>
|
255
|
+
<div class="clear"></div>
|
256
|
+
</div> <!-- /POST -->
|
257
|
+
|
258
|
+
<div id="cookies">
|
259
|
+
<h3 id="cookie-info">COOKIES</h3>
|
260
|
+
<% unless req.cookies.empty? %>
|
261
|
+
<table class="req">
|
262
|
+
<tr>
|
263
|
+
<th>Variable</th>
|
264
|
+
<th>Value</th>
|
265
|
+
</tr>
|
266
|
+
<% req.cookies.each { |key, val| %>
|
267
|
+
<tr>
|
268
|
+
<td><%=h key %></td>
|
269
|
+
<td class="code"><div><%=h val.inspect %></div></td>
|
270
|
+
</tr>
|
271
|
+
<% } %>
|
272
|
+
</table>
|
273
|
+
<% else %>
|
274
|
+
<p class="no-data">No cookie data.</p>
|
275
|
+
<% end %>
|
276
|
+
<div class="clear"></div>
|
277
|
+
</div> <!-- /COOKIES -->
|
278
|
+
|
279
|
+
<div id="rack">
|
280
|
+
<h3 id="env-info">Rack ENV</h3>
|
281
|
+
<table class="req">
|
282
|
+
<tr>
|
283
|
+
<th>Variable</th>
|
284
|
+
<th>Value</th>
|
285
|
+
</tr>
|
286
|
+
<% env.sort_by { |k, v| k.to_s }.each { |key, val| %>
|
287
|
+
<tr>
|
288
|
+
<td><%=h key %></td>
|
289
|
+
<td class="code"><div><%=h val %></div></td>
|
290
|
+
</tr>
|
291
|
+
<% } %>
|
292
|
+
</table>
|
293
|
+
<div class="clear"></div>
|
294
|
+
</div> <!-- /RACK ENV -->
|
295
|
+
|
296
|
+
<p id="explanation">You're seeing this error because you use you have
|
297
|
+
enabled the <code>show_exceptions</code> option.</p>
|
298
|
+
</div> <!-- /WRAP -->
|
299
|
+
</body>
|
300
|
+
</html>
|
301
|
+
HTML
|
302
|
+
end
|
303
|
+
end
|
data/lib/sinatra/test.rb
CHANGED
data/sinatra.gemspec
CHANGED
@@ -3,8 +3,8 @@ Gem::Specification.new do |s|
|
|
3
3
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
4
4
|
|
5
5
|
s.name = 'sinatra'
|
6
|
-
s.version = '0.9.
|
7
|
-
s.date = '2009-
|
6
|
+
s.version = '0.9.2'
|
7
|
+
s.date = '2009-05-18'
|
8
8
|
|
9
9
|
s.description = "Classy web-development dressed in a DSL"
|
10
10
|
s.summary = "Classy web-development dressed in a DSL"
|
@@ -60,6 +60,7 @@ Gem::Specification.new do |s|
|
|
60
60
|
lib/sinatra/images/404.png
|
61
61
|
lib/sinatra/images/500.png
|
62
62
|
lib/sinatra/main.rb
|
63
|
+
lib/sinatra/showexceptions.rb
|
63
64
|
lib/sinatra/test.rb
|
64
65
|
lib/sinatra/test/bacon.rb
|
65
66
|
lib/sinatra/test/rspec.rb
|
@@ -68,6 +69,7 @@ Gem::Specification.new do |s|
|
|
68
69
|
sinatra.gemspec
|
69
70
|
test/base_test.rb
|
70
71
|
test/builder_test.rb
|
72
|
+
test/contest.rb
|
71
73
|
test/data/reload_app_file.rb
|
72
74
|
test/erb_test.rb
|
73
75
|
test/extensions_test.rb
|
@@ -78,10 +80,11 @@ Gem::Specification.new do |s|
|
|
78
80
|
test/mapped_error_test.rb
|
79
81
|
test/middleware_test.rb
|
80
82
|
test/options_test.rb
|
81
|
-
test/
|
83
|
+
test/render_backtrace_test.rb
|
82
84
|
test/request_test.rb
|
83
85
|
test/response_test.rb
|
84
86
|
test/result_test.rb
|
87
|
+
test/route_added_hook_test.rb
|
85
88
|
test/routing_test.rb
|
86
89
|
test/sass_test.rb
|
87
90
|
test/server_test.rb
|
@@ -89,6 +92,10 @@ Gem::Specification.new do |s|
|
|
89
92
|
test/static_test.rb
|
90
93
|
test/templates_test.rb
|
91
94
|
test/test_test.rb
|
95
|
+
test/views/error.builder
|
96
|
+
test/views/error.erb
|
97
|
+
test/views/error.haml
|
98
|
+
test/views/error.sass
|
92
99
|
test/views/hello.builder
|
93
100
|
test/views/hello.erb
|
94
101
|
test/views/hello.haml
|
@@ -104,7 +111,9 @@ Gem::Specification.new do |s|
|
|
104
111
|
s.test_files = s.files.select {|path| path =~ /^test\/.*_test.rb/}
|
105
112
|
|
106
113
|
s.extra_rdoc_files = %w[README.rdoc LICENSE]
|
107
|
-
s.add_dependency 'rack',
|
114
|
+
s.add_dependency 'rack', '>= 0.9.1'
|
115
|
+
s.add_development_dependency 'shotgun', '>= 0.2', '< 1.0'
|
116
|
+
s.add_development_dependency 'rack-test', '>= 0.3.0'
|
108
117
|
|
109
118
|
s.has_rdoc = true
|
110
119
|
s.homepage = "http://sinatra.rubyforge.org"
|
data/test/base_test.rb
CHANGED
@@ -1,130 +1,160 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/helper'
|
2
2
|
|
3
|
-
|
3
|
+
class BaseTest < Test::Unit::TestCase
|
4
|
+
def test_default
|
5
|
+
assert true
|
6
|
+
end
|
4
7
|
|
5
|
-
|
6
|
-
|
7
|
-
'
|
8
|
+
describe 'Sinatra::Base subclasses' do
|
9
|
+
class TestApp < Sinatra::Base
|
10
|
+
get '/' do
|
11
|
+
'Hello World'
|
12
|
+
end
|
8
13
|
end
|
9
|
-
end
|
10
14
|
|
11
|
-
|
12
|
-
|
13
|
-
|
15
|
+
it 'include Rack::Utils' do
|
16
|
+
assert TestApp.included_modules.include?(Rack::Utils)
|
17
|
+
end
|
14
18
|
|
15
|
-
|
16
|
-
|
19
|
+
it 'processes requests with #call' do
|
20
|
+
assert TestApp.respond_to?(:call)
|
17
21
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
22
|
+
request = Rack::MockRequest.new(TestApp)
|
23
|
+
response = request.get('/')
|
24
|
+
assert response.ok?
|
25
|
+
assert_equal 'Hello World', response.body
|
26
|
+
end
|
23
27
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
28
|
+
class TestApp < Sinatra::Base
|
29
|
+
get '/state' do
|
30
|
+
@foo ||= "new"
|
31
|
+
body = "Foo: #{@foo}"
|
32
|
+
@foo = 'discard'
|
33
|
+
body
|
34
|
+
end
|
29
35
|
end
|
30
|
-
end
|
31
36
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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
|
38
56
|
end
|
39
57
|
end
|
40
|
-
end
|
41
58
|
|
42
|
-
describe "Sinatra::Base as Rack middleware" do
|
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']] }
|
43
64
|
|
44
|
-
|
45
|
-
|
65
|
+
class TestMiddleware < Sinatra::Base
|
66
|
+
end
|
46
67
|
|
47
|
-
|
48
|
-
|
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
|
49
72
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
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
|
54
88
|
|
55
|
-
it 'exposes the downstream app' do
|
56
89
|
middleware = TestMiddleware.new(app)
|
57
|
-
|
58
|
-
end
|
90
|
+
request = Rack::MockRequest.new(middleware)
|
59
91
|
|
60
|
-
|
61
|
-
|
62
|
-
|
92
|
+
it 'intercepts requests' do
|
93
|
+
response = request.get('/')
|
94
|
+
assert response.ok?
|
95
|
+
assert_equal 'Hello from middleware', response.body
|
63
96
|
end
|
64
|
-
end
|
65
97
|
|
66
|
-
|
67
|
-
|
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
|
68
103
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
end
|
104
|
+
it 'calls #route_missing before forwarding downstream' do
|
105
|
+
response = request.get('/missing')
|
106
|
+
assert_equal '1', response['X-Route-Missing']
|
107
|
+
end
|
74
108
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
109
|
+
class TestMiddleware < Sinatra::Base
|
110
|
+
get '/low-level-forward' do
|
111
|
+
app.call(env)
|
112
|
+
end
|
113
|
+
end
|
80
114
|
|
81
|
-
|
82
|
-
|
83
|
-
|
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
|
84
120
|
end
|
85
|
-
end
|
86
121
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
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'
|
131
|
+
end
|
132
|
+
end
|
93
133
|
|
94
|
-
|
95
|
-
|
96
|
-
response['X-Middleware'] = 'true'
|
97
|
-
res = forward
|
98
|
-
assert_nil res
|
134
|
+
it 'forwards the request downstream and integrates the response into the current context' do
|
135
|
+
response = request.get('/explicit-forward')
|
99
136
|
assert_equal 210, response.status
|
100
137
|
assert_equal 'true', response['X-Downstream']
|
101
|
-
assert_equal
|
102
|
-
'
|
138
|
+
assert_equal 'Hello after explicit forward', response.body
|
139
|
+
assert_equal '28', response['Content-Length']
|
103
140
|
end
|
104
|
-
end
|
105
141
|
|
106
|
-
|
107
|
-
|
108
|
-
assert_equal 210, response.status
|
109
|
-
assert_equal 'true', response['X-Downstream']
|
110
|
-
assert_equal 'Hello after explicit forward', response.body
|
111
|
-
assert_equal '28', response['Content-Length']
|
112
|
-
end
|
142
|
+
app_content_length = lambda {|env|
|
143
|
+
[200, {'Content-Length' => '16'}, 'From downstream!']}
|
113
144
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
'From after explicit forward!'
|
145
|
+
class TestMiddlewareContentLength < Sinatra::Base
|
146
|
+
get '/forward' do
|
147
|
+
res = forward
|
148
|
+
'From after explicit forward!'
|
149
|
+
end
|
120
150
|
end
|
121
|
-
end
|
122
151
|
|
123
|
-
|
124
|
-
|
152
|
+
middleware_content_length = TestMiddlewareContentLength.new(app_content_length)
|
153
|
+
request_content_length = Rack::MockRequest.new(middleware_content_length)
|
125
154
|
|
126
|
-
|
127
|
-
|
128
|
-
|
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
|
129
159
|
end
|
130
160
|
end
|