sinatra 2.2.0 → 3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +93 -16
- data/Gemfile +44 -65
- data/README.md +104 -401
- data/Rakefile +65 -65
- data/VERSION +1 -1
- data/examples/chat.rb +5 -3
- data/examples/rainbows.rb +3 -1
- data/examples/simple.rb +2 -0
- data/examples/stream.ru +2 -0
- data/lib/sinatra/base.rb +345 -335
- data/lib/sinatra/indifferent_hash.rb +25 -33
- data/lib/sinatra/main.rb +18 -16
- data/lib/sinatra/show_exceptions.rb +17 -15
- data/lib/sinatra/version.rb +3 -1
- data/lib/sinatra.rb +2 -0
- data/sinatra.gemspec +38 -33
- metadata +38 -29
- data/README.de.md +0 -3239
- data/README.es.md +0 -3231
- data/README.fr.md +0 -3111
- data/README.hu.md +0 -728
- data/README.ja.md +0 -2844
- data/README.ko.md +0 -2967
- data/README.malayalam.md +0 -3141
- data/README.pt-br.md +0 -3787
- data/README.pt-pt.md +0 -791
- data/README.ru.md +0 -3207
- data/README.zh.md +0 -2934
data/lib/sinatra/base.rb
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# coding: utf-8
|
|
2
1
|
# frozen_string_literal: true
|
|
3
2
|
|
|
4
3
|
# external dependencies
|
|
@@ -10,7 +9,6 @@ require 'mustermann/sinatra'
|
|
|
10
9
|
require 'mustermann/regular'
|
|
11
10
|
|
|
12
11
|
# stdlib dependencies
|
|
13
|
-
require 'thread'
|
|
14
12
|
require 'time'
|
|
15
13
|
require 'uri'
|
|
16
14
|
|
|
@@ -23,19 +21,20 @@ module Sinatra
|
|
|
23
21
|
# The request object. See Rack::Request for more info:
|
|
24
22
|
# http://rubydoc.info/github/rack/rack/master/Rack/Request
|
|
25
23
|
class Request < Rack::Request
|
|
26
|
-
HEADER_PARAM = /\s*[\w.]+=(?:[\w.]+|"(?:[^"\\]|\\.)*")?\s
|
|
27
|
-
HEADER_VALUE_WITH_PARAMS =
|
|
24
|
+
HEADER_PARAM = /\s*[\w.]+=(?:[\w.]+|"(?:[^"\\]|\\.)*")?\s*/.freeze
|
|
25
|
+
HEADER_VALUE_WITH_PARAMS = %r{(?:(?:\w+|\*)/(?:\w+(?:\.|-|\+)?|\*)*)\s*(?:;#{HEADER_PARAM})*}.freeze
|
|
28
26
|
|
|
29
27
|
# Returns an array of acceptable media types for the response
|
|
30
28
|
def accept
|
|
31
|
-
@env['sinatra.accept'] ||=
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
29
|
+
@env['sinatra.accept'] ||= if @env.include?('HTTP_ACCEPT') && (@env['HTTP_ACCEPT'].to_s != '')
|
|
30
|
+
@env['HTTP_ACCEPT']
|
|
31
|
+
.to_s
|
|
32
|
+
.scan(HEADER_VALUE_WITH_PARAMS)
|
|
33
|
+
.map! { |e| AcceptEntry.new(e) }
|
|
34
|
+
.sort
|
|
35
|
+
else
|
|
36
|
+
[AcceptEntry.new('*/*')]
|
|
37
|
+
end
|
|
39
38
|
end
|
|
40
39
|
|
|
41
40
|
def accept?(type)
|
|
@@ -44,8 +43,10 @@ module Sinatra
|
|
|
44
43
|
|
|
45
44
|
def preferred_type(*types)
|
|
46
45
|
return accept.first if types.empty?
|
|
46
|
+
|
|
47
47
|
types.flatten!
|
|
48
48
|
return types.first if accept.empty?
|
|
49
|
+
|
|
49
50
|
accept.detect do |accept_header|
|
|
50
51
|
type = types.detect { |t| MimeTypeEntry.new(t).accepts?(accept_header) }
|
|
51
52
|
return type if type
|
|
@@ -55,23 +56,23 @@ module Sinatra
|
|
|
55
56
|
alias secure? ssl?
|
|
56
57
|
|
|
57
58
|
def forwarded?
|
|
58
|
-
@env.include?
|
|
59
|
+
@env.include? 'HTTP_X_FORWARDED_HOST'
|
|
59
60
|
end
|
|
60
61
|
|
|
61
62
|
def safe?
|
|
62
|
-
get?
|
|
63
|
+
get? || head? || options? || trace?
|
|
63
64
|
end
|
|
64
65
|
|
|
65
66
|
def idempotent?
|
|
66
|
-
safe?
|
|
67
|
+
safe? || put? || delete? || link? || unlink?
|
|
67
68
|
end
|
|
68
69
|
|
|
69
70
|
def link?
|
|
70
|
-
request_method ==
|
|
71
|
+
request_method == 'LINK'
|
|
71
72
|
end
|
|
72
73
|
|
|
73
74
|
def unlink?
|
|
74
|
-
request_method ==
|
|
75
|
+
request_method == 'UNLINK'
|
|
75
76
|
end
|
|
76
77
|
|
|
77
78
|
def params
|
|
@@ -95,17 +96,17 @@ module Sinatra
|
|
|
95
96
|
|
|
96
97
|
@entry = entry
|
|
97
98
|
@type = entry[/[^;]+/].delete(' ')
|
|
98
|
-
@params =
|
|
99
|
+
@params = params.to_h
|
|
99
100
|
@q = @params.delete('q') { 1.0 }.to_f
|
|
100
101
|
end
|
|
101
102
|
|
|
102
103
|
def <=>(other)
|
|
103
|
-
other.priority <=>
|
|
104
|
+
other.priority <=> priority
|
|
104
105
|
end
|
|
105
106
|
|
|
106
107
|
def priority
|
|
107
108
|
# We sort in descending order; better matches should be higher.
|
|
108
|
-
[
|
|
109
|
+
[@q, -@type.count('*'), @params.size]
|
|
109
110
|
end
|
|
110
111
|
|
|
111
112
|
def to_str
|
|
@@ -117,7 +118,7 @@ module Sinatra
|
|
|
117
118
|
end
|
|
118
119
|
|
|
119
120
|
def respond_to?(*args)
|
|
120
|
-
super
|
|
121
|
+
super || to_str.respond_to?(*args)
|
|
121
122
|
end
|
|
122
123
|
|
|
123
124
|
def method_missing(*args, &block)
|
|
@@ -136,7 +137,7 @@ module Sinatra
|
|
|
136
137
|
end
|
|
137
138
|
|
|
138
139
|
@type = entry[/[^;]+/].delete(' ')
|
|
139
|
-
@params =
|
|
140
|
+
@params = params.to_h
|
|
140
141
|
end
|
|
141
142
|
|
|
142
143
|
def accepts?(entry)
|
|
@@ -150,7 +151,7 @@ module Sinatra
|
|
|
150
151
|
def matches_params?(params)
|
|
151
152
|
return true if @params.empty?
|
|
152
153
|
|
|
153
|
-
params.all? { |k,v| !@params.
|
|
154
|
+
params.all? { |k, v| !@params.key?(k) || @params[k] == v }
|
|
154
155
|
end
|
|
155
156
|
end
|
|
156
157
|
end
|
|
@@ -160,7 +161,7 @@ module Sinatra
|
|
|
160
161
|
# http://rubydoc.info/github/rack/rack/master/Rack/Response
|
|
161
162
|
# http://rubydoc.info/github/rack/rack/master/Rack/Response/Helpers
|
|
162
163
|
class Response < Rack::Response
|
|
163
|
-
DROP_BODY_RESPONSES = [204, 304]
|
|
164
|
+
DROP_BODY_RESPONSES = [204, 304].freeze
|
|
164
165
|
|
|
165
166
|
def body=(value)
|
|
166
167
|
value = value.body while Rack::Response === value
|
|
@@ -175,8 +176,8 @@ module Sinatra
|
|
|
175
176
|
result = body
|
|
176
177
|
|
|
177
178
|
if drop_content_info?
|
|
178
|
-
headers.delete
|
|
179
|
-
headers.delete
|
|
179
|
+
headers.delete 'Content-Length'
|
|
180
|
+
headers.delete 'Content-Type'
|
|
180
181
|
end
|
|
181
182
|
|
|
182
183
|
if drop_body?
|
|
@@ -187,7 +188,7 @@ module Sinatra
|
|
|
187
188
|
if calculate_content_length?
|
|
188
189
|
# if some other code has already set Content-Length, don't muck with it
|
|
189
190
|
# currently, this would be the static file-handler
|
|
190
|
-
headers[
|
|
191
|
+
headers['Content-Length'] = body.map(&:bytesize).reduce(0, :+).to_s
|
|
191
192
|
end
|
|
192
193
|
|
|
193
194
|
[status, headers, result]
|
|
@@ -196,11 +197,11 @@ module Sinatra
|
|
|
196
197
|
private
|
|
197
198
|
|
|
198
199
|
def calculate_content_length?
|
|
199
|
-
headers[
|
|
200
|
+
headers['Content-Type'] && !headers['Content-Length'] && (Array === body)
|
|
200
201
|
end
|
|
201
202
|
|
|
202
203
|
def drop_content_info?
|
|
203
|
-
informational?
|
|
204
|
+
informational? || drop_body?
|
|
204
205
|
end
|
|
205
206
|
|
|
206
207
|
def drop_body?
|
|
@@ -215,8 +216,10 @@ module Sinatra
|
|
|
215
216
|
# still be able to run.
|
|
216
217
|
class ExtendedRack < Struct.new(:app)
|
|
217
218
|
def call(env)
|
|
218
|
-
result
|
|
219
|
-
|
|
219
|
+
result = app.call(env)
|
|
220
|
+
callback = env['async.callback']
|
|
221
|
+
return result unless callback && async?(*result)
|
|
222
|
+
|
|
220
223
|
after_response { callback.call result }
|
|
221
224
|
setup_close(env, *result)
|
|
222
225
|
throw :async
|
|
@@ -224,20 +227,23 @@ module Sinatra
|
|
|
224
227
|
|
|
225
228
|
private
|
|
226
229
|
|
|
227
|
-
def setup_close(env,
|
|
228
|
-
return unless body.respond_to?
|
|
230
|
+
def setup_close(env, _status, _headers, body)
|
|
231
|
+
return unless body.respond_to?(:close) && env.include?('async.close')
|
|
232
|
+
|
|
229
233
|
env['async.close'].callback { body.close }
|
|
230
234
|
env['async.close'].errback { body.close }
|
|
231
235
|
end
|
|
232
236
|
|
|
233
237
|
def after_response(&block)
|
|
234
|
-
raise NotImplementedError,
|
|
238
|
+
raise NotImplementedError, 'only supports EventMachine at the moment' unless defined? EventMachine
|
|
239
|
+
|
|
235
240
|
EventMachine.next_tick(&block)
|
|
236
241
|
end
|
|
237
242
|
|
|
238
|
-
def async?(status,
|
|
243
|
+
def async?(status, _headers, body)
|
|
239
244
|
return true if status == -1
|
|
240
|
-
|
|
245
|
+
|
|
246
|
+
body.respond_to?(:callback) && body.respond_to?(:errback)
|
|
241
247
|
end
|
|
242
248
|
end
|
|
243
249
|
|
|
@@ -249,7 +255,7 @@ module Sinatra
|
|
|
249
255
|
end
|
|
250
256
|
|
|
251
257
|
superclass.class_eval do
|
|
252
|
-
|
|
258
|
+
alias_method :call_without_check, :call unless method_defined? :call_without_check
|
|
253
259
|
def call(env)
|
|
254
260
|
env['sinatra.commonlogger'] = true
|
|
255
261
|
call_without_check(env)
|
|
@@ -257,11 +263,14 @@ module Sinatra
|
|
|
257
263
|
end
|
|
258
264
|
end
|
|
259
265
|
|
|
260
|
-
class
|
|
266
|
+
class Error < StandardError # :nodoc:
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
class BadRequest < Error # :nodoc:
|
|
261
270
|
def http_status; 400 end
|
|
262
271
|
end
|
|
263
272
|
|
|
264
|
-
class NotFound <
|
|
273
|
+
class NotFound < Error # :nodoc:
|
|
265
274
|
def http_status; 404 end
|
|
266
275
|
end
|
|
267
276
|
|
|
@@ -293,7 +302,7 @@ module Sinatra
|
|
|
293
302
|
|
|
294
303
|
# Halt processing and redirect to the URI provided.
|
|
295
304
|
def redirect(uri, *args)
|
|
296
|
-
if env['HTTP_VERSION'] == 'HTTP/1.1'
|
|
305
|
+
if (env['HTTP_VERSION'] == 'HTTP/1.1') && (env['REQUEST_METHOD'] != 'GET')
|
|
297
306
|
status 303
|
|
298
307
|
else
|
|
299
308
|
status 302
|
|
@@ -308,18 +317,19 @@ module Sinatra
|
|
|
308
317
|
# Generates the absolute URI for a given path in the app.
|
|
309
318
|
# Takes Rack routers and reverse proxies into account.
|
|
310
319
|
def uri(addr = nil, absolute = true, add_script_name = true)
|
|
311
|
-
return addr if addr =~ /\A[a-z][a-z0-9
|
|
320
|
+
return addr if addr =~ /\A[a-z][a-z0-9+.\-]*:/i
|
|
321
|
+
|
|
312
322
|
uri = [host = String.new]
|
|
313
323
|
if absolute
|
|
314
324
|
host << "http#{'s' if request.secure?}://"
|
|
315
|
-
if request.forwarded?
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
325
|
+
host << if request.forwarded? || (request.port != (request.secure? ? 443 : 80))
|
|
326
|
+
request.host_with_port
|
|
327
|
+
else
|
|
328
|
+
request.host
|
|
329
|
+
end
|
|
320
330
|
end
|
|
321
331
|
uri << request.script_name.to_s if add_script_name
|
|
322
|
-
uri << (addr
|
|
332
|
+
uri << (addr || request.path_info).to_s
|
|
323
333
|
File.join uri
|
|
324
334
|
end
|
|
325
335
|
|
|
@@ -328,7 +338,10 @@ module Sinatra
|
|
|
328
338
|
|
|
329
339
|
# Halt processing and return the error status provided.
|
|
330
340
|
def error(code, body = nil)
|
|
331
|
-
|
|
341
|
+
if code.respond_to? :to_str
|
|
342
|
+
body = code.to_str
|
|
343
|
+
code = 500
|
|
344
|
+
end
|
|
332
345
|
response.body = body unless body.nil?
|
|
333
346
|
halt code
|
|
334
347
|
end
|
|
@@ -363,11 +376,13 @@ module Sinatra
|
|
|
363
376
|
# extension.
|
|
364
377
|
def content_type(type = nil, params = {})
|
|
365
378
|
return response['Content-Type'] unless type
|
|
379
|
+
|
|
366
380
|
default = params.delete :default
|
|
367
381
|
mime_type = mime_type(type) || default
|
|
368
|
-
|
|
382
|
+
raise format('Unknown media type: %p', type) if mime_type.nil?
|
|
383
|
+
|
|
369
384
|
mime_type = mime_type.dup
|
|
370
|
-
unless params.include?
|
|
385
|
+
unless params.include?(:charset) || settings.add_charset.all? { |p| !(p === mime_type) }
|
|
371
386
|
params[:charset] = params.delete('charset') || settings.default_encoding
|
|
372
387
|
end
|
|
373
388
|
params.delete :charset if mime_type.include? 'charset'
|
|
@@ -381,27 +396,34 @@ module Sinatra
|
|
|
381
396
|
response['Content-Type'] = mime_type
|
|
382
397
|
end
|
|
383
398
|
|
|
399
|
+
# https://html.spec.whatwg.org/#multipart-form-data
|
|
400
|
+
MULTIPART_FORM_DATA_REPLACEMENT_TABLE = {
|
|
401
|
+
'"' => '%22',
|
|
402
|
+
"\r" => '%0D',
|
|
403
|
+
"\n" => '%0A'
|
|
404
|
+
}.freeze
|
|
405
|
+
|
|
384
406
|
# Set the Content-Disposition to "attachment" with the specified filename,
|
|
385
407
|
# instructing the user agents to prompt to save.
|
|
386
408
|
def attachment(filename = nil, disposition = :attachment)
|
|
387
409
|
response['Content-Disposition'] = disposition.to_s.dup
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
410
|
+
return unless filename
|
|
411
|
+
|
|
412
|
+
params = format('; filename="%s"', File.basename(filename).gsub(/["\r\n]/, MULTIPART_FORM_DATA_REPLACEMENT_TABLE))
|
|
413
|
+
response['Content-Disposition'] << params
|
|
414
|
+
ext = File.extname(filename)
|
|
415
|
+
content_type(ext) unless response['Content-Type'] || ext.empty?
|
|
394
416
|
end
|
|
395
417
|
|
|
396
418
|
# Use the contents of the file at +path+ as the response body.
|
|
397
419
|
def send_file(path, opts = {})
|
|
398
|
-
if opts[:type]
|
|
399
|
-
content_type opts[:type] || File.extname(path), :
|
|
420
|
+
if opts[:type] || !response['Content-Type']
|
|
421
|
+
content_type opts[:type] || File.extname(path), default: 'application/octet-stream'
|
|
400
422
|
end
|
|
401
423
|
|
|
402
424
|
disposition = opts[:disposition]
|
|
403
425
|
filename = opts[:filename]
|
|
404
|
-
disposition = :attachment if disposition.nil?
|
|
426
|
+
disposition = :attachment if disposition.nil? && filename
|
|
405
427
|
filename = path if filename.nil?
|
|
406
428
|
attachment(filename, disposition) if disposition
|
|
407
429
|
|
|
@@ -410,7 +432,7 @@ module Sinatra
|
|
|
410
432
|
file = Rack::File.new(File.dirname(settings.app_file))
|
|
411
433
|
result = file.serving(request, path)
|
|
412
434
|
|
|
413
|
-
result[1].each { |k,v| headers[k] ||= v }
|
|
435
|
+
result[1].each { |k, v| headers[k] ||= v }
|
|
414
436
|
headers['Content-Length'] = result[1]['Content-Length']
|
|
415
437
|
opts[:status] &&= Integer(opts[:status])
|
|
416
438
|
halt (opts[:status] || result[0]), result[2]
|
|
@@ -431,12 +453,16 @@ module Sinatra
|
|
|
431
453
|
def self.defer(*) yield end
|
|
432
454
|
|
|
433
455
|
def initialize(scheduler = self.class, keep_open = false, &back)
|
|
434
|
-
@back
|
|
435
|
-
@
|
|
456
|
+
@back = back.to_proc
|
|
457
|
+
@scheduler = scheduler
|
|
458
|
+
@keep_open = keep_open
|
|
459
|
+
@callbacks = []
|
|
460
|
+
@closed = false
|
|
436
461
|
end
|
|
437
462
|
|
|
438
463
|
def close
|
|
439
464
|
return if closed?
|
|
465
|
+
|
|
440
466
|
@closed = true
|
|
441
467
|
@scheduler.schedule { @callbacks.each { |c| c.call } }
|
|
442
468
|
end
|
|
@@ -460,6 +486,7 @@ module Sinatra
|
|
|
460
486
|
|
|
461
487
|
def callback(&block)
|
|
462
488
|
return yield if closed?
|
|
489
|
+
|
|
463
490
|
@callbacks << block
|
|
464
491
|
end
|
|
465
492
|
|
|
@@ -493,18 +520,18 @@ module Sinatra
|
|
|
493
520
|
# See RFC 2616 / 14.9 for more on standard cache control directives:
|
|
494
521
|
# http://tools.ietf.org/html/rfc2616#section-14.9.1
|
|
495
522
|
def cache_control(*values)
|
|
496
|
-
if values.last.
|
|
523
|
+
if values.last.is_a?(Hash)
|
|
497
524
|
hash = values.pop
|
|
498
|
-
hash.reject! { |
|
|
525
|
+
hash.reject! { |_k, v| v == false }
|
|
499
526
|
hash.reject! { |k, v| values << k if v == true }
|
|
500
527
|
else
|
|
501
528
|
hash = {}
|
|
502
529
|
end
|
|
503
530
|
|
|
504
|
-
values.map! { |value| value.to_s.tr('_','-') }
|
|
531
|
+
values.map! { |value| value.to_s.tr('_', '-') }
|
|
505
532
|
hash.each do |key, value|
|
|
506
533
|
key = key.to_s.tr('_', '-')
|
|
507
|
-
value = value.to_i if [
|
|
534
|
+
value = value.to_i if %w[max-age s-maxage].include? key
|
|
508
535
|
values << "#{key}=#{value}"
|
|
509
536
|
end
|
|
510
537
|
|
|
@@ -521,7 +548,7 @@ module Sinatra
|
|
|
521
548
|
# => Expires: Mon, 08 Jun 2009 08:50:17 GMT
|
|
522
549
|
#
|
|
523
550
|
def expires(amount, *values)
|
|
524
|
-
values << {} unless values.last.
|
|
551
|
+
values << {} unless values.last.is_a?(Hash)
|
|
525
552
|
|
|
526
553
|
if amount.is_a? Integer
|
|
527
554
|
time = Time.now + amount.to_i
|
|
@@ -531,7 +558,7 @@ module Sinatra
|
|
|
531
558
|
max_age = time - Time.now
|
|
532
559
|
end
|
|
533
560
|
|
|
534
|
-
values.last.merge!(:max_age
|
|
561
|
+
values.last.merge!(max_age: max_age) { |_key, v1, v2| v1 || v2 }
|
|
535
562
|
cache_control(*values)
|
|
536
563
|
|
|
537
564
|
response['Expires'] = time.httpdate
|
|
@@ -546,17 +573,18 @@ module Sinatra
|
|
|
546
573
|
# with a '304 Not Modified' response.
|
|
547
574
|
def last_modified(time)
|
|
548
575
|
return unless time
|
|
576
|
+
|
|
549
577
|
time = time_for time
|
|
550
578
|
response['Last-Modified'] = time.httpdate
|
|
551
579
|
return if env['HTTP_IF_NONE_MATCH']
|
|
552
580
|
|
|
553
|
-
if status == 200
|
|
581
|
+
if (status == 200) && env['HTTP_IF_MODIFIED_SINCE']
|
|
554
582
|
# compare based on seconds since epoch
|
|
555
583
|
since = Time.httpdate(env['HTTP_IF_MODIFIED_SINCE']).to_i
|
|
556
584
|
halt 304 if since >= time.to_i
|
|
557
585
|
end
|
|
558
586
|
|
|
559
|
-
if (success?
|
|
587
|
+
if (success? || (status == 412)) && env['HTTP_IF_UNMODIFIED_SINCE']
|
|
560
588
|
# compare based on seconds since epoch
|
|
561
589
|
since = Time.httpdate(env['HTTP_IF_UNMODIFIED_SINCE']).to_i
|
|
562
590
|
halt 412 if since < time.to_i
|
|
@@ -564,7 +592,7 @@ module Sinatra
|
|
|
564
592
|
rescue ArgumentError
|
|
565
593
|
end
|
|
566
594
|
|
|
567
|
-
ETAG_KINDS = [
|
|
595
|
+
ETAG_KINDS = %i[strong weak].freeze
|
|
568
596
|
# Set the response entity tag (HTTP 'ETag' header) and halt if conditional
|
|
569
597
|
# GET matches. The +value+ argument is an identifier that uniquely
|
|
570
598
|
# identifies the current version of the resource. The +kind+ argument
|
|
@@ -576,27 +604,31 @@ module Sinatra
|
|
|
576
604
|
# GET or HEAD, a '304 Not Modified' response is sent.
|
|
577
605
|
def etag(value, options = {})
|
|
578
606
|
# Before touching this code, please double check RFC 2616 14.24 and 14.26.
|
|
579
|
-
options = {:
|
|
607
|
+
options = { kind: options } unless Hash === options
|
|
580
608
|
kind = options[:kind] || :strong
|
|
581
609
|
new_resource = options.fetch(:new_resource) { request.post? }
|
|
582
610
|
|
|
583
611
|
unless ETAG_KINDS.include?(kind)
|
|
584
|
-
raise ArgumentError,
|
|
612
|
+
raise ArgumentError, ':strong or :weak expected'
|
|
585
613
|
end
|
|
586
614
|
|
|
587
|
-
value = '"%s"'
|
|
615
|
+
value = format('"%s"', value)
|
|
588
616
|
value = "W/#{value}" if kind == :weak
|
|
589
617
|
response['ETag'] = value
|
|
590
618
|
|
|
591
|
-
|
|
592
|
-
if etag_matches? env['HTTP_IF_NONE_MATCH'], new_resource
|
|
593
|
-
halt(request.safe? ? 304 : 412)
|
|
594
|
-
end
|
|
619
|
+
return unless success? || status == 304
|
|
595
620
|
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
621
|
+
if etag_matches?(env['HTTP_IF_NONE_MATCH'], new_resource)
|
|
622
|
+
halt(request.safe? ? 304 : 412)
|
|
623
|
+
end
|
|
624
|
+
|
|
625
|
+
if env['HTTP_IF_MATCH']
|
|
626
|
+
return if etag_matches?(env['HTTP_IF_MATCH'], new_resource)
|
|
627
|
+
|
|
628
|
+
halt 412
|
|
599
629
|
end
|
|
630
|
+
|
|
631
|
+
nil
|
|
600
632
|
end
|
|
601
633
|
|
|
602
634
|
# Sugar for redirect (example: redirect back)
|
|
@@ -649,8 +681,8 @@ module Sinatra
|
|
|
649
681
|
else
|
|
650
682
|
value.to_time
|
|
651
683
|
end
|
|
652
|
-
rescue ArgumentError =>
|
|
653
|
-
raise
|
|
684
|
+
rescue ArgumentError => e
|
|
685
|
+
raise e
|
|
654
686
|
rescue Exception
|
|
655
687
|
raise ArgumentError, "unable to convert #{value.inspect} to a Time object"
|
|
656
688
|
end
|
|
@@ -660,11 +692,13 @@ module Sinatra
|
|
|
660
692
|
# Helper method checking if a ETag value list includes the current ETag.
|
|
661
693
|
def etag_matches?(list, new_resource = request.post?)
|
|
662
694
|
return !new_resource if list == '*'
|
|
695
|
+
|
|
663
696
|
list.to_s.split(/\s*,\s*/).include? response['ETag']
|
|
664
697
|
end
|
|
665
698
|
|
|
666
699
|
def with_params(temp_params)
|
|
667
|
-
original
|
|
700
|
+
original = @params
|
|
701
|
+
@params = temp_params
|
|
668
702
|
yield
|
|
669
703
|
ensure
|
|
670
704
|
@params = original if original
|
|
@@ -682,7 +716,7 @@ module Sinatra
|
|
|
682
716
|
# Possible options are:
|
|
683
717
|
# :content_type The content type to use, same arguments as content_type.
|
|
684
718
|
# :layout If set to something falsy, no layout is rendered, otherwise
|
|
685
|
-
# the specified layout is used
|
|
719
|
+
# the specified layout is used
|
|
686
720
|
# :layout_engine Engine to use for rendering the layout.
|
|
687
721
|
# :locals A hash with local variables that should be available
|
|
688
722
|
# in the template
|
|
@@ -704,36 +738,10 @@ module Sinatra
|
|
|
704
738
|
render(:erb, template, options, locals, &block)
|
|
705
739
|
end
|
|
706
740
|
|
|
707
|
-
def erubis(template, options = {}, locals = {})
|
|
708
|
-
warn "Sinatra::Templates#erubis is deprecated and will be removed, use #erb instead.\n" \
|
|
709
|
-
"If you have Erubis installed, it will be used automatically."
|
|
710
|
-
render :erubis, template, options, locals
|
|
711
|
-
end
|
|
712
|
-
|
|
713
741
|
def haml(template, options = {}, locals = {}, &block)
|
|
714
742
|
render(:haml, template, options, locals, &block)
|
|
715
743
|
end
|
|
716
744
|
|
|
717
|
-
def sass(template, options = {}, locals = {})
|
|
718
|
-
options.merge! :layout => false, :default_content_type => :css
|
|
719
|
-
render :sass, template, options, locals
|
|
720
|
-
end
|
|
721
|
-
|
|
722
|
-
def scss(template, options = {}, locals = {})
|
|
723
|
-
options.merge! :layout => false, :default_content_type => :css
|
|
724
|
-
render :scss, template, options, locals
|
|
725
|
-
end
|
|
726
|
-
|
|
727
|
-
def less(template, options = {}, locals = {})
|
|
728
|
-
options.merge! :layout => false, :default_content_type => :css
|
|
729
|
-
render :less, template, options, locals
|
|
730
|
-
end
|
|
731
|
-
|
|
732
|
-
def stylus(template, options = {}, locals = {})
|
|
733
|
-
options.merge! :layout => false, :default_content_type => :css
|
|
734
|
-
render :styl, template, options, locals
|
|
735
|
-
end
|
|
736
|
-
|
|
737
745
|
def builder(template = nil, options = {}, locals = {}, &block)
|
|
738
746
|
options[:default_content_type] = :xml
|
|
739
747
|
render_ruby(:builder, template, options, locals, &block)
|
|
@@ -748,10 +756,6 @@ module Sinatra
|
|
|
748
756
|
render :markdown, template, options, locals
|
|
749
757
|
end
|
|
750
758
|
|
|
751
|
-
def textile(template, options = {}, locals = {})
|
|
752
|
-
render :textile, template, options, locals
|
|
753
|
-
end
|
|
754
|
-
|
|
755
759
|
def rdoc(template, options = {}, locals = {})
|
|
756
760
|
render :rdoc, template, options, locals
|
|
757
761
|
end
|
|
@@ -760,19 +764,10 @@ module Sinatra
|
|
|
760
764
|
render :asciidoc, template, options, locals
|
|
761
765
|
end
|
|
762
766
|
|
|
763
|
-
def radius(template, options = {}, locals = {})
|
|
764
|
-
render :radius, template, options, locals
|
|
765
|
-
end
|
|
766
|
-
|
|
767
767
|
def markaby(template = nil, options = {}, locals = {}, &block)
|
|
768
768
|
render_ruby(:mab, template, options, locals, &block)
|
|
769
769
|
end
|
|
770
770
|
|
|
771
|
-
def coffee(template, options = {}, locals = {})
|
|
772
|
-
options.merge! :layout => false, :default_content_type => :js
|
|
773
|
-
render :coffee, template, options, locals
|
|
774
|
-
end
|
|
775
|
-
|
|
776
771
|
def nokogiri(template = nil, options = {}, locals = {}, &block)
|
|
777
772
|
options[:default_content_type] = :xml
|
|
778
773
|
render_ruby(:nokogiri, template, options, locals, &block)
|
|
@@ -782,18 +777,6 @@ module Sinatra
|
|
|
782
777
|
render(:slim, template, options, locals, &block)
|
|
783
778
|
end
|
|
784
779
|
|
|
785
|
-
def creole(template, options = {}, locals = {})
|
|
786
|
-
render :creole, template, options, locals
|
|
787
|
-
end
|
|
788
|
-
|
|
789
|
-
def mediawiki(template, options = {}, locals = {})
|
|
790
|
-
render :mediawiki, template, options, locals
|
|
791
|
-
end
|
|
792
|
-
|
|
793
|
-
def wlang(template, options = {}, locals = {}, &block)
|
|
794
|
-
render(:wlang, template, options, locals, &block)
|
|
795
|
-
end
|
|
796
|
-
|
|
797
780
|
def yajl(template, options = {}, locals = {})
|
|
798
781
|
options[:default_content_type] = :json
|
|
799
782
|
render :yajl, template, options, locals
|
|
@@ -818,24 +801,27 @@ module Sinatra
|
|
|
818
801
|
|
|
819
802
|
# logic shared between builder and nokogiri
|
|
820
803
|
def render_ruby(engine, template, options = {}, locals = {}, &block)
|
|
821
|
-
|
|
822
|
-
|
|
804
|
+
if template.is_a?(Hash)
|
|
805
|
+
options = template
|
|
806
|
+
template = nil
|
|
807
|
+
end
|
|
808
|
+
template = proc { block } if template.nil?
|
|
823
809
|
render engine, template, options, locals
|
|
824
810
|
end
|
|
825
811
|
|
|
826
812
|
def render(engine, data, options = {}, locals = {}, &block)
|
|
827
813
|
# merge app-level options
|
|
828
814
|
engine_options = settings.respond_to?(engine) ? settings.send(engine) : {}
|
|
829
|
-
options.merge!(engine_options) { |
|
|
815
|
+
options.merge!(engine_options) { |_key, v1, _v2| v1 }
|
|
830
816
|
|
|
831
817
|
# extract generic options
|
|
832
818
|
locals = options.delete(:locals) || locals || {}
|
|
833
|
-
views = options.delete(:views) || settings.views ||
|
|
819
|
+
views = options.delete(:views) || settings.views || './views'
|
|
834
820
|
layout = options[:layout]
|
|
835
821
|
layout = false if layout.nil? && options.include?(:layout)
|
|
836
822
|
eat_errors = layout.nil?
|
|
837
|
-
layout = engine_options[:layout] if layout.nil?
|
|
838
|
-
layout = @default_layout if layout.nil?
|
|
823
|
+
layout = engine_options[:layout] if layout.nil? || (layout == true && engine_options[:layout] != false)
|
|
824
|
+
layout = @default_layout if layout.nil? || (layout == true)
|
|
839
825
|
layout_options = options.delete(:layout_options) || {}
|
|
840
826
|
content_type = options.delete(:default_content_type)
|
|
841
827
|
content_type = options.delete(:content_type) || content_type
|
|
@@ -860,8 +846,9 @@ module Sinatra
|
|
|
860
846
|
|
|
861
847
|
# render layout
|
|
862
848
|
if layout
|
|
863
|
-
|
|
864
|
-
|
|
849
|
+
extra_options = { views: views, layout: false, eat_errors: eat_errors, scope: scope }
|
|
850
|
+
options = options.merge(extra_options).merge!(layout_options)
|
|
851
|
+
|
|
865
852
|
catch(:layout_missing) { return render(layout_engine, layout, options, locals) { output } }
|
|
866
853
|
end
|
|
867
854
|
|
|
@@ -886,12 +873,13 @@ module Sinatra
|
|
|
886
873
|
@preferred_extension = engine.to_s
|
|
887
874
|
find_template(views, data, template) do |file|
|
|
888
875
|
path ||= file # keep the initial path rather than the last one
|
|
889
|
-
|
|
876
|
+
found = File.exist?(file)
|
|
877
|
+
if found
|
|
890
878
|
path = file
|
|
891
879
|
break
|
|
892
880
|
end
|
|
893
881
|
end
|
|
894
|
-
throw :layout_missing if eat_errors
|
|
882
|
+
throw :layout_missing if eat_errors && !found
|
|
895
883
|
template.new(path, 1, options)
|
|
896
884
|
end
|
|
897
885
|
end
|
|
@@ -907,9 +895,11 @@ module Sinatra
|
|
|
907
895
|
end
|
|
908
896
|
|
|
909
897
|
def compile_block_template(template, options, &body)
|
|
910
|
-
|
|
911
|
-
path =
|
|
912
|
-
line =
|
|
898
|
+
first_location = caller_locations.first
|
|
899
|
+
path = first_location.path
|
|
900
|
+
line = first_location.lineno
|
|
901
|
+
path = options[:path] || path
|
|
902
|
+
line = options[:line] || line
|
|
913
903
|
template.new(path, line.to_i, options, &body)
|
|
914
904
|
end
|
|
915
905
|
end
|
|
@@ -925,7 +915,7 @@ module Sinatra
|
|
|
925
915
|
attr_accessor :app, :env, :request, :response, :params
|
|
926
916
|
attr_reader :template_cache
|
|
927
917
|
|
|
928
|
-
def initialize(app = nil, **
|
|
918
|
+
def initialize(app = nil, **_kwargs)
|
|
929
919
|
super()
|
|
930
920
|
@app = app
|
|
931
921
|
@template_cache = Tilt::Cache.new
|
|
@@ -952,7 +942,7 @@ module Sinatra
|
|
|
952
942
|
unless @response['Content-Type']
|
|
953
943
|
if Array === body && body[0].respond_to?(:content_type)
|
|
954
944
|
content_type body[0].content_type
|
|
955
|
-
elsif default = settings.default_content_type
|
|
945
|
+
elsif (default = settings.default_content_type)
|
|
956
946
|
content_type default
|
|
957
947
|
end
|
|
958
948
|
end
|
|
@@ -970,12 +960,6 @@ module Sinatra
|
|
|
970
960
|
self.class.settings
|
|
971
961
|
end
|
|
972
962
|
|
|
973
|
-
def options
|
|
974
|
-
warn "Sinatra::Base#options is deprecated and will be removed, " \
|
|
975
|
-
"use #settings instead."
|
|
976
|
-
settings
|
|
977
|
-
end
|
|
978
|
-
|
|
979
963
|
# Exit the current block, halts any further processing
|
|
980
964
|
# of the request, and returns the specified response.
|
|
981
965
|
def halt(*response)
|
|
@@ -992,7 +976,8 @@ module Sinatra
|
|
|
992
976
|
|
|
993
977
|
# Forward the request to the downstream app -- middleware only.
|
|
994
978
|
def forward
|
|
995
|
-
|
|
979
|
+
raise 'downstream app not set' unless @app.respond_to? :call
|
|
980
|
+
|
|
996
981
|
status, headers, body = @app.call env
|
|
997
982
|
@response.status = status
|
|
998
983
|
@response.body = body
|
|
@@ -1014,18 +999,18 @@ module Sinatra
|
|
|
1014
999
|
|
|
1015
1000
|
# Run routes defined on the class and all superclasses.
|
|
1016
1001
|
def route!(base = settings, pass_block = nil)
|
|
1017
|
-
|
|
1018
|
-
routes.each do |pattern, conditions, block|
|
|
1019
|
-
response.delete_header('Content-Type') unless @pinned_response
|
|
1002
|
+
routes = base.routes[@request.request_method]
|
|
1020
1003
|
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
route_eval { block[*args] }
|
|
1024
|
-
end
|
|
1004
|
+
routes&.each do |pattern, conditions, block|
|
|
1005
|
+
response.delete_header('Content-Type') unless @pinned_response
|
|
1025
1006
|
|
|
1026
|
-
|
|
1027
|
-
|
|
1007
|
+
returned_pass_block = process_route(pattern, conditions) do |*args|
|
|
1008
|
+
env['sinatra.route'] = "#{@request.request_method} #{pattern}"
|
|
1009
|
+
route_eval { block[*args] }
|
|
1028
1010
|
end
|
|
1011
|
+
|
|
1012
|
+
# don't wipe out pass_block in superclass
|
|
1013
|
+
pass_block = returned_pass_block if returned_pass_block
|
|
1029
1014
|
end
|
|
1030
1015
|
|
|
1031
1016
|
# Run routes defined in superclass.
|
|
@@ -1049,15 +1034,17 @@ module Sinatra
|
|
|
1049
1034
|
# Returns pass block.
|
|
1050
1035
|
def process_route(pattern, conditions, block = nil, values = [])
|
|
1051
1036
|
route = @request.path_info
|
|
1052
|
-
route = '/' if route.empty?
|
|
1037
|
+
route = '/' if route.empty? && !settings.empty_path_info?
|
|
1053
1038
|
route = route[0..-2] if !settings.strict_paths? && route != '/' && route.end_with?('/')
|
|
1054
|
-
return unless params = pattern.params(route)
|
|
1055
1039
|
|
|
1056
|
-
params.
|
|
1040
|
+
params = pattern.params(route)
|
|
1041
|
+
return unless params
|
|
1042
|
+
|
|
1043
|
+
params.delete('ignore') # TODO: better params handling, maybe turn it into "smart" object or detect changes
|
|
1057
1044
|
force_encoding(params)
|
|
1058
|
-
@params = @params.merge(params) if params.any?
|
|
1045
|
+
@params = @params.merge(params) { |_k, v1, v2| v2 || v1 } if params.any?
|
|
1059
1046
|
|
|
1060
|
-
regexp_exists = pattern.is_a?(Mustermann::Regular) || (pattern.respond_to?(:patterns) && pattern.patterns.any? {|subpattern| subpattern.is_a?(Mustermann::Regular)}
|
|
1047
|
+
regexp_exists = pattern.is_a?(Mustermann::Regular) || (pattern.respond_to?(:patterns) && pattern.patterns.any? { |subpattern| subpattern.is_a?(Mustermann::Regular) })
|
|
1061
1048
|
if regexp_exists
|
|
1062
1049
|
captures = pattern.match(route).captures.map { |c| URI_INSTANCE.unescape(c) if c }
|
|
1063
1050
|
values += captures
|
|
@@ -1070,7 +1057,7 @@ module Sinatra
|
|
|
1070
1057
|
conditions.each { |c| throw :pass if c.bind(self).call == false }
|
|
1071
1058
|
block ? block[self, values] : yield(self, values)
|
|
1072
1059
|
end
|
|
1073
|
-
rescue
|
|
1060
|
+
rescue StandardError
|
|
1074
1061
|
@env['sinatra.error.params'] = @params
|
|
1075
1062
|
raise
|
|
1076
1063
|
ensure
|
|
@@ -1084,35 +1071,35 @@ module Sinatra
|
|
|
1084
1071
|
# a NotFound exception. Subclasses can override this method to perform
|
|
1085
1072
|
# custom route miss logic.
|
|
1086
1073
|
def route_missing
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
raise NotFound, "#{request.request_method} #{request.path_info}"
|
|
1091
|
-
end
|
|
1074
|
+
raise NotFound unless @app
|
|
1075
|
+
|
|
1076
|
+
forward
|
|
1092
1077
|
end
|
|
1093
1078
|
|
|
1094
1079
|
# Attempt to serve static files from public directory. Throws :halt when
|
|
1095
1080
|
# a matching file is found, returns nil otherwise.
|
|
1096
1081
|
def static!(options = {})
|
|
1097
1082
|
return if (public_dir = settings.public_folder).nil?
|
|
1083
|
+
|
|
1098
1084
|
path = "#{public_dir}#{URI_INSTANCE.unescape(request.path_info)}"
|
|
1099
1085
|
return unless valid_path?(path)
|
|
1100
1086
|
|
|
1101
1087
|
path = File.expand_path(path)
|
|
1102
|
-
return unless path.start_with?(File.expand_path(public_dir)
|
|
1088
|
+
return unless path.start_with?("#{File.expand_path(public_dir)}/")
|
|
1089
|
+
|
|
1103
1090
|
return unless File.file?(path)
|
|
1104
1091
|
|
|
1105
1092
|
env['sinatra.static_file'] = path
|
|
1106
1093
|
cache_control(*settings.static_cache_control) if settings.static_cache_control?
|
|
1107
|
-
send_file path, options.merge(:
|
|
1094
|
+
send_file path, options.merge(disposition: nil)
|
|
1108
1095
|
end
|
|
1109
1096
|
|
|
1110
1097
|
# Run the block with 'throw :halt' support and apply result to the response.
|
|
1111
|
-
def invoke
|
|
1112
|
-
res = catch(:halt)
|
|
1098
|
+
def invoke(&block)
|
|
1099
|
+
res = catch(:halt, &block)
|
|
1113
1100
|
|
|
1114
|
-
res = [res] if Integer === res
|
|
1115
|
-
if Array === res
|
|
1101
|
+
res = [res] if (Integer === res) || (String === res)
|
|
1102
|
+
if (Array === res) && (Integer === res.first)
|
|
1116
1103
|
res = res.dup
|
|
1117
1104
|
status(res.shift)
|
|
1118
1105
|
body(res.pop)
|
|
@@ -1128,6 +1115,7 @@ module Sinatra
|
|
|
1128
1115
|
# Avoid passing frozen string in force_encoding
|
|
1129
1116
|
@params.merge!(@request.params).each do |key, val|
|
|
1130
1117
|
next unless val.respond_to?(:force_encoding)
|
|
1118
|
+
|
|
1131
1119
|
val = val.dup if val.frozen?
|
|
1132
1120
|
@params[key] = force_encoding(val)
|
|
1133
1121
|
end
|
|
@@ -1139,39 +1127,43 @@ module Sinatra
|
|
|
1139
1127
|
end
|
|
1140
1128
|
route!
|
|
1141
1129
|
end
|
|
1142
|
-
rescue ::Exception =>
|
|
1143
|
-
invoke { handle_exception!(
|
|
1130
|
+
rescue ::Exception => e
|
|
1131
|
+
invoke { handle_exception!(e) }
|
|
1144
1132
|
ensure
|
|
1145
1133
|
begin
|
|
1146
1134
|
filter! :after unless env['sinatra.static_file']
|
|
1147
|
-
rescue ::Exception =>
|
|
1148
|
-
invoke { handle_exception!(
|
|
1135
|
+
rescue ::Exception => e
|
|
1136
|
+
invoke { handle_exception!(e) } unless @env['sinatra.error']
|
|
1149
1137
|
end
|
|
1150
1138
|
end
|
|
1151
1139
|
|
|
1152
1140
|
# Error handling during requests.
|
|
1153
1141
|
def handle_exception!(boom)
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1142
|
+
error_params = @env['sinatra.error.params']
|
|
1143
|
+
|
|
1144
|
+
@params = @params.merge(error_params) if error_params
|
|
1145
|
+
|
|
1157
1146
|
@env['sinatra.error'] = boom
|
|
1158
1147
|
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1148
|
+
http_status = if boom.is_a? Sinatra::Error
|
|
1149
|
+
if boom.respond_to? :http_status
|
|
1150
|
+
boom.http_status
|
|
1151
|
+
elsif settings.use_code? && boom.respond_to?(:code)
|
|
1152
|
+
boom.code
|
|
1153
|
+
end
|
|
1154
|
+
end
|
|
1155
|
+
|
|
1156
|
+
http_status = 500 unless http_status&.between?(400, 599)
|
|
1157
|
+
status(http_status)
|
|
1166
1158
|
|
|
1167
1159
|
if server_error?
|
|
1168
1160
|
dump_errors! boom if settings.dump_errors?
|
|
1169
|
-
raise boom if settings.show_exceptions?
|
|
1161
|
+
raise boom if settings.show_exceptions? && (settings.show_exceptions != :after_handler)
|
|
1170
1162
|
elsif not_found?
|
|
1171
1163
|
headers['X-Cascade'] = 'pass' if settings.x_cascade?
|
|
1172
1164
|
end
|
|
1173
1165
|
|
|
1174
|
-
if res = error_block!(boom.class, boom) || error_block!(status, boom)
|
|
1166
|
+
if (res = error_block!(boom.class, boom) || error_block!(status, boom))
|
|
1175
1167
|
return res
|
|
1176
1168
|
end
|
|
1177
1169
|
|
|
@@ -1180,12 +1172,14 @@ module Sinatra
|
|
|
1180
1172
|
body Rack::Utils.escape_html(boom.message)
|
|
1181
1173
|
else
|
|
1182
1174
|
content_type 'text/html'
|
|
1183
|
-
body
|
|
1175
|
+
body "<h1>#{not_found? ? 'Not Found' : 'Bad Request'}</h1>"
|
|
1184
1176
|
end
|
|
1185
1177
|
end
|
|
1186
1178
|
|
|
1187
1179
|
return unless server_error?
|
|
1188
|
-
|
|
1180
|
+
|
|
1181
|
+
raise boom if settings.raise_errors? || settings.show_exceptions?
|
|
1182
|
+
|
|
1189
1183
|
error_block! Exception, boom
|
|
1190
1184
|
end
|
|
1191
1185
|
|
|
@@ -1193,7 +1187,10 @@ module Sinatra
|
|
|
1193
1187
|
def error_block!(key, *block_params)
|
|
1194
1188
|
base = settings
|
|
1195
1189
|
while base.respond_to?(:errors)
|
|
1196
|
-
|
|
1190
|
+
args_array = base.errors[key]
|
|
1191
|
+
|
|
1192
|
+
next base = base.superclass unless args_array
|
|
1193
|
+
|
|
1197
1194
|
args_array.reverse_each do |args|
|
|
1198
1195
|
first = args == args_array.first
|
|
1199
1196
|
args += [block_params]
|
|
@@ -1201,32 +1198,26 @@ module Sinatra
|
|
|
1201
1198
|
return resp unless resp.nil? && !first
|
|
1202
1199
|
end
|
|
1203
1200
|
end
|
|
1204
|
-
return false unless key.respond_to?
|
|
1201
|
+
return false unless key.respond_to?(:superclass) && (key.superclass < Exception)
|
|
1202
|
+
|
|
1205
1203
|
error_block!(key.superclass, *block_params)
|
|
1206
1204
|
end
|
|
1207
1205
|
|
|
1208
1206
|
def dump_errors!(boom)
|
|
1209
|
-
msg = ["#{Time.now.strftime(
|
|
1207
|
+
msg = ["#{Time.now.strftime('%Y-%m-%d %H:%M:%S')} - #{boom.class} - #{boom.message}:", *boom.backtrace].join("\n\t")
|
|
1210
1208
|
@env['rack.errors'].puts(msg)
|
|
1211
1209
|
end
|
|
1212
1210
|
|
|
1213
1211
|
class << self
|
|
1214
1212
|
CALLERS_TO_IGNORE = [ # :nodoc:
|
|
1215
|
-
|
|
1216
|
-
/
|
|
1213
|
+
%r{/sinatra(/(base|main|show_exceptions))?\.rb$}, # all sinatra code
|
|
1214
|
+
%r{lib/tilt.*\.rb$}, # all tilt code
|
|
1217
1215
|
/^\(.*\)$/, # generated code
|
|
1218
|
-
/
|
|
1216
|
+
%r{rubygems/(custom|core_ext/kernel)_require\.rb$}, # rubygems require hacks
|
|
1219
1217
|
/active_support/, # active_support require hacks
|
|
1220
|
-
|
|
1221
|
-
/<internal
|
|
1222
|
-
|
|
1223
|
-
]
|
|
1224
|
-
|
|
1225
|
-
# contrary to what the comment said previously, rubinius never supported this
|
|
1226
|
-
if defined?(RUBY_IGNORE_CALLERS)
|
|
1227
|
-
warn "RUBY_IGNORE_CALLERS is deprecated and will no longer be supported by Sinatra 2.0"
|
|
1228
|
-
CALLERS_TO_IGNORE.concat(RUBY_IGNORE_CALLERS)
|
|
1229
|
-
end
|
|
1218
|
+
%r{bundler(/(?:runtime|inline))?\.rb}, # bundler require hacks
|
|
1219
|
+
/<internal:/ # internal in ruby >= 1.9.2
|
|
1220
|
+
].freeze
|
|
1230
1221
|
|
|
1231
1222
|
attr_reader :routes, :filters, :templates, :errors
|
|
1232
1223
|
|
|
@@ -1235,17 +1226,17 @@ module Sinatra
|
|
|
1235
1226
|
def reset!
|
|
1236
1227
|
@conditions = []
|
|
1237
1228
|
@routes = {}
|
|
1238
|
-
@filters = {:
|
|
1229
|
+
@filters = { before: [], after: [] }
|
|
1239
1230
|
@errors = {}
|
|
1240
1231
|
@middleware = []
|
|
1241
1232
|
@prototype = nil
|
|
1242
1233
|
@extensions = []
|
|
1243
1234
|
|
|
1244
|
-
if superclass.respond_to?(:templates)
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1235
|
+
@templates = if superclass.respond_to?(:templates)
|
|
1236
|
+
Hash.new { |_hash, key| superclass.templates[key] }
|
|
1237
|
+
else
|
|
1238
|
+
{}
|
|
1239
|
+
end
|
|
1249
1240
|
end
|
|
1250
1241
|
|
|
1251
1242
|
# Extension modules registered on this class and all superclasses.
|
|
@@ -1269,16 +1260,21 @@ module Sinatra
|
|
|
1269
1260
|
# Sets an option to the given value. If the value is a proc,
|
|
1270
1261
|
# the proc will be called every time the option is accessed.
|
|
1271
1262
|
def set(option, value = (not_set = true), ignore_setter = false, &block)
|
|
1272
|
-
raise ArgumentError if block
|
|
1273
|
-
|
|
1263
|
+
raise ArgumentError if block && !not_set
|
|
1264
|
+
|
|
1265
|
+
if block
|
|
1266
|
+
value = block
|
|
1267
|
+
not_set = false
|
|
1268
|
+
end
|
|
1274
1269
|
|
|
1275
1270
|
if not_set
|
|
1276
1271
|
raise ArgumentError unless option.respond_to?(:each)
|
|
1277
|
-
|
|
1272
|
+
|
|
1273
|
+
option.each { |k, v| set(k, v) }
|
|
1278
1274
|
return self
|
|
1279
1275
|
end
|
|
1280
1276
|
|
|
1281
|
-
if respond_to?("#{option}=")
|
|
1277
|
+
if respond_to?("#{option}=") && !ignore_setter
|
|
1282
1278
|
return __send__("#{option}=", value)
|
|
1283
1279
|
end
|
|
1284
1280
|
|
|
@@ -1317,7 +1313,7 @@ module Sinatra
|
|
|
1317
1313
|
# class, or an HTTP status code to specify which errors should be
|
|
1318
1314
|
# handled.
|
|
1319
1315
|
def error(*codes, &block)
|
|
1320
|
-
args = compile!
|
|
1316
|
+
args = compile! 'ERROR', /.*/, block
|
|
1321
1317
|
codes = codes.flat_map(&method(:Array))
|
|
1322
1318
|
codes << Exception if codes.empty?
|
|
1323
1319
|
codes << Sinatra::NotFound if codes.include?(404)
|
|
@@ -1343,7 +1339,7 @@ module Sinatra
|
|
|
1343
1339
|
# Load embedded templates from the file; uses the caller's __FILE__
|
|
1344
1340
|
# when no file is specified.
|
|
1345
1341
|
def inline_templates=(file = nil)
|
|
1346
|
-
file = (
|
|
1342
|
+
file = (caller_files.first || File.expand_path($0)) if file.nil? || file == true
|
|
1347
1343
|
|
|
1348
1344
|
begin
|
|
1349
1345
|
io = ::IO.respond_to?(:binread) ? ::IO.binread(file) : ::IO.read(file)
|
|
@@ -1352,23 +1348,24 @@ module Sinatra
|
|
|
1352
1348
|
app, data = nil
|
|
1353
1349
|
end
|
|
1354
1350
|
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1351
|
+
return unless data
|
|
1352
|
+
|
|
1353
|
+
encoding = if app && app =~ /([^\n]*\n)?#[^\n]*coding: *(\S+)/m
|
|
1354
|
+
$2
|
|
1355
|
+
else
|
|
1356
|
+
settings.default_encoding
|
|
1357
|
+
end
|
|
1358
|
+
|
|
1359
|
+
lines = app.count("\n") + 1
|
|
1360
|
+
template = nil
|
|
1361
|
+
force_encoding data, encoding
|
|
1362
|
+
data.each_line do |line|
|
|
1363
|
+
lines += 1
|
|
1364
|
+
if line =~ /^@@\s*(.*\S)\s*$/
|
|
1365
|
+
template = force_encoding(String.new, encoding)
|
|
1366
|
+
templates[$1.to_sym] = [template, file, lines]
|
|
1367
|
+
elsif template
|
|
1368
|
+
template << line
|
|
1372
1369
|
end
|
|
1373
1370
|
end
|
|
1374
1371
|
end
|
|
@@ -1377,8 +1374,10 @@ module Sinatra
|
|
|
1377
1374
|
def mime_type(type, value = nil)
|
|
1378
1375
|
return type if type.nil?
|
|
1379
1376
|
return type.to_s if type.to_s.include?('/')
|
|
1380
|
-
|
|
1377
|
+
|
|
1378
|
+
type = ".#{type}" unless type.to_s[0] == '.'
|
|
1381
1379
|
return Rack::Mime.mime_type(type, nil) unless value
|
|
1380
|
+
|
|
1382
1381
|
Rack::Mime::MIME_TYPES[type] = value
|
|
1383
1382
|
end
|
|
1384
1383
|
|
|
@@ -1387,7 +1386,7 @@ module Sinatra
|
|
|
1387
1386
|
# mime_types :js # => ['application/javascript', 'text/javascript']
|
|
1388
1387
|
def mime_types(type)
|
|
1389
1388
|
type = mime_type type
|
|
1390
|
-
type =~
|
|
1389
|
+
type =~ %r{^application/(xml|javascript)$} ? [type, "text/#{$1}"] : [type]
|
|
1391
1390
|
end
|
|
1392
1391
|
|
|
1393
1392
|
# Define a before filter; runs before all requests within the same
|
|
@@ -1416,7 +1415,7 @@ module Sinatra
|
|
|
1416
1415
|
end
|
|
1417
1416
|
|
|
1418
1417
|
def public=(value)
|
|
1419
|
-
|
|
1418
|
+
warn_for_deprecation ':public is no longer used to avoid overloading Module#public, use :public_folder or :public_dir instead'
|
|
1420
1419
|
set(:public_folder, value)
|
|
1421
1420
|
end
|
|
1422
1421
|
|
|
@@ -1438,14 +1437,21 @@ module Sinatra
|
|
|
1438
1437
|
route('HEAD', path, opts, &block)
|
|
1439
1438
|
end
|
|
1440
1439
|
|
|
1441
|
-
def put(path, opts = {}, &
|
|
1442
|
-
|
|
1443
|
-
def
|
|
1444
|
-
|
|
1445
|
-
def
|
|
1446
|
-
|
|
1447
|
-
def
|
|
1448
|
-
|
|
1440
|
+
def put(path, opts = {}, &block) route 'PUT', path, opts, &block end
|
|
1441
|
+
|
|
1442
|
+
def post(path, opts = {}, &block) route 'POST', path, opts, &block end
|
|
1443
|
+
|
|
1444
|
+
def delete(path, opts = {}, &block) route 'DELETE', path, opts, &block end
|
|
1445
|
+
|
|
1446
|
+
def head(path, opts = {}, &block) route 'HEAD', path, opts, &block end
|
|
1447
|
+
|
|
1448
|
+
def options(path, opts = {}, &block) route 'OPTIONS', path, opts, &block end
|
|
1449
|
+
|
|
1450
|
+
def patch(path, opts = {}, &block) route 'PATCH', path, opts, &block end
|
|
1451
|
+
|
|
1452
|
+
def link(path, opts = {}, &block) route 'LINK', path, opts, &block end
|
|
1453
|
+
|
|
1454
|
+
def unlink(path, opts = {}, &block) route 'UNLINK', path, opts, &block end
|
|
1449
1455
|
|
|
1450
1456
|
# Makes the methods defined in the block and in the Modules given
|
|
1451
1457
|
# in `extensions` available to the handlers and templates
|
|
@@ -1485,37 +1491,39 @@ module Sinatra
|
|
|
1485
1491
|
# Stop the self-hosted server if running.
|
|
1486
1492
|
def quit!
|
|
1487
1493
|
return unless running?
|
|
1494
|
+
|
|
1488
1495
|
# Use Thin's hard #stop! if available, otherwise just #stop.
|
|
1489
1496
|
running_server.respond_to?(:stop!) ? running_server.stop! : running_server.stop
|
|
1490
|
-
|
|
1497
|
+
warn '== Sinatra has ended his set (crowd applauds)' unless suppress_messages?
|
|
1491
1498
|
set :running_server, nil
|
|
1492
1499
|
set :handler_name, nil
|
|
1493
1500
|
end
|
|
1494
1501
|
|
|
1495
|
-
|
|
1502
|
+
alias stop! quit!
|
|
1496
1503
|
|
|
1497
1504
|
# Run the Sinatra app as a self-hosted server using
|
|
1498
|
-
# Puma, Mongrel, or WEBrick (in that order). If given a block, will call
|
|
1505
|
+
# Puma, Falcon, Mongrel, or WEBrick (in that order). If given a block, will call
|
|
1499
1506
|
# with the constructed handler once we have taken the stage.
|
|
1500
1507
|
def run!(options = {}, &block)
|
|
1501
1508
|
return if running?
|
|
1509
|
+
|
|
1502
1510
|
set options
|
|
1503
1511
|
handler = Rack::Handler.pick(server)
|
|
1504
1512
|
handler_name = handler.name.gsub(/.*::/, '')
|
|
1505
1513
|
server_settings = settings.respond_to?(:server_settings) ? settings.server_settings : {}
|
|
1506
|
-
server_settings.merge!(:
|
|
1514
|
+
server_settings.merge!(Port: port, Host: bind)
|
|
1507
1515
|
|
|
1508
1516
|
begin
|
|
1509
1517
|
start_server(handler, server_settings, handler_name, &block)
|
|
1510
1518
|
rescue Errno::EADDRINUSE
|
|
1511
|
-
|
|
1519
|
+
warn "== Someone is already performing on port #{port}!"
|
|
1512
1520
|
raise
|
|
1513
1521
|
ensure
|
|
1514
1522
|
quit!
|
|
1515
1523
|
end
|
|
1516
1524
|
end
|
|
1517
1525
|
|
|
1518
|
-
|
|
1526
|
+
alias start! run!
|
|
1519
1527
|
|
|
1520
1528
|
# Check whether the self-hosted server is running or not.
|
|
1521
1529
|
def running?
|
|
@@ -1533,10 +1541,11 @@ module Sinatra
|
|
|
1533
1541
|
# Create a new instance of the class fronted by its middleware
|
|
1534
1542
|
# pipeline. The object is guaranteed to respond to #call but may not be
|
|
1535
1543
|
# an instance of the class new was called on.
|
|
1536
|
-
def new(*args,
|
|
1537
|
-
instance = new!(*args,
|
|
1544
|
+
def new(*args, &block)
|
|
1545
|
+
instance = new!(*args, &block)
|
|
1538
1546
|
Wrapper.new(build(instance).to_app, instance)
|
|
1539
1547
|
end
|
|
1548
|
+
ruby2_keywords :new if respond_to?(:ruby2_keywords, true)
|
|
1540
1549
|
|
|
1541
1550
|
# Creates a Rack::Builder instance with all the middleware set up and
|
|
1542
1551
|
# the given +app+ as end point.
|
|
@@ -1558,12 +1567,6 @@ module Sinatra
|
|
|
1558
1567
|
cleaned_caller(1).flatten
|
|
1559
1568
|
end
|
|
1560
1569
|
|
|
1561
|
-
# Like caller_files, but containing Arrays rather than strings with the
|
|
1562
|
-
# first element being the file, and the second being the line.
|
|
1563
|
-
def caller_locations
|
|
1564
|
-
cleaned_caller 2
|
|
1565
|
-
end
|
|
1566
|
-
|
|
1567
1570
|
private
|
|
1568
1571
|
|
|
1569
1572
|
# Starts the server by running the Rack Handler.
|
|
@@ -1574,7 +1577,7 @@ module Sinatra
|
|
|
1574
1577
|
# Run the instance we created:
|
|
1575
1578
|
handler.run(self, **server_settings) do |server|
|
|
1576
1579
|
unless suppress_messages?
|
|
1577
|
-
|
|
1580
|
+
warn "== Sinatra (v#{Sinatra::VERSION}) has taken the stage on #{port} for #{environment} with backup from #{handler_name}"
|
|
1578
1581
|
end
|
|
1579
1582
|
|
|
1580
1583
|
setup_traps
|
|
@@ -1591,18 +1594,18 @@ module Sinatra
|
|
|
1591
1594
|
end
|
|
1592
1595
|
|
|
1593
1596
|
def setup_traps
|
|
1594
|
-
|
|
1595
|
-
at_exit { quit! }
|
|
1597
|
+
return unless traps?
|
|
1596
1598
|
|
|
1597
|
-
|
|
1598
|
-
old_handler = trap(signal) do
|
|
1599
|
-
quit!
|
|
1600
|
-
old_handler.call if old_handler.respond_to?(:call)
|
|
1601
|
-
end
|
|
1602
|
-
end
|
|
1599
|
+
at_exit { quit! }
|
|
1603
1600
|
|
|
1604
|
-
|
|
1601
|
+
%i[INT TERM].each do |signal|
|
|
1602
|
+
old_handler = trap(signal) do
|
|
1603
|
+
quit!
|
|
1604
|
+
old_handler.call if old_handler.respond_to?(:call)
|
|
1605
|
+
end
|
|
1605
1606
|
end
|
|
1607
|
+
|
|
1608
|
+
set :traps, false
|
|
1606
1609
|
end
|
|
1607
1610
|
|
|
1608
1611
|
# Dynamically defines a method on settings.
|
|
@@ -1630,18 +1633,21 @@ module Sinatra
|
|
|
1630
1633
|
end
|
|
1631
1634
|
end
|
|
1632
1635
|
end
|
|
1633
|
-
|
|
1636
|
+
alias agent user_agent
|
|
1634
1637
|
|
|
1635
1638
|
# Condition for matching mimetypes. Accepts file extensions.
|
|
1636
1639
|
def provides(*types)
|
|
1637
1640
|
types.map! { |t| mime_types(t) }
|
|
1638
1641
|
types.flatten!
|
|
1639
1642
|
condition do
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1643
|
+
response_content_type = response['Content-Type']
|
|
1644
|
+
preferred_type = request.preferred_type(types)
|
|
1645
|
+
|
|
1646
|
+
if response_content_type
|
|
1647
|
+
types.include?(response_content_type) || types.include?(response_content_type[/^[^;]+/])
|
|
1648
|
+
elsif preferred_type
|
|
1649
|
+
params = (preferred_type.respond_to?(:params) ? preferred_type.params : {})
|
|
1650
|
+
content_type(preferred_type, params)
|
|
1645
1651
|
true
|
|
1646
1652
|
else
|
|
1647
1653
|
false
|
|
@@ -1650,7 +1656,7 @@ module Sinatra
|
|
|
1650
1656
|
end
|
|
1651
1657
|
|
|
1652
1658
|
def route(verb, path, options = {}, &block)
|
|
1653
|
-
enable :empty_path_info if path ==
|
|
1659
|
+
enable :empty_path_info if path == '' && empty_path_info.nil?
|
|
1654
1660
|
signature = compile!(verb, path, block, **options)
|
|
1655
1661
|
(@routes[verb] ||= []) << signature
|
|
1656
1662
|
invoke_hook(:route_added, verb, path, block)
|
|
@@ -1679,12 +1685,13 @@ module Sinatra
|
|
|
1679
1685
|
pattern = compile(path, route_mustermann_opts)
|
|
1680
1686
|
method_name = "#{verb} #{path}"
|
|
1681
1687
|
unbound_method = generate_method(method_name, &block)
|
|
1682
|
-
conditions
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
proc { |a,
|
|
1688
|
+
conditions = @conditions
|
|
1689
|
+
@conditions = []
|
|
1690
|
+
wrapper = block.arity.zero? ?
|
|
1691
|
+
proc { |a, _p| unbound_method.bind(a).call } :
|
|
1692
|
+
proc { |a, p| unbound_method.bind(a).call(*p) }
|
|
1686
1693
|
|
|
1687
|
-
[
|
|
1694
|
+
[pattern, conditions, wrapper]
|
|
1688
1695
|
end
|
|
1689
1696
|
|
|
1690
1697
|
def compile(path, route_mustermann_opts = {})
|
|
@@ -1702,7 +1709,7 @@ module Sinatra
|
|
|
1702
1709
|
end
|
|
1703
1710
|
|
|
1704
1711
|
def setup_middleware(builder)
|
|
1705
|
-
middleware.each { |c,a,b| builder.use(c, *a, &b) }
|
|
1712
|
+
middleware.each { |c, a, b| builder.use(c, *a, &b) }
|
|
1706
1713
|
end
|
|
1707
1714
|
|
|
1708
1715
|
def setup_logging(builder)
|
|
@@ -1732,9 +1739,10 @@ module Sinatra
|
|
|
1732
1739
|
|
|
1733
1740
|
def setup_protection(builder)
|
|
1734
1741
|
return unless protection?
|
|
1742
|
+
|
|
1735
1743
|
options = Hash === protection ? protection.dup : {}
|
|
1736
1744
|
options = {
|
|
1737
|
-
img_src:
|
|
1745
|
+
img_src: "'self' data:",
|
|
1738
1746
|
font_src: "'self'"
|
|
1739
1747
|
}.merge options
|
|
1740
1748
|
|
|
@@ -1748,6 +1756,7 @@ module Sinatra
|
|
|
1748
1756
|
|
|
1749
1757
|
def setup_sessions(builder)
|
|
1750
1758
|
return unless sessions?
|
|
1759
|
+
|
|
1751
1760
|
options = {}
|
|
1752
1761
|
options[:secret] = session_secret if session_secret?
|
|
1753
1762
|
options.merge! sessions.to_hash if sessions.respond_to? :to_hash
|
|
@@ -1770,15 +1779,15 @@ module Sinatra
|
|
|
1770
1779
|
end
|
|
1771
1780
|
|
|
1772
1781
|
# used for deprecation warnings
|
|
1773
|
-
def
|
|
1774
|
-
|
|
1782
|
+
def warn_for_deprecation(message)
|
|
1783
|
+
warn message + "\n\tfrom #{cleaned_caller.first.join(':')}"
|
|
1775
1784
|
end
|
|
1776
1785
|
|
|
1777
1786
|
# Like Kernel#caller but excluding certain magic entries
|
|
1778
1787
|
def cleaned_caller(keep = 3)
|
|
1779
|
-
caller(1)
|
|
1780
|
-
map!
|
|
1781
|
-
reject { |file, *_| CALLERS_TO_IGNORE.any? { |pattern| file =~ pattern } }
|
|
1788
|
+
caller(1)
|
|
1789
|
+
.map! { |line| line.split(/:(?=\d|in )/, 3)[0, keep] }
|
|
1790
|
+
.reject { |file, *_| CALLERS_TO_IGNORE.any? { |pattern| file =~ pattern } }
|
|
1782
1791
|
end
|
|
1783
1792
|
end
|
|
1784
1793
|
|
|
@@ -1786,6 +1795,7 @@ module Sinatra
|
|
|
1786
1795
|
# which is UTF-8 by default
|
|
1787
1796
|
def self.force_encoding(data, encoding = default_encoding)
|
|
1788
1797
|
return if data == settings || data.is_a?(Tempfile)
|
|
1798
|
+
|
|
1789
1799
|
if data.respond_to? :force_encoding
|
|
1790
1800
|
data.force_encoding(encoding).encode!
|
|
1791
1801
|
elsif data.respond_to? :each_value
|
|
@@ -1796,24 +1806,26 @@ module Sinatra
|
|
|
1796
1806
|
data
|
|
1797
1807
|
end
|
|
1798
1808
|
|
|
1799
|
-
def force_encoding(*args)
|
|
1809
|
+
def force_encoding(*args)
|
|
1810
|
+
settings.force_encoding(*args)
|
|
1811
|
+
end
|
|
1800
1812
|
|
|
1801
1813
|
reset!
|
|
1802
1814
|
|
|
1803
1815
|
set :environment, (ENV['APP_ENV'] || ENV['RACK_ENV'] || :development).to_sym
|
|
1804
|
-
set :raise_errors,
|
|
1805
|
-
set :dump_errors,
|
|
1806
|
-
set :show_exceptions,
|
|
1816
|
+
set :raise_errors, proc { test? }
|
|
1817
|
+
set :dump_errors, proc { !test? }
|
|
1818
|
+
set :show_exceptions, proc { development? }
|
|
1807
1819
|
set :sessions, false
|
|
1808
|
-
set :session_store, Rack::
|
|
1820
|
+
set :session_store, Rack::Protection::EncryptedCookie
|
|
1809
1821
|
set :logging, false
|
|
1810
1822
|
set :protection, true
|
|
1811
1823
|
set :method_override, false
|
|
1812
1824
|
set :use_code, false
|
|
1813
|
-
set :default_encoding,
|
|
1825
|
+
set :default_encoding, 'utf-8'
|
|
1814
1826
|
set :x_cascade, true
|
|
1815
1827
|
set :add_charset, %w[javascript xml xhtml+xml].map { |t| "application/#{t}" }
|
|
1816
|
-
settings.add_charset <<
|
|
1828
|
+
settings.add_charset << %r{^text/}
|
|
1817
1829
|
set :mustermann_opts, {}
|
|
1818
1830
|
set :default_content_type, 'text/html'
|
|
1819
1831
|
|
|
@@ -1823,12 +1835,12 @@ module Sinatra
|
|
|
1823
1835
|
set :session_secret, SecureRandom.hex(64)
|
|
1824
1836
|
rescue LoadError, NotImplementedError
|
|
1825
1837
|
# SecureRandom raises a NotImplementedError if no random device is available
|
|
1826
|
-
set :session_secret,
|
|
1838
|
+
set :session_secret, format('%064x', Kernel.rand((2**256) - 1))
|
|
1827
1839
|
end
|
|
1828
1840
|
|
|
1829
1841
|
class << self
|
|
1830
|
-
|
|
1831
|
-
|
|
1842
|
+
alias methodoverride? method_override?
|
|
1843
|
+
alias methodoverride= method_override=
|
|
1832
1844
|
end
|
|
1833
1845
|
|
|
1834
1846
|
set :run, false # start server via at-exit hook?
|
|
@@ -1836,21 +1848,17 @@ module Sinatra
|
|
|
1836
1848
|
set :handler_name, nil
|
|
1837
1849
|
set :traps, true
|
|
1838
1850
|
set :server, %w[HTTP webrick]
|
|
1839
|
-
set :bind,
|
|
1851
|
+
set :bind, proc { development? ? 'localhost' : '0.0.0.0' }
|
|
1840
1852
|
set :port, Integer(ENV['PORT'] && !ENV['PORT'].empty? ? ENV['PORT'] : 4567)
|
|
1841
1853
|
set :quiet, false
|
|
1842
1854
|
|
|
1843
1855
|
ruby_engine = defined?(RUBY_ENGINE) && RUBY_ENGINE
|
|
1844
1856
|
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
server.unshift 'mongrel' if ruby_engine.nil?
|
|
1851
|
-
server.unshift 'thin' if ruby_engine != 'jruby'
|
|
1852
|
-
server.unshift 'trinidad' if ruby_engine == 'jruby'
|
|
1853
|
-
end
|
|
1857
|
+
server.unshift 'puma'
|
|
1858
|
+
server.unshift 'falcon' if ruby_engine != 'jruby'
|
|
1859
|
+
server.unshift 'mongrel' if ruby_engine.nil?
|
|
1860
|
+
server.unshift 'thin' if ruby_engine != 'jruby'
|
|
1861
|
+
server.unshift 'trinidad' if ruby_engine == 'jruby'
|
|
1854
1862
|
|
|
1855
1863
|
set :absolute_redirects, true
|
|
1856
1864
|
set :prefixed_redirects, false
|
|
@@ -1858,14 +1866,14 @@ module Sinatra
|
|
|
1858
1866
|
set :strict_paths, true
|
|
1859
1867
|
|
|
1860
1868
|
set :app_file, nil
|
|
1861
|
-
set :root,
|
|
1862
|
-
set :views,
|
|
1863
|
-
set :reload_templates,
|
|
1869
|
+
set :root, proc { app_file && File.expand_path(File.dirname(app_file)) }
|
|
1870
|
+
set :views, proc { root && File.join(root, 'views') }
|
|
1871
|
+
set :reload_templates, proc { development? }
|
|
1864
1872
|
set :lock, false
|
|
1865
1873
|
set :threaded, true
|
|
1866
1874
|
|
|
1867
|
-
set :public_folder,
|
|
1868
|
-
set :static,
|
|
1875
|
+
set :public_folder, proc { root && File.join(root, 'public') }
|
|
1876
|
+
set :static, proc { public_folder && File.exist?(public_folder) }
|
|
1869
1877
|
set :static_cache_control, false
|
|
1870
1878
|
|
|
1871
1879
|
error ::Exception do
|
|
@@ -1884,7 +1892,7 @@ module Sinatra
|
|
|
1884
1892
|
error NotFound do
|
|
1885
1893
|
content_type 'text/html'
|
|
1886
1894
|
|
|
1887
|
-
if
|
|
1895
|
+
if instance_of?(Sinatra::Application)
|
|
1888
1896
|
code = <<-RUBY.gsub(/^ {12}/, '')
|
|
1889
1897
|
#{request.request_method.downcase} '#{request.path_info}' do
|
|
1890
1898
|
"Hello World"
|
|
@@ -1899,11 +1907,11 @@ module Sinatra
|
|
|
1899
1907
|
end
|
|
1900
1908
|
RUBY
|
|
1901
1909
|
|
|
1902
|
-
file = settings.app_file.to_s.sub(settings.root.to_s, '').sub(
|
|
1910
|
+
file = settings.app_file.to_s.sub(settings.root.to_s, '').sub(%r{^/}, '')
|
|
1903
1911
|
code = "# in #{file}\n#{code}" unless file.empty?
|
|
1904
1912
|
end
|
|
1905
1913
|
|
|
1906
|
-
|
|
1914
|
+
<<-HTML.gsub(/^ {10}/, '')
|
|
1907
1915
|
<!DOCTYPE html>
|
|
1908
1916
|
<html>
|
|
1909
1917
|
<head>
|
|
@@ -1915,7 +1923,7 @@ module Sinatra
|
|
|
1915
1923
|
</head>
|
|
1916
1924
|
<body>
|
|
1917
1925
|
<h2>Sinatra doesn’t know this ditty.</h2>
|
|
1918
|
-
<img src='#{uri
|
|
1926
|
+
<img src='#{uri '/__sinatra__/404.png'}'>
|
|
1919
1927
|
<div id="c">
|
|
1920
1928
|
Try this:
|
|
1921
1929
|
<pre>#{Rack::Utils.escape_html(code)}</pre>
|
|
@@ -1935,12 +1943,12 @@ module Sinatra
|
|
|
1935
1943
|
# top-level. Subclassing Sinatra::Base is highly recommended for
|
|
1936
1944
|
# modular applications.
|
|
1937
1945
|
class Application < Base
|
|
1938
|
-
set :logging,
|
|
1946
|
+
set :logging, proc { !test? }
|
|
1939
1947
|
set :method_override, true
|
|
1940
|
-
set :run,
|
|
1948
|
+
set :run, proc { !test? }
|
|
1941
1949
|
set :app_file, nil
|
|
1942
1950
|
|
|
1943
|
-
def self.register(*extensions, &block)
|
|
1951
|
+
def self.register(*extensions, &block) # :nodoc:
|
|
1944
1952
|
added_methods = extensions.flat_map(&:public_instance_methods)
|
|
1945
1953
|
Delegator.delegate(*added_methods)
|
|
1946
1954
|
super(*extensions, &block)
|
|
@@ -1950,11 +1958,12 @@ module Sinatra
|
|
|
1950
1958
|
# Sinatra delegation mixin. Mixing this module into an object causes all
|
|
1951
1959
|
# methods to be delegated to the Sinatra::Application class. Used primarily
|
|
1952
1960
|
# at the top-level.
|
|
1953
|
-
module Delegator
|
|
1961
|
+
module Delegator # :nodoc:
|
|
1954
1962
|
def self.delegate(*methods)
|
|
1955
1963
|
methods.each do |method_name|
|
|
1956
1964
|
define_method(method_name) do |*args, &block|
|
|
1957
1965
|
return super(*args, &block) if respond_to? method_name
|
|
1966
|
+
|
|
1958
1967
|
Delegator.target.send(method_name, *args, &block)
|
|
1959
1968
|
end
|
|
1960
1969
|
# ensure keyword argument passing is compatible with ruby >= 2.7
|
|
@@ -1977,7 +1986,8 @@ module Sinatra
|
|
|
1977
1986
|
|
|
1978
1987
|
class Wrapper
|
|
1979
1988
|
def initialize(stack, instance)
|
|
1980
|
-
@stack
|
|
1989
|
+
@stack = stack
|
|
1990
|
+
@instance = instance
|
|
1981
1991
|
end
|
|
1982
1992
|
|
|
1983
1993
|
def settings
|