sinatra-sinatra 0.9.0.2 → 0.9.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/AUTHORS +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
|