sinatra 1.3.6 → 1.4.0.a
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sinatra might be problematic. Click here for more details.
- data/CHANGES +96 -22
- data/Gemfile +11 -3
- data/README.de.md +2590 -0
- data/README.es.rdoc +66 -38
- data/README.fr.md +2630 -0
- data/README.hu.rdoc +3 -2
- data/README.jp.rdoc +16 -3
- data/README.ko.rdoc +11 -5
- data/README.md +2699 -0
- data/README.pt-br.rdoc +152 -21
- data/README.pt-pt.rdoc +3 -2
- data/README.ru.md +2724 -0
- data/README.zh.rdoc +3 -3
- data/Rakefile +3 -4
- data/examples/chat.rb +3 -3
- data/lib/sinatra/base.rb +433 -247
- data/lib/sinatra/main.rb +4 -2
- data/lib/sinatra/showexceptions.rb +6 -1
- data/lib/sinatra/version.rb +1 -1
- data/test/base_test.rb +21 -9
- data/test/builder_test.rb +15 -19
- data/test/coffee_test.rb +4 -6
- data/test/compile_test.rb +154 -0
- data/test/contest.rb +4 -6
- data/test/creole_test.rb +5 -5
- data/test/delegator_test.rb +1 -3
- data/test/erb_test.rb +32 -20
- data/test/extensions_test.rb +1 -3
- data/test/filter_test.rb +65 -56
- data/test/haml_test.rb +34 -26
- data/test/helpers_test.rb +331 -221
- data/test/integration_helper.rb +8 -0
- data/test/integration_test.rb +3 -1
- data/test/less_test.rb +10 -8
- data/test/liquid_test.rb +22 -4
- data/test/mapped_error_test.rb +122 -96
- data/test/markaby_test.rb +5 -5
- data/test/markdown_test.rb +5 -5
- data/test/middleware_test.rb +3 -3
- data/test/nokogiri_test.rb +4 -6
- data/test/rabl_test.rb +89 -0
- data/test/radius_test.rb +4 -4
- data/test/rdoc_test.rb +7 -7
- data/test/readme_test.rb +14 -30
- data/test/request_test.rb +15 -0
- data/test/response_test.rb +3 -4
- data/test/result_test.rb +11 -33
- data/test/route_added_hook_test.rb +10 -10
- data/test/routing_test.rb +123 -1
- data/test/sass_test.rb +26 -26
- data/test/scss_test.rb +16 -16
- data/test/server_test.rb +2 -2
- data/test/settings_test.rb +48 -4
- data/test/sinatra_test.rb +2 -7
- data/test/slim_test.rb +37 -23
- data/test/static_test.rb +56 -15
- data/test/streaming_test.rb +11 -2
- data/test/templates_test.rb +117 -45
- data/test/textile_test.rb +9 -9
- data/test/views/hello.rabl +2 -0
- data/test/views/hello.wlang +1 -0
- data/test/views/hello.yajl +1 -0
- data/test/views/layout2.rabl +3 -0
- data/test/views/layout2.wlang +2 -0
- data/test/wlang_test.rb +87 -0
- data/test/yajl_test.rb +86 -0
- metadata +27 -17
- data/README.de.rdoc +0 -2097
- data/README.fr.rdoc +0 -2036
- data/README.rdoc +0 -2017
- data/README.ru.rdoc +0 -1785
data/README.zh.rdoc
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
<i>注:本文档仅仅是英文版的翻译,会出现内容没有及时更新的情况发生。
|
3
3
|
如有不一致的地方,请以英文版为准。</i>
|
4
4
|
|
5
|
-
Sinatra是一个基于Ruby语言,以最小精力为代价快速创建web应用为目的的DSL(
|
5
|
+
Sinatra是一个基于Ruby语言,以最小精力为代价快速创建web应用为目的的{DSL}[http://en.wikipedia.org/wiki/Domain-specific_language](
|
6
6
|
领域专属语言):
|
7
7
|
|
8
8
|
# myapp.rb
|
@@ -15,7 +15,7 @@ Sinatra是一个基于Ruby语言,以最小精力为代价快速创建web应用
|
|
15
15
|
安装gem然后运行:
|
16
16
|
|
17
17
|
gem install sinatra
|
18
|
-
ruby
|
18
|
+
ruby myapp.rb
|
19
19
|
|
20
20
|
在该地址查看: http://localhost:4567
|
21
21
|
|
@@ -1016,7 +1016,7 @@ Sinatra并不理解。使用 +mime_type+ 通过文件扩展名来注册它们:
|
|
1016
1016
|
|
1017
1017
|
这些辅助方法并不会为你做任何缓存,而是将必要的信息传送给你的缓存
|
1018
1018
|
如果你在寻找缓存的快速解决方案,试试
|
1019
|
-
{rack-cache}[
|
1019
|
+
{rack-cache}[https://github.com/rtomayko/rack-cache]:
|
1020
1020
|
|
1021
1021
|
require "rack/cache"
|
1022
1022
|
require "sinatra"
|
data/Rakefile
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
# encoding: UTF-8
|
2
1
|
require 'rake/clean'
|
3
2
|
require 'rake/testtask'
|
4
3
|
require 'fileutils'
|
@@ -99,8 +98,8 @@ desc "list of contributors"
|
|
99
98
|
task :thanks, [:release,:backports] do |t, a|
|
100
99
|
a.with_defaults :release => "#{prev_version}..HEAD",
|
101
100
|
:backports => "#{prev_feature}.0..#{prev_feature}.x"
|
102
|
-
included = `git log --format=format:"%aN\t%s" #{a.release}`.lines.
|
103
|
-
excluded = `git log --format=format:"%aN\t%s" #{a.backports}`.lines.
|
101
|
+
included = `git log --format=format:"%aN\t%s" #{a.release}`.lines.to_a
|
102
|
+
excluded = `git log --format=format:"%aN\t%s" #{a.backports}`.lines.to_a
|
104
103
|
commits = (included - excluded).group_by { |c| c[/^[^\t]+/] }
|
105
104
|
authors = commits.keys.sort_by { |n| - commits[n].size } - team
|
106
105
|
puts authors[0..-2].join(', ') << " and " << authors.last,
|
@@ -167,7 +166,7 @@ if defined?(Gem)
|
|
167
166
|
|
168
167
|
task 'release' => ['test', package('.gem')] do
|
169
168
|
if File.binread("CHANGES") =~ /= \d\.\d\.\d . not yet released$/i
|
170
|
-
fail 'please update changes first'
|
169
|
+
fail 'please update changes first' unless %x{git symbolic-ref HEAD} == "refs/heads/prerelease\n"
|
171
170
|
end
|
172
171
|
|
173
172
|
sh <<-SH
|
data/examples/chat.rb
CHANGED
@@ -42,9 +42,6 @@ __END__
|
|
42
42
|
|
43
43
|
@@ chat
|
44
44
|
<pre id='chat'></pre>
|
45
|
-
<form>
|
46
|
-
<input id='msg' placeholder='type message here...' />
|
47
|
-
</form>
|
48
45
|
|
49
46
|
<script>
|
50
47
|
// reading
|
@@ -59,3 +56,6 @@ __END__
|
|
59
56
|
});
|
60
57
|
</script>
|
61
58
|
|
59
|
+
<form>
|
60
|
+
<input id='msg' placeholder='type message here...' />
|
61
|
+
</form>
|
data/lib/sinatra/base.rb
CHANGED
@@ -16,11 +16,14 @@ module Sinatra
|
|
16
16
|
# The request object. See Rack::Request for more info:
|
17
17
|
# http://rack.rubyforge.org/doc/classes/Rack/Request.html
|
18
18
|
class Request < Rack::Request
|
19
|
+
HEADER_PARAM = /\s*[\w.]+=(?:[\w.]+|"(?:[^"\\]|\\.)*")?\s*/
|
20
|
+
HEADER_VALUE_WITH_PARAMS = /(?:(?:\w+|\*)\/(?:\w+|\*))\s*(?:;#{HEADER_PARAM})*/
|
21
|
+
|
19
22
|
# Returns an array of acceptable media types for the response
|
20
23
|
def accept
|
21
24
|
@env['sinatra.accept'] ||= begin
|
22
|
-
entries = @env['HTTP_ACCEPT'].to_s.
|
23
|
-
entries.map { |e|
|
25
|
+
entries = @env['HTTP_ACCEPT'].to_s.scan(HEADER_VALUE_WITH_PARAMS)
|
26
|
+
entries.map { |e| AcceptEntry.new(e) }.sort
|
24
27
|
end
|
25
28
|
end
|
26
29
|
|
@@ -50,11 +53,37 @@ module Sinatra
|
|
50
53
|
|
51
54
|
private
|
52
55
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
56
|
+
class AcceptEntry
|
57
|
+
attr_accessor :params
|
58
|
+
|
59
|
+
def initialize(entry)
|
60
|
+
params = entry.scan(HEADER_PARAM).map do |s|
|
61
|
+
key, value = s.strip.split('=', 2)
|
62
|
+
value = value[1..-2].gsub(/\\(.)/, '\1') if value.start_with?('"')
|
63
|
+
[key, value]
|
64
|
+
end
|
65
|
+
|
66
|
+
@type = entry[/[^;]+/].delete(' ')
|
67
|
+
@params = Hash[params]
|
68
|
+
@q = @params.delete('q') { "1.0" }.to_f
|
69
|
+
end
|
70
|
+
|
71
|
+
def <=>(other)
|
72
|
+
other.priority <=> self.priority
|
73
|
+
end
|
74
|
+
|
75
|
+
def priority
|
76
|
+
# We sort in descending order; better matches should be higher.
|
77
|
+
[ @q, -@type.count('*'), @params.size ]
|
78
|
+
end
|
79
|
+
|
80
|
+
def [](param)
|
81
|
+
@params[param]
|
82
|
+
end
|
83
|
+
|
84
|
+
def to_str
|
85
|
+
@type
|
86
|
+
end
|
58
87
|
end
|
59
88
|
end
|
60
89
|
|
@@ -96,7 +125,7 @@ module Sinatra
|
|
96
125
|
headers["Content-Length"] = body.inject(0) { |l, p| l + Rack::Utils.bytesize(p) }.to_s
|
97
126
|
end
|
98
127
|
|
99
|
-
[status.to_i,
|
128
|
+
[status.to_i, header, result]
|
100
129
|
end
|
101
130
|
|
102
131
|
private
|
@@ -130,7 +159,7 @@ module Sinatra
|
|
130
159
|
|
131
160
|
private
|
132
161
|
|
133
|
-
def setup_close(env, status,
|
162
|
+
def setup_close(env, status, header, body)
|
134
163
|
return unless body.respond_to? :close and env.include? 'async.close'
|
135
164
|
env['async.close'].callback { body.close }
|
136
165
|
env['async.close'].errback { body.close }
|
@@ -164,24 +193,25 @@ module Sinatra
|
|
164
193
|
end
|
165
194
|
|
166
195
|
class NotFound < NameError #:nodoc:
|
167
|
-
def
|
196
|
+
def http_status; 404 end
|
168
197
|
end
|
169
198
|
|
170
199
|
# Methods available to routes, before/after filters, and views.
|
171
200
|
module Helpers
|
172
201
|
# Set or retrieve the response status code.
|
173
|
-
def status(value=nil)
|
202
|
+
def status(value = nil)
|
174
203
|
response.status = value if value
|
175
204
|
response.status
|
176
205
|
end
|
177
206
|
|
178
207
|
# Set or retrieve the response body. When a block is given,
|
179
208
|
# evaluation is deferred until the body is read with #each.
|
180
|
-
def body(value=nil, &block)
|
209
|
+
def body(value = nil, &block)
|
181
210
|
if block_given?
|
182
211
|
def block.each; yield(call) end
|
183
212
|
response.body = block
|
184
213
|
elsif value
|
214
|
+
headers.delete 'Content-Length' unless request.head?
|
185
215
|
response.body = value
|
186
216
|
else
|
187
217
|
response.body
|
@@ -198,7 +228,7 @@ module Sinatra
|
|
198
228
|
|
199
229
|
# According to RFC 2616 section 14.30, "the field value consists of a
|
200
230
|
# single absolute URI"
|
201
|
-
response['Location'] = uri(uri, settings.absolute_redirects?, settings.prefixed_redirects?)
|
231
|
+
response['Location'] = uri(uri.to_s, settings.absolute_redirects?, settings.prefixed_redirects?)
|
202
232
|
halt(*args)
|
203
233
|
end
|
204
234
|
|
@@ -224,19 +254,19 @@ module Sinatra
|
|
224
254
|
alias to uri
|
225
255
|
|
226
256
|
# Halt processing and return the error status provided.
|
227
|
-
def error(code, body=nil)
|
257
|
+
def error(code, body = nil)
|
228
258
|
code, body = 500, code.to_str if code.respond_to? :to_str
|
229
259
|
response.body = body unless body.nil?
|
230
260
|
halt code
|
231
261
|
end
|
232
262
|
|
233
263
|
# Halt processing and return a 404 Not Found.
|
234
|
-
def not_found(body=nil)
|
264
|
+
def not_found(body = nil)
|
235
265
|
error 404, body
|
236
266
|
end
|
237
267
|
|
238
268
|
# Set multiple response headers with Hash.
|
239
|
-
def headers(hash=nil)
|
269
|
+
def headers(hash = nil)
|
240
270
|
response.headers.merge! hash if hash
|
241
271
|
response.headers
|
242
272
|
end
|
@@ -258,7 +288,7 @@ module Sinatra
|
|
258
288
|
|
259
289
|
# Set the Content-Type of the response body given a media type or file
|
260
290
|
# extension.
|
261
|
-
def content_type(type = nil, params={})
|
291
|
+
def content_type(type = nil, params = {})
|
262
292
|
return response['Content-Type'] unless type
|
263
293
|
default = params.delete :default
|
264
294
|
mime_type = mime_type(type) || default
|
@@ -270,15 +300,18 @@ module Sinatra
|
|
270
300
|
params.delete :charset if mime_type.include? 'charset'
|
271
301
|
unless params.empty?
|
272
302
|
mime_type << (mime_type.include?(';') ? ', ' : ';')
|
273
|
-
mime_type << params.map
|
303
|
+
mime_type << params.map do |key, val|
|
304
|
+
val = val.inspect if val =~ /[";,]/
|
305
|
+
"#{key}=#{val}"
|
306
|
+
end.join(', ')
|
274
307
|
end
|
275
308
|
response['Content-Type'] = mime_type
|
276
309
|
end
|
277
310
|
|
278
311
|
# Set the Content-Disposition to "attachment" with the specified filename,
|
279
312
|
# instructing the user agents to prompt to save.
|
280
|
-
def attachment(filename=nil)
|
281
|
-
response['Content-Disposition'] =
|
313
|
+
def attachment(filename = nil, disposition = 'attachment')
|
314
|
+
response['Content-Disposition'] = disposition.to_s
|
282
315
|
if filename
|
283
316
|
params = '; filename="%s"' % File.basename(filename)
|
284
317
|
response['Content-Disposition'] << params
|
@@ -288,16 +321,16 @@ module Sinatra
|
|
288
321
|
end
|
289
322
|
|
290
323
|
# Use the contents of the file at +path+ as the response body.
|
291
|
-
def send_file(path, opts={})
|
324
|
+
def send_file(path, opts = {})
|
292
325
|
if opts[:type] or not response['Content-Type']
|
293
326
|
content_type opts[:type] || File.extname(path), :default => 'application/octet-stream'
|
294
327
|
end
|
295
328
|
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
329
|
+
disposition = opts[:disposition]
|
330
|
+
filename = opts[:filename]
|
331
|
+
disposition = 'attachment' if disposition.nil? and filename
|
332
|
+
filename = path if filename.nil?
|
333
|
+
attachment(filename, disposition) if disposition
|
301
334
|
|
302
335
|
last_modified opts[:last_modified] if opts[:last_modified]
|
303
336
|
|
@@ -305,7 +338,8 @@ module Sinatra
|
|
305
338
|
file.path = path
|
306
339
|
result = file.serving env
|
307
340
|
result[1].each { |k,v| headers[k] ||= v }
|
308
|
-
|
341
|
+
headers['Content-Length'] = result[1]['Content-Length']
|
342
|
+
halt opts[:status] || result[0], result[2]
|
309
343
|
rescue Errno::ENOENT
|
310
344
|
not_found
|
311
345
|
end
|
@@ -356,6 +390,10 @@ module Sinatra
|
|
356
390
|
end
|
357
391
|
|
358
392
|
alias errback callback
|
393
|
+
|
394
|
+
def closed?
|
395
|
+
@closed
|
396
|
+
end
|
359
397
|
end
|
360
398
|
|
361
399
|
# Allows to start sending data to the client even though later parts of
|
@@ -575,7 +613,7 @@ module Sinatra
|
|
575
613
|
#
|
576
614
|
# Possible options are:
|
577
615
|
# :content_type The content type to use, same arguments as content_type.
|
578
|
-
# :layout If set to
|
616
|
+
# :layout If set to something falsy, no layout is rendered, otherwise
|
579
617
|
# the specified layout is used (Ignored for `sass` and `less`)
|
580
618
|
# :layout_engine Engine to use for rendering the layout.
|
581
619
|
# :locals A hash with local variables that should be available
|
@@ -593,82 +631,96 @@ module Sinatra
|
|
593
631
|
@default_layout = :layout
|
594
632
|
end
|
595
633
|
|
596
|
-
def erb(template, options={}, locals={})
|
597
|
-
render
|
634
|
+
def erb(template, options = {}, locals = {}, &block)
|
635
|
+
render(:erb, template, options, locals, &block)
|
598
636
|
end
|
599
637
|
|
600
|
-
def erubis(template, options={}, locals={})
|
638
|
+
def erubis(template, options = {}, locals = {})
|
601
639
|
warn "Sinatra::Templates#erubis is deprecated and will be removed, use #erb instead.\n" \
|
602
640
|
"If you have Erubis installed, it will be used automatically."
|
603
641
|
render :erubis, template, options, locals
|
604
642
|
end
|
605
643
|
|
606
|
-
def haml(template, options={}, locals={})
|
607
|
-
render
|
644
|
+
def haml(template, options = {}, locals = {}, &block)
|
645
|
+
render(:haml, template, options, locals, &block)
|
608
646
|
end
|
609
647
|
|
610
|
-
def sass(template, options={}, locals={})
|
648
|
+
def sass(template, options = {}, locals = {})
|
611
649
|
options.merge! :layout => false, :default_content_type => :css
|
612
650
|
render :sass, template, options, locals
|
613
651
|
end
|
614
652
|
|
615
|
-
def scss(template, options={}, locals={})
|
653
|
+
def scss(template, options = {}, locals = {})
|
616
654
|
options.merge! :layout => false, :default_content_type => :css
|
617
655
|
render :scss, template, options, locals
|
618
656
|
end
|
619
657
|
|
620
|
-
def less(template, options={}, locals={})
|
658
|
+
def less(template, options = {}, locals = {})
|
621
659
|
options.merge! :layout => false, :default_content_type => :css
|
622
660
|
render :less, template, options, locals
|
623
661
|
end
|
624
662
|
|
625
|
-
def builder(template=nil, options={}, locals={}, &block)
|
663
|
+
def builder(template = nil, options = {}, locals = {}, &block)
|
626
664
|
options[:default_content_type] = :xml
|
627
665
|
render_ruby(:builder, template, options, locals, &block)
|
628
666
|
end
|
629
667
|
|
630
|
-
def liquid(template, options={}, locals={})
|
631
|
-
render
|
668
|
+
def liquid(template, options = {}, locals = {}, &block)
|
669
|
+
render(:liquid, template, options, locals, &block)
|
632
670
|
end
|
633
671
|
|
634
|
-
def markdown(template, options={}, locals={})
|
672
|
+
def markdown(template, options = {}, locals = {})
|
635
673
|
render :markdown, template, options, locals
|
636
674
|
end
|
637
675
|
|
638
|
-
def textile(template, options={}, locals={})
|
676
|
+
def textile(template, options = {}, locals = {})
|
639
677
|
render :textile, template, options, locals
|
640
678
|
end
|
641
679
|
|
642
|
-
def rdoc(template, options={}, locals={})
|
680
|
+
def rdoc(template, options = {}, locals = {})
|
643
681
|
render :rdoc, template, options, locals
|
644
682
|
end
|
645
683
|
|
646
|
-
def radius(template, options={}, locals={})
|
684
|
+
def radius(template, options = {}, locals = {})
|
647
685
|
render :radius, template, options, locals
|
648
686
|
end
|
649
687
|
|
650
|
-
def markaby(template=nil, options={}, locals={}, &block)
|
688
|
+
def markaby(template = nil, options = {}, locals = {}, &block)
|
651
689
|
render_ruby(:mab, template, options, locals, &block)
|
652
690
|
end
|
653
691
|
|
654
|
-
def coffee(template, options={}, locals={})
|
692
|
+
def coffee(template, options = {}, locals = {})
|
655
693
|
options.merge! :layout => false, :default_content_type => :js
|
656
694
|
render :coffee, template, options, locals
|
657
695
|
end
|
658
696
|
|
659
|
-
def nokogiri(template=nil, options={}, locals={}, &block)
|
697
|
+
def nokogiri(template = nil, options = {}, locals = {}, &block)
|
660
698
|
options[:default_content_type] = :xml
|
661
699
|
render_ruby(:nokogiri, template, options, locals, &block)
|
662
700
|
end
|
663
701
|
|
664
|
-
def slim(template, options={}, locals={})
|
665
|
-
render
|
702
|
+
def slim(template, options = {}, locals = {}, &block)
|
703
|
+
render(:slim, template, options, locals, &block)
|
666
704
|
end
|
667
705
|
|
668
|
-
def creole(template, options={}, locals={})
|
706
|
+
def creole(template, options = {}, locals = {})
|
669
707
|
render :creole, template, options, locals
|
670
708
|
end
|
671
709
|
|
710
|
+
def wlang(template, options = {}, locals = {}, &block)
|
711
|
+
render(:wlang, template, options, locals, &block)
|
712
|
+
end
|
713
|
+
|
714
|
+
def yajl(template, options = {}, locals = {})
|
715
|
+
options[:default_content_type] = :json
|
716
|
+
render :yajl, template, options, locals
|
717
|
+
end
|
718
|
+
|
719
|
+
def rabl(template, options = {}, locals = {})
|
720
|
+
Rabl.register!
|
721
|
+
render :rabl, template, options, locals
|
722
|
+
end
|
723
|
+
|
672
724
|
# Calls the given block for every possible template file in views,
|
673
725
|
# named name.ext, where ext is registered on engine.
|
674
726
|
def find_template(views, name, engine)
|
@@ -679,29 +731,36 @@ module Sinatra
|
|
679
731
|
end
|
680
732
|
end
|
681
733
|
|
682
|
-
|
734
|
+
private
|
683
735
|
# logic shared between builder and nokogiri
|
684
|
-
def render_ruby(engine, template, options={}, locals={}, &block)
|
736
|
+
def render_ruby(engine, template, options = {}, locals = {}, &block)
|
685
737
|
options, template = template, nil if template.is_a?(Hash)
|
686
738
|
template = Proc.new { block } if template.nil?
|
687
739
|
render engine, template, options, locals
|
688
740
|
end
|
689
741
|
|
690
|
-
def render(engine, data, options={}, locals={}, &block)
|
742
|
+
def render(engine, data, options = {}, locals = {}, &block)
|
691
743
|
# merge app-level options
|
692
|
-
|
693
|
-
options
|
694
|
-
options[:default_encoding] ||= settings.default_encoding
|
744
|
+
engine_options = settings.respond_to?(engine) ? settings.send(engine) : {}
|
745
|
+
options = engine_options.merge(options)
|
695
746
|
|
696
747
|
# extract generic options
|
697
748
|
locals = options.delete(:locals) || locals || {}
|
698
749
|
views = options.delete(:views) || settings.views || "./views"
|
699
|
-
layout = options
|
750
|
+
layout = options[:layout]
|
751
|
+
layout = false if layout.nil? && options.include?(:layout)
|
700
752
|
eat_errors = layout.nil?
|
701
|
-
layout =
|
702
|
-
|
703
|
-
|
704
|
-
|
753
|
+
layout = engine_options[:layout] if layout.nil? or layout == true
|
754
|
+
layout = @default_layout if layout.nil? or layout == true
|
755
|
+
layout_options = options.delete(:layout_options) || {}
|
756
|
+
content_type = options.delete(:content_type) || options.delete(:default_content_type)
|
757
|
+
layout_engine = options.delete(:layout_engine) || engine
|
758
|
+
scope = options.delete(:scope) || self
|
759
|
+
options.delete(:layout)
|
760
|
+
|
761
|
+
# set some defaults
|
762
|
+
options[:outvar] ||= '@_out_buf'
|
763
|
+
options[:default_encoding] ||= settings.default_encoding
|
705
764
|
|
706
765
|
# compile and render template
|
707
766
|
begin
|
@@ -716,6 +775,7 @@ module Sinatra
|
|
716
775
|
# render layout
|
717
776
|
if layout
|
718
777
|
options = options.merge(:views => views, :layout => false, :eat_errors => eat_errors, :scope => scope)
|
778
|
+
options.merge! layout_options
|
719
779
|
catch(:layout_missing) { return render(layout_engine, layout, options, locals) { output } }
|
720
780
|
end
|
721
781
|
|
@@ -725,7 +785,7 @@ module Sinatra
|
|
725
785
|
|
726
786
|
def compile_template(engine, data, options, views)
|
727
787
|
eat_errors = options.delete :eat_errors
|
728
|
-
template_cache.fetch engine, data, options do
|
788
|
+
template_cache.fetch engine, data, options, views do
|
729
789
|
template = Tilt[engine]
|
730
790
|
raise "Template engine not found: #{engine}" if template.nil?
|
731
791
|
|
@@ -768,7 +828,7 @@ module Sinatra
|
|
768
828
|
attr_accessor :app
|
769
829
|
attr_reader :template_cache
|
770
830
|
|
771
|
-
def initialize(app=nil)
|
831
|
+
def initialize(app = nil)
|
772
832
|
super()
|
773
833
|
@app = app
|
774
834
|
@template_cache = Tilt::Cache.new
|
@@ -792,7 +852,7 @@ module Sinatra
|
|
792
852
|
|
793
853
|
@response['Content-Type'] = nil
|
794
854
|
invoke { dispatch! }
|
795
|
-
invoke { error_block!(response.status) }
|
855
|
+
invoke { error_block!(response.status) } unless @env['sinatra.error']
|
796
856
|
|
797
857
|
unless @response['Content-Type']
|
798
858
|
if Array === body and body[0].respond_to? :content_type
|
@@ -846,165 +906,184 @@ module Sinatra
|
|
846
906
|
end
|
847
907
|
|
848
908
|
private
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
# Run routes defined on the class and all superclasses.
|
856
|
-
def route!(base = settings, pass_block=nil)
|
857
|
-
if routes = base.routes[@request.request_method]
|
858
|
-
routes.each do |pattern, keys, conditions, block|
|
859
|
-
pass_block = process_route(pattern, keys, conditions) do |*args|
|
860
|
-
route_eval { block[*args] }
|
861
|
-
end
|
862
|
-
end
|
863
|
-
end
|
909
|
+
# Run filters defined on the class and all superclasses.
|
910
|
+
def filter!(type, base = settings)
|
911
|
+
filter! type, base.superclass if base.superclass.respond_to?(:filters)
|
912
|
+
base.filters[type].each { |args| process_route(*args) }
|
913
|
+
end
|
864
914
|
|
865
|
-
|
866
|
-
|
867
|
-
|
915
|
+
# Run routes defined on the class and all superclasses.
|
916
|
+
def route!(base = settings, pass_block = nil)
|
917
|
+
if routes = base.routes[@request.request_method]
|
918
|
+
routes.each do |pattern, keys, conditions, block|
|
919
|
+
pass_block = process_route(pattern, keys, conditions) do |*args|
|
920
|
+
route_eval { block[*args] }
|
921
|
+
end
|
868
922
|
end
|
869
|
-
|
870
|
-
route_eval(&pass_block) if pass_block
|
871
|
-
route_missing
|
872
923
|
end
|
873
924
|
|
874
|
-
# Run
|
875
|
-
|
876
|
-
|
925
|
+
# Run routes defined in superclass.
|
926
|
+
if base.superclass.respond_to?(:routes)
|
927
|
+
return route!(base.superclass, pass_block)
|
877
928
|
end
|
878
929
|
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
#
|
883
|
-
# Returns pass block.
|
884
|
-
def process_route(pattern, keys, conditions, block = nil, values = [])
|
885
|
-
route = @request.path_info
|
886
|
-
route = '/' if route.empty? and not settings.empty_path_info?
|
887
|
-
return unless match = pattern.match(route)
|
888
|
-
values += match.captures.to_a.map { |v| force_encoding URI.decode_www_form_component(v) if v }
|
930
|
+
route_eval(&pass_block) if pass_block
|
931
|
+
route_missing
|
932
|
+
end
|
889
933
|
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
934
|
+
# Run a route block and throw :halt with the result.
|
935
|
+
def route_eval
|
936
|
+
throw :halt, yield
|
937
|
+
end
|
894
938
|
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
939
|
+
# If the current request matches pattern and conditions, fill params
|
940
|
+
# with keys and call the given block.
|
941
|
+
# Revert params afterwards.
|
942
|
+
#
|
943
|
+
# Returns pass block.
|
944
|
+
def process_route(pattern, keys, conditions, block = nil, values = [])
|
945
|
+
route = @request.path_info
|
946
|
+
route = '/' if route.empty? and not settings.empty_path_info?
|
947
|
+
return unless match = pattern.match(route)
|
948
|
+
values += match.captures.to_a.map { |v| force_encoding URI.decode_www_form_component(v) if v }
|
949
|
+
|
950
|
+
if values.any?
|
951
|
+
original, @params = params, params.merge('splat' => [], 'captures' => values)
|
952
|
+
keys.zip(values) { |k,v| Array === @params[k] ? @params[k] << v : @params[k] = v if v }
|
901
953
|
end
|
902
954
|
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
# a NotFound exception. Subclasses can override this method to perform
|
907
|
-
# custom route miss logic.
|
908
|
-
def route_missing
|
909
|
-
if @app
|
910
|
-
forward
|
911
|
-
else
|
912
|
-
raise NotFound
|
913
|
-
end
|
955
|
+
catch(:pass) do
|
956
|
+
conditions.each { |c| throw :pass if c.bind(self).call == false }
|
957
|
+
block ? block[self, values] : yield(self, values)
|
914
958
|
end
|
959
|
+
ensure
|
960
|
+
@params = original if original
|
961
|
+
end
|
915
962
|
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
963
|
+
# No matching route was found or all routes passed. The default
|
964
|
+
# implementation is to forward the request downstream when running
|
965
|
+
# as middleware (@app is non-nil); when no downstream app is set, raise
|
966
|
+
# a NotFound exception. Subclasses can override this method to perform
|
967
|
+
# custom route miss logic.
|
968
|
+
def route_missing
|
969
|
+
if @app
|
970
|
+
forward
|
971
|
+
else
|
972
|
+
raise NotFound
|
973
|
+
end
|
974
|
+
end
|
921
975
|
|
922
|
-
|
923
|
-
|
976
|
+
# Attempt to serve static files from public directory. Throws :halt when
|
977
|
+
# a matching file is found, returns nil otherwise.
|
978
|
+
def static!
|
979
|
+
return if (public_dir = settings.public_folder).nil?
|
980
|
+
public_dir = File.expand_path(public_dir)
|
924
981
|
|
925
|
-
|
926
|
-
|
927
|
-
send_file path, :disposition => nil
|
928
|
-
end
|
982
|
+
path = File.expand_path(public_dir + unescape(request.path_info))
|
983
|
+
return unless path.start_with?(public_dir) and File.file?(path)
|
929
984
|
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
next unless value.is_a?(Hash)
|
935
|
-
params[key] = indifferent_params(value)
|
936
|
-
end
|
937
|
-
end
|
985
|
+
env['sinatra.static_file'] = path
|
986
|
+
cache_control(*settings.static_cache_control) if settings.static_cache_control?
|
987
|
+
send_file path, :disposition => nil
|
988
|
+
end
|
938
989
|
|
939
|
-
|
940
|
-
|
941
|
-
|
990
|
+
# Enable string or symbol key access to the nested params hash.
|
991
|
+
def indifferent_params(object)
|
992
|
+
case object
|
993
|
+
when Hash
|
994
|
+
new_hash = indifferent_hash
|
995
|
+
object.each { |key, value| new_hash[key] = indifferent_params(value) }
|
996
|
+
new_hash
|
997
|
+
when Array
|
998
|
+
object.map { |item| indifferent_params(item) }
|
999
|
+
else
|
1000
|
+
object
|
942
1001
|
end
|
1002
|
+
end
|
943
1003
|
|
944
|
-
|
945
|
-
|
946
|
-
|
947
|
-
|
948
|
-
if Array === res and Fixnum === res.first
|
949
|
-
status(res.shift)
|
950
|
-
body(res.pop)
|
951
|
-
headers(*res)
|
952
|
-
elsif res.respond_to? :each
|
953
|
-
body res
|
954
|
-
end
|
955
|
-
nil # avoid double setting the same response tuple twice
|
956
|
-
end
|
1004
|
+
# Creates a Hash with indifferent access.
|
1005
|
+
def indifferent_hash
|
1006
|
+
Hash.new {|hash,key| hash[key.to_s] if Symbol === key }
|
1007
|
+
end
|
957
1008
|
|
958
|
-
|
959
|
-
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
end
|
1009
|
+
# Run the block with 'throw :halt' support and apply result to the response.
|
1010
|
+
def invoke
|
1011
|
+
res = catch(:halt) { yield }
|
1012
|
+
res = [res] if Fixnum === res or String === res
|
1013
|
+
if Array === res and Fixnum === res.first
|
1014
|
+
res = res.dup
|
1015
|
+
status(res.shift)
|
1016
|
+
body(res.pop)
|
1017
|
+
headers(*res)
|
1018
|
+
elsif res.respond_to? :each
|
1019
|
+
body res
|
1020
|
+
end
|
1021
|
+
nil # avoid double setting the same response tuple twice
|
1022
|
+
end
|
970
1023
|
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
|
1024
|
+
# Dispatch a request with error handling.
|
1025
|
+
def dispatch!
|
1026
|
+
invoke do
|
1027
|
+
static! if settings.static? && (request.get? || request.head?)
|
1028
|
+
filter! :before
|
1029
|
+
route!
|
1030
|
+
end
|
1031
|
+
rescue ::Exception => boom
|
1032
|
+
invoke { handle_exception!(boom) }
|
1033
|
+
ensure
|
1034
|
+
filter! :after unless env['sinatra.static_file']
|
1035
|
+
end
|
975
1036
|
|
976
|
-
|
977
|
-
|
978
|
-
|
979
|
-
end
|
1037
|
+
# Error handling during requests.
|
1038
|
+
def handle_exception!(boom)
|
1039
|
+
@env['sinatra.error'] = boom
|
980
1040
|
|
981
|
-
|
982
|
-
|
983
|
-
|
984
|
-
|
1041
|
+
if boom.respond_to? :http_status
|
1042
|
+
status(boom.http_status)
|
1043
|
+
elsif settings.use_code? and boom.respond_to? :code and boom.code.between? 400, 599
|
1044
|
+
status(boom.code)
|
1045
|
+
else
|
1046
|
+
status(500)
|
1047
|
+
end
|
985
1048
|
|
986
|
-
|
987
|
-
|
988
|
-
|
989
|
-
|
1049
|
+
status(500) unless status.between? 400, 599
|
1050
|
+
|
1051
|
+
if server_error?
|
1052
|
+
dump_errors! boom if settings.dump_errors?
|
1053
|
+
raise boom if settings.show_exceptions? and settings.show_exceptions != :after_handler
|
1054
|
+
end
|
1055
|
+
|
1056
|
+
if not_found?
|
1057
|
+
headers['X-Cascade'] = 'pass' if settings.x_cascade?
|
1058
|
+
body '<h1>Not Found</h1>'
|
990
1059
|
end
|
991
1060
|
|
992
|
-
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
1061
|
+
res = error_block!(boom.class, boom) || error_block!(status, boom)
|
1062
|
+
return res if res or not server_error?
|
1063
|
+
raise boom if settings.raise_errors? or settings.show_exceptions?
|
1064
|
+
error_block! Exception, boom
|
1065
|
+
end
|
1066
|
+
|
1067
|
+
# Find an custom error block for the key(s) specified.
|
1068
|
+
def error_block!(key, *block_params)
|
1069
|
+
base = settings
|
1070
|
+
while base.respond_to?(:errors)
|
1071
|
+
next base = base.superclass unless args_array = base.errors[key]
|
1072
|
+
args_array.reverse_each do |args|
|
1073
|
+
first = args == args_array.first
|
997
1074
|
args += [block_params]
|
998
|
-
|
1075
|
+
resp = process_route(*args)
|
1076
|
+
return resp unless resp.nil? && !first
|
999
1077
|
end
|
1000
|
-
return false unless key.respond_to? :superclass and key.superclass < Exception
|
1001
|
-
error_block!(key.superclass, *block_params)
|
1002
1078
|
end
|
1079
|
+
return false unless key.respond_to? :superclass and key.superclass < Exception
|
1080
|
+
error_block!(key.superclass, *block_params)
|
1081
|
+
end
|
1003
1082
|
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1083
|
+
def dump_errors!(boom)
|
1084
|
+
msg = ["#{boom.class} - #{boom.message}:", *boom.backtrace].join("\n\t")
|
1085
|
+
@env['rack.errors'].puts(msg)
|
1086
|
+
end
|
1008
1087
|
|
1009
1088
|
class << self
|
1010
1089
|
attr_reader :routes, :filters, :templates, :errors
|
@@ -1099,12 +1178,13 @@ module Sinatra
|
|
1099
1178
|
args = compile! "ERROR", //, block
|
1100
1179
|
codes = codes.map { |c| Array(c) }.flatten
|
1101
1180
|
codes << Exception if codes.empty?
|
1102
|
-
codes.each { |c| @errors[c]
|
1181
|
+
codes.each { |c| (@errors[c] ||= []) << args }
|
1103
1182
|
end
|
1104
1183
|
|
1105
1184
|
# Sugar for `error(404) { ... }`
|
1106
1185
|
def not_found(&block)
|
1107
|
-
error
|
1186
|
+
error(404, &block)
|
1187
|
+
error(Sinatra::NotFound, &block)
|
1108
1188
|
end
|
1109
1189
|
|
1110
1190
|
# Define a named template. The block must return the template source.
|
@@ -1114,13 +1194,13 @@ module Sinatra
|
|
1114
1194
|
end
|
1115
1195
|
|
1116
1196
|
# Define the layout template. The block must return the template source.
|
1117
|
-
def layout(name
|
1197
|
+
def layout(name = :layout, &block)
|
1118
1198
|
template name, &block
|
1119
1199
|
end
|
1120
1200
|
|
1121
1201
|
# Load embeded templates from the file; uses the caller's __FILE__
|
1122
1202
|
# when no file is specified.
|
1123
|
-
def inline_templates=(file=nil)
|
1203
|
+
def inline_templates=(file = nil)
|
1124
1204
|
file = (file.nil? || file == true) ? (caller_files.first || File.expand_path($0)) : file
|
1125
1205
|
|
1126
1206
|
begin
|
@@ -1152,7 +1232,7 @@ module Sinatra
|
|
1152
1232
|
end
|
1153
1233
|
|
1154
1234
|
# Lookup or register a mime type in Rack's mime registry.
|
1155
|
-
def mime_type(type, value=nil)
|
1235
|
+
def mime_type(type, value = nil)
|
1156
1236
|
return type if type.nil? || type.to_s.include?('/')
|
1157
1237
|
type = ".#{type}" unless type.to_s[0] == ?.
|
1158
1238
|
return Rack::Mime.mime_type(type, nil) unless value
|
@@ -1194,10 +1274,18 @@ module Sinatra
|
|
1194
1274
|
end
|
1195
1275
|
|
1196
1276
|
def public=(value)
|
1197
|
-
warn ":public is no longer used to avoid overloading Module#public, use :
|
1277
|
+
warn ":public is no longer used to avoid overloading Module#public, use :public_dir instead"
|
1198
1278
|
set(:public_folder, value)
|
1199
1279
|
end
|
1200
1280
|
|
1281
|
+
def public_dir=(value)
|
1282
|
+
self.public_folder = value
|
1283
|
+
end
|
1284
|
+
|
1285
|
+
def public_dir
|
1286
|
+
public_folder
|
1287
|
+
end
|
1288
|
+
|
1201
1289
|
private
|
1202
1290
|
# Dynamically defines a method on settings.
|
1203
1291
|
def define_singleton(name, content = Proc.new)
|
@@ -1232,8 +1320,11 @@ module Sinatra
|
|
1232
1320
|
types.map! { |t| mime_types(t) }
|
1233
1321
|
types.flatten!
|
1234
1322
|
condition do
|
1235
|
-
if type =
|
1236
|
-
|
1323
|
+
if type = response['Content-Type']
|
1324
|
+
types.include? type or types.include? type[/^[^;]+/]
|
1325
|
+
elsif type = request.preferred_type(types)
|
1326
|
+
params = (type.respond_to?(:params) ? type.params : {})
|
1327
|
+
content_type(type, params)
|
1237
1328
|
true
|
1238
1329
|
else
|
1239
1330
|
false
|
@@ -1244,7 +1335,7 @@ module Sinatra
|
|
1244
1335
|
public
|
1245
1336
|
# Defining a `GET` handler also automatically defines
|
1246
1337
|
# a `HEAD` handler.
|
1247
|
-
def get(path, opts={}, &block)
|
1338
|
+
def get(path, opts = {}, &block)
|
1248
1339
|
conditions = @conditions.dup
|
1249
1340
|
route('GET', path, opts, &block)
|
1250
1341
|
|
@@ -1252,15 +1343,15 @@ module Sinatra
|
|
1252
1343
|
route('HEAD', path, opts, &block)
|
1253
1344
|
end
|
1254
1345
|
|
1255
|
-
def put(path, opts={}, &bk) route 'PUT', path, opts, &bk end
|
1256
|
-
def post(path, opts={}, &bk) route 'POST', path, opts, &bk end
|
1257
|
-
def delete(path, opts={}, &bk) route 'DELETE', path, opts, &bk end
|
1258
|
-
def head(path, opts={}, &bk) route 'HEAD', path, opts, &bk end
|
1259
|
-
def options(path, opts={}, &bk) route 'OPTIONS', path, opts, &bk end
|
1260
|
-
def patch(path, opts={}, &bk) route 'PATCH', path, opts, &bk end
|
1346
|
+
def put(path, opts = {}, &bk) route 'PUT', path, opts, &bk end
|
1347
|
+
def post(path, opts = {}, &bk) route 'POST', path, opts, &bk end
|
1348
|
+
def delete(path, opts = {}, &bk) route 'DELETE', path, opts, &bk end
|
1349
|
+
def head(path, opts = {}, &bk) route 'HEAD', path, opts, &bk end
|
1350
|
+
def options(path, opts = {}, &bk) route 'OPTIONS', path, opts, &bk end
|
1351
|
+
def patch(path, opts = {}, &bk) route 'PATCH', path, opts, &bk end
|
1261
1352
|
|
1262
1353
|
private
|
1263
|
-
def route(verb, path, options={}, &block)
|
1354
|
+
def route(verb, path, options = {}, &block)
|
1264
1355
|
# Because of self.options.host
|
1265
1356
|
host_name(options.delete(:host)) if options.key?(:host)
|
1266
1357
|
enable :empty_path_info if path == "" and empty_path_info.nil?
|
@@ -1296,17 +1387,26 @@ module Sinatra
|
|
1296
1387
|
def compile(path)
|
1297
1388
|
keys = []
|
1298
1389
|
if path.respond_to? :to_str
|
1299
|
-
|
1390
|
+
ignore = ""
|
1391
|
+
pattern = path.to_str.gsub(/[^\?\%\\\/\:\*\w]/) do |c|
|
1392
|
+
ignore << escaped(c).join if c.match(/[\.@]/)
|
1393
|
+
patt = encoded(c)
|
1394
|
+
patt.gsub(/%[\da-fA-F]{2}/) do |match|
|
1395
|
+
match.split(//).map {|char| char =~ /[A-Z]/ ? "[#{char}#{char.tr('A-Z', 'a-z')}]" : char}.join
|
1396
|
+
end
|
1397
|
+
end
|
1300
1398
|
pattern.gsub!(/((:\w+)|\*)/) do |match|
|
1301
1399
|
if match == "*"
|
1302
1400
|
keys << 'splat'
|
1303
1401
|
"(.*?)"
|
1304
1402
|
else
|
1305
1403
|
keys << $2[1..-1]
|
1306
|
-
|
1404
|
+
ignore_pattern = safe_ignore(ignore)
|
1405
|
+
|
1406
|
+
ignore_pattern
|
1307
1407
|
end
|
1308
1408
|
end
|
1309
|
-
[
|
1409
|
+
[/\A#{pattern}\z/, keys]
|
1310
1410
|
elsif path.respond_to?(:keys) && path.respond_to?(:match)
|
1311
1411
|
[path, path.keys]
|
1312
1412
|
elsif path.respond_to?(:names) && path.respond_to?(:match)
|
@@ -1322,11 +1422,38 @@ module Sinatra
|
|
1322
1422
|
|
1323
1423
|
def encoded(char)
|
1324
1424
|
enc = URI.escape(char)
|
1325
|
-
enc = "(?:#{
|
1425
|
+
enc = "(?:#{escaped(char, enc).join('|')})" if enc == char
|
1326
1426
|
enc = "(?:#{enc}|#{encoded('+')})" if char == " "
|
1327
1427
|
enc
|
1328
1428
|
end
|
1329
1429
|
|
1430
|
+
def escaped(char, enc = URI.escape(char))
|
1431
|
+
[Regexp.escape(enc), URI.escape(char, /./)]
|
1432
|
+
end
|
1433
|
+
|
1434
|
+
def safe_ignore(ignore)
|
1435
|
+
unsafe_ignore = []
|
1436
|
+
ignore = ignore.gsub(/%[\da-fA-F]{2}/) do |hex|
|
1437
|
+
unsafe_ignore << hex[1..2]
|
1438
|
+
''
|
1439
|
+
end
|
1440
|
+
unsafe_patterns = unsafe_ignore.map do |unsafe|
|
1441
|
+
chars = unsafe.split(//).map do |char|
|
1442
|
+
if char =~ /[A-Z]/
|
1443
|
+
char <<= char.tr('A-Z', 'a-z')
|
1444
|
+
end
|
1445
|
+
char
|
1446
|
+
end
|
1447
|
+
|
1448
|
+
"|(?:%[^#{chars[0]}].|%[#{chars[0]}][^#{chars[1]}])"
|
1449
|
+
end
|
1450
|
+
if unsafe_patterns.length > 0
|
1451
|
+
"((?:[^#{ignore}/?#%]#{unsafe_patterns.join()})+)"
|
1452
|
+
else
|
1453
|
+
"([^#{ignore}/?#]+)"
|
1454
|
+
end
|
1455
|
+
end
|
1456
|
+
|
1330
1457
|
public
|
1331
1458
|
# Makes the methods defined in the block and in the Modules given
|
1332
1459
|
# in `extensions` available to the handlers and templates
|
@@ -1369,13 +1496,14 @@ module Sinatra
|
|
1369
1496
|
end
|
1370
1497
|
|
1371
1498
|
# Run the Sinatra app as a self-hosted server using
|
1372
|
-
# Thin, Mongrel or WEBrick (in that order). If given a block, will call
|
1499
|
+
# Thin, Puma, Mongrel, or WEBrick (in that order). If given a block, will call
|
1373
1500
|
# with the constructed handler once we have taken the stage.
|
1374
|
-
def run!(options={})
|
1501
|
+
def run!(options = {})
|
1375
1502
|
set options
|
1376
|
-
handler
|
1377
|
-
handler_name
|
1378
|
-
|
1503
|
+
handler = detect_rack_handler
|
1504
|
+
handler_name = handler.name.gsub(/.*::/, '')
|
1505
|
+
server_settings = settings.respond_to?(:server_settings) ? settings.server_settings : {}
|
1506
|
+
handler.run self, server_settings.merge(:Port => port, :Host => bind) do |server|
|
1379
1507
|
unless handler_name =~ /cgi/i
|
1380
1508
|
$stderr.puts "== Sinatra/#{Sinatra::VERSION} has taken the stage " +
|
1381
1509
|
"on #{port} for #{environment} with backup from #{handler_name}"
|
@@ -1401,15 +1529,17 @@ module Sinatra
|
|
1401
1529
|
# pipeline. The object is guaranteed to respond to #call but may not be
|
1402
1530
|
# an instance of the class new was called on.
|
1403
1531
|
def new(*args, &bk)
|
1404
|
-
|
1532
|
+
instance = new!(*args, &bk)
|
1533
|
+
Wrapper.new(build(instance).to_app, instance)
|
1405
1534
|
end
|
1406
1535
|
|
1407
1536
|
# Creates a Rack::Builder instance with all the middleware set up and
|
1408
|
-
#
|
1409
|
-
def build(
|
1537
|
+
# the given +app+ as end point.
|
1538
|
+
def build(app)
|
1539
|
+
builder = Rack::Builder.new
|
1410
1540
|
setup_default_middleware builder
|
1411
1541
|
setup_middleware builder
|
1412
|
-
builder.run
|
1542
|
+
builder.run app
|
1413
1543
|
builder
|
1414
1544
|
end
|
1415
1545
|
|
@@ -1460,8 +1590,9 @@ module Sinatra
|
|
1460
1590
|
def setup_protection(builder)
|
1461
1591
|
return unless protection?
|
1462
1592
|
options = Hash === protection ? protection.dup : {}
|
1593
|
+
protect_session = options.fetch(:session) { sessions? }
|
1463
1594
|
options[:except] = Array options[:except]
|
1464
|
-
options[:except] += [:session_hijacking, :remote_token] unless
|
1595
|
+
options[:except] += [:session_hijacking, :remote_token] unless protect_session
|
1465
1596
|
options[:reaction] ||= :drop_session
|
1466
1597
|
builder.use Rack::Protection, options
|
1467
1598
|
end
|
@@ -1577,7 +1708,9 @@ module Sinatra
|
|
1577
1708
|
set :logging, false
|
1578
1709
|
set :protection, true
|
1579
1710
|
set :method_override, false
|
1711
|
+
set :use_code, false
|
1580
1712
|
set :default_encoding, "utf-8"
|
1713
|
+
set :x_cascade, true
|
1581
1714
|
set :add_charset, %w[javascript xml xhtml+xml json].map { |t| "application/#{t}" }
|
1582
1715
|
settings.add_charset << /^text\//
|
1583
1716
|
|
@@ -1597,9 +1730,21 @@ module Sinatra
|
|
1597
1730
|
|
1598
1731
|
set :run, false # start server via at-exit hook?
|
1599
1732
|
set :running, false # is the built-in server running now?
|
1600
|
-
set :server, %w[
|
1601
|
-
set :bind, '0.0.0.0'
|
1602
|
-
set :port, 4567
|
1733
|
+
set :server, %w[http webrick]
|
1734
|
+
set :bind, Proc.new { development? ? 'localhost' : '0.0.0.0' }
|
1735
|
+
set :port, Integer(ENV['PORT'] || 4567)
|
1736
|
+
|
1737
|
+
ruby_engine = defined?(RUBY_ENGINE) && RUBY_ENGINE
|
1738
|
+
|
1739
|
+
if ruby_engine == 'macruby'
|
1740
|
+
server.unshift 'control_tower'
|
1741
|
+
else
|
1742
|
+
server.unshift 'mongrel' if ruby_engine.nil?
|
1743
|
+
server.unshift 'puma' if ruby_engine != 'rbx'
|
1744
|
+
server.unshift 'thin' if ruby_engine != 'jruby'
|
1745
|
+
server.unshift 'puma' if ruby_engine == 'rbx'
|
1746
|
+
server.unshift 'trinidad' if ruby_engine =='jruby'
|
1747
|
+
end
|
1603
1748
|
|
1604
1749
|
set :absolute_redirects, true
|
1605
1750
|
set :prefixed_redirects, false
|
@@ -1632,25 +1777,44 @@ module Sinatra
|
|
1632
1777
|
error NotFound do
|
1633
1778
|
content_type 'text/html'
|
1634
1779
|
|
1635
|
-
|
1636
|
-
|
1637
|
-
|
1638
|
-
|
1639
|
-
|
1640
|
-
|
1641
|
-
|
1642
|
-
|
1643
|
-
|
1644
|
-
|
1645
|
-
|
1646
|
-
|
1647
|
-
|
1648
|
-
|
1649
|
-
|
1650
|
-
|
1651
|
-
|
1652
|
-
|
1653
|
-
|
1780
|
+
if self.class == Sinatra::Application
|
1781
|
+
code = <<-RUBY.gsub(/^ {12}/, '')
|
1782
|
+
#{request.request_method.downcase} '#{request.path_info}' do
|
1783
|
+
"Hello World"
|
1784
|
+
end
|
1785
|
+
RUBY
|
1786
|
+
else
|
1787
|
+
code = <<-RUBY.gsub(/^ {12}/, '')
|
1788
|
+
class #{self.class}
|
1789
|
+
#{request.request_method.downcase} '#{request.path_info}' do
|
1790
|
+
"Hello World"
|
1791
|
+
end
|
1792
|
+
end
|
1793
|
+
RUBY
|
1794
|
+
|
1795
|
+
file = settings.app_file.to_s.sub(settings.root.to_s, '').sub(/^\//, '')
|
1796
|
+
code = "# in #{file}\n#{code}" unless file.empty?
|
1797
|
+
end
|
1798
|
+
|
1799
|
+
(<<-HTML).gsub(/^ {10}/, '')
|
1800
|
+
<!DOCTYPE html>
|
1801
|
+
<html>
|
1802
|
+
<head>
|
1803
|
+
<style type="text/css">
|
1804
|
+
body { text-align:center;font-family:helvetica,arial;font-size:22px;
|
1805
|
+
color:#888;margin:20px}
|
1806
|
+
#c {margin:0 auto;width:500px;text-align:left}
|
1807
|
+
</style>
|
1808
|
+
</head>
|
1809
|
+
<body>
|
1810
|
+
<h2>Sinatra doesn’t know this ditty.</h2>
|
1811
|
+
<img src='#{uri "/__sinatra__/404.png"}'>
|
1812
|
+
<div id="c">
|
1813
|
+
Try this:
|
1814
|
+
<pre>#{code}</pre>
|
1815
|
+
</div>
|
1816
|
+
</body>
|
1817
|
+
</html>
|
1654
1818
|
HTML
|
1655
1819
|
end
|
1656
1820
|
end
|
@@ -1694,7 +1858,7 @@ module Sinatra
|
|
1694
1858
|
delegate :get, :patch, :put, :post, :delete, :head, :options, :template, :layout,
|
1695
1859
|
:before, :after, :error, :not_found, :configure, :set, :mime_type,
|
1696
1860
|
:enable, :disable, :use, :development?, :test?, :production?,
|
1697
|
-
:helpers, :settings
|
1861
|
+
:helpers, :settings, :register
|
1698
1862
|
|
1699
1863
|
class << self
|
1700
1864
|
attr_accessor :target
|
@@ -1703,9 +1867,31 @@ module Sinatra
|
|
1703
1867
|
self.target = Application
|
1704
1868
|
end
|
1705
1869
|
|
1870
|
+
class Wrapper
|
1871
|
+
def initialize(stack, instance)
|
1872
|
+
@stack, @instance = stack, instance
|
1873
|
+
end
|
1874
|
+
|
1875
|
+
def settings
|
1876
|
+
@instance.settings
|
1877
|
+
end
|
1878
|
+
|
1879
|
+
def helpers
|
1880
|
+
@instance
|
1881
|
+
end
|
1882
|
+
|
1883
|
+
def call(env)
|
1884
|
+
@stack.call(env)
|
1885
|
+
end
|
1886
|
+
|
1887
|
+
def inspect
|
1888
|
+
"#<#{@instance.class} app_file=#{settings.app_file.inspect}>"
|
1889
|
+
end
|
1890
|
+
end
|
1891
|
+
|
1706
1892
|
# Create a new Sinatra application. The block is evaluated in the new app's
|
1707
1893
|
# class scope.
|
1708
|
-
def self.new(base=Base, options={}, &block)
|
1894
|
+
def self.new(base = Base, options = {}, &block)
|
1709
1895
|
base = Class.new(base)
|
1710
1896
|
base.class_eval(&block) if block_given?
|
1711
1897
|
base
|