Syd-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 +9 -8
- data/README.rdoc +52 -81
- data/Rakefile +8 -59
- data/compat/app_test.rb +2 -1
- data/compat/streaming_test.rb +13 -1
- data/lib/sinatra/base.rb +129 -56
- data/lib/sinatra/compat.rb +12 -12
- data/lib/sinatra/main.rb +1 -6
- 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 +395 -333
- 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
|
-
Special thanks to the following extraordinary individuals,
|
7
|
+
Special thanks to the following extraordinary individuals, without whom
|
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
|
-
task :default => :test
|
5
|
+
task :default => [:compat, :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/app_test.rb
CHANGED
@@ -8,7 +8,8 @@ context "Sinatra" do
|
|
8
8
|
|
9
9
|
specify "should put all DSL methods on (main)" do
|
10
10
|
object = Object.new
|
11
|
-
|
11
|
+
methods = %w[get put post head delete configure template helpers set]
|
12
|
+
methods.each do |method|
|
12
13
|
object.private_methods.should.include(method)
|
13
14
|
end
|
14
15
|
end
|
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
|
@@ -243,7 +253,6 @@ module Sinatra
|
|
243
253
|
locals_assigns = locals.to_a.collect { |k,v| "#{k} = locals[:#{k}]" }
|
244
254
|
src = "#{locals_assigns.join("\n")}\n#{instance.src}"
|
245
255
|
eval src, binding, '(__ERB__)', locals_assigns.length + 1
|
246
|
-
instance.result(binding)
|
247
256
|
end
|
248
257
|
|
249
258
|
def haml(template, options={})
|
@@ -310,7 +319,11 @@ module Sinatra
|
|
310
319
|
@request = Request.new(env)
|
311
320
|
@response = Response.new
|
312
321
|
@params = nil
|
313
|
-
|
322
|
+
|
323
|
+
invoke { dispatch! }
|
324
|
+
invoke { error_block!(response.status) }
|
325
|
+
|
326
|
+
@response.body = [] if @env['REQUEST_METHOD'] == 'HEAD'
|
314
327
|
@response.finish
|
315
328
|
end
|
316
329
|
|
@@ -319,7 +332,8 @@ module Sinatra
|
|
319
332
|
end
|
320
333
|
|
321
334
|
def halt(*response)
|
322
|
-
|
335
|
+
response = response.first if response.length == 1
|
336
|
+
throw :halt, response
|
323
337
|
end
|
324
338
|
|
325
339
|
def pass
|
@@ -327,19 +341,21 @@ module Sinatra
|
|
327
341
|
end
|
328
342
|
|
329
343
|
private
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
344
|
+
# Run before filters and then locate and run a matching route.
|
345
|
+
def route!
|
346
|
+
@params = nested_params(@request.params)
|
347
|
+
|
348
|
+
# before filters
|
349
|
+
self.class.filters.each { |block| instance_eval(&block) }
|
335
350
|
|
351
|
+
# routes
|
336
352
|
if routes = self.class.routes[@request.request_method]
|
353
|
+
original_params = @params
|
337
354
|
path = @request.path_info
|
338
|
-
original_params = nested_params(@request.params)
|
339
355
|
|
340
|
-
routes.each do |pattern, keys, conditions,
|
341
|
-
if
|
342
|
-
values =
|
356
|
+
routes.each do |pattern, keys, conditions, block|
|
357
|
+
if match = pattern.match(path)
|
358
|
+
values = match.captures.map{|val| val && unescape(val) }
|
343
359
|
params =
|
344
360
|
if keys.any?
|
345
361
|
keys.zip(values).inject({}) do |hash,(k,v)|
|
@@ -357,24 +373,27 @@ module Sinatra
|
|
357
373
|
end
|
358
374
|
@params = original_params.merge(params)
|
359
375
|
|
360
|
-
catch(:pass)
|
376
|
+
catch(:pass) do
|
361
377
|
conditions.each { |cond|
|
362
378
|
throw :pass if instance_eval(&cond) == false }
|
363
|
-
|
364
|
-
|
379
|
+
throw :halt, instance_eval(&block)
|
380
|
+
end
|
365
381
|
end
|
366
382
|
end
|
367
383
|
end
|
384
|
+
|
368
385
|
raise NotFound
|
369
386
|
end
|
370
387
|
|
371
388
|
def nested_params(params)
|
372
389
|
return indifferent_hash.merge(params) if !params.keys.join.include?('[')
|
373
390
|
params.inject indifferent_hash do |res, (key,val)|
|
374
|
-
if key
|
375
|
-
|
376
|
-
|
377
|
-
head.inject(res){ |
|
391
|
+
if key.include?('[')
|
392
|
+
head = key.split(/[\]\[]+/)
|
393
|
+
last = head.pop
|
394
|
+
head.inject(res){ |hash,k| hash[k] ||= indifferent_hash }[last] = val
|
395
|
+
else
|
396
|
+
res[key] = val
|
378
397
|
end
|
379
398
|
res
|
380
399
|
end
|
@@ -384,7 +403,8 @@ module Sinatra
|
|
384
403
|
Hash.new {|hash,key| hash[key.to_s] if Symbol === key }
|
385
404
|
end
|
386
405
|
|
387
|
-
|
406
|
+
# Run the block with 'throw :halt' support and apply result to the response.
|
407
|
+
def invoke(&block)
|
388
408
|
res = catch(:halt) { instance_eval(&block) }
|
389
409
|
return if res.nil?
|
390
410
|
|
@@ -416,30 +436,48 @@ module Sinatra
|
|
416
436
|
res
|
417
437
|
end
|
418
438
|
|
419
|
-
|
420
|
-
|
421
|
-
|
439
|
+
# Dispatch a request with error handling.
|
440
|
+
def dispatch!
|
441
|
+
route!
|
422
442
|
rescue NotFound => boom
|
423
443
|
@env['sinatra.error'] = boom
|
424
444
|
@response.status = 404
|
425
445
|
@response.body = ['<h1>Not Found</h1>']
|
426
|
-
|
427
|
-
|
446
|
+
error_block! boom.class, NotFound
|
447
|
+
|
428
448
|
rescue ::Exception => boom
|
429
449
|
@env['sinatra.error'] = boom
|
430
450
|
|
431
451
|
if options.dump_errors?
|
432
|
-
|
433
|
-
|
452
|
+
backtrace = clean_backtrace(boom.backtrace)
|
453
|
+
msg = ["#{boom.class} - #{boom.message}:", *backtrace].join("\n ")
|
454
|
+
@env['rack.errors'].write(msg)
|
434
455
|
end
|
435
456
|
|
436
457
|
raise boom if options.raise_errors?
|
437
458
|
@response.status = 500
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
459
|
+
error_block! boom.class, Exception
|
460
|
+
end
|
461
|
+
|
462
|
+
# Find an custom error block for the key(s) specified.
|
463
|
+
def error_block!(*keys)
|
464
|
+
errmap = self.class.errors
|
465
|
+
keys.each do |key|
|
466
|
+
if block = errmap[key]
|
467
|
+
res = instance_eval(&block)
|
468
|
+
return res
|
469
|
+
end
|
442
470
|
end
|
471
|
+
nil
|
472
|
+
end
|
473
|
+
|
474
|
+
def clean_backtrace(trace)
|
475
|
+
return trace unless options.clean_trace?
|
476
|
+
|
477
|
+
trace.reject { |line|
|
478
|
+
line =~ /lib\/sinatra.*\.rb/ ||
|
479
|
+
(defined?(Gem) && line.include?(Gem.dir))
|
480
|
+
}.map! { |line| line.gsub(/^\.\//, '') }
|
443
481
|
end
|
444
482
|
|
445
483
|
@routes = {}
|
@@ -569,7 +607,7 @@ module Sinatra
|
|
569
607
|
route('GET', path, opts, &block)
|
570
608
|
|
571
609
|
@conditions = conditions
|
572
|
-
|
610
|
+
route('HEAD', path, opts, &block)
|
573
611
|
end
|
574
612
|
|
575
613
|
def put(path, opts={}, &bk); route 'PUT', path, opts, &bk; end
|
@@ -597,18 +635,22 @@ module Sinatra
|
|
597
635
|
def compile(path)
|
598
636
|
keys = []
|
599
637
|
if path.respond_to? :to_str
|
638
|
+
special_chars = %w{. + ( )}
|
600
639
|
pattern =
|
601
|
-
URI.encode(path).gsub(/((:\w+)
|
602
|
-
|
640
|
+
URI.encode(path).gsub(/((:\w+)|[\*#{special_chars.join}])/) do |match|
|
641
|
+
case match
|
642
|
+
when "*"
|
603
643
|
keys << 'splat'
|
604
644
|
"(.*?)"
|
645
|
+
when *special_chars
|
646
|
+
Regexp.escape(match)
|
605
647
|
else
|
606
648
|
keys << $2[1..-1]
|
607
649
|
"([^/?&#\.]+)"
|
608
650
|
end
|
609
651
|
end
|
610
652
|
[/^#{pattern}$/, keys]
|
611
|
-
elsif path.respond_to?
|
653
|
+
elsif path.respond_to? :match
|
612
654
|
[path, keys]
|
613
655
|
else
|
614
656
|
raise TypeError, path
|
@@ -616,6 +658,11 @@ module Sinatra
|
|
616
658
|
end
|
617
659
|
|
618
660
|
public
|
661
|
+
def helpers(*modules, &block)
|
662
|
+
include *modules unless modules.empty?
|
663
|
+
class_eval(&block) if block
|
664
|
+
end
|
665
|
+
|
619
666
|
def development? ; environment == :development ; end
|
620
667
|
def test? ; environment == :test ; end
|
621
668
|
def production? ; environment == :production ; end
|
@@ -630,8 +677,8 @@ module Sinatra
|
|
630
677
|
end
|
631
678
|
|
632
679
|
def run!(options={})
|
633
|
-
set
|
634
|
-
handler =
|
680
|
+
set options
|
681
|
+
handler = detect_rack_handler
|
635
682
|
handler_name = handler.name.gsub(/.*::/, '')
|
636
683
|
puts "== Sinatra/#{Sinatra::VERSION} has taken the stage " +
|
637
684
|
"on #{port} for #{environment} with backup from #{handler_name}"
|
@@ -652,6 +699,18 @@ module Sinatra
|
|
652
699
|
end
|
653
700
|
|
654
701
|
private
|
702
|
+
def detect_rack_handler
|
703
|
+
servers = Array(self.server)
|
704
|
+
servers.each do |server_name|
|
705
|
+
begin
|
706
|
+
return Rack::Handler.get(server_name)
|
707
|
+
rescue LoadError
|
708
|
+
rescue NameError
|
709
|
+
end
|
710
|
+
end
|
711
|
+
fail "Server handler (#{servers.join(',')}) not found."
|
712
|
+
end
|
713
|
+
|
655
714
|
def construct_middleware(builder=Rack::Builder.new)
|
656
715
|
builder.use Rack::Session::Cookie if sessions?
|
657
716
|
builder.use Rack::CommonLogger if logging?
|
@@ -691,6 +750,7 @@ module Sinatra
|
|
691
750
|
|
692
751
|
set :raise_errors, true
|
693
752
|
set :dump_errors, false
|
753
|
+
set :clean_trace, true
|
694
754
|
set :sessions, false
|
695
755
|
set :logging, false
|
696
756
|
set :methodoverride, false
|
@@ -698,7 +758,7 @@ module Sinatra
|
|
698
758
|
set :environment, (ENV['RACK_ENV'] || :development).to_sym
|
699
759
|
|
700
760
|
set :run, false
|
701
|
-
set :server,
|
761
|
+
set :server, %w[thin mongrel webrick]
|
702
762
|
set :host, '0.0.0.0'
|
703
763
|
set :port, 4567
|
704
764
|
|
@@ -771,7 +831,7 @@ module Sinatra
|
|
771
831
|
<div id="c">
|
772
832
|
<img src="/__sinatra__/500.png">
|
773
833
|
<h1>#{escape_html(heading)}</h1>
|
774
|
-
<pre
|
834
|
+
<pre>#{escape_html(clean_backtrace(err.backtrace) * "\n")}</pre>
|
775
835
|
<h2>Params</h2>
|
776
836
|
<pre>#{escape_html(params.inspect)}</pre>
|
777
837
|
</div>
|
@@ -790,7 +850,8 @@ module Sinatra
|
|
790
850
|
set :methodoverride, true
|
791
851
|
set :static, true
|
792
852
|
set :run, false
|
793
|
-
set :reload, Proc.new { app_file? && development? }
|
853
|
+
set :reload, Proc.new { app_file? && app_file !~ /\.ru$/i && development? }
|
854
|
+
set :lock, Proc.new { reload? }
|
794
855
|
|
795
856
|
def self.reloading?
|
796
857
|
@reloading ||= false
|
@@ -801,8 +862,10 @@ module Sinatra
|
|
801
862
|
end
|
802
863
|
|
803
864
|
def self.call(env)
|
804
|
-
|
805
|
-
|
865
|
+
synchronize do
|
866
|
+
reload! if reload?
|
867
|
+
super
|
868
|
+
end
|
806
869
|
end
|
807
870
|
|
808
871
|
def self.reload!
|
@@ -813,26 +876,36 @@ module Sinatra
|
|
813
876
|
@reloading = false
|
814
877
|
end
|
815
878
|
|
879
|
+
private
|
880
|
+
@@mutex = Mutex.new
|
881
|
+
def self.synchronize(&block)
|
882
|
+
if lock?
|
883
|
+
@@mutex.synchronize(&block)
|
884
|
+
else
|
885
|
+
yield
|
886
|
+
end
|
887
|
+
end
|
816
888
|
end
|
817
889
|
|
818
890
|
class Application < Default
|
819
891
|
end
|
820
892
|
|
821
893
|
module Delegator
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
::Sinatra::Application.#{method_name}(*args, &b)
|
832
|
-
end
|
833
|
-
private :#{method_name}
|
834
|
-
RUBY
|
894
|
+
def self.delegate(*methods)
|
895
|
+
methods.each do |method_name|
|
896
|
+
eval <<-RUBY, binding, '(__DELEGATE__)', 1
|
897
|
+
def #{method_name}(*args, &b)
|
898
|
+
::Sinatra::Application.#{method_name}(*args, &b)
|
899
|
+
end
|
900
|
+
private :#{method_name}
|
901
|
+
RUBY
|
902
|
+
end
|
835
903
|
end
|
904
|
+
|
905
|
+
delegate :get, :put, :post, :delete, :head, :template, :layout, :before,
|
906
|
+
:error, :not_found, :configures, :configure, :set, :set_option,
|
907
|
+
:set_options, :enable, :disable, :use, :development?, :test?,
|
908
|
+
:production?, :use_in_file_templates!, :helpers
|
836
909
|
end
|
837
910
|
|
838
911
|
def self.new(base=Base, options={}, &block)
|