sinatra 0.9.0.5 → 0.9.1
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/AUTHORS +8 -7
- data/CHANGES +70 -0
- data/README.rdoc +86 -155
- data/Rakefile +9 -59
- data/compat/app_test.rb +3 -21
- data/compat/application_test.rb +0 -72
- data/compat/pipeline_test.rb +0 -26
- data/compat/sessions_test.rb +3 -0
- data/compat/streaming_test.rb +15 -3
- data/lib/sinatra/base.rb +290 -142
- data/lib/sinatra/compat.rb +30 -30
- data/lib/sinatra/main.rb +4 -5
- data/lib/sinatra/test/bacon.rb +2 -0
- data/lib/sinatra/test/rspec.rb +2 -0
- data/lib/sinatra/test/spec.rb +2 -0
- data/lib/sinatra/test/unit.rb +2 -0
- data/lib/sinatra/test.rb +62 -50
- data/sinatra.gemspec +7 -3
- data/test/base_test.rb +108 -46
- data/test/erb_test.rb +31 -0
- data/test/extensions_test.rb +84 -0
- data/test/helper.rb +65 -9
- data/test/helpers_test.rb +469 -333
- data/test/mapped_error_test.rb +6 -6
- data/test/middleware_test.rb +13 -3
- data/test/options_test.rb +278 -1
- data/test/reload_test.rb +7 -0
- data/test/response_test.rb +42 -0
- data/test/result_test.rb +10 -0
- data/test/routing_test.rb +269 -2
- data/test/server_test.rb +41 -0
- data/test/static_test.rb +8 -25
- data/test/test_test.rb +144 -0
- metadata +13 -2
data/lib/sinatra/base.rb
CHANGED
@@ -1,18 +1,21 @@
|
|
1
|
+
require 'thread'
|
1
2
|
require 'time'
|
2
3
|
require 'uri'
|
3
4
|
require 'rack'
|
4
5
|
require 'rack/builder'
|
5
6
|
|
6
7
|
module Sinatra
|
7
|
-
VERSION = '0.9.
|
8
|
+
VERSION = '0.9.1'
|
8
9
|
|
10
|
+
# The request object. See Rack::Request for more info:
|
11
|
+
# http://rack.rubyforge.org/doc/classes/Rack/Request.html
|
9
12
|
class Request < Rack::Request
|
10
13
|
def user_agent
|
11
14
|
@env['HTTP_USER_AGENT']
|
12
15
|
end
|
13
16
|
|
14
17
|
def accept
|
15
|
-
@env['HTTP_ACCEPT'].split(',').map { |a| a.strip }
|
18
|
+
@env['HTTP_ACCEPT'].to_s.split(',').map { |a| a.strip }
|
16
19
|
end
|
17
20
|
|
18
21
|
# Override Rack 0.9.x's #params implementation (see #72 in lighthouse)
|
@@ -23,6 +26,10 @@ module Sinatra
|
|
23
26
|
end
|
24
27
|
end
|
25
28
|
|
29
|
+
# The response object. See Rack::Response and Rack::ResponseHelpers for
|
30
|
+
# more info:
|
31
|
+
# http://rack.rubyforge.org/doc/classes/Rack/Response.html
|
32
|
+
# http://rack.rubyforge.org/doc/classes/Rack/Response/Helpers.html
|
26
33
|
class Response < Rack::Response
|
27
34
|
def initialize
|
28
35
|
@status, @body = 200, []
|
@@ -42,19 +49,20 @@ module Sinatra
|
|
42
49
|
else
|
43
50
|
body = @body || []
|
44
51
|
body = [body] if body.respond_to? :to_str
|
45
|
-
if
|
52
|
+
if body.respond_to?(:to_ary)
|
46
53
|
header["Content-Length"] = body.to_ary.
|
47
|
-
inject(0) { |len, part| len + part.
|
54
|
+
inject(0) { |len, part| len + part.bytesize }.to_s
|
48
55
|
end
|
49
56
|
[status.to_i, header.to_hash, body]
|
50
57
|
end
|
51
58
|
end
|
52
59
|
end
|
53
60
|
|
54
|
-
class NotFound < NameError
|
61
|
+
class NotFound < NameError #:nodoc:
|
55
62
|
def code ; 404 ; end
|
56
63
|
end
|
57
64
|
|
65
|
+
# Methods available to routes, before filters, and views.
|
58
66
|
module Helpers
|
59
67
|
# Set or retrieve the response status code.
|
60
68
|
def status(value=nil)
|
@@ -82,7 +90,7 @@ module Sinatra
|
|
82
90
|
|
83
91
|
# Halt processing and return the error status provided.
|
84
92
|
def error(code, body=nil)
|
85
|
-
code, body
|
93
|
+
code, body = 500, code.to_str if code.respond_to? :to_str
|
86
94
|
response.body = body unless body.nil?
|
87
95
|
halt code
|
88
96
|
end
|
@@ -92,6 +100,12 @@ module Sinatra
|
|
92
100
|
error 404, body
|
93
101
|
end
|
94
102
|
|
103
|
+
# Set multiple response headers with Hash.
|
104
|
+
def headers(hash=nil)
|
105
|
+
response.headers.merge! hash if hash
|
106
|
+
response.headers
|
107
|
+
end
|
108
|
+
|
95
109
|
# Access the underlying Rack session.
|
96
110
|
def session
|
97
111
|
env['rack.session'] ||= {}
|
@@ -125,15 +139,24 @@ module Sinatra
|
|
125
139
|
end
|
126
140
|
end
|
127
141
|
|
128
|
-
# Use the contents of the file as the response body
|
142
|
+
# Use the contents of the file at +path+ as the response body.
|
129
143
|
def send_file(path, opts={})
|
130
144
|
stat = File.stat(path)
|
131
145
|
last_modified stat.mtime
|
146
|
+
|
132
147
|
content_type media_type(opts[:type]) ||
|
133
148
|
media_type(File.extname(path)) ||
|
134
149
|
response['Content-Type'] ||
|
135
150
|
'application/octet-stream'
|
151
|
+
|
136
152
|
response['Content-Length'] ||= (opts[:length] || stat.size).to_s
|
153
|
+
|
154
|
+
if opts[:disposition] == 'attachment' || opts[:filename]
|
155
|
+
attachment opts[:filename] || path
|
156
|
+
elsif opts[:disposition] == 'inline'
|
157
|
+
response['Content-Disposition'] = 'inline'
|
158
|
+
end
|
159
|
+
|
137
160
|
halt StaticFile.open(path, 'rb')
|
138
161
|
rescue Errno::ENOENT
|
139
162
|
not_found
|
@@ -185,11 +208,42 @@ module Sinatra
|
|
185
208
|
halt 304 if etags.include?(value) || etags.include?('*')
|
186
209
|
end
|
187
210
|
end
|
211
|
+
|
212
|
+
## Sugar for redirect (example: redirect back)
|
213
|
+
def back ; request.referer ; end
|
214
|
+
|
188
215
|
end
|
189
216
|
|
217
|
+
# Template rendering methods. Each method takes a the name of a template
|
218
|
+
# to render as a Symbol and returns a String with the rendered output.
|
190
219
|
module Templates
|
191
|
-
def
|
192
|
-
|
220
|
+
def erb(template, options={})
|
221
|
+
require 'erb' unless defined? ::ERB
|
222
|
+
render :erb, template, options
|
223
|
+
end
|
224
|
+
|
225
|
+
def haml(template, options={})
|
226
|
+
require 'haml' unless defined? ::Haml
|
227
|
+
options[:options] ||= self.class.haml if self.class.respond_to? :haml
|
228
|
+
render :haml, template, options
|
229
|
+
end
|
230
|
+
|
231
|
+
def sass(template, options={}, &block)
|
232
|
+
require 'sass' unless defined? ::Sass
|
233
|
+
options[:layout] = false
|
234
|
+
render :sass, template, options
|
235
|
+
end
|
236
|
+
|
237
|
+
def builder(template=nil, options={}, &block)
|
238
|
+
require 'builder' unless defined? ::Builder
|
239
|
+
options, template = template, nil if template.is_a?(Hash)
|
240
|
+
template = lambda { block } if template.nil?
|
241
|
+
render :builder, template, options
|
242
|
+
end
|
243
|
+
|
244
|
+
private
|
245
|
+
def render(engine, template, options={}) #:nodoc:
|
246
|
+
data = lookup_template(engine, template, options)
|
193
247
|
output = __send__("render_#{engine}", template, data, options)
|
194
248
|
layout, data = lookup_layout(engine, options)
|
195
249
|
if layout
|
@@ -220,7 +274,7 @@ module Sinatra
|
|
220
274
|
return if options[:layout] == false
|
221
275
|
options.delete(:layout) if options[:layout] == true
|
222
276
|
template = options[:layout] || :layout
|
223
|
-
data
|
277
|
+
data = lookup_template(engine, template, options)
|
224
278
|
[template, data]
|
225
279
|
rescue Errno::ENOENT
|
226
280
|
nil
|
@@ -232,25 +286,18 @@ module Sinatra
|
|
232
286
|
"#{views_dir}/#{template}.#{engine}"
|
233
287
|
end
|
234
288
|
|
235
|
-
def erb(template, options={})
|
236
|
-
require 'erb' unless defined? ::ERB
|
237
|
-
render :erb, template, options
|
238
|
-
end
|
239
|
-
|
240
289
|
def render_erb(template, data, options, &block)
|
290
|
+
original_out_buf = @_out_buf
|
241
291
|
data = data.call if data.kind_of? Proc
|
242
|
-
|
292
|
+
|
293
|
+
instance = ::ERB.new(data, nil, nil, '@_out_buf')
|
243
294
|
locals = options[:locals] || {}
|
244
295
|
locals_assigns = locals.to_a.collect { |k,v| "#{k} = locals[:#{k}]" }
|
296
|
+
|
245
297
|
src = "#{locals_assigns.join("\n")}\n#{instance.src}"
|
246
298
|
eval src, binding, '(__ERB__)', locals_assigns.length + 1
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
def haml(template, options={})
|
251
|
-
require 'haml' unless defined? ::Haml
|
252
|
-
options[:options] ||= self.class.haml if self.class.respond_to? :haml
|
253
|
-
render :haml, template, options
|
299
|
+
@_out_buf, result = original_out_buf, @_out_buf
|
300
|
+
result
|
254
301
|
end
|
255
302
|
|
256
303
|
def render_haml(template, data, options, &block)
|
@@ -258,24 +305,11 @@ module Sinatra
|
|
258
305
|
engine.render(self, options[:locals] || {}, &block)
|
259
306
|
end
|
260
307
|
|
261
|
-
def sass(template, options={}, &block)
|
262
|
-
require 'sass' unless defined? ::Sass
|
263
|
-
options[:layout] = false
|
264
|
-
render :sass, template, options
|
265
|
-
end
|
266
|
-
|
267
308
|
def render_sass(template, data, options, &block)
|
268
309
|
engine = ::Sass::Engine.new(data, options[:sass] || {})
|
269
310
|
engine.render
|
270
311
|
end
|
271
312
|
|
272
|
-
def builder(template=nil, options={}, &block)
|
273
|
-
require 'builder' unless defined? ::Builder
|
274
|
-
options, template = template, nil if template.is_a?(Hash)
|
275
|
-
template = lambda { block } if template.nil?
|
276
|
-
render :builder, template, options
|
277
|
-
end
|
278
|
-
|
279
313
|
def render_builder(template, data, options, &block)
|
280
314
|
xml = ::Builder::XmlMarkup.new(:indent => 2)
|
281
315
|
if data.respond_to?(:to_str)
|
@@ -285,9 +319,9 @@ module Sinatra
|
|
285
319
|
end
|
286
320
|
xml.target!
|
287
321
|
end
|
288
|
-
|
289
322
|
end
|
290
323
|
|
324
|
+
# Base class for all Sinatra applications and middleware.
|
291
325
|
class Base
|
292
326
|
include Rack::Utils
|
293
327
|
include Helpers
|
@@ -300,6 +334,7 @@ module Sinatra
|
|
300
334
|
yield self if block_given?
|
301
335
|
end
|
302
336
|
|
337
|
+
# Rack call interface.
|
303
338
|
def call(env)
|
304
339
|
dup.call!(env)
|
305
340
|
end
|
@@ -315,23 +350,45 @@ module Sinatra
|
|
315
350
|
invoke { dispatch! }
|
316
351
|
invoke { error_block!(response.status) }
|
317
352
|
|
318
|
-
|
319
|
-
|
353
|
+
status, header, body = @response.finish
|
354
|
+
|
355
|
+
# Never produce a body on HEAD requests. Do retain the Content-Length
|
356
|
+
# unless it's "0", in which case we assume it was calculated erroneously
|
357
|
+
# for a manual HEAD response and remove it entirely.
|
358
|
+
if @env['REQUEST_METHOD'] == 'HEAD'
|
359
|
+
body = []
|
360
|
+
header.delete('Content-Length') if header['Content-Length'] == '0'
|
361
|
+
end
|
362
|
+
|
363
|
+
[status, header, body]
|
320
364
|
end
|
321
365
|
|
366
|
+
# Access options defined with Base.set.
|
322
367
|
def options
|
323
368
|
self.class
|
324
369
|
end
|
325
370
|
|
371
|
+
# Exit the current block and halt the response.
|
326
372
|
def halt(*response)
|
327
373
|
response = response.first if response.length == 1
|
328
374
|
throw :halt, response
|
329
375
|
end
|
330
376
|
|
377
|
+
# Pass control to the next matching route.
|
331
378
|
def pass
|
332
379
|
throw :pass
|
333
380
|
end
|
334
381
|
|
382
|
+
# Forward the request to the downstream app -- middleware only.
|
383
|
+
def forward
|
384
|
+
fail "downstream app not set" unless @app.respond_to? :call
|
385
|
+
status, headers, body = @app.call(@request.env)
|
386
|
+
@response.status = status
|
387
|
+
@response.body = body
|
388
|
+
@response.headers.merge! headers
|
389
|
+
nil
|
390
|
+
end
|
391
|
+
|
335
392
|
private
|
336
393
|
# Run before filters and then locate and run a matching route.
|
337
394
|
def route!
|
@@ -343,11 +400,11 @@ module Sinatra
|
|
343
400
|
# routes
|
344
401
|
if routes = self.class.routes[@request.request_method]
|
345
402
|
original_params = @params
|
346
|
-
path
|
403
|
+
path = unescape(@request.path_info)
|
347
404
|
|
348
405
|
routes.each do |pattern, keys, conditions, block|
|
349
406
|
if match = pattern.match(path)
|
350
|
-
values = match.captures.
|
407
|
+
values = match.captures.to_a
|
351
408
|
params =
|
352
409
|
if keys.any?
|
353
410
|
keys.zip(values).inject({}) do |hash,(k,v)|
|
@@ -364,6 +421,7 @@ module Sinatra
|
|
364
421
|
{}
|
365
422
|
end
|
366
423
|
@params = original_params.merge(params)
|
424
|
+
@block_params = values
|
367
425
|
|
368
426
|
catch(:pass) do
|
369
427
|
conditions.each { |cond|
|
@@ -374,16 +432,22 @@ module Sinatra
|
|
374
432
|
end
|
375
433
|
end
|
376
434
|
|
377
|
-
|
435
|
+
# No matching route found or all routes passed -- forward downstream
|
436
|
+
# when running as middleware; 404 when running as normal app.
|
437
|
+
if @app
|
438
|
+
forward
|
439
|
+
else
|
440
|
+
raise NotFound
|
441
|
+
end
|
378
442
|
end
|
379
443
|
|
380
444
|
def nested_params(params)
|
381
445
|
return indifferent_hash.merge(params) if !params.keys.join.include?('[')
|
382
446
|
params.inject indifferent_hash do |res, (key,val)|
|
383
|
-
if key
|
384
|
-
|
385
|
-
|
386
|
-
head.inject(res){ |
|
447
|
+
if key.include?('[')
|
448
|
+
head = key.split(/[\]\[]+/)
|
449
|
+
last = head.pop
|
450
|
+
head.inject(res){ |hash,k| hash[k] ||= indifferent_hash }[last] = val
|
387
451
|
else
|
388
452
|
res[key] = val
|
389
453
|
end
|
@@ -412,7 +476,7 @@ module Sinatra
|
|
412
476
|
headers.each { |k, v| @response.headers[k] = v } if headers
|
413
477
|
elsif res.length == 2
|
414
478
|
@response.status = res.first
|
415
|
-
@response.body
|
479
|
+
@response.body = res.last
|
416
480
|
else
|
417
481
|
raise TypeError, "#{res.inspect} not supported"
|
418
482
|
end
|
@@ -432,20 +496,24 @@ module Sinatra
|
|
432
496
|
def dispatch!
|
433
497
|
route!
|
434
498
|
rescue NotFound => boom
|
499
|
+
handle_not_found!(boom)
|
500
|
+
rescue ::Exception => boom
|
501
|
+
handle_exception!(boom)
|
502
|
+
end
|
503
|
+
|
504
|
+
def handle_not_found!(boom)
|
435
505
|
@env['sinatra.error'] = boom
|
436
|
-
@response.status
|
437
|
-
@response.body
|
506
|
+
@response.status = 404
|
507
|
+
@response.body = ['<h1>Not Found</h1>']
|
438
508
|
error_block! boom.class, NotFound
|
509
|
+
end
|
439
510
|
|
440
|
-
|
511
|
+
def handle_exception!(boom)
|
441
512
|
@env['sinatra.error'] = boom
|
442
513
|
|
443
|
-
if options.dump_errors?
|
444
|
-
|
445
|
-
@env['rack.errors'].write msg
|
446
|
-
end
|
514
|
+
dump_errors!(boom) if options.dump_errors?
|
515
|
+
raise boom if options.raise_errors?
|
447
516
|
|
448
|
-
raise boom if options.raise_errors?
|
449
517
|
@response.status = 500
|
450
518
|
error_block! boom.class, Exception
|
451
519
|
end
|
@@ -462,25 +530,42 @@ module Sinatra
|
|
462
530
|
nil
|
463
531
|
end
|
464
532
|
|
533
|
+
def dump_errors!(boom)
|
534
|
+
backtrace = clean_backtrace(boom.backtrace)
|
535
|
+
msg = ["#{boom.class} - #{boom.message}:",
|
536
|
+
*backtrace].join("\n ")
|
537
|
+
@env['rack.errors'].write(msg)
|
538
|
+
end
|
539
|
+
|
540
|
+
def clean_backtrace(trace)
|
541
|
+
return trace unless options.clean_trace?
|
542
|
+
|
543
|
+
trace.reject { |line|
|
544
|
+
line =~ /lib\/sinatra.*\.rb/ ||
|
545
|
+
(defined?(Gem) && line.include?(Gem.dir))
|
546
|
+
}.map! { |line| line.gsub(/^\.\//, '') }
|
547
|
+
end
|
548
|
+
|
465
549
|
@routes = {}
|
466
550
|
@filters = []
|
467
551
|
@conditions = []
|
468
552
|
@templates = {}
|
469
553
|
@middleware = []
|
470
|
-
@callsite = nil
|
471
554
|
@errors = {}
|
555
|
+
@prototype = nil
|
472
556
|
|
473
557
|
class << self
|
474
558
|
attr_accessor :routes, :filters, :conditions, :templates,
|
475
559
|
:middleware, :errors
|
476
560
|
|
561
|
+
public
|
477
562
|
def set(option, value=self)
|
478
563
|
if value.kind_of?(Proc)
|
479
564
|
metadef(option, &value)
|
480
565
|
metadef("#{option}?") { !!__send__(option) }
|
481
566
|
metadef("#{option}=") { |val| set(option, Proc.new{val}) }
|
482
567
|
elsif value == self && option.respond_to?(:to_hash)
|
483
|
-
option.to_hash.each(
|
568
|
+
option.to_hash.each { |k,v| set(k, v) }
|
484
569
|
elsif respond_to?("#{option}=")
|
485
570
|
__send__ "#{option}=", value
|
486
571
|
else
|
@@ -518,14 +603,10 @@ module Sinatra
|
|
518
603
|
end
|
519
604
|
|
520
605
|
def use_in_file_templates!
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
/rubygems\/custom_require\.rb/
|
526
|
-
].all? { |x| s !~ x }
|
527
|
-
end
|
528
|
-
file = line.sub(/:\d+.*$/, '')
|
606
|
+
ignore = [/lib\/sinatra.*\.rb/, /\(.*\)/, /rubygems\/custom_require\.rb/]
|
607
|
+
file = caller.
|
608
|
+
map { |line| line.sub(/:\d+.*$/, '') }.
|
609
|
+
find { |line| ignore.all? { |pattern| line !~ pattern } }
|
529
610
|
if data = ::IO.read(file).split('__END__')[1]
|
530
611
|
data.gsub!(/\r\n/, "\n")
|
531
612
|
template = nil
|
@@ -554,6 +635,7 @@ module Sinatra
|
|
554
635
|
@conditions << block
|
555
636
|
end
|
556
637
|
|
638
|
+
private
|
557
639
|
def host_name(pattern)
|
558
640
|
condition { pattern === request.host }
|
559
641
|
end
|
@@ -584,6 +666,7 @@ module Sinatra
|
|
584
666
|
}
|
585
667
|
end
|
586
668
|
|
669
|
+
public
|
587
670
|
def get(path, opts={}, &block)
|
588
671
|
conditions = @conditions.dup
|
589
672
|
route('GET', path, opts, &block)
|
@@ -608,7 +691,12 @@ module Sinatra
|
|
608
691
|
|
609
692
|
define_method "#{verb} #{path}", &block
|
610
693
|
unbound_method = instance_method("#{verb} #{path}")
|
611
|
-
block =
|
694
|
+
block =
|
695
|
+
if block.arity != 0
|
696
|
+
lambda { unbound_method.bind(self).call(*@block_params) }
|
697
|
+
else
|
698
|
+
lambda { unbound_method.bind(self).call }
|
699
|
+
end
|
612
700
|
|
613
701
|
(routes[verb] ||= []).
|
614
702
|
push([pattern, keys, conditions, block]).last
|
@@ -617,18 +705,22 @@ module Sinatra
|
|
617
705
|
def compile(path)
|
618
706
|
keys = []
|
619
707
|
if path.respond_to? :to_str
|
708
|
+
special_chars = %w{. + ( )}
|
620
709
|
pattern =
|
621
|
-
|
622
|
-
|
710
|
+
path.gsub(/((:\w+)|[\*#{special_chars.join}])/) do |match|
|
711
|
+
case match
|
712
|
+
when "*"
|
623
713
|
keys << 'splat'
|
624
714
|
"(.*?)"
|
715
|
+
when *special_chars
|
716
|
+
Regexp.escape(match)
|
625
717
|
else
|
626
718
|
keys << $2[1..-1]
|
627
|
-
"([
|
719
|
+
"([^/?&#]+)"
|
628
720
|
end
|
629
721
|
end
|
630
722
|
[/^#{pattern}$/, keys]
|
631
|
-
elsif path.respond_to?
|
723
|
+
elsif path.respond_to? :match
|
632
724
|
[path, keys]
|
633
725
|
else
|
634
726
|
raise TypeError, path
|
@@ -636,39 +728,103 @@ module Sinatra
|
|
636
728
|
end
|
637
729
|
|
638
730
|
public
|
731
|
+
def helpers(*extensions, &block)
|
732
|
+
class_eval(&block) if block_given?
|
733
|
+
include *extensions if extensions.any?
|
734
|
+
end
|
735
|
+
|
736
|
+
def register(*extensions, &block)
|
737
|
+
extensions << Module.new(&block) if block_given?
|
738
|
+
extensions.each do |extension|
|
739
|
+
extend extension
|
740
|
+
extension.registered(self) if extension.respond_to?(:registered)
|
741
|
+
end
|
742
|
+
end
|
743
|
+
|
639
744
|
def development? ; environment == :development ; end
|
640
745
|
def test? ; environment == :test ; end
|
641
746
|
def production? ; environment == :production ; end
|
642
747
|
|
643
748
|
def configure(*envs, &block)
|
749
|
+
return if reloading?
|
644
750
|
yield if envs.empty? || envs.include?(environment.to_sym)
|
645
751
|
end
|
646
752
|
|
647
753
|
def use(middleware, *args, &block)
|
648
|
-
|
754
|
+
@prototype = nil
|
649
755
|
@middleware << [middleware, args, block]
|
650
756
|
end
|
651
757
|
|
652
758
|
def run!(options={})
|
653
759
|
set options
|
654
|
-
handler
|
760
|
+
handler = detect_rack_handler
|
655
761
|
handler_name = handler.name.gsub(/.*::/, '')
|
656
762
|
puts "== Sinatra/#{Sinatra::VERSION} has taken the stage " +
|
657
|
-
"on #{port} for #{environment} with backup from #{handler_name}"
|
763
|
+
"on #{port} for #{environment} with backup from #{handler_name}" unless handler_name =~/cgi/i
|
658
764
|
handler.run self, :Host => host, :Port => port do |server|
|
659
765
|
trap(:INT) do
|
660
766
|
## Use thins' hard #stop! if available, otherwise just #stop
|
661
767
|
server.respond_to?(:stop!) ? server.stop! : server.stop
|
662
|
-
puts "\n== Sinatra has ended his set (crowd applauds)"
|
768
|
+
puts "\n== Sinatra has ended his set (crowd applauds)" unless handler_name =~/cgi/i
|
663
769
|
end
|
664
770
|
end
|
665
771
|
rescue Errno::EADDRINUSE => e
|
666
772
|
puts "== Someone is already performing on port #{port}!"
|
667
773
|
end
|
668
774
|
|
775
|
+
# The prototype instance used to process requests.
|
776
|
+
def prototype
|
777
|
+
@prototype ||= new
|
778
|
+
end
|
779
|
+
|
780
|
+
# Create a new instance of the class fronted by its middleware
|
781
|
+
# pipeline. The object is guaranteed to respond to #call but may not be
|
782
|
+
# an instance of the class new was called on.
|
783
|
+
def new(*args, &bk)
|
784
|
+
builder = Rack::Builder.new
|
785
|
+
builder.use Rack::Session::Cookie if sessions? && !test?
|
786
|
+
builder.use Rack::CommonLogger if logging?
|
787
|
+
builder.use Rack::MethodOverride if methodoverride?
|
788
|
+
@middleware.each { |c, args, bk| builder.use(c, *args, &bk) }
|
789
|
+
builder.run super
|
790
|
+
builder.to_app
|
791
|
+
end
|
792
|
+
|
669
793
|
def call(env)
|
670
|
-
|
671
|
-
|
794
|
+
synchronize do
|
795
|
+
reload! if reload?
|
796
|
+
prototype.call(env)
|
797
|
+
end
|
798
|
+
end
|
799
|
+
|
800
|
+
def reloading?
|
801
|
+
@reloading
|
802
|
+
end
|
803
|
+
|
804
|
+
def reload!
|
805
|
+
@reloading = true
|
806
|
+
reset!
|
807
|
+
$LOADED_FEATURES.delete("sinatra.rb")
|
808
|
+
::Kernel.load app_file
|
809
|
+
@reloading = false
|
810
|
+
end
|
811
|
+
|
812
|
+
def reset!(base=superclass)
|
813
|
+
@routes = base.dupe_routes
|
814
|
+
@templates = base.templates.dup
|
815
|
+
@conditions = []
|
816
|
+
@filters = base.filters.dup
|
817
|
+
@errors = base.errors.dup
|
818
|
+
@middleware = base.middleware.dup
|
819
|
+
@prototype = nil
|
820
|
+
end
|
821
|
+
|
822
|
+
protected
|
823
|
+
def dupe_routes
|
824
|
+
routes.inject({}) do |hash,(request_method,routes)|
|
825
|
+
hash[request_method] = routes.dup
|
826
|
+
hash
|
827
|
+
end
|
672
828
|
end
|
673
829
|
|
674
830
|
private
|
@@ -678,39 +834,23 @@ module Sinatra
|
|
678
834
|
begin
|
679
835
|
return Rack::Handler.get(server_name)
|
680
836
|
rescue LoadError
|
837
|
+
rescue NameError
|
681
838
|
end
|
682
839
|
end
|
683
840
|
fail "Server handler (#{servers.join(',')}) not found."
|
684
841
|
end
|
685
842
|
|
686
|
-
def construct_middleware(builder=Rack::Builder.new)
|
687
|
-
builder.use Rack::Session::Cookie if sessions?
|
688
|
-
builder.use Rack::CommonLogger if logging?
|
689
|
-
builder.use Rack::MethodOverride if methodoverride?
|
690
|
-
@middleware.each { |c, args, bk| builder.use(c, *args, &bk) }
|
691
|
-
builder.run new
|
692
|
-
@callsite = builder.to_app
|
693
|
-
end
|
694
|
-
|
695
|
-
def reset_middleware
|
696
|
-
@callsite = nil
|
697
|
-
end
|
698
|
-
|
699
843
|
def inherited(subclass)
|
700
|
-
subclass.
|
701
|
-
subclass.templates = templates.dup
|
702
|
-
subclass.conditions = []
|
703
|
-
subclass.filters = filters.dup
|
704
|
-
subclass.errors = errors.dup
|
705
|
-
subclass.middleware = middleware.dup
|
706
|
-
subclass.send :reset_middleware
|
844
|
+
subclass.reset! self
|
707
845
|
super
|
708
846
|
end
|
709
847
|
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
848
|
+
@@mutex = Mutex.new
|
849
|
+
def synchronize(&block)
|
850
|
+
if lock?
|
851
|
+
@@mutex.synchronize(&block)
|
852
|
+
else
|
853
|
+
yield
|
714
854
|
end
|
715
855
|
end
|
716
856
|
|
@@ -722,6 +862,7 @@ module Sinatra
|
|
722
862
|
|
723
863
|
set :raise_errors, true
|
724
864
|
set :dump_errors, false
|
865
|
+
set :clean_trace, true
|
725
866
|
set :sessions, false
|
726
867
|
set :logging, false
|
727
868
|
set :methodoverride, false
|
@@ -737,13 +878,13 @@ module Sinatra
|
|
737
878
|
set :root, Proc.new { app_file && File.expand_path(File.dirname(app_file)) }
|
738
879
|
set :views, Proc.new { root && File.join(root, 'views') }
|
739
880
|
set :public, Proc.new { root && File.join(root, 'public') }
|
881
|
+
set :reload, Proc.new { app_file? && app_file !~ /\.ru$/i && development? }
|
882
|
+
set :lock, Proc.new { reload? }
|
740
883
|
|
741
884
|
# static files route
|
742
885
|
get(/.*[^\/]$/) do
|
743
886
|
pass unless options.static? && options.public?
|
744
|
-
|
745
|
-
path = File.expand_path(public_dir + unescape(request.path_info))
|
746
|
-
pass if path[0, public_dir.length] != public_dir
|
887
|
+
path = options.public + unescape(request.path_info)
|
747
888
|
pass unless File.file?(path)
|
748
889
|
send_file path, :disposition => nil
|
749
890
|
end
|
@@ -804,7 +945,7 @@ module Sinatra
|
|
804
945
|
<div id="c">
|
805
946
|
<img src="/__sinatra__/500.png">
|
806
947
|
<h1>#{escape_html(heading)}</h1>
|
807
|
-
<pre
|
948
|
+
<pre>#{escape_html(clean_backtrace(err.backtrace) * "\n")}</pre>
|
808
949
|
<h2>Params</h2>
|
809
950
|
<pre>#{escape_html(params.inspect)}</pre>
|
810
951
|
</div>
|
@@ -815,57 +956,44 @@ module Sinatra
|
|
815
956
|
end
|
816
957
|
end
|
817
958
|
|
959
|
+
# Base class for classic style (top-level) applications.
|
818
960
|
class Default < Base
|
819
|
-
set :raise_errors,
|
961
|
+
set :raise_errors, Proc.new { test? }
|
820
962
|
set :dump_errors, true
|
821
963
|
set :sessions, false
|
822
|
-
set :logging,
|
964
|
+
set :logging, Proc.new { ! test? }
|
823
965
|
set :methodoverride, true
|
824
966
|
set :static, true
|
825
|
-
set :run,
|
826
|
-
set :reload, Proc.new { app_file? && development? }
|
967
|
+
set :run, Proc.new { ! test? }
|
827
968
|
|
828
|
-
def self.
|
829
|
-
|
969
|
+
def self.register(*extensions, &block) #:nodoc:
|
970
|
+
added_methods = extensions.map {|m| m.public_instance_methods }.flatten
|
971
|
+
Delegator.delegate *added_methods
|
972
|
+
super(*extensions, &block)
|
830
973
|
end
|
831
|
-
|
832
|
-
def self.configure(*envs)
|
833
|
-
super unless reloading?
|
834
|
-
end
|
835
|
-
|
836
|
-
def self.call(env)
|
837
|
-
reload! if reload?
|
838
|
-
super
|
839
|
-
end
|
840
|
-
|
841
|
-
def self.reload!
|
842
|
-
@reloading = true
|
843
|
-
superclass.send :inherited, self
|
844
|
-
$LOADED_FEATURES.delete("sinatra.rb")
|
845
|
-
::Kernel.load app_file
|
846
|
-
@reloading = false
|
847
|
-
end
|
848
|
-
|
849
974
|
end
|
850
975
|
|
976
|
+
# The top-level Application. All DSL methods executed on main are delegated
|
977
|
+
# to this class.
|
851
978
|
class Application < Default
|
852
979
|
end
|
853
980
|
|
854
|
-
module Delegator
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
::Sinatra::Application.#{method_name}(*args, &b)
|
865
|
-
end
|
866
|
-
private :#{method_name}
|
867
|
-
RUBY
|
981
|
+
module Delegator #:nodoc:
|
982
|
+
def self.delegate(*methods)
|
983
|
+
methods.each do |method_name|
|
984
|
+
eval <<-RUBY, binding, '(__DELEGATE__)', 1
|
985
|
+
def #{method_name}(*args, &b)
|
986
|
+
::Sinatra::Application.#{method_name}(*args, &b)
|
987
|
+
end
|
988
|
+
private :#{method_name}
|
989
|
+
RUBY
|
990
|
+
end
|
868
991
|
end
|
992
|
+
|
993
|
+
delegate :get, :put, :post, :delete, :head, :template, :layout, :before,
|
994
|
+
:error, :not_found, :configures, :configure, :set, :set_option,
|
995
|
+
:set_options, :enable, :disable, :use, :development?, :test?,
|
996
|
+
:production?, :use_in_file_templates!, :helpers
|
869
997
|
end
|
870
998
|
|
871
999
|
def self.new(base=Base, options={}, &block)
|
@@ -873,4 +1001,24 @@ module Sinatra
|
|
873
1001
|
base.send :class_eval, &block if block_given?
|
874
1002
|
base
|
875
1003
|
end
|
1004
|
+
|
1005
|
+
# Extend the top-level DSL with the modules provided.
|
1006
|
+
def self.register(*extensions, &block)
|
1007
|
+
Default.register(*extensions, &block)
|
1008
|
+
end
|
1009
|
+
|
1010
|
+
# Include the helper modules provided in Sinatra's request context.
|
1011
|
+
def self.helpers(*extensions, &block)
|
1012
|
+
Default.helpers(*extensions, &block)
|
1013
|
+
end
|
1014
|
+
end
|
1015
|
+
|
1016
|
+
class String #:nodoc:
|
1017
|
+
# Define String#each under 1.9 for Rack compatibility. This should be
|
1018
|
+
# removed once Rack is fully 1.9 compatible.
|
1019
|
+
alias_method :each, :each_line unless ''.respond_to? :each
|
1020
|
+
|
1021
|
+
# Define String#bytesize as an alias to String#length for Ruby 1.8.6 and
|
1022
|
+
# earlier.
|
1023
|
+
alias_method :bytesize, :length unless ''.respond_to? :bytesize
|
876
1024
|
end
|