darkhelmet-sinatra 0.9.1.1 → 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.
- data/AUTHORS +2 -0
- data/CHANGES +180 -0
- data/LICENSE +1 -1
- data/README.jp.rdoc +552 -0
- data/README.rdoc +177 -38
- data/Rakefile +18 -25
- data/lib/sinatra.rb +1 -2
- data/lib/sinatra/base.rb +405 -305
- data/lib/sinatra/main.rb +5 -24
- data/lib/sinatra/showexceptions.rb +303 -0
- data/lib/sinatra/tilt.rb +509 -0
- data/sinatra.gemspec +21 -51
- 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/erubis_test.rb +82 -0
- data/test/extensions_test.rb +24 -8
- data/test/filter_test.rb +99 -3
- data/test/haml_test.rb +25 -3
- data/test/helper.rb +43 -48
- data/test/helpers_test.rb +500 -424
- data/test/mapped_error_test.rb +163 -137
- data/test/middleware_test.rb +3 -3
- data/test/request_test.rb +16 -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 +170 -22
- data/test/sass_test.rb +44 -1
- data/test/server_test.rb +19 -13
- data/test/sinatra_test.rb +1 -1
- data/test/static_test.rb +9 -2
- data/test/templates_test.rb +78 -11
- 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.erubis +1 -0
- data/test/views/layout2.erubis +2 -0
- metadata +37 -55
- data/compat/app_test.rb +0 -282
- data/compat/application_test.rb +0 -262
- data/compat/builder_test.rb +0 -101
- data/compat/compat_test.rb +0 -12
- data/compat/custom_error_test.rb +0 -62
- data/compat/erb_test.rb +0 -136
- data/compat/events_test.rb +0 -78
- data/compat/filter_test.rb +0 -30
- data/compat/haml_test.rb +0 -233
- data/compat/helper.rb +0 -30
- data/compat/mapped_error_test.rb +0 -72
- data/compat/pipeline_test.rb +0 -45
- data/compat/public/foo.xml +0 -1
- data/compat/sass_test.rb +0 -57
- data/compat/sessions_test.rb +0 -42
- data/compat/streaming_test.rb +0 -133
- data/compat/sym_params_test.rb +0 -19
- data/compat/template_test.rb +0 -30
- data/compat/use_in_file_templates_test.rb +0 -47
- data/compat/views/foo.builder +0 -1
- data/compat/views/foo.erb +0 -1
- data/compat/views/foo.haml +0 -1
- data/compat/views/foo.sass +0 -2
- data/compat/views/foo_layout.erb +0 -2
- data/compat/views/foo_layout.haml +0 -2
- data/compat/views/layout_test/foo.builder +0 -1
- data/compat/views/layout_test/foo.erb +0 -1
- data/compat/views/layout_test/foo.haml +0 -1
- data/compat/views/layout_test/foo.sass +0 -2
- data/compat/views/layout_test/layout.builder +0 -3
- data/compat/views/layout_test/layout.erb +0 -1
- data/compat/views/layout_test/layout.haml +0 -1
- data/compat/views/layout_test/layout.sass +0 -2
- data/compat/views/no_layout/no_layout.builder +0 -1
- data/compat/views/no_layout/no_layout.haml +0 -1
- data/lib/sinatra/compat.rb +0 -250
- data/lib/sinatra/test.rb +0 -126
- data/lib/sinatra/test/bacon.rb +0 -19
- data/lib/sinatra/test/rspec.rb +0 -13
- data/lib/sinatra/test/spec.rb +0 -11
- data/lib/sinatra/test/unit.rb +0 -13
- data/test/data/reload_app_file.rb +0 -3
- data/test/options_test.rb +0 -374
- data/test/reload_test.rb +0 -68
- data/test/test_test.rb +0 -144
data/lib/sinatra.rb
CHANGED
data/lib/sinatra/base.rb
CHANGED
@@ -3,10 +3,17 @@ require 'time'
|
|
3
3
|
require 'uri'
|
4
4
|
require 'rack'
|
5
5
|
require 'rack/builder'
|
6
|
-
require '
|
6
|
+
require 'sinatra/showexceptions'
|
7
|
+
|
8
|
+
# require tilt if available; fall back on bundled version.
|
9
|
+
begin
|
10
|
+
require 'tilt'
|
11
|
+
rescue LoadError
|
12
|
+
require 'sinatra/tilt'
|
13
|
+
end
|
7
14
|
|
8
15
|
module Sinatra
|
9
|
-
VERSION = '0.
|
16
|
+
VERSION = '0.10.1'
|
10
17
|
|
11
18
|
# The request object. See Rack::Request for more info:
|
12
19
|
# http://rack.rubyforge.org/doc/classes/Rack/Request.html
|
@@ -15,6 +22,7 @@ module Sinatra
|
|
15
22
|
@env['HTTP_USER_AGENT']
|
16
23
|
end
|
17
24
|
|
25
|
+
# Returns an array of acceptable media types for the response
|
18
26
|
def accept
|
19
27
|
@env['HTTP_ACCEPT'].to_s.split(',').map { |a| a.strip }
|
20
28
|
end
|
@@ -22,9 +30,13 @@ module Sinatra
|
|
22
30
|
# Override Rack 0.9.x's #params implementation (see #72 in lighthouse)
|
23
31
|
def params
|
24
32
|
self.GET.update(self.POST)
|
25
|
-
rescue EOFError
|
33
|
+
rescue EOFError, Errno::ESPIPE
|
26
34
|
self.GET
|
27
35
|
end
|
36
|
+
|
37
|
+
def secure?
|
38
|
+
(@env['HTTP_X_FORWARDED_PROTO'] || @env['rack.url_scheme']) == 'https'
|
39
|
+
end
|
28
40
|
end
|
29
41
|
|
30
42
|
# The response object. See Rack::Response and Rack::ResponseHelpers for
|
@@ -32,16 +44,6 @@ module Sinatra
|
|
32
44
|
# http://rack.rubyforge.org/doc/classes/Rack/Response.html
|
33
45
|
# http://rack.rubyforge.org/doc/classes/Rack/Response/Helpers.html
|
34
46
|
class Response < Rack::Response
|
35
|
-
def initialize
|
36
|
-
@status, @body = 200, []
|
37
|
-
@header = Rack::Utils::HeaderHash.new({'Content-Type' => 'text/html'})
|
38
|
-
end
|
39
|
-
|
40
|
-
def write(str)
|
41
|
-
@body << str.to_s
|
42
|
-
str
|
43
|
-
end
|
44
|
-
|
45
47
|
def finish
|
46
48
|
@body = block if block_given?
|
47
49
|
if [204, 304].include?(status.to_i)
|
@@ -52,7 +54,7 @@ module Sinatra
|
|
52
54
|
body = [body] if body.respond_to? :to_str
|
53
55
|
if body.respond_to?(:to_ary)
|
54
56
|
header["Content-Length"] = body.to_ary.
|
55
|
-
inject(0) { |len, part| len +
|
57
|
+
inject(0) { |len, part| len + Rack::Utils.bytesize(part) }.to_s
|
56
58
|
end
|
57
59
|
[status.to_i, header.to_hash, body]
|
58
60
|
end
|
@@ -63,7 +65,7 @@ module Sinatra
|
|
63
65
|
def code ; 404 ; end
|
64
66
|
end
|
65
67
|
|
66
|
-
# Methods available to routes, before filters, and views.
|
68
|
+
# Methods available to routes, before/after filters, and views.
|
67
69
|
module Helpers
|
68
70
|
# Set or retrieve the response status code.
|
69
71
|
def status(value=nil)
|
@@ -113,20 +115,20 @@ module Sinatra
|
|
113
115
|
end
|
114
116
|
|
115
117
|
# Look up a media type by file extension in Rack's mime registry.
|
116
|
-
def
|
117
|
-
Base.
|
118
|
+
def mime_type(type)
|
119
|
+
Base.mime_type(type)
|
118
120
|
end
|
119
121
|
|
120
122
|
# Set the Content-Type of the response body given a media type or file
|
121
123
|
# extension.
|
122
124
|
def content_type(type, params={})
|
123
|
-
|
124
|
-
fail "Unknown media type: %p" % type if
|
125
|
+
mime_type = self.mime_type(type)
|
126
|
+
fail "Unknown media type: %p" % type if mime_type.nil?
|
125
127
|
if params.any?
|
126
128
|
params = params.collect { |kv| "%s=%s" % kv }.join(', ')
|
127
|
-
response['Content-Type'] = [
|
129
|
+
response['Content-Type'] = [mime_type, params].join(";")
|
128
130
|
else
|
129
|
-
response['Content-Type'] =
|
131
|
+
response['Content-Type'] = mime_type
|
130
132
|
end
|
131
133
|
end
|
132
134
|
|
@@ -145,8 +147,8 @@ module Sinatra
|
|
145
147
|
stat = File.stat(path)
|
146
148
|
last_modified stat.mtime
|
147
149
|
|
148
|
-
content_type
|
149
|
-
|
150
|
+
content_type mime_type(opts[:type]) ||
|
151
|
+
mime_type(File.extname(path)) ||
|
150
152
|
response['Content-Type'] ||
|
151
153
|
'application/octet-stream'
|
152
154
|
|
@@ -163,6 +165,8 @@ module Sinatra
|
|
163
165
|
not_found
|
164
166
|
end
|
165
167
|
|
168
|
+
# Rack response body used to deliver static files. The file contents are
|
169
|
+
# generated iteratively in 8K chunks.
|
166
170
|
class StaticFile < ::File #:nodoc:
|
167
171
|
alias_method :to_path, :path
|
168
172
|
def each
|
@@ -173,6 +177,57 @@ module Sinatra
|
|
173
177
|
end
|
174
178
|
end
|
175
179
|
|
180
|
+
# Specify response freshness policy for HTTP caches (Cache-Control header).
|
181
|
+
# Any number of non-value directives (:public, :private, :no_cache,
|
182
|
+
# :no_store, :must_revalidate, :proxy_revalidate) may be passed along with
|
183
|
+
# a Hash of value directives (:max_age, :min_stale, :s_max_age).
|
184
|
+
#
|
185
|
+
# cache_control :public, :must_revalidate, :max_age => 60
|
186
|
+
# => Cache-Control: public, must-revalidate, max-age=60
|
187
|
+
#
|
188
|
+
# See RFC 2616 / 14.9 for more on standard cache control directives:
|
189
|
+
# http://tools.ietf.org/html/rfc2616#section-14.9.1
|
190
|
+
def cache_control(*values)
|
191
|
+
if values.last.kind_of?(Hash)
|
192
|
+
hash = values.pop
|
193
|
+
hash.reject! { |k,v| v == false }
|
194
|
+
hash.reject! { |k,v| values << k if v == true }
|
195
|
+
else
|
196
|
+
hash = {}
|
197
|
+
end
|
198
|
+
|
199
|
+
values = values.map { |value| value.to_s.tr('_','-') }
|
200
|
+
hash.each { |k,v| values << [k.to_s.tr('_', '-'), v].join('=') }
|
201
|
+
|
202
|
+
response['Cache-Control'] = values.join(', ') if values.any?
|
203
|
+
end
|
204
|
+
|
205
|
+
# Set the Expires header and Cache-Control/max-age directive. Amount
|
206
|
+
# can be an integer number of seconds in the future or a Time object
|
207
|
+
# indicating when the response should be considered "stale". The remaining
|
208
|
+
# "values" arguments are passed to the #cache_control helper:
|
209
|
+
#
|
210
|
+
# expires 500, :public, :must_revalidate
|
211
|
+
# => Cache-Control: public, must-revalidate, max-age=60
|
212
|
+
# => Expires: Mon, 08 Jun 2009 08:50:17 GMT
|
213
|
+
#
|
214
|
+
def expires(amount, *values)
|
215
|
+
values << {} unless values.last.kind_of?(Hash)
|
216
|
+
|
217
|
+
if amount.respond_to?(:to_time)
|
218
|
+
max_age = amount.to_time - Time.now
|
219
|
+
time = amount.to_time
|
220
|
+
else
|
221
|
+
max_age = amount
|
222
|
+
time = Time.now + amount
|
223
|
+
end
|
224
|
+
|
225
|
+
values.last.merge!(:max_age => max_age)
|
226
|
+
cache_control(*values)
|
227
|
+
|
228
|
+
response['Expires'] = time.httpdate
|
229
|
+
end
|
230
|
+
|
176
231
|
# Set the last modified time of the resource (HTTP 'Last-Modified' header)
|
177
232
|
# and halt if conditional GET matches. The +time+ argument is a Time,
|
178
233
|
# DateTime, or other object that responds to +to_time+.
|
@@ -190,7 +245,7 @@ module Sinatra
|
|
190
245
|
|
191
246
|
# Set the response entity tag (HTTP 'ETag' header) and halt if conditional
|
192
247
|
# GET matches. The +value+ argument is an identifier that uniquely
|
193
|
-
# identifies the current version of the resource. The +
|
248
|
+
# identifies the current version of the resource. The +kind+ argument
|
194
249
|
# indicates whether the etag should be used as a :strong (default) or :weak
|
195
250
|
# cache validator.
|
196
251
|
#
|
@@ -215,110 +270,90 @@ module Sinatra
|
|
215
270
|
|
216
271
|
end
|
217
272
|
|
218
|
-
# Template rendering methods. Each method takes
|
219
|
-
# to render as a Symbol and returns a String with the rendered output
|
273
|
+
# Template rendering methods. Each method takes the name of a template
|
274
|
+
# to render as a Symbol and returns a String with the rendered output,
|
275
|
+
# as well as an optional hash with additional options.
|
276
|
+
#
|
277
|
+
# `template` is either the name or path of the template as symbol
|
278
|
+
# (Use `:'subdir/myview'` for views in subdirectories), or a string
|
279
|
+
# that will be rendered.
|
280
|
+
#
|
281
|
+
# Possible options are:
|
282
|
+
# :layout If set to false, no layout is rendered, otherwise
|
283
|
+
# the specified layout is used (Ignored for `sass`)
|
284
|
+
# :locals A hash with local variables that should be available
|
285
|
+
# in the template
|
220
286
|
module Templates
|
221
|
-
def erb(template, options={})
|
222
|
-
|
223
|
-
|
287
|
+
def erb(template, options={}, locals={})
|
288
|
+
render :erb, template, options, locals
|
289
|
+
end
|
290
|
+
|
291
|
+
def erubis(template, options={}, locals={})
|
292
|
+
render :erubis, template, options, locals
|
224
293
|
end
|
225
294
|
|
226
|
-
def haml(template, options={})
|
227
|
-
|
228
|
-
options[:options] ||= self.class.haml if self.class.respond_to? :haml
|
229
|
-
render :haml, template, options
|
295
|
+
def haml(template, options={}, locals={})
|
296
|
+
render :haml, template, options, locals
|
230
297
|
end
|
231
298
|
|
232
|
-
def sass(template, options={},
|
233
|
-
require 'sass' unless defined? ::Sass
|
299
|
+
def sass(template, options={}, locals={})
|
234
300
|
options[:layout] = false
|
235
|
-
render :sass, template, options
|
301
|
+
render :sass, template, options, locals
|
236
302
|
end
|
237
303
|
|
238
|
-
def builder(template=nil, options={}, &block)
|
239
|
-
require 'builder' unless defined? ::Builder
|
304
|
+
def builder(template=nil, options={}, locals={}, &block)
|
240
305
|
options, template = template, nil if template.is_a?(Hash)
|
241
|
-
template =
|
242
|
-
render :builder, template, options
|
306
|
+
template = Proc.new { block } if template.nil?
|
307
|
+
render :builder, template, options, locals
|
243
308
|
end
|
244
309
|
|
245
310
|
private
|
246
|
-
def render(engine,
|
247
|
-
|
248
|
-
|
249
|
-
layout, data = lookup_layout(engine, options)
|
250
|
-
if layout
|
251
|
-
__send__("render_#{engine}", layout, data, options) { output }
|
252
|
-
else
|
253
|
-
output
|
254
|
-
end
|
255
|
-
end
|
256
|
-
|
257
|
-
def lookup_template(engine, template, options={})
|
258
|
-
case template
|
259
|
-
when Symbol
|
260
|
-
if cached = self.class.templates[template]
|
261
|
-
lookup_template(engine, cached, options)
|
262
|
-
else
|
263
|
-
::File.read(template_path(engine, template, options))
|
264
|
-
end
|
265
|
-
when Proc
|
266
|
-
template.call
|
267
|
-
when String
|
268
|
-
template
|
269
|
-
else
|
270
|
-
raise ArgumentError
|
271
|
-
end
|
272
|
-
end
|
273
|
-
|
274
|
-
def lookup_layout(engine, options)
|
275
|
-
return if options[:layout] == false
|
276
|
-
options.delete(:layout) if options[:layout] == true
|
277
|
-
template = options[:layout] || :layout
|
278
|
-
data = lookup_template(engine, template, options)
|
279
|
-
[template, data]
|
280
|
-
rescue Errno::ENOENT
|
281
|
-
nil
|
282
|
-
end
|
283
|
-
|
284
|
-
def template_path(engine, template, options={})
|
285
|
-
views_dir =
|
286
|
-
options[:views_directory] || self.options.views || "./views"
|
287
|
-
"#{views_dir}/#{template}.#{engine}"
|
288
|
-
end
|
289
|
-
|
290
|
-
def render_erb(template, data, options, &block)
|
291
|
-
original_out_buf = @_out_buf
|
292
|
-
data = data.call if data.kind_of? Proc
|
311
|
+
def render(engine, data, options={}, locals={}, &block)
|
312
|
+
# merge app-level options
|
313
|
+
options = settings.send(engine).merge(options) if settings.respond_to?(engine)
|
293
314
|
|
294
|
-
|
295
|
-
locals = options
|
296
|
-
|
315
|
+
# extract generic options
|
316
|
+
locals = options.delete(:locals) || locals || {}
|
317
|
+
views = options.delete(:views) || settings.views || "./views"
|
318
|
+
layout = options.delete(:layout)
|
319
|
+
layout = :layout if layout.nil? || layout == true
|
297
320
|
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
result
|
302
|
-
end
|
321
|
+
# compile and render template
|
322
|
+
template = compile_template(engine, data, options, views)
|
323
|
+
output = template.render(self, locals, &block)
|
303
324
|
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
325
|
+
# render layout
|
326
|
+
if layout
|
327
|
+
begin
|
328
|
+
options = options.merge(:views => views, :layout => false)
|
329
|
+
output = render(engine, layout, options, locals) { output }
|
330
|
+
rescue Errno::ENOENT
|
331
|
+
end
|
332
|
+
end
|
308
333
|
|
309
|
-
|
310
|
-
engine = ::Sass::Engine.new(data, options[:sass] || {})
|
311
|
-
engine.render
|
334
|
+
output
|
312
335
|
end
|
313
336
|
|
314
|
-
def
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
337
|
+
def compile_template(engine, data, options, views)
|
338
|
+
@template_cache.fetch engine, data, options do
|
339
|
+
case
|
340
|
+
when data.is_a?(Symbol)
|
341
|
+
body, path, line = self.class.templates[data]
|
342
|
+
if body
|
343
|
+
body = body.call if body.respond_to?(:call)
|
344
|
+
Tilt[engine].new(path, line.to_i, options) { body }
|
345
|
+
else
|
346
|
+
path = ::File.join(views, "#{data}.#{engine}")
|
347
|
+
Tilt[engine].new(path, 1, options)
|
348
|
+
end
|
349
|
+
when data.is_a?(Proc) || data.is_a?(String)
|
350
|
+
body = data.is_a?(String) ? Proc.new { data } : data
|
351
|
+
path, line = self.class.caller_locations.first
|
352
|
+
Tilt[engine].new(path, line.to_i, options, &body)
|
353
|
+
else
|
354
|
+
raise ArgumentError
|
355
|
+
end
|
320
356
|
end
|
321
|
-
xml.target!
|
322
357
|
end
|
323
358
|
end
|
324
359
|
|
@@ -332,6 +367,7 @@ module Sinatra
|
|
332
367
|
|
333
368
|
def initialize(app=nil)
|
334
369
|
@app = app
|
370
|
+
@template_cache = Tilt::Cache.new
|
335
371
|
yield self if block_given?
|
336
372
|
end
|
337
373
|
|
@@ -346,7 +382,7 @@ module Sinatra
|
|
346
382
|
@env = env
|
347
383
|
@request = Request.new(env)
|
348
384
|
@response = Response.new
|
349
|
-
@params =
|
385
|
+
@params = indifferent_params(@request.params)
|
350
386
|
|
351
387
|
invoke { dispatch! }
|
352
388
|
invoke { error_block!(response.status) }
|
@@ -364,20 +400,24 @@ module Sinatra
|
|
364
400
|
[status, header, body]
|
365
401
|
end
|
366
402
|
|
367
|
-
# Access
|
368
|
-
def
|
403
|
+
# Access settings defined with Base.set.
|
404
|
+
def settings
|
369
405
|
self.class
|
370
406
|
end
|
407
|
+
alias_method :options, :settings
|
371
408
|
|
372
|
-
# Exit the current block
|
409
|
+
# Exit the current block, halts any further processing
|
410
|
+
# of the request, and returns the specified response.
|
373
411
|
def halt(*response)
|
374
412
|
response = response.first if response.length == 1
|
375
413
|
throw :halt, response
|
376
414
|
end
|
377
415
|
|
378
416
|
# Pass control to the next matching route.
|
379
|
-
|
380
|
-
|
417
|
+
# If there are no more matching routes, Sinatra will
|
418
|
+
# return a 404 response.
|
419
|
+
def pass(&block)
|
420
|
+
throw :pass, block
|
381
421
|
end
|
382
422
|
|
383
423
|
# Forward the request to the downstream app -- middleware only.
|
@@ -391,21 +431,26 @@ module Sinatra
|
|
391
431
|
end
|
392
432
|
|
393
433
|
private
|
394
|
-
# Run before filters
|
395
|
-
def
|
396
|
-
|
434
|
+
# Run before filters defined on the class and all superclasses.
|
435
|
+
def before_filter!(base=self.class)
|
436
|
+
before_filter!(base.superclass) if base.superclass.respond_to?(:before_filters)
|
437
|
+
base.before_filters.each { |block| instance_eval(&block) }
|
438
|
+
end
|
397
439
|
|
398
|
-
|
399
|
-
|
440
|
+
# Run after filters defined on the class and all superclasses.
|
441
|
+
def after_filter!(base=self.class)
|
442
|
+
after_filter!(base.superclass) if base.superclass.respond_to?(:after_filters)
|
443
|
+
base.after_filters.each { |block| instance_eval(&block) }
|
444
|
+
end
|
400
445
|
|
401
|
-
|
402
|
-
|
446
|
+
# Run routes defined on the class and all superclasses.
|
447
|
+
def route!(base=self.class, pass_block=nil)
|
448
|
+
if routes = base.routes[@request.request_method]
|
403
449
|
original_params = @params
|
404
450
|
path = unescape(@request.path_info)
|
405
451
|
|
406
|
-
routes.each do |pattern,
|
452
|
+
routes.each do |pattern, keys, conditions, block|
|
407
453
|
if match = pattern.match(path)
|
408
|
-
@env['rack.errors'].write("=== Path '#{path}' matched route '#{rpath}'\n".green)
|
409
454
|
values = match.captures.to_a
|
410
455
|
params =
|
411
456
|
if keys.any?
|
@@ -425,19 +470,39 @@ module Sinatra
|
|
425
470
|
@params = original_params.merge(params)
|
426
471
|
@block_params = values
|
427
472
|
|
428
|
-
catch(:pass) do
|
473
|
+
pass_block = catch(:pass) do
|
429
474
|
conditions.each { |cond|
|
430
475
|
throw :pass if instance_eval(&cond) == false }
|
431
|
-
|
476
|
+
route_eval(&block)
|
432
477
|
end
|
433
|
-
else
|
434
|
-
@env['rack.errors'].write("=== Path '#{path}' failed route '#{rpath}'\n".red)
|
435
478
|
end
|
436
479
|
end
|
480
|
+
|
481
|
+
@params = original_params
|
437
482
|
end
|
438
483
|
|
439
|
-
#
|
440
|
-
|
484
|
+
# Run routes defined in superclass.
|
485
|
+
if base.superclass.respond_to?(:routes)
|
486
|
+
route! base.superclass, pass_block
|
487
|
+
return
|
488
|
+
end
|
489
|
+
|
490
|
+
route_eval(&pass_block) if pass_block
|
491
|
+
|
492
|
+
route_missing
|
493
|
+
end
|
494
|
+
|
495
|
+
# Run a route block and throw :halt with the result.
|
496
|
+
def route_eval(&block)
|
497
|
+
throw :halt, instance_eval(&block)
|
498
|
+
end
|
499
|
+
|
500
|
+
# No matching route was found or all routes passed. The default
|
501
|
+
# implementation is to forward the request downstream when running
|
502
|
+
# as middleware (@app is non-nil); when no downstream app is set, raise
|
503
|
+
# a NotFound exception. Subclasses can override this method to perform
|
504
|
+
# custom route miss logic.
|
505
|
+
def route_missing
|
441
506
|
if @app
|
442
507
|
forward
|
443
508
|
else
|
@@ -445,17 +510,25 @@ module Sinatra
|
|
445
510
|
end
|
446
511
|
end
|
447
512
|
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
513
|
+
# Attempt to serve static files from public directory. Throws :halt when
|
514
|
+
# a matching file is found, returns nil otherwise.
|
515
|
+
def static!
|
516
|
+
return if (public_dir = settings.public).nil?
|
517
|
+
public_dir = File.expand_path(public_dir)
|
518
|
+
|
519
|
+
path = File.expand_path(public_dir + unescape(request.path_info))
|
520
|
+
return if path[0, public_dir.length] != public_dir
|
521
|
+
return unless File.file?(path)
|
522
|
+
|
523
|
+
send_file path, :disposition => nil
|
524
|
+
end
|
525
|
+
|
526
|
+
# Enable string or symbol key access to the nested params hash.
|
527
|
+
def indifferent_params(params)
|
528
|
+
params = indifferent_hash.merge(params)
|
529
|
+
params.each do |key, value|
|
530
|
+
next unless value.is_a?(Hash)
|
531
|
+
params[key] = indifferent_params(value)
|
459
532
|
end
|
460
533
|
end
|
461
534
|
|
@@ -498,25 +571,30 @@ module Sinatra
|
|
498
571
|
|
499
572
|
# Dispatch a request with error handling.
|
500
573
|
def dispatch!
|
574
|
+
static! if settings.static? && (request.get? || request.head?)
|
575
|
+
before_filter!
|
501
576
|
route!
|
502
577
|
rescue NotFound => boom
|
503
578
|
handle_not_found!(boom)
|
504
579
|
rescue ::Exception => boom
|
505
580
|
handle_exception!(boom)
|
581
|
+
ensure
|
582
|
+
after_filter!
|
506
583
|
end
|
507
584
|
|
508
585
|
def handle_not_found!(boom)
|
509
|
-
@env['sinatra.error']
|
510
|
-
@response.status
|
511
|
-
@response.
|
586
|
+
@env['sinatra.error'] = boom
|
587
|
+
@response.status = 404
|
588
|
+
@response.headers['X-Cascade'] = 'pass'
|
589
|
+
@response.body = ['<h1>Not Found</h1>']
|
512
590
|
error_block! boom.class, NotFound
|
513
591
|
end
|
514
592
|
|
515
593
|
def handle_exception!(boom)
|
516
594
|
@env['sinatra.error'] = boom
|
517
595
|
|
518
|
-
dump_errors!(boom) if
|
519
|
-
raise boom if
|
596
|
+
dump_errors!(boom) if settings.dump_errors?
|
597
|
+
raise boom if settings.raise_errors? || settings.show_exceptions?
|
520
598
|
|
521
599
|
@response.status = 500
|
522
600
|
error_block! boom.class, Exception
|
@@ -524,11 +602,16 @@ module Sinatra
|
|
524
602
|
|
525
603
|
# Find an custom error block for the key(s) specified.
|
526
604
|
def error_block!(*keys)
|
527
|
-
errmap = self.class.errors
|
528
605
|
keys.each do |key|
|
529
|
-
|
530
|
-
|
531
|
-
|
606
|
+
base = self.class
|
607
|
+
while base.respond_to?(:errors)
|
608
|
+
if block = base.errors[key]
|
609
|
+
# found a handler, eval and return result
|
610
|
+
res = instance_eval(&block)
|
611
|
+
return res
|
612
|
+
else
|
613
|
+
base = base.superclass
|
614
|
+
end
|
532
615
|
end
|
533
616
|
end
|
534
617
|
nil
|
@@ -538,11 +621,11 @@ module Sinatra
|
|
538
621
|
backtrace = clean_backtrace(boom.backtrace)
|
539
622
|
msg = ["#{boom.class} - #{boom.message}:",
|
540
623
|
*backtrace].join("\n ")
|
541
|
-
@env['rack.errors'].
|
624
|
+
@env['rack.errors'].puts(msg)
|
542
625
|
end
|
543
626
|
|
544
627
|
def clean_backtrace(trace)
|
545
|
-
return trace unless
|
628
|
+
return trace unless settings.clean_trace?
|
546
629
|
|
547
630
|
trace.reject { |line|
|
548
631
|
line =~ /lib\/sinatra.*\.rb/ ||
|
@@ -550,20 +633,46 @@ module Sinatra
|
|
550
633
|
}.map! { |line| line.gsub(/^\.\//, '') }
|
551
634
|
end
|
552
635
|
|
553
|
-
@routes = {}
|
554
|
-
@filters = []
|
555
|
-
@conditions = []
|
556
|
-
@templates = {}
|
557
|
-
@middleware = []
|
558
|
-
@errors = {}
|
559
|
-
@prototype = nil
|
560
|
-
@extensions = []
|
561
|
-
|
562
636
|
class << self
|
563
|
-
|
564
|
-
|
637
|
+
attr_reader :routes, :before_filters, :after_filters, :templates, :errors
|
638
|
+
|
639
|
+
def reset!
|
640
|
+
@conditions = []
|
641
|
+
@routes = {}
|
642
|
+
@before_filters = []
|
643
|
+
@after_filters = []
|
644
|
+
@errors = {}
|
645
|
+
@middleware = []
|
646
|
+
@prototype = nil
|
647
|
+
@extensions = []
|
648
|
+
|
649
|
+
if superclass.respond_to?(:templates)
|
650
|
+
@templates = Hash.new { |hash,key| superclass.templates[key] }
|
651
|
+
else
|
652
|
+
@templates = {}
|
653
|
+
end
|
654
|
+
end
|
565
655
|
|
566
|
-
|
656
|
+
# Extension modules registered on this class and all superclasses.
|
657
|
+
def extensions
|
658
|
+
if superclass.respond_to?(:extensions)
|
659
|
+
(@extensions + superclass.extensions).uniq
|
660
|
+
else
|
661
|
+
@extensions
|
662
|
+
end
|
663
|
+
end
|
664
|
+
|
665
|
+
# Middleware used in this class and all superclasses.
|
666
|
+
def middleware
|
667
|
+
if superclass.respond_to?(:middleware)
|
668
|
+
superclass.middleware + @middleware
|
669
|
+
else
|
670
|
+
@middleware
|
671
|
+
end
|
672
|
+
end
|
673
|
+
|
674
|
+
# Sets an option to the given value. If the value is a proc,
|
675
|
+
# the proc will be called every time the option is accessed.
|
567
676
|
def set(option, value=self)
|
568
677
|
if value.kind_of?(Proc)
|
569
678
|
metadef(option, &value)
|
@@ -579,45 +688,59 @@ module Sinatra
|
|
579
688
|
self
|
580
689
|
end
|
581
690
|
|
691
|
+
# Same as calling `set :option, true` for each of the given options.
|
582
692
|
def enable(*opts)
|
583
693
|
opts.each { |key| set(key, true) }
|
584
694
|
end
|
585
695
|
|
696
|
+
# Same as calling `set :option, false` for each of the given options.
|
586
697
|
def disable(*opts)
|
587
698
|
opts.each { |key| set(key, false) }
|
588
699
|
end
|
589
700
|
|
701
|
+
# Define a custom error handler. Optionally takes either an Exception
|
702
|
+
# class, or an HTTP status code to specify which errors should be
|
703
|
+
# handled.
|
590
704
|
def error(codes=Exception, &block)
|
591
|
-
|
592
|
-
codes.each { |err| error(err, &block) }
|
593
|
-
else
|
594
|
-
@errors[codes] = block
|
595
|
-
end
|
705
|
+
Array(codes).each { |code| @errors[code] = block }
|
596
706
|
end
|
597
707
|
|
708
|
+
# Sugar for `error(404) { ... }`
|
598
709
|
def not_found(&block)
|
599
710
|
error 404, &block
|
600
711
|
end
|
601
712
|
|
713
|
+
# Define a named template. The block must return the template source.
|
602
714
|
def template(name, &block)
|
603
|
-
|
715
|
+
filename, line = caller_locations.first
|
716
|
+
templates[name] = [block, filename, line.to_i]
|
604
717
|
end
|
605
718
|
|
719
|
+
# Define the layout template. The block must return the template source.
|
606
720
|
def layout(name=:layout, &block)
|
607
721
|
template name, &block
|
608
722
|
end
|
609
723
|
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
data
|
724
|
+
# Load embeded templates from the file; uses the caller's __FILE__
|
725
|
+
# when no file is specified.
|
726
|
+
def inline_templates=(file=nil)
|
727
|
+
file = (file.nil? || file == true) ? caller_files.first : file
|
728
|
+
|
729
|
+
begin
|
730
|
+
app, data =
|
731
|
+
::IO.read(file).gsub("\r\n", "\n").split(/^__END__$/, 2)
|
732
|
+
rescue Errno::ENOENT
|
733
|
+
app, data = nil
|
734
|
+
end
|
735
|
+
|
736
|
+
if data
|
737
|
+
lines = app.count("\n") + 1
|
617
738
|
template = nil
|
618
739
|
data.each_line do |line|
|
740
|
+
lines += 1
|
619
741
|
if line =~ /^@@\s*(.*)/
|
620
|
-
template =
|
742
|
+
template = ''
|
743
|
+
templates[$1.to_sym] = [template, file, lines]
|
621
744
|
elsif template
|
622
745
|
template << line
|
623
746
|
end
|
@@ -625,17 +748,30 @@ module Sinatra
|
|
625
748
|
end
|
626
749
|
end
|
627
750
|
|
628
|
-
#
|
629
|
-
def
|
751
|
+
# Lookup or register a mime type in Rack's mime registry.
|
752
|
+
def mime_type(type, value=nil)
|
630
753
|
return type if type.nil? || type.to_s.include?('/')
|
631
754
|
type = ".#{type}" unless type.to_s[0] == ?.
|
632
|
-
Rack::Mime.mime_type(type, nil)
|
755
|
+
return Rack::Mime.mime_type(type, nil) unless value
|
756
|
+
Rack::Mime::MIME_TYPES[type] = value
|
633
757
|
end
|
634
758
|
|
759
|
+
# Define a before filter; runs before all requests within the same
|
760
|
+
# context as route handlers and may access/modify the request and
|
761
|
+
# response.
|
635
762
|
def before(&block)
|
636
|
-
@
|
763
|
+
@before_filters << block
|
764
|
+
end
|
765
|
+
|
766
|
+
# Define an after filter; runs after all requests within the same
|
767
|
+
# context as route handlers and may access/modify the request and
|
768
|
+
# response.
|
769
|
+
def after(&block)
|
770
|
+
@after_filters << block
|
637
771
|
end
|
638
772
|
|
773
|
+
# Add a route condition. The route is considered non-matching when the
|
774
|
+
# block returns false.
|
639
775
|
def condition(&block)
|
640
776
|
@conditions << block
|
641
777
|
end
|
@@ -655,10 +791,11 @@ module Sinatra
|
|
655
791
|
end
|
656
792
|
}
|
657
793
|
end
|
794
|
+
alias_method :agent, :user_agent
|
658
795
|
|
659
|
-
def
|
796
|
+
def provides(*types)
|
660
797
|
types = [types] unless types.kind_of? Array
|
661
|
-
types.map!{|t|
|
798
|
+
types.map!{|t| mime_type(t)}
|
662
799
|
|
663
800
|
condition {
|
664
801
|
matching_types = (request.accept & types)
|
@@ -672,6 +809,8 @@ module Sinatra
|
|
672
809
|
end
|
673
810
|
|
674
811
|
public
|
812
|
+
# Defining a `GET` handler also automatically defines
|
813
|
+
# a `HEAD` handler.
|
675
814
|
def get(path, opts={}, &block)
|
676
815
|
conditions = @conditions.dup
|
677
816
|
route('GET', path, opts, &block)
|
@@ -680,16 +819,17 @@ module Sinatra
|
|
680
819
|
route('HEAD', path, opts, &block)
|
681
820
|
end
|
682
821
|
|
683
|
-
def put(path, opts={}, &bk);
|
684
|
-
def post(path, opts={}, &bk);
|
685
|
-
def delete(path, opts={}, &bk); route 'DELETE', path, opts, &bk
|
686
|
-
def head(path, opts={}, &bk);
|
822
|
+
def put(path, opts={}, &bk); route 'PUT', path, opts, &bk end
|
823
|
+
def post(path, opts={}, &bk); route 'POST', path, opts, &bk end
|
824
|
+
def delete(path, opts={}, &bk); route 'DELETE', path, opts, &bk end
|
825
|
+
def head(path, opts={}, &bk); route 'HEAD', path, opts, &bk end
|
687
826
|
|
688
827
|
private
|
689
|
-
def route(verb, path,
|
690
|
-
|
691
|
-
|
692
|
-
|
828
|
+
def route(verb, path, options={}, &block)
|
829
|
+
# Because of self.options.host
|
830
|
+
host_name(options.delete(:host)) if options.key?(:host)
|
831
|
+
|
832
|
+
options.each {|option, args| send(option, *args)}
|
693
833
|
|
694
834
|
pattern, keys = compile(path)
|
695
835
|
conditions, @conditions = @conditions, []
|
@@ -703,10 +843,10 @@ module Sinatra
|
|
703
843
|
lambda { unbound_method.bind(self).call }
|
704
844
|
end
|
705
845
|
|
706
|
-
invoke_hook(:route_added, verb, path)
|
846
|
+
invoke_hook(:route_added, verb, path, block)
|
707
847
|
|
708
|
-
(routes[verb] ||= []).
|
709
|
-
push([pattern,
|
848
|
+
(@routes[verb] ||= []).
|
849
|
+
push([pattern, keys, conditions, block]).last
|
710
850
|
end
|
711
851
|
|
712
852
|
def invoke_hook(name, *args)
|
@@ -718,7 +858,7 @@ module Sinatra
|
|
718
858
|
if path.respond_to? :to_str
|
719
859
|
special_chars = %w{. + ( )}
|
720
860
|
pattern =
|
721
|
-
path.gsub(/((:\w+)|[\*#{special_chars.join}])/) do |match|
|
861
|
+
path.to_str.gsub(/((:\w+)|[\*#{special_chars.join}])/) do |match|
|
722
862
|
case match
|
723
863
|
when "*"
|
724
864
|
keys << 'splat'
|
@@ -731,6 +871,8 @@ module Sinatra
|
|
731
871
|
end
|
732
872
|
end
|
733
873
|
[/^#{pattern}$/, keys]
|
874
|
+
elsif path.respond_to?(:keys) && path.respond_to?(:match)
|
875
|
+
[path, path.keys]
|
734
876
|
elsif path.respond_to? :match
|
735
877
|
[path, keys]
|
736
878
|
else
|
@@ -739,13 +881,11 @@ module Sinatra
|
|
739
881
|
end
|
740
882
|
|
741
883
|
public
|
884
|
+
# Makes the methods defined in the block and in the Modules given
|
885
|
+
# in `extensions` available to the handlers and templates
|
742
886
|
def helpers(*extensions, &block)
|
743
887
|
class_eval(&block) if block_given?
|
744
|
-
include
|
745
|
-
end
|
746
|
-
|
747
|
-
def extensions
|
748
|
-
(@extensions + (superclass.extensions rescue [])).uniq
|
888
|
+
include(*extensions) if extensions.any?
|
749
889
|
end
|
750
890
|
|
751
891
|
def register(*extensions, &block)
|
@@ -757,20 +897,24 @@ module Sinatra
|
|
757
897
|
end
|
758
898
|
end
|
759
899
|
|
760
|
-
def development
|
761
|
-
def
|
762
|
-
def
|
900
|
+
def development?; environment == :development end
|
901
|
+
def production?; environment == :production end
|
902
|
+
def test?; environment == :test end
|
763
903
|
|
904
|
+
# Set configuration options for Sinatra and/or the app.
|
905
|
+
# Allows scoping of settings for certain environments.
|
764
906
|
def configure(*envs, &block)
|
765
|
-
|
766
|
-
yield if envs.empty? || envs.include?(environment.to_sym)
|
907
|
+
yield self if envs.empty? || envs.include?(environment.to_sym)
|
767
908
|
end
|
768
909
|
|
910
|
+
# Use the specified Rack middleware
|
769
911
|
def use(middleware, *args, &block)
|
770
912
|
@prototype = nil
|
771
913
|
@middleware << [middleware, args, block]
|
772
914
|
end
|
773
915
|
|
916
|
+
# Run the Sinatra app as a self-hosted server using
|
917
|
+
# Thin, Mongrel or WEBrick (in that order)
|
774
918
|
def run!(options={})
|
775
919
|
set options
|
776
920
|
handler = detect_rack_handler
|
@@ -783,6 +927,7 @@ module Sinatra
|
|
783
927
|
server.respond_to?(:stop!) ? server.stop! : server.stop
|
784
928
|
puts "\n== Sinatra has ended his set (crowd applauds)" unless handler_name =~/cgi/i
|
785
929
|
end
|
930
|
+
set :running, true
|
786
931
|
end
|
787
932
|
rescue Errno::EADDRINUSE => e
|
788
933
|
puts "== Someone is already performing on port #{port}!"
|
@@ -798,50 +943,18 @@ module Sinatra
|
|
798
943
|
# an instance of the class new was called on.
|
799
944
|
def new(*args, &bk)
|
800
945
|
builder = Rack::Builder.new
|
801
|
-
builder.use Rack::Session::Cookie if sessions?
|
802
|
-
builder.use Rack::CommonLogger
|
803
|
-
builder.use Rack::MethodOverride
|
804
|
-
|
946
|
+
builder.use Rack::Session::Cookie if sessions?
|
947
|
+
builder.use Rack::CommonLogger if logging?
|
948
|
+
builder.use Rack::MethodOverride if methodoverride?
|
949
|
+
builder.use ShowExceptions if show_exceptions?
|
950
|
+
middleware.each { |c,a,b| builder.use(c, *a, &b) }
|
951
|
+
|
805
952
|
builder.run super
|
806
953
|
builder.to_app
|
807
954
|
end
|
808
955
|
|
809
956
|
def call(env)
|
810
|
-
synchronize
|
811
|
-
reload! if reload?
|
812
|
-
prototype.call(env)
|
813
|
-
end
|
814
|
-
end
|
815
|
-
|
816
|
-
def reloading?
|
817
|
-
@reloading
|
818
|
-
end
|
819
|
-
|
820
|
-
def reload!
|
821
|
-
@reloading = true
|
822
|
-
reset!
|
823
|
-
$LOADED_FEATURES.delete("sinatra.rb")
|
824
|
-
::Kernel.load app_file
|
825
|
-
@reloading = false
|
826
|
-
end
|
827
|
-
|
828
|
-
def reset!(base=superclass)
|
829
|
-
@routes = base.dupe_routes
|
830
|
-
@templates = base.templates.dup
|
831
|
-
@conditions = []
|
832
|
-
@filters = base.filters.dup
|
833
|
-
@errors = base.errors.dup
|
834
|
-
@middleware = base.middleware.dup
|
835
|
-
@prototype = nil
|
836
|
-
@extensions = []
|
837
|
-
end
|
838
|
-
|
839
|
-
protected
|
840
|
-
def dupe_routes
|
841
|
-
routes.inject({}) do |hash,(request_method,routes)|
|
842
|
-
hash[request_method] = routes.dup
|
843
|
-
hash
|
844
|
-
end
|
957
|
+
synchronize { prototype.call(env) }
|
845
958
|
end
|
846
959
|
|
847
960
|
private
|
@@ -849,7 +962,7 @@ module Sinatra
|
|
849
962
|
servers = Array(self.server)
|
850
963
|
servers.each do |server_name|
|
851
964
|
begin
|
852
|
-
return Rack::Handler.get(server_name)
|
965
|
+
return Rack::Handler.get(server_name.downcase)
|
853
966
|
rescue LoadError
|
854
967
|
rescue NameError
|
855
968
|
end
|
@@ -858,7 +971,7 @@ module Sinatra
|
|
858
971
|
end
|
859
972
|
|
860
973
|
def inherited(subclass)
|
861
|
-
subclass.reset!
|
974
|
+
subclass.reset!
|
862
975
|
super
|
863
976
|
end
|
864
977
|
|
@@ -875,18 +988,47 @@ module Sinatra
|
|
875
988
|
(class << self; self; end).
|
876
989
|
send :define_method, message, &block
|
877
990
|
end
|
991
|
+
|
992
|
+
public
|
993
|
+
CALLERS_TO_IGNORE = [
|
994
|
+
/\/sinatra(\/(base|main|showexceptions))?\.rb$/, # all sinatra code
|
995
|
+
/lib\/tilt.*\.rb$/, # all tilt code
|
996
|
+
/\(.*\)/, # generated code
|
997
|
+
/custom_require\.rb$/, # rubygems require hacks
|
998
|
+
/active_support/, # active_support require hacks
|
999
|
+
]
|
1000
|
+
|
1001
|
+
# add rubinius (and hopefully other VM impls) ignore patterns ...
|
1002
|
+
CALLERS_TO_IGNORE.concat(RUBY_IGNORE_CALLERS) if defined?(RUBY_IGNORE_CALLERS)
|
1003
|
+
|
1004
|
+
# Like Kernel#caller but excluding certain magic entries and without
|
1005
|
+
# line / method information; the resulting array contains filenames only.
|
1006
|
+
def caller_files
|
1007
|
+
caller_locations.
|
1008
|
+
map { |file,line| file }
|
1009
|
+
end
|
1010
|
+
|
1011
|
+
def caller_locations
|
1012
|
+
caller(1).
|
1013
|
+
map { |line| line.split(/:(?=\d|in )/)[0,2] }.
|
1014
|
+
reject { |file,line| CALLERS_TO_IGNORE.any? { |pattern| file =~ pattern } }
|
1015
|
+
end
|
878
1016
|
end
|
879
1017
|
|
1018
|
+
reset!
|
1019
|
+
|
880
1020
|
set :raise_errors, true
|
881
1021
|
set :dump_errors, false
|
882
1022
|
set :clean_trace, true
|
1023
|
+
set :show_exceptions, false
|
883
1024
|
set :sessions, false
|
884
1025
|
set :logging, false
|
885
1026
|
set :methodoverride, false
|
886
1027
|
set :static, false
|
887
1028
|
set :environment, (ENV['RACK_ENV'] || :development).to_sym
|
888
1029
|
|
889
|
-
set :run, false
|
1030
|
+
set :run, false # start server via at-exit hook?
|
1031
|
+
set :running, false # is the built-in server running now?
|
890
1032
|
set :server, %w[thin mongrel webrick]
|
891
1033
|
set :host, '0.0.0.0'
|
892
1034
|
set :port, 4567
|
@@ -895,18 +1037,7 @@ module Sinatra
|
|
895
1037
|
set :root, Proc.new { app_file && File.expand_path(File.dirname(app_file)) }
|
896
1038
|
set :views, Proc.new { root && File.join(root, 'views') }
|
897
1039
|
set :public, Proc.new { root && File.join(root, 'public') }
|
898
|
-
set :
|
899
|
-
set :lock, Proc.new { reload? }
|
900
|
-
|
901
|
-
# static files route
|
902
|
-
get(/.*[^\/]$/) do
|
903
|
-
pass unless options.static? && options.public?
|
904
|
-
public_dir = File.expand_path(options.public)
|
905
|
-
path = File.expand_path(public_dir + unescape(request.path_info))
|
906
|
-
pass if path[0, public_dir.length] != public_dir
|
907
|
-
pass unless File.file?(path)
|
908
|
-
send_file path, :disposition => nil
|
909
|
-
end
|
1040
|
+
set :lock, false
|
910
1041
|
|
911
1042
|
error ::Exception do
|
912
1043
|
response.status = 500
|
@@ -922,6 +1053,8 @@ module Sinatra
|
|
922
1053
|
end
|
923
1054
|
|
924
1055
|
error NotFound do
|
1056
|
+
content_type 'text/html'
|
1057
|
+
|
925
1058
|
(<<-HTML).gsub(/^ {8}/, '')
|
926
1059
|
<!DOCTYPE html>
|
927
1060
|
<html>
|
@@ -943,41 +1076,13 @@ module Sinatra
|
|
943
1076
|
</html>
|
944
1077
|
HTML
|
945
1078
|
end
|
946
|
-
|
947
|
-
error do
|
948
|
-
next unless err = request.env['sinatra.error']
|
949
|
-
heading = err.class.name + ' - ' + err.message.to_s
|
950
|
-
(<<-HTML).gsub(/^ {8}/, '')
|
951
|
-
<!DOCTYPE html>
|
952
|
-
<html>
|
953
|
-
<head>
|
954
|
-
<style type="text/css">
|
955
|
-
body {font-family:verdana;color:#333}
|
956
|
-
#c {margin-left:20px}
|
957
|
-
h1 {color:#1D6B8D;margin:0;margin-top:-30px}
|
958
|
-
h2 {color:#1D6B8D;font-size:18px}
|
959
|
-
pre {border-left:2px solid #ddd;padding-left:10px;color:#000}
|
960
|
-
img {margin-top:10px}
|
961
|
-
</style>
|
962
|
-
</head>
|
963
|
-
<body>
|
964
|
-
<div id="c">
|
965
|
-
<img src="/__sinatra__/500.png">
|
966
|
-
<h1>#{escape_html(heading)}</h1>
|
967
|
-
<pre>#{escape_html(clean_backtrace(err.backtrace) * "\n")}</pre>
|
968
|
-
<h2>Params</h2>
|
969
|
-
<pre>#{escape_html(params.inspect)}</pre>
|
970
|
-
</div>
|
971
|
-
</body>
|
972
|
-
</html>
|
973
|
-
HTML
|
974
|
-
end
|
975
1079
|
end
|
976
1080
|
end
|
977
1081
|
|
978
1082
|
# Base class for classic style (top-level) applications.
|
979
1083
|
class Default < Base
|
980
1084
|
set :raise_errors, Proc.new { test? }
|
1085
|
+
set :show_exceptions, Proc.new { development? }
|
981
1086
|
set :dump_errors, true
|
982
1087
|
set :sessions, false
|
983
1088
|
set :logging, Proc.new { ! test? }
|
@@ -987,7 +1092,7 @@ module Sinatra
|
|
987
1092
|
|
988
1093
|
def self.register(*extensions, &block) #:nodoc:
|
989
1094
|
added_methods = extensions.map {|m| m.public_instance_methods }.flatten
|
990
|
-
Delegator.delegate
|
1095
|
+
Delegator.delegate(*added_methods)
|
991
1096
|
super(*extensions, &block)
|
992
1097
|
end
|
993
1098
|
end
|
@@ -997,24 +1102,29 @@ module Sinatra
|
|
997
1102
|
class Application < Default
|
998
1103
|
end
|
999
1104
|
|
1105
|
+
# Sinatra delegation mixin. Mixing this module into an object causes all
|
1106
|
+
# methods to be delegated to the Sinatra::Application class. Used primarily
|
1107
|
+
# at the top-level.
|
1000
1108
|
module Delegator #:nodoc:
|
1001
1109
|
def self.delegate(*methods)
|
1002
1110
|
methods.each do |method_name|
|
1003
1111
|
eval <<-RUBY, binding, '(__DELEGATE__)', 1
|
1004
1112
|
def #{method_name}(*args, &b)
|
1005
|
-
::Sinatra::Application
|
1113
|
+
::Sinatra::Application.send(#{method_name.inspect}, *args, &b)
|
1006
1114
|
end
|
1007
|
-
private
|
1115
|
+
private #{method_name.inspect}
|
1008
1116
|
RUBY
|
1009
1117
|
end
|
1010
1118
|
end
|
1011
1119
|
|
1012
|
-
delegate :get, :put, :post, :delete, :head, :template, :layout,
|
1013
|
-
:
|
1014
|
-
:
|
1120
|
+
delegate :get, :put, :post, :delete, :head, :template, :layout,
|
1121
|
+
:before, :after, :error, :not_found, :configure, :set, :mime_type,
|
1122
|
+
:enable, :disable, :use, :development?, :test?,
|
1015
1123
|
:production?, :use_in_file_templates!, :helpers
|
1016
1124
|
end
|
1017
1125
|
|
1126
|
+
# Create a new Sinatra application. The block is evaluated in the new app's
|
1127
|
+
# class scope.
|
1018
1128
|
def self.new(base=Base, options={}, &block)
|
1019
1129
|
base = Class.new(base)
|
1020
1130
|
base.send :class_eval, &block if block_given?
|
@@ -1031,13 +1141,3 @@ module Sinatra
|
|
1031
1141
|
Default.helpers(*extensions, &block)
|
1032
1142
|
end
|
1033
1143
|
end
|
1034
|
-
|
1035
|
-
class String #:nodoc:
|
1036
|
-
# Define String#each under 1.9 for Rack compatibility. This should be
|
1037
|
-
# removed once Rack is fully 1.9 compatible.
|
1038
|
-
alias_method :each, :each_line unless ''.respond_to? :each
|
1039
|
-
|
1040
|
-
# Define String#bytesize as an alias to String#length for Ruby 1.8.6 and
|
1041
|
-
# earlier.
|
1042
|
-
alias_method :bytesize, :length unless ''.respond_to? :bytesize
|
1043
|
-
end
|