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 +8 -7
- data/README.rdoc +52 -81
- data/Rakefile +7 -58
- data/compat/streaming_test.rb +13 -1
- data/lib/sinatra/base.rb +106 -38
- data/lib/sinatra/compat.rb +12 -12
- data/lib/sinatra/main.rb +1 -2
- data/lib/sinatra/test.rb +14 -8
- data/lib/sinatra/test/bacon.rb +1 -1
- data/lib/sinatra/test/rspec.rb +3 -1
- data/lib/sinatra/test/unit.rb +1 -1
- data/sinatra.gemspec +2 -2
- data/test/filter_test.rb +24 -0
- data/test/helper.rb +24 -5
- data/test/helpers_test.rb +373 -353
- data/test/middleware_test.rb +2 -2
- data/test/options_test.rb +43 -0
- data/test/reload_test.rb +7 -0
- data/test/result_test.rb +10 -0
- data/test/routing_test.rb +98 -2
- data/test/static_test.rb +10 -0
- metadata +2 -2
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
|
4
|
-
provided by Adam Wiggins (adamwiggins), Chris Wanstrath (defunkt),
|
5
|
-
the rest of the
|
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
|
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
|
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
|
-
|
13
|
+
Install the gem and run with:
|
14
14
|
|
15
|
-
|
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
|
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
|
-
|
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
|
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:
|
185
|
-
|
186
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
==
|
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
|
-
|
492
|
-
|
493
|
-
|
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
|
-
|
494
|
+
cd myapp
|
495
|
+
git clone git://github.com/sinatra/sinatra.git
|
496
|
+
ruby -Isinatra/lib myapp.rb
|
497
497
|
|
498
|
-
|
499
|
-
|
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
|
506
|
+
"I'm running version " + Sinatra::VERSION
|
518
507
|
end
|
519
508
|
|
520
|
-
|
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
|
-
|
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
|
-
|
548
|
-
(irc://chat.freenode.net/#sinatra)
|
514
|
+
== More
|
549
515
|
|
550
|
-
|
551
|
-
|
552
|
-
|
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
|
-
|
10
|
-
|
11
|
-
|
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'
|
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
|
data/compat/streaming_test.rb
CHANGED
@@ -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
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
331
|
-
|
332
|
-
|
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,
|
341
|
-
if
|
342
|
-
values =
|
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
|
-
|
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
|
-
|
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
|
-
|
420
|
-
|
421
|
-
|
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
|
-
|
427
|
-
|
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
|
-
|
433
|
-
|
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
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
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
|
-
|
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+)
|
602
|
-
|
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
|
634
|
-
handler =
|
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,
|
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
|
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
|
-
|
805
|
-
|
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
|