sinatra-sinatra 0.9.0.2 → 0.9.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/AUTHORS CHANGED
@@ -1,13 +1,13 @@
1
1
  Sinatra was designed and developed by Blake Mizerany (bmizerany) in
2
2
  California. Continued development would not be possible without the ongoing
3
- financial support provided by Heroku <heroku.com> and the emotional support
4
- provided by Adam Wiggins (adamwiggins), Chris Wanstrath (defunkt), PJ Hyett (pjhyett), and
5
- the rest of the Github crew.
3
+ financial support provided by [Heroku](http://heroku.com) and the emotional
4
+ support provided by Adam Wiggins (adamwiggins), Chris Wanstrath (defunkt),
5
+ PJ Hyett (pjhyett), and the rest of the GitHub crew.
6
6
 
7
7
  Special thanks to the following extraordinary individuals, who-out which
8
8
  Sinatra would not be possible:
9
9
 
10
- * Ryan Tomayko (rtomayko) for constantly fixing whitespace errors 60d5006
10
+ * Ryan Tomayko (rtomayko) for constantly fixing whitespace errors 60d5006
11
11
  * Ezra Zygmuntowicz (ezmobius) for initial help and letting Blake steal
12
12
  some of merbs internal code.
13
13
  * Christopher Schneid (cschneid) for The Book, the blog (gittr.com),
@@ -16,7 +16,7 @@ Sinatra would not be possible:
16
16
  the README, and hanging in there when times were rough.
17
17
  * Simon Rozet (sr) for a ton of doc patches, HAML options, and all that
18
18
  advocacy stuff he's going to do for 1.0.
19
- * Erik Kastner (kastner) for fixing MIME_TYPES under Rack 0.5.
19
+ * Erik Kastner (kastner) for fixing `MIME_TYPES` under Rack 0.5.
20
20
  * Ben Bleything (bleything) for caring about HTTP status codes and doc fixes.
21
21
  * Igal Koshevoy (igal) for root path detection under Thin/Passenger.
22
22
  * Jon Crosby (jcrosby) for coffee breaks, doc fixes, and just because, man.
@@ -27,12 +27,13 @@ Sinatra would not be possible:
27
27
  * Victor Hugo Borja (vic) for splat'n routes specs and doco.
28
28
  * Avdi Grimm (avdi) for basic RSpec support.
29
29
  * Jack Danger Canty for a more accurate root directory and for making me
30
- watch this just now: http://www.youtube.com/watch?v=ueaHLHgskkw.
30
+ watch [this](http://www.youtube.com/watch?v=ueaHLHgskkw) just now.
31
31
  * Mathew Walker for making escaped paths work with static files.
32
32
  * Millions of Us for having the problem that led to Sinatra's conception.
33
33
  * Songbird for the problems that helped Sinatra's future become realized.
34
- * Rick Olsen (technoweenie) for the killer plug at RailsConf '08.
34
+ * Rick Olson (technoweenie) for the killer plug at RailsConf '08.
35
35
  * Steven Garcia for the amazing custom artwork you see on 404's and 500's
36
+ * Pat Nakajima (nakajima) for fixing non-nested params in nested params Hash's.
36
37
 
37
38
  and last but not least:
38
39
 
data/README.rdoc CHANGED
@@ -10,12 +10,20 @@ effort:
10
10
  'Hello world!'
11
11
  end
12
12
 
13
- Run with <tt>ruby myapp.rb</tt> and view at <tt>http://localhost:4567</tt>
13
+ Install the gem and run with:
14
14
 
15
- == HTTP Methods
15
+ sudo gem install sinatra
16
+ ruby myapp.rb
17
+
18
+ View at: http://localhost:4567
19
+
20
+ == Routes
21
+
22
+ In Sinatra, a route is an HTTP method paired with an URL matching pattern.
23
+ Each route is associated with a block:
16
24
 
17
25
  get '/' do
18
- .. show things ..
26
+ .. show something ..
19
27
  end
20
28
 
21
29
  post '/' do
@@ -30,21 +38,13 @@ Run with <tt>ruby myapp.rb</tt> and view at <tt>http://localhost:4567</tt>
30
38
  .. annihilate something ..
31
39
  end
32
40
 
33
- == Routes
34
-
35
- Routes are matched based on the order of declaration. The first route that
41
+ Routes are matched in the order they are defined. The first route that
36
42
  matches the request is invoked.
37
43
 
38
- Basic routes:
39
-
40
- get '/hi' do
41
- ...
42
- end
43
-
44
44
  Route patterns may include named parameters, accessible via the
45
45
  <tt>params</tt> hash:
46
46
 
47
- get '/:name' do
47
+ get '/hello/:name' do
48
48
  # matches "GET /foo" and "GET /bar"
49
49
  # params[:name] is 'foo' or 'bar'
50
50
  "Hello #{params[:name]}!"
@@ -86,9 +86,13 @@ a different location by setting the <tt>:public</tt> option:
86
86
 
87
87
  set :public, File.dirname(__FILE__) + '/static'
88
88
 
89
+ Note that the public directory name is not included in the URL. A file
90
+ <tt>./public/css/style.css</tt> is made available as
91
+ <tt>http://example.com/css/style.css</tt>.
92
+
89
93
  == Views / Templates
90
94
 
91
- Templates are assumed to be located directly under a <tt>./views</tt>
95
+ Templates are assumed to be located directly under the <tt>./views</tt>
92
96
  directory. To use a different views directory:
93
97
 
94
98
  set :views, File.dirname(__FILE__) + '/templates'
@@ -141,7 +145,7 @@ Renders <tt>./views/stylesheet.sass</tt>.
141
145
 
142
146
  Renders the inlined template string.
143
147
 
144
- === Accessing Variables
148
+ === Accessing Variables in Templates
145
149
 
146
150
  Templates are evaluated within the same context as the route blocks. Instance
147
151
  variables set in route blocks are available in templates:
@@ -181,12 +185,13 @@ Templates may be defined at the end of the source file:
181
185
  @@ index
182
186
  %div.title Hello world!!!!!
183
187
 
184
- NOTE: Sinatra will automaticly load any in-file-templates in the
185
- source file that first required sinatra. If you have in-file-templates
186
- in another source file you will need to explicitly call
187
- +use_in_file_templates! on main in that file.
188
+ NOTE: In-file templates defined in the source file that requires sinatra
189
+ are automatically loaded. Call the <tt>use_in_file_templates!</tt>
190
+ method explicitly if you have in-file templates in another source file.
188
191
 
189
- It's also possible to define named templates using the top-level template
192
+ === Named Templates
193
+
194
+ It's possible to define named templates using the top-level <tt>template</tt>
190
195
  method:
191
196
 
192
197
  template :layout do
@@ -249,7 +254,7 @@ You can also specify a body when halting ...
249
254
 
250
255
  halt 'this will be the body'
251
256
 
252
- Set the status and body ...
257
+ Or set the status and body ...
253
258
 
254
259
  halt 401, 'go away!'
255
260
 
@@ -317,7 +322,7 @@ code is 404, the <tt>not_found</tt> handler is invoked:
317
322
 
318
323
  The +error+ handler is invoked any time an exception is raised from a route
319
324
  block or before filter. The exception object can be obtained from the
320
- 'sinatra.error' Rack variable:
325
+ <tt>sinatra.error</tt> Rack variable:
321
326
 
322
327
  error do
323
328
  'Sorry there was a nasty error - ' + env['sinatra.error'].name
@@ -339,8 +344,8 @@ You get this:
339
344
 
340
345
  So what happened was... something bad
341
346
 
342
- Sinatra installs special not_found and error handlers when running under
343
- the development environment.
347
+ Sinatra installs special <tt>not_found</tt> and <tt>error</tt> handlers when
348
+ running under the development environment.
344
349
 
345
350
  == Mime types
346
351
 
@@ -480,73 +485,39 @@ Options are:
480
485
  -s # specify rack server/handler (default is thin)
481
486
  -x # turn on the mutex lock (default is off)
482
487
 
483
- == Contributing
484
-
485
- === Tools
486
-
487
- Besides Ruby itself, you only need a text editor, preferably one that supports
488
- Ruby syntax hilighting. VIM and Emacs are a fine choice on any platform, but
489
- feel free to use whatever you're familiar with.
488
+ == The Bleeding Edge
490
489
 
491
- Sinatra uses the Git source code management system. If you're unfamiliar with
492
- Git, you can find more information and tutorials on http://git.or.cz/ as well
493
- as http://git-scm.com/. Scott Chacon created a great series of introductory
494
- screencasts about Git, which you can find here: http://www.gitcasts.com/
490
+ If you would like to use Sinatra's latest bleeding code, create a local
491
+ clone and run your app with the <tt>sinatra/lib</tt> directory on the
492
+ <tt>LOAD_PATH</tt>:
495
493
 
496
- === First Time: Cloning The Sinatra Repo
494
+ cd myapp
495
+ git clone git://github.com/sinatra/sinatra.git
496
+ ruby -Isinatra/lib myapp.rb
497
497
 
498
- cd where/you/keep/your/projects
499
- git clone git://github.com/bmizerany/sinatra.git
500
- cd sinatra
501
- cd path/to/your_project
502
- ln -s ../sinatra/
503
-
504
- === Updating Your Existing Sinatra Clone
505
-
506
- cd where/you/keep/sinatra
507
- git pull
508
-
509
- === Using Edge Sinatra in Your App
510
-
511
- at the top of your sinatra_app.rb file:
498
+ Alternatively, you can add the <tt>sinatra/lib<tt> directory to the
499
+ <tt>LOAD_PATH</tt> in your application:
512
500
 
513
501
  $LOAD_PATH.unshift File.dirname(__FILE__) + '/sinatra/lib'
502
+ require 'rubygems'
514
503
  require 'sinatra'
515
504
 
516
505
  get '/about' do
517
- "I'm running on Version " + Sinatra::VERSION
506
+ "I'm running version " + Sinatra::VERSION
518
507
  end
519
508
 
520
- === Contributing a Patch
521
-
522
- There are several ways to do this. Probably the easiest (and preferred) way is
523
- to fork Sinatra on GitHub (http://github.com/bmizerany/sinatra), push your
524
- changes to your Sinatra repo, and then send Blake Mizerany (bmizerany on
525
- GitHub) a pull request.
526
-
527
- You can also create a patch file and attach it to a feature request or bug fix
528
- on the issue tracker (see below) or send it to the mailing list (see Community
529
- section).
509
+ To update the Sinatra sources in the future:
530
510
 
531
- === Issue Tracking and Feature Requests
532
-
533
- http://sinatra.lighthouseapp.com/
534
-
535
- == Community
536
-
537
- === Mailing List
538
-
539
- http://groups.google.com/group/sinatrarb
540
-
541
- If you have a problem or question, please make sure to include all the
542
- relevant information in your mail, like the Sinatra version you're using, what
543
- version of Ruby you have, and so on.
544
-
545
- === IRC Channel
511
+ cd myproject/sinatra
512
+ git pull
546
513
 
547
- You can find us on the Freenode network in the channel #sinatra
548
- (irc://chat.freenode.net/#sinatra)
514
+ == More
549
515
 
550
- There's usually someone online at any given time, but we cannot pay attention
551
- to the channel all the time, so please stick around for a while after asking a
552
- question.
516
+ * {Project Website}[http://sinatra.github.com/] - Additional documentation,
517
+ news, and links to other resources.
518
+ * {Contributing}[http://sinatra.github.com/contribute.html] - Find a bug? Need
519
+ help? Have a patch?
520
+ * {Lighthouse}[http://sinatra.lighthouseapp.com] - Issue tracking and release
521
+ planning.
522
+ * {Mailing List}[http://groups.google.com/group/sinatrarb]
523
+ * {IRC: #sinatra}[irc://chat.freenode.net/#sinatra] on http://freenode.net
data/Rakefile CHANGED
@@ -1,23 +1,18 @@
1
- require 'rubygems'
2
1
  require 'rake/clean'
2
+ require 'rake/testtask'
3
3
  require 'fileutils'
4
4
 
5
5
  task :default => :test
6
+ task :spec => :test
6
7
 
7
8
  # SPECS ===============================================================
8
9
 
9
- desc 'Run specs with story style output'
10
- task :spec do
11
- pattern = ENV['TEST'] || '.*'
12
- sh "specrb --testcase '#{pattern}' --specdox -Ilib:test test/*_test.rb"
13
- end
14
-
15
- desc 'Run specs with unit test style output'
16
- task :test do |t|
17
- sh "specrb -Ilib:test test/*_test.rb"
10
+ Rake::TestTask.new(:test) do |t|
11
+ t.test_files = FileList['test/*_test.rb']
12
+ t.ruby_opts = ['-rubygems'] if defined? Gem
18
13
  end
19
14
 
20
- desc 'Run compatibility specs'
15
+ desc 'Run compatibility specs (requires test/spec)'
21
16
  task :compat do |t|
22
17
  pattern = ENV['TEST'] || '.*'
23
18
  sh "specrb --testcase '#{pattern}' -Ilib:test compat/*_test.rb"
@@ -67,11 +62,6 @@ end
67
62
 
68
63
  # Rubyforge Release / Publish Tasks ==================================
69
64
 
70
- desc 'Publish website to rubyforge'
71
- task 'publish:doc' => 'doc/api/index.html' do
72
- sh 'scp -rp doc/* rubyforge.org:/var/www/gforge-projects/sinatra/'
73
- end
74
-
75
65
  desc 'Publish gem and tarball to rubyforge'
76
66
  task 'publish:gem' => [package('.gem'), package('.tar.gz')] do |t|
77
67
  sh <<-end
@@ -84,7 +74,7 @@ end
84
74
  # Building docs requires HAML and the hanna gem:
85
75
  # gem install mislav-hanna --source=http://gems.github.com
86
76
 
87
- task 'doc' => ['doc:api','doc:site']
77
+ task 'doc' => ['doc:api']
88
78
 
89
79
  desc 'Generate Hanna RDoc under doc/api'
90
80
  task 'doc:api' => ['doc/api/index.html']
@@ -110,47 +100,6 @@ def rdoc_to_html(file_name)
110
100
  rdoc.convert(File.read(file_name))
111
101
  end
112
102
 
113
- def haml(locals={})
114
- require 'haml'
115
- template = File.read('doc/template.haml')
116
- haml = Haml::Engine.new(template, :format => :html4, :attr_wrapper => '"')
117
- haml.render(Object.new, locals)
118
- end
119
-
120
- desc 'Build website HTML and stuff'
121
- task 'doc:site' => ['doc/index.html', 'doc/book.html']
122
-
123
- file 'doc/index.html' => %w[README.rdoc doc/template.haml] do |file|
124
- File.open(file.name, 'w') do |file|
125
- file << haml(:title => 'Sinatra', :content => rdoc_to_html('README.rdoc'))
126
- end
127
- end
128
- CLEAN.include 'doc/index.html'
129
-
130
- file 'doc/book.html' => ['book/output/sinatra-book.html'] do |file|
131
- File.open(file.name, 'w') do |file|
132
- book_content = File.read('book/output/sinatra-book.html')
133
- file << haml(:title => 'Sinatra Book', :content => book_content)
134
- end
135
- end
136
- CLEAN.include 'doc/book.html'
137
-
138
- file 'book/output/sinatra-book.html' => FileList['book/**'] do |f|
139
- unless File.directory?('book')
140
- sh 'git clone git://github.com/cschneid/sinatra-book.git book'
141
- end
142
- sh((<<-SH).strip.gsub(/\s+/, ' '))
143
- cd book &&
144
- git fetch origin &&
145
- git rebase origin/master &&
146
- thor book:build
147
- SH
148
- end
149
- CLEAN.include 'book/output/sinatra-book.html'
150
-
151
- desc 'Build the Sinatra book'
152
- task 'doc:book' => ['book/output/sinatra-book.html']
153
-
154
103
  # Gemspec Helpers ====================================================
155
104
 
156
105
  def source_version
@@ -105,7 +105,7 @@ context "SendData" do
105
105
  end
106
106
 
107
107
  # Deprecated. The Content-Disposition is no longer handled by sendfile.
108
- specify "should include a Content-Disposition header" do
108
+ xspecify "should include a Content-Disposition header" do
109
109
  get '/' do
110
110
  send_file File.dirname(__FILE__) + '/public/foo.xml'
111
111
  end
@@ -118,4 +118,16 @@ context "SendData" do
118
118
  headers['Content-Transfer-Encoding'].should.equal 'binary'
119
119
  end
120
120
 
121
+ specify "should include a Content-Disposition header when :disposition set to attachment" do
122
+ get '/' do
123
+ send_file File.dirname(__FILE__) + '/public/foo.xml',
124
+ :disposition => 'attachment'
125
+ end
126
+
127
+ get_it '/'
128
+
129
+ should.be.ok
130
+ headers['Content-Disposition'].should.not.be.nil
131
+ headers['Content-Disposition'].should.equal 'attachment; filename="foo.xml"'
132
+ end
121
133
  end
data/lib/sinatra/base.rb CHANGED
@@ -125,15 +125,24 @@ module Sinatra
125
125
  end
126
126
  end
127
127
 
128
- # Use the contents of the file as the response body and attempt to
128
+ # Use the contents of the file at +path+ as the response body.
129
129
  def send_file(path, opts={})
130
130
  stat = File.stat(path)
131
131
  last_modified stat.mtime
132
+
132
133
  content_type media_type(opts[:type]) ||
133
134
  media_type(File.extname(path)) ||
134
135
  response['Content-Type'] ||
135
136
  'application/octet-stream'
137
+
136
138
  response['Content-Length'] ||= (opts[:length] || stat.size).to_s
139
+
140
+ if opts[:disposition] == 'attachment' || opts[:filename]
141
+ attachment opts[:filename] || path
142
+ elsif opts[:disposition] == 'inline'
143
+ response['Content-Disposition'] = 'inline'
144
+ end
145
+
137
146
  halt StaticFile.open(path, 'rb')
138
147
  rescue Errno::ENOENT
139
148
  not_found
@@ -142,6 +151,7 @@ module Sinatra
142
151
  class StaticFile < ::File #:nodoc:
143
152
  alias_method :to_path, :path
144
153
  def each
154
+ rewind
145
155
  while buf = read(8192)
146
156
  yield buf
147
157
  end
@@ -310,7 +320,11 @@ module Sinatra
310
320
  @request = Request.new(env)
311
321
  @response = Response.new
312
322
  @params = nil
313
- error_detection { dispatch! }
323
+
324
+ invoke { dispatch! }
325
+ invoke { error_block!(response.status) }
326
+
327
+ @response.body = [] if @env['REQUEST_METHOD'] == 'HEAD'
314
328
  @response.finish
315
329
  end
316
330
 
@@ -319,7 +333,8 @@ module Sinatra
319
333
  end
320
334
 
321
335
  def halt(*response)
322
- throw :halt, *response
336
+ response = response.first if response.length == 1
337
+ throw :halt, response
323
338
  end
324
339
 
325
340
  def pass
@@ -327,19 +342,21 @@ module Sinatra
327
342
  end
328
343
 
329
344
  private
330
- def dispatch!
331
- self.class.filters.each do |block|
332
- res = catch(:halt) { instance_eval(&block) ; :continue }
333
- return unless res == :continue
334
- end
345
+ # Run before filters and then locate and run a matching route.
346
+ def route!
347
+ @params = nested_params(@request.params)
335
348
 
349
+ # before filters
350
+ self.class.filters.each { |block| instance_eval(&block) }
351
+
352
+ # routes
336
353
  if routes = self.class.routes[@request.request_method]
354
+ original_params = @params
337
355
  path = @request.path_info
338
- original_params = nested_params(@request.params)
339
356
 
340
- routes.each do |pattern, keys, conditions, method_name|
341
- if pattern =~ path
342
- values = $~.captures.map{|val| val && unescape(val) }
357
+ routes.each do |pattern, keys, conditions, block|
358
+ if match = pattern.match(path)
359
+ values = match.captures.map{|val| val && unescape(val) }
343
360
  params =
344
361
  if keys.any?
345
362
  keys.zip(values).inject({}) do |hash,(k,v)|
@@ -357,14 +374,15 @@ module Sinatra
357
374
  end
358
375
  @params = original_params.merge(params)
359
376
 
360
- catch(:pass) {
377
+ catch(:pass) do
361
378
  conditions.each { |cond|
362
379
  throw :pass if instance_eval(&cond) == false }
363
- return invoke(method_name)
364
- }
380
+ throw :halt, instance_eval(&block)
381
+ end
365
382
  end
366
383
  end
367
384
  end
385
+
368
386
  raise NotFound
369
387
  end
370
388
 
@@ -375,6 +393,8 @@ module Sinatra
375
393
  splat = key.scan(/(^[^\[]+)|\[([^\]]+)\]/).flatten.compact
376
394
  head, last = splat[0..-2], splat[-1]
377
395
  head.inject(res){ |s,v| s[v] ||= indifferent_hash }[last] = val
396
+ else
397
+ res[key] = val
378
398
  end
379
399
  res
380
400
  end
@@ -384,7 +404,8 @@ module Sinatra
384
404
  Hash.new {|hash,key| hash[key.to_s] if Symbol === key }
385
405
  end
386
406
 
387
- def invoke(block)
407
+ # Run the block with 'throw :halt' support and apply result to the response.
408
+ def invoke(&block)
388
409
  res = catch(:halt) { instance_eval(&block) }
389
410
  return if res.nil?
390
411
 
@@ -416,30 +437,48 @@ module Sinatra
416
437
  res
417
438
  end
418
439
 
419
- def error_detection
420
- errmap = self.class.errors
421
- yield
440
+ # Dispatch a request with error handling.
441
+ def dispatch!
442
+ route!
422
443
  rescue NotFound => boom
423
444
  @env['sinatra.error'] = boom
424
445
  @response.status = 404
425
446
  @response.body = ['<h1>Not Found</h1>']
426
- handler = errmap[boom.class] || errmap[NotFound]
427
- invoke handler unless handler.nil?
447
+ error_block! boom.class, NotFound
448
+
428
449
  rescue ::Exception => boom
429
450
  @env['sinatra.error'] = boom
430
451
 
431
452
  if options.dump_errors?
432
- msg = ["#{boom.class} - #{boom.message}:", *boom.backtrace].join("\n ")
433
- @env['rack.errors'] << msg
453
+ backtrace = clean_backtrace(boom.backtrace)
454
+ msg = ["#{boom.class} - #{boom.message}:", *backtrace].join("\n ")
455
+ @env['rack.errors'].write(msg)
434
456
  end
435
457
 
436
458
  raise boom if options.raise_errors?
437
459
  @response.status = 500
438
- invoke errmap[boom.class] || errmap[Exception]
439
- ensure
440
- if @response.status >= 400 && errmap.key?(response.status)
441
- invoke errmap[response.status]
460
+ error_block! boom.class, Exception
461
+ end
462
+
463
+ # Find an custom error block for the key(s) specified.
464
+ def error_block!(*keys)
465
+ errmap = self.class.errors
466
+ keys.each do |key|
467
+ if block = errmap[key]
468
+ res = instance_eval(&block)
469
+ return res
470
+ end
442
471
  end
472
+ nil
473
+ end
474
+
475
+ def clean_backtrace(trace)
476
+ return trace unless options.clean_trace?
477
+
478
+ trace.reject { |line|
479
+ line =~ /lib\/sinatra.*\.rb/ ||
480
+ (defined?(Gem) && line.include?(Gem.dir))
481
+ }.map! { |line| line.gsub(/^\.\//, '') }
443
482
  end
444
483
 
445
484
  @routes = {}
@@ -569,7 +608,7 @@ module Sinatra
569
608
  route('GET', path, opts, &block)
570
609
 
571
610
  @conditions = conditions
572
- head(path, opts) { invoke(block) ; [] }
611
+ route('HEAD', path, opts, &block)
573
612
  end
574
613
 
575
614
  def put(path, opts={}, &bk); route 'PUT', path, opts, &bk; end
@@ -597,18 +636,22 @@ module Sinatra
597
636
  def compile(path)
598
637
  keys = []
599
638
  if path.respond_to? :to_str
639
+ special_chars = %w{. + ( )}
600
640
  pattern =
601
- URI.encode(path).gsub(/((:\w+)|\*)/) do |match|
602
- if match == "*"
641
+ URI.encode(path).gsub(/((:\w+)|[\*#{special_chars.join}])/) do |match|
642
+ case match
643
+ when "*"
603
644
  keys << 'splat'
604
645
  "(.*?)"
646
+ when *special_chars
647
+ Regexp.escape(match)
605
648
  else
606
649
  keys << $2[1..-1]
607
650
  "([^/?&#\.]+)"
608
651
  end
609
652
  end
610
653
  [/^#{pattern}$/, keys]
611
- elsif path.respond_to? :=~
654
+ elsif path.respond_to? :match
612
655
  [path, keys]
613
656
  else
614
657
  raise TypeError, path
@@ -630,8 +673,8 @@ module Sinatra
630
673
  end
631
674
 
632
675
  def run!(options={})
633
- set(options)
634
- handler = Rack::Handler.get(server)
676
+ set options
677
+ handler = detect_rack_handler
635
678
  handler_name = handler.name.gsub(/.*::/, '')
636
679
  puts "== Sinatra/#{Sinatra::VERSION} has taken the stage " +
637
680
  "on #{port} for #{environment} with backup from #{handler_name}"
@@ -652,6 +695,18 @@ module Sinatra
652
695
  end
653
696
 
654
697
  private
698
+ def detect_rack_handler
699
+ servers = Array(self.server)
700
+ servers.each do |server_name|
701
+ begin
702
+ return Rack::Handler.get(server_name)
703
+ rescue LoadError
704
+ rescue NameError
705
+ end
706
+ end
707
+ fail "Server handler (#{servers.join(',')}) not found."
708
+ end
709
+
655
710
  def construct_middleware(builder=Rack::Builder.new)
656
711
  builder.use Rack::Session::Cookie if sessions?
657
712
  builder.use Rack::CommonLogger if logging?
@@ -691,6 +746,7 @@ module Sinatra
691
746
 
692
747
  set :raise_errors, true
693
748
  set :dump_errors, false
749
+ set :clean_trace, true
694
750
  set :sessions, false
695
751
  set :logging, false
696
752
  set :methodoverride, false
@@ -698,7 +754,7 @@ module Sinatra
698
754
  set :environment, (ENV['RACK_ENV'] || :development).to_sym
699
755
 
700
756
  set :run, false
701
- set :server, (defined?(Rack::Handler::Thin) ? "thin" : "mongrel")
757
+ set :server, %w[thin mongrel webrick]
702
758
  set :host, '0.0.0.0'
703
759
  set :port, 4567
704
760
 
@@ -771,7 +827,7 @@ module Sinatra
771
827
  <div id="c">
772
828
  <img src="/__sinatra__/500.png">
773
829
  <h1>#{escape_html(heading)}</h1>
774
- <pre class='trace'>#{escape_html(err.backtrace.join("\n"))}</pre>
830
+ <pre>#{escape_html(clean_backtrace(err.backtrace) * "\n")}</pre>
775
831
  <h2>Params</h2>
776
832
  <pre>#{escape_html(params.inspect)}</pre>
777
833
  </div>
@@ -790,7 +846,8 @@ module Sinatra
790
846
  set :methodoverride, true
791
847
  set :static, true
792
848
  set :run, false
793
- set :reload, Proc.new { app_file? && development? }
849
+ set :reload, Proc.new { app_file? && app_file !~ /\.ru$/i && development? }
850
+ set :lock, Proc.new { reload? }
794
851
 
795
852
  def self.reloading?
796
853
  @reloading ||= false
@@ -801,8 +858,10 @@ module Sinatra
801
858
  end
802
859
 
803
860
  def self.call(env)
804
- reload! if reload?
805
- super
861
+ synchronize do
862
+ reload! if reload?
863
+ super
864
+ end
806
865
  end
807
866
 
808
867
  def self.reload!
@@ -813,6 +872,15 @@ module Sinatra
813
872
  @reloading = false
814
873
  end
815
874
 
875
+ private
876
+ @@mutex = Mutex.new
877
+ def self.synchronize(&block)
878
+ if lock?
879
+ @@mutex.synchronize(&block)
880
+ else
881
+ yield
882
+ end
883
+ end
816
884
  end
817
885
 
818
886
  class Application < Default