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/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.0.5'
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 header["Content-Length"].nil? && body.respond_to?(:to_ary)
52
+ if body.respond_to?(:to_ary)
46
53
  header["Content-Length"] = body.to_ary.
47
- inject(0) { |len, part| len + part.length }.to_s
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 = 500, code.to_str if code.respond_to? :to_str
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 and attempt to
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 render(engine, template, options={})
192
- data = lookup_template(engine, template, options)
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 = lookup_template(engine, template, options)
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
- instance = ::ERB.new(data)
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
- instance.result(binding)
248
- end
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
- @response.body = [] if @env['REQUEST_METHOD'] == 'HEAD'
319
- @response.finish
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 = @request.path_info
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.map{|val| val && unescape(val) }
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
- raise NotFound
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
- splat = key.scan(/(^[^\[]+)|\[([^\]]+)\]/).flatten.compact
385
- head, last = splat[0..-2], splat[-1]
386
- head.inject(res){ |s,v| s[v] ||= indifferent_hash }[last] = val
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 = res.last
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 = 404
437
- @response.body = ['<h1>Not Found</h1>']
506
+ @response.status = 404
507
+ @response.body = ['<h1>Not Found</h1>']
438
508
  error_block! boom.class, NotFound
509
+ end
439
510
 
440
- rescue ::Exception => boom
511
+ def handle_exception!(boom)
441
512
  @env['sinatra.error'] = boom
442
513
 
443
- if options.dump_errors?
444
- msg = ["#{boom.class} - #{boom.message}:", *boom.backtrace].join("\n ")
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(&method(:set))
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
- line = caller.detect do |s|
522
- [
523
- /lib\/sinatra.*\.rb/,
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 = lambda { unbound_method.bind(self).call }
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
- URI.encode(path).gsub(/((:\w+)|\*)/) do |match|
622
- if match == "*"
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
- reset_middleware
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 = detect_rack_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
- construct_middleware if @callsite.nil?
671
- @callsite.call(env)
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.routes = dupe_routes
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
- def dupe_routes
711
- routes.inject({}) do |hash,(request_method,routes)|
712
- hash[request_method] = routes.dup
713
- hash
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
- public_dir = File.expand_path(options.public)
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 class='trace'>#{escape_html(err.backtrace.join("\n"))}</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, false
961
+ set :raise_errors, Proc.new { test? }
820
962
  set :dump_errors, true
821
963
  set :sessions, false
822
- set :logging, true
964
+ set :logging, Proc.new { ! test? }
823
965
  set :methodoverride, true
824
966
  set :static, true
825
- set :run, false
826
- set :reload, Proc.new { app_file? && development? }
967
+ set :run, Proc.new { ! test? }
827
968
 
828
- def self.reloading?
829
- @reloading ||= false
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
- METHODS = %w[
856
- get put post delete head template layout before error not_found
857
- configures configure set set_option set_options enable disable use
858
- development? test? production? use_in_file_templates!
859
- ]
860
-
861
- METHODS.each do |method_name|
862
- eval <<-RUBY, binding, '(__DELEGATE__)', 1
863
- def #{method_name}(*args, &b)
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