sinatra 2.2.3 → 3.0.6
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +101 -17
- data/CONTRIBUTING.md +11 -11
- data/Gemfile +44 -64
- data/MAINTENANCE.md +2 -2
- data/README.md +116 -414
- data/Rakefile +66 -75
- data/VERSION +1 -1
- data/examples/chat.rb +25 -12
- data/examples/rainbows.rb +3 -1
- data/examples/simple.rb +2 -0
- data/examples/stream.ru +2 -0
- data/lib/sinatra/base.rb +354 -336
- 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 +34 -26
- 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
|
|
@@ -21,21 +19,22 @@ require 'sinatra/version'
|
|
21
19
|
|
22
20
|
module Sinatra
|
23
21
|
# The request object. See Rack::Request for more info:
|
24
|
-
#
|
22
|
+
# https://rubydoc.info/github/rack/rack/main/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,17 +151,17 @@ 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
|
157
158
|
|
158
159
|
# The response object. See Rack::Response and Rack::Response::Helpers for
|
159
160
|
# more info:
|
160
|
-
#
|
161
|
-
#
|
161
|
+
# https://rubydoc.info/github/rack/rack/main/Rack/Response
|
162
|
+
# https://rubydoc.info/github/rack/rack/main/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
|
|
@@ -282,7 +291,7 @@ module Sinatra
|
|
282
291
|
elsif value
|
283
292
|
# Rack 2.0 returns a Rack::File::Iterator here instead of
|
284
293
|
# Rack::File as it was in the previous API.
|
285
|
-
unless request.head? || value.is_a?(Rack::
|
294
|
+
unless request.head? || value.is_a?(Rack::Files::Iterator) || value.is_a?(Stream)
|
286
295
|
headers.delete 'Content-Length'
|
287
296
|
end
|
288
297
|
response.body = value
|
@@ -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.to_s =~ /\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'
|
@@ -402,22 +417,22 @@ module Sinatra
|
|
402
417
|
|
403
418
|
# Use the contents of the file at +path+ as the response body.
|
404
419
|
def send_file(path, opts = {})
|
405
|
-
if opts[:type]
|
406
|
-
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'
|
407
422
|
end
|
408
423
|
|
409
424
|
disposition = opts[:disposition]
|
410
425
|
filename = opts[:filename]
|
411
|
-
disposition = :attachment if disposition.nil?
|
426
|
+
disposition = :attachment if disposition.nil? && filename
|
412
427
|
filename = path if filename.nil?
|
413
428
|
attachment(filename, disposition) if disposition
|
414
429
|
|
415
430
|
last_modified opts[:last_modified] if opts[:last_modified]
|
416
431
|
|
417
|
-
file = Rack::
|
432
|
+
file = Rack::Files.new(File.dirname(settings.app_file))
|
418
433
|
result = file.serving(request, path)
|
419
434
|
|
420
|
-
result[1].each { |k,v| headers[k] ||= v }
|
435
|
+
result[1].each { |k, v| headers[k] ||= v }
|
421
436
|
headers['Content-Length'] = result[1]['Content-Length']
|
422
437
|
opts[:status] &&= Integer(opts[:status])
|
423
438
|
halt (opts[:status] || result[0]), result[2]
|
@@ -438,12 +453,16 @@ module Sinatra
|
|
438
453
|
def self.defer(*) yield end
|
439
454
|
|
440
455
|
def initialize(scheduler = self.class, keep_open = false, &back)
|
441
|
-
@back
|
442
|
-
@
|
456
|
+
@back = back.to_proc
|
457
|
+
@scheduler = scheduler
|
458
|
+
@keep_open = keep_open
|
459
|
+
@callbacks = []
|
460
|
+
@closed = false
|
443
461
|
end
|
444
462
|
|
445
463
|
def close
|
446
464
|
return if closed?
|
465
|
+
|
447
466
|
@closed = true
|
448
467
|
@scheduler.schedule { @callbacks.each { |c| c.call } }
|
449
468
|
end
|
@@ -455,8 +474,9 @@ module Sinatra
|
|
455
474
|
@back.call(self)
|
456
475
|
rescue Exception => e
|
457
476
|
@scheduler.schedule { raise e }
|
477
|
+
ensure
|
478
|
+
close unless @keep_open
|
458
479
|
end
|
459
|
-
close unless @keep_open
|
460
480
|
end
|
461
481
|
end
|
462
482
|
|
@@ -467,6 +487,7 @@ module Sinatra
|
|
467
487
|
|
468
488
|
def callback(&block)
|
469
489
|
return yield if closed?
|
490
|
+
|
470
491
|
@callbacks << block
|
471
492
|
end
|
472
493
|
|
@@ -486,7 +507,16 @@ module Sinatra
|
|
486
507
|
def stream(keep_open = false)
|
487
508
|
scheduler = env['async.callback'] ? EventMachine : Stream
|
488
509
|
current = @params.dup
|
489
|
-
|
510
|
+
stream = if scheduler == Stream && keep_open
|
511
|
+
Stream.new(scheduler, false) do |out|
|
512
|
+
until out.closed?
|
513
|
+
with_params(current) { yield(out) }
|
514
|
+
end
|
515
|
+
end
|
516
|
+
else
|
517
|
+
Stream.new(scheduler, keep_open) { |out| with_params(current) { yield(out) } }
|
518
|
+
end
|
519
|
+
body stream
|
490
520
|
end
|
491
521
|
|
492
522
|
# Specify response freshness policy for HTTP caches (Cache-Control header).
|
@@ -500,18 +530,18 @@ module Sinatra
|
|
500
530
|
# See RFC 2616 / 14.9 for more on standard cache control directives:
|
501
531
|
# http://tools.ietf.org/html/rfc2616#section-14.9.1
|
502
532
|
def cache_control(*values)
|
503
|
-
if values.last.
|
533
|
+
if values.last.is_a?(Hash)
|
504
534
|
hash = values.pop
|
505
|
-
hash.reject! { |
|
535
|
+
hash.reject! { |_k, v| v == false }
|
506
536
|
hash.reject! { |k, v| values << k if v == true }
|
507
537
|
else
|
508
538
|
hash = {}
|
509
539
|
end
|
510
540
|
|
511
|
-
values.map! { |value| value.to_s.tr('_','-') }
|
541
|
+
values.map! { |value| value.to_s.tr('_', '-') }
|
512
542
|
hash.each do |key, value|
|
513
543
|
key = key.to_s.tr('_', '-')
|
514
|
-
value = value.to_i if [
|
544
|
+
value = value.to_i if %w[max-age s-maxage].include? key
|
515
545
|
values << "#{key}=#{value}"
|
516
546
|
end
|
517
547
|
|
@@ -528,7 +558,7 @@ module Sinatra
|
|
528
558
|
# => Expires: Mon, 08 Jun 2009 08:50:17 GMT
|
529
559
|
#
|
530
560
|
def expires(amount, *values)
|
531
|
-
values << {} unless values.last.
|
561
|
+
values << {} unless values.last.is_a?(Hash)
|
532
562
|
|
533
563
|
if amount.is_a? Integer
|
534
564
|
time = Time.now + amount.to_i
|
@@ -538,7 +568,7 @@ module Sinatra
|
|
538
568
|
max_age = time - Time.now
|
539
569
|
end
|
540
570
|
|
541
|
-
values.last.merge!(:max_age
|
571
|
+
values.last.merge!(max_age: max_age) { |_key, v1, v2| v1 || v2 }
|
542
572
|
cache_control(*values)
|
543
573
|
|
544
574
|
response['Expires'] = time.httpdate
|
@@ -553,17 +583,18 @@ module Sinatra
|
|
553
583
|
# with a '304 Not Modified' response.
|
554
584
|
def last_modified(time)
|
555
585
|
return unless time
|
586
|
+
|
556
587
|
time = time_for time
|
557
588
|
response['Last-Modified'] = time.httpdate
|
558
589
|
return if env['HTTP_IF_NONE_MATCH']
|
559
590
|
|
560
|
-
if status == 200
|
591
|
+
if (status == 200) && env['HTTP_IF_MODIFIED_SINCE']
|
561
592
|
# compare based on seconds since epoch
|
562
593
|
since = Time.httpdate(env['HTTP_IF_MODIFIED_SINCE']).to_i
|
563
594
|
halt 304 if since >= time.to_i
|
564
595
|
end
|
565
596
|
|
566
|
-
if (success?
|
597
|
+
if (success? || (status == 412)) && env['HTTP_IF_UNMODIFIED_SINCE']
|
567
598
|
# compare based on seconds since epoch
|
568
599
|
since = Time.httpdate(env['HTTP_IF_UNMODIFIED_SINCE']).to_i
|
569
600
|
halt 412 if since < time.to_i
|
@@ -571,7 +602,7 @@ module Sinatra
|
|
571
602
|
rescue ArgumentError
|
572
603
|
end
|
573
604
|
|
574
|
-
ETAG_KINDS = [
|
605
|
+
ETAG_KINDS = %i[strong weak].freeze
|
575
606
|
# Set the response entity tag (HTTP 'ETag' header) and halt if conditional
|
576
607
|
# GET matches. The +value+ argument is an identifier that uniquely
|
577
608
|
# identifies the current version of the resource. The +kind+ argument
|
@@ -583,27 +614,31 @@ module Sinatra
|
|
583
614
|
# GET or HEAD, a '304 Not Modified' response is sent.
|
584
615
|
def etag(value, options = {})
|
585
616
|
# Before touching this code, please double check RFC 2616 14.24 and 14.26.
|
586
|
-
options = {:
|
617
|
+
options = { kind: options } unless Hash === options
|
587
618
|
kind = options[:kind] || :strong
|
588
619
|
new_resource = options.fetch(:new_resource) { request.post? }
|
589
620
|
|
590
621
|
unless ETAG_KINDS.include?(kind)
|
591
|
-
raise ArgumentError,
|
622
|
+
raise ArgumentError, ':strong or :weak expected'
|
592
623
|
end
|
593
624
|
|
594
|
-
value = '"%s"'
|
625
|
+
value = format('"%s"', value)
|
595
626
|
value = "W/#{value}" if kind == :weak
|
596
627
|
response['ETag'] = value
|
597
628
|
|
598
|
-
|
599
|
-
if etag_matches? env['HTTP_IF_NONE_MATCH'], new_resource
|
600
|
-
halt(request.safe? ? 304 : 412)
|
601
|
-
end
|
629
|
+
return unless success? || status == 304
|
602
630
|
|
603
|
-
|
604
|
-
|
605
|
-
|
631
|
+
if etag_matches?(env['HTTP_IF_NONE_MATCH'], new_resource)
|
632
|
+
halt(request.safe? ? 304 : 412)
|
633
|
+
end
|
634
|
+
|
635
|
+
if env['HTTP_IF_MATCH']
|
636
|
+
return if etag_matches?(env['HTTP_IF_MATCH'], new_resource)
|
637
|
+
|
638
|
+
halt 412
|
606
639
|
end
|
640
|
+
|
641
|
+
nil
|
607
642
|
end
|
608
643
|
|
609
644
|
# Sugar for redirect (example: redirect back)
|
@@ -656,8 +691,8 @@ module Sinatra
|
|
656
691
|
else
|
657
692
|
value.to_time
|
658
693
|
end
|
659
|
-
rescue ArgumentError =>
|
660
|
-
raise
|
694
|
+
rescue ArgumentError => e
|
695
|
+
raise e
|
661
696
|
rescue Exception
|
662
697
|
raise ArgumentError, "unable to convert #{value.inspect} to a Time object"
|
663
698
|
end
|
@@ -667,11 +702,13 @@ module Sinatra
|
|
667
702
|
# Helper method checking if a ETag value list includes the current ETag.
|
668
703
|
def etag_matches?(list, new_resource = request.post?)
|
669
704
|
return !new_resource if list == '*'
|
705
|
+
|
670
706
|
list.to_s.split(/\s*,\s*/).include? response['ETag']
|
671
707
|
end
|
672
708
|
|
673
709
|
def with_params(temp_params)
|
674
|
-
original
|
710
|
+
original = @params
|
711
|
+
@params = temp_params
|
675
712
|
yield
|
676
713
|
ensure
|
677
714
|
@params = original if original
|
@@ -689,7 +726,7 @@ module Sinatra
|
|
689
726
|
# Possible options are:
|
690
727
|
# :content_type The content type to use, same arguments as content_type.
|
691
728
|
# :layout If set to something falsy, no layout is rendered, otherwise
|
692
|
-
# the specified layout is used
|
729
|
+
# the specified layout is used
|
693
730
|
# :layout_engine Engine to use for rendering the layout.
|
694
731
|
# :locals A hash with local variables that should be available
|
695
732
|
# in the template
|
@@ -711,36 +748,10 @@ module Sinatra
|
|
711
748
|
render(:erb, template, options, locals, &block)
|
712
749
|
end
|
713
750
|
|
714
|
-
def erubis(template, options = {}, locals = {})
|
715
|
-
warn "Sinatra::Templates#erubis is deprecated and will be removed, use #erb instead.\n" \
|
716
|
-
"If you have Erubis installed, it will be used automatically."
|
717
|
-
render :erubis, template, options, locals
|
718
|
-
end
|
719
|
-
|
720
751
|
def haml(template, options = {}, locals = {}, &block)
|
721
752
|
render(:haml, template, options, locals, &block)
|
722
753
|
end
|
723
754
|
|
724
|
-
def sass(template, options = {}, locals = {})
|
725
|
-
options.merge! :layout => false, :default_content_type => :css
|
726
|
-
render :sass, template, options, locals
|
727
|
-
end
|
728
|
-
|
729
|
-
def scss(template, options = {}, locals = {})
|
730
|
-
options.merge! :layout => false, :default_content_type => :css
|
731
|
-
render :scss, template, options, locals
|
732
|
-
end
|
733
|
-
|
734
|
-
def less(template, options = {}, locals = {})
|
735
|
-
options.merge! :layout => false, :default_content_type => :css
|
736
|
-
render :less, template, options, locals
|
737
|
-
end
|
738
|
-
|
739
|
-
def stylus(template, options = {}, locals = {})
|
740
|
-
options.merge! :layout => false, :default_content_type => :css
|
741
|
-
render :styl, template, options, locals
|
742
|
-
end
|
743
|
-
|
744
755
|
def builder(template = nil, options = {}, locals = {}, &block)
|
745
756
|
options[:default_content_type] = :xml
|
746
757
|
render_ruby(:builder, template, options, locals, &block)
|
@@ -755,10 +766,6 @@ module Sinatra
|
|
755
766
|
render :markdown, template, options, locals
|
756
767
|
end
|
757
768
|
|
758
|
-
def textile(template, options = {}, locals = {})
|
759
|
-
render :textile, template, options, locals
|
760
|
-
end
|
761
|
-
|
762
769
|
def rdoc(template, options = {}, locals = {})
|
763
770
|
render :rdoc, template, options, locals
|
764
771
|
end
|
@@ -767,19 +774,10 @@ module Sinatra
|
|
767
774
|
render :asciidoc, template, options, locals
|
768
775
|
end
|
769
776
|
|
770
|
-
def radius(template, options = {}, locals = {})
|
771
|
-
render :radius, template, options, locals
|
772
|
-
end
|
773
|
-
|
774
777
|
def markaby(template = nil, options = {}, locals = {}, &block)
|
775
778
|
render_ruby(:mab, template, options, locals, &block)
|
776
779
|
end
|
777
780
|
|
778
|
-
def coffee(template, options = {}, locals = {})
|
779
|
-
options.merge! :layout => false, :default_content_type => :js
|
780
|
-
render :coffee, template, options, locals
|
781
|
-
end
|
782
|
-
|
783
781
|
def nokogiri(template = nil, options = {}, locals = {}, &block)
|
784
782
|
options[:default_content_type] = :xml
|
785
783
|
render_ruby(:nokogiri, template, options, locals, &block)
|
@@ -789,18 +787,6 @@ module Sinatra
|
|
789
787
|
render(:slim, template, options, locals, &block)
|
790
788
|
end
|
791
789
|
|
792
|
-
def creole(template, options = {}, locals = {})
|
793
|
-
render :creole, template, options, locals
|
794
|
-
end
|
795
|
-
|
796
|
-
def mediawiki(template, options = {}, locals = {})
|
797
|
-
render :mediawiki, template, options, locals
|
798
|
-
end
|
799
|
-
|
800
|
-
def wlang(template, options = {}, locals = {}, &block)
|
801
|
-
render(:wlang, template, options, locals, &block)
|
802
|
-
end
|
803
|
-
|
804
790
|
def yajl(template, options = {}, locals = {})
|
805
791
|
options[:default_content_type] = :json
|
806
792
|
render :yajl, template, options, locals
|
@@ -825,24 +811,27 @@ module Sinatra
|
|
825
811
|
|
826
812
|
# logic shared between builder and nokogiri
|
827
813
|
def render_ruby(engine, template, options = {}, locals = {}, &block)
|
828
|
-
|
829
|
-
|
814
|
+
if template.is_a?(Hash)
|
815
|
+
options = template
|
816
|
+
template = nil
|
817
|
+
end
|
818
|
+
template = proc { block } if template.nil?
|
830
819
|
render engine, template, options, locals
|
831
820
|
end
|
832
821
|
|
833
822
|
def render(engine, data, options = {}, locals = {}, &block)
|
834
823
|
# merge app-level options
|
835
824
|
engine_options = settings.respond_to?(engine) ? settings.send(engine) : {}
|
836
|
-
options.merge!(engine_options) { |
|
825
|
+
options.merge!(engine_options) { |_key, v1, _v2| v1 }
|
837
826
|
|
838
827
|
# extract generic options
|
839
828
|
locals = options.delete(:locals) || locals || {}
|
840
|
-
views = options.delete(:views) || settings.views ||
|
829
|
+
views = options.delete(:views) || settings.views || './views'
|
841
830
|
layout = options[:layout]
|
842
831
|
layout = false if layout.nil? && options.include?(:layout)
|
843
832
|
eat_errors = layout.nil?
|
844
|
-
layout = engine_options[:layout] if layout.nil?
|
845
|
-
layout = @default_layout if layout.nil?
|
833
|
+
layout = engine_options[:layout] if layout.nil? || (layout == true && engine_options[:layout] != false)
|
834
|
+
layout = @default_layout if layout.nil? || (layout == true)
|
846
835
|
layout_options = options.delete(:layout_options) || {}
|
847
836
|
content_type = options.delete(:default_content_type)
|
848
837
|
content_type = options.delete(:content_type) || content_type
|
@@ -867,8 +856,9 @@ module Sinatra
|
|
867
856
|
|
868
857
|
# render layout
|
869
858
|
if layout
|
870
|
-
|
871
|
-
|
859
|
+
extra_options = { views: views, layout: false, eat_errors: eat_errors, scope: scope }
|
860
|
+
options = options.merge(extra_options).merge!(layout_options)
|
861
|
+
|
872
862
|
catch(:layout_missing) { return render(layout_engine, layout, options, locals) { output } }
|
873
863
|
end
|
874
864
|
|
@@ -893,12 +883,13 @@ module Sinatra
|
|
893
883
|
@preferred_extension = engine.to_s
|
894
884
|
find_template(views, data, template) do |file|
|
895
885
|
path ||= file # keep the initial path rather than the last one
|
896
|
-
|
886
|
+
found = File.exist?(file)
|
887
|
+
if found
|
897
888
|
path = file
|
898
889
|
break
|
899
890
|
end
|
900
891
|
end
|
901
|
-
throw :layout_missing if eat_errors
|
892
|
+
throw :layout_missing if eat_errors && !found
|
902
893
|
template.new(path, 1, options)
|
903
894
|
end
|
904
895
|
end
|
@@ -914,9 +905,11 @@ module Sinatra
|
|
914
905
|
end
|
915
906
|
|
916
907
|
def compile_block_template(template, options, &body)
|
917
|
-
|
918
|
-
path =
|
919
|
-
line =
|
908
|
+
first_location = caller_locations.first
|
909
|
+
path = first_location.path
|
910
|
+
line = first_location.lineno
|
911
|
+
path = options[:path] || path
|
912
|
+
line = options[:line] || line
|
920
913
|
template.new(path, line.to_i, options, &body)
|
921
914
|
end
|
922
915
|
end
|
@@ -932,7 +925,7 @@ module Sinatra
|
|
932
925
|
attr_accessor :app, :env, :request, :response, :params
|
933
926
|
attr_reader :template_cache
|
934
927
|
|
935
|
-
def initialize(app = nil, **
|
928
|
+
def initialize(app = nil, **_kwargs)
|
936
929
|
super()
|
937
930
|
@app = app
|
938
931
|
@template_cache = Tilt::Cache.new
|
@@ -959,7 +952,7 @@ module Sinatra
|
|
959
952
|
unless @response['Content-Type']
|
960
953
|
if Array === body && body[0].respond_to?(:content_type)
|
961
954
|
content_type body[0].content_type
|
962
|
-
elsif default = settings.default_content_type
|
955
|
+
elsif (default = settings.default_content_type)
|
963
956
|
content_type default
|
964
957
|
end
|
965
958
|
end
|
@@ -977,12 +970,6 @@ module Sinatra
|
|
977
970
|
self.class.settings
|
978
971
|
end
|
979
972
|
|
980
|
-
def options
|
981
|
-
warn "Sinatra::Base#options is deprecated and will be removed, " \
|
982
|
-
"use #settings instead."
|
983
|
-
settings
|
984
|
-
end
|
985
|
-
|
986
973
|
# Exit the current block, halts any further processing
|
987
974
|
# of the request, and returns the specified response.
|
988
975
|
def halt(*response)
|
@@ -999,7 +986,8 @@ module Sinatra
|
|
999
986
|
|
1000
987
|
# Forward the request to the downstream app -- middleware only.
|
1001
988
|
def forward
|
1002
|
-
|
989
|
+
raise 'downstream app not set' unless @app.respond_to? :call
|
990
|
+
|
1003
991
|
status, headers, body = @app.call env
|
1004
992
|
@response.status = status
|
1005
993
|
@response.body = body
|
@@ -1021,18 +1009,18 @@ module Sinatra
|
|
1021
1009
|
|
1022
1010
|
# Run routes defined on the class and all superclasses.
|
1023
1011
|
def route!(base = settings, pass_block = nil)
|
1024
|
-
|
1025
|
-
routes.each do |pattern, conditions, block|
|
1026
|
-
response.delete_header('Content-Type') unless @pinned_response
|
1012
|
+
routes = base.routes[@request.request_method]
|
1027
1013
|
|
1028
|
-
|
1029
|
-
|
1030
|
-
route_eval { block[*args] }
|
1031
|
-
end
|
1014
|
+
routes&.each do |pattern, conditions, block|
|
1015
|
+
response.delete_header('Content-Type') unless @pinned_response
|
1032
1016
|
|
1033
|
-
|
1034
|
-
|
1017
|
+
returned_pass_block = process_route(pattern, conditions) do |*args|
|
1018
|
+
env['sinatra.route'] = "#{@request.request_method} #{pattern}"
|
1019
|
+
route_eval { block[*args] }
|
1035
1020
|
end
|
1021
|
+
|
1022
|
+
# don't wipe out pass_block in superclass
|
1023
|
+
pass_block = returned_pass_block if returned_pass_block
|
1036
1024
|
end
|
1037
1025
|
|
1038
1026
|
# Run routes defined in superclass.
|
@@ -1056,15 +1044,17 @@ module Sinatra
|
|
1056
1044
|
# Returns pass block.
|
1057
1045
|
def process_route(pattern, conditions, block = nil, values = [])
|
1058
1046
|
route = @request.path_info
|
1059
|
-
route = '/' if route.empty?
|
1047
|
+
route = '/' if route.empty? && !settings.empty_path_info?
|
1060
1048
|
route = route[0..-2] if !settings.strict_paths? && route != '/' && route.end_with?('/')
|
1061
|
-
return unless params = pattern.params(route)
|
1062
1049
|
|
1063
|
-
params.
|
1050
|
+
params = pattern.params(route)
|
1051
|
+
return unless params
|
1052
|
+
|
1053
|
+
params.delete('ignore') # TODO: better params handling, maybe turn it into "smart" object or detect changes
|
1064
1054
|
force_encoding(params)
|
1065
|
-
@params = @params.merge(params) if params.any?
|
1055
|
+
@params = @params.merge(params) { |_k, v1, v2| v2 || v1 } if params.any?
|
1066
1056
|
|
1067
|
-
regexp_exists = pattern.is_a?(Mustermann::Regular) || (pattern.respond_to?(:patterns) && pattern.patterns.any? {|subpattern| subpattern.is_a?(Mustermann::Regular)}
|
1057
|
+
regexp_exists = pattern.is_a?(Mustermann::Regular) || (pattern.respond_to?(:patterns) && pattern.patterns.any? { |subpattern| subpattern.is_a?(Mustermann::Regular) })
|
1068
1058
|
if regexp_exists
|
1069
1059
|
captures = pattern.match(route).captures.map { |c| URI_INSTANCE.unescape(c) if c }
|
1070
1060
|
values += captures
|
@@ -1077,7 +1067,7 @@ module Sinatra
|
|
1077
1067
|
conditions.each { |c| throw :pass if c.bind(self).call == false }
|
1078
1068
|
block ? block[self, values] : yield(self, values)
|
1079
1069
|
end
|
1080
|
-
rescue
|
1070
|
+
rescue StandardError
|
1081
1071
|
@env['sinatra.error.params'] = @params
|
1082
1072
|
raise
|
1083
1073
|
ensure
|
@@ -1091,35 +1081,35 @@ module Sinatra
|
|
1091
1081
|
# a NotFound exception. Subclasses can override this method to perform
|
1092
1082
|
# custom route miss logic.
|
1093
1083
|
def route_missing
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
raise NotFound, "#{request.request_method} #{request.path_info}"
|
1098
|
-
end
|
1084
|
+
raise NotFound unless @app
|
1085
|
+
|
1086
|
+
forward
|
1099
1087
|
end
|
1100
1088
|
|
1101
1089
|
# Attempt to serve static files from public directory. Throws :halt when
|
1102
1090
|
# a matching file is found, returns nil otherwise.
|
1103
1091
|
def static!(options = {})
|
1104
1092
|
return if (public_dir = settings.public_folder).nil?
|
1093
|
+
|
1105
1094
|
path = "#{public_dir}#{URI_INSTANCE.unescape(request.path_info)}"
|
1106
1095
|
return unless valid_path?(path)
|
1107
1096
|
|
1108
1097
|
path = File.expand_path(path)
|
1109
|
-
return unless path.start_with?(File.expand_path(public_dir)
|
1098
|
+
return unless path.start_with?("#{File.expand_path(public_dir)}/")
|
1099
|
+
|
1110
1100
|
return unless File.file?(path)
|
1111
1101
|
|
1112
1102
|
env['sinatra.static_file'] = path
|
1113
1103
|
cache_control(*settings.static_cache_control) if settings.static_cache_control?
|
1114
|
-
send_file path, options.merge(:
|
1104
|
+
send_file path, options.merge(disposition: nil)
|
1115
1105
|
end
|
1116
1106
|
|
1117
1107
|
# Run the block with 'throw :halt' support and apply result to the response.
|
1118
|
-
def invoke
|
1119
|
-
res = catch(:halt)
|
1108
|
+
def invoke(&block)
|
1109
|
+
res = catch(:halt, &block)
|
1120
1110
|
|
1121
|
-
res = [res] if Integer === res
|
1122
|
-
if Array === res
|
1111
|
+
res = [res] if (Integer === res) || (String === res)
|
1112
|
+
if (Array === res) && (Integer === res.first)
|
1123
1113
|
res = res.dup
|
1124
1114
|
status(res.shift)
|
1125
1115
|
body(res.pop)
|
@@ -1135,6 +1125,7 @@ module Sinatra
|
|
1135
1125
|
# Avoid passing frozen string in force_encoding
|
1136
1126
|
@params.merge!(@request.params).each do |key, val|
|
1137
1127
|
next unless val.respond_to?(:force_encoding)
|
1128
|
+
|
1138
1129
|
val = val.dup if val.frozen?
|
1139
1130
|
@params[key] = force_encoding(val)
|
1140
1131
|
end
|
@@ -1146,39 +1137,43 @@ module Sinatra
|
|
1146
1137
|
end
|
1147
1138
|
route!
|
1148
1139
|
end
|
1149
|
-
rescue ::Exception =>
|
1150
|
-
invoke { handle_exception!(
|
1140
|
+
rescue ::Exception => e
|
1141
|
+
invoke { handle_exception!(e) }
|
1151
1142
|
ensure
|
1152
1143
|
begin
|
1153
1144
|
filter! :after unless env['sinatra.static_file']
|
1154
|
-
rescue ::Exception =>
|
1155
|
-
invoke { handle_exception!(
|
1145
|
+
rescue ::Exception => e
|
1146
|
+
invoke { handle_exception!(e) } unless @env['sinatra.error']
|
1156
1147
|
end
|
1157
1148
|
end
|
1158
1149
|
|
1159
1150
|
# Error handling during requests.
|
1160
1151
|
def handle_exception!(boom)
|
1161
|
-
|
1162
|
-
|
1163
|
-
|
1152
|
+
error_params = @env['sinatra.error.params']
|
1153
|
+
|
1154
|
+
@params = @params.merge(error_params) if error_params
|
1155
|
+
|
1164
1156
|
@env['sinatra.error'] = boom
|
1165
1157
|
|
1166
|
-
|
1167
|
-
|
1168
|
-
|
1169
|
-
|
1170
|
-
|
1171
|
-
|
1172
|
-
|
1158
|
+
http_status = if boom.is_a? Sinatra::Error
|
1159
|
+
if boom.respond_to? :http_status
|
1160
|
+
boom.http_status
|
1161
|
+
elsif settings.use_code? && boom.respond_to?(:code)
|
1162
|
+
boom.code
|
1163
|
+
end
|
1164
|
+
end
|
1165
|
+
|
1166
|
+
http_status = 500 unless http_status&.between?(400, 599)
|
1167
|
+
status(http_status)
|
1173
1168
|
|
1174
1169
|
if server_error?
|
1175
1170
|
dump_errors! boom if settings.dump_errors?
|
1176
|
-
raise boom if settings.show_exceptions?
|
1171
|
+
raise boom if settings.show_exceptions? && (settings.show_exceptions != :after_handler)
|
1177
1172
|
elsif not_found?
|
1178
1173
|
headers['X-Cascade'] = 'pass' if settings.x_cascade?
|
1179
1174
|
end
|
1180
1175
|
|
1181
|
-
if res = error_block!(boom.class, boom) || error_block!(status, boom)
|
1176
|
+
if (res = error_block!(boom.class, boom) || error_block!(status, boom))
|
1182
1177
|
return res
|
1183
1178
|
end
|
1184
1179
|
|
@@ -1187,12 +1182,14 @@ module Sinatra
|
|
1187
1182
|
body Rack::Utils.escape_html(boom.message)
|
1188
1183
|
else
|
1189
1184
|
content_type 'text/html'
|
1190
|
-
body
|
1185
|
+
body "<h1>#{not_found? ? 'Not Found' : 'Bad Request'}</h1>"
|
1191
1186
|
end
|
1192
1187
|
end
|
1193
1188
|
|
1194
1189
|
return unless server_error?
|
1195
|
-
|
1190
|
+
|
1191
|
+
raise boom if settings.raise_errors? || settings.show_exceptions?
|
1192
|
+
|
1196
1193
|
error_block! Exception, boom
|
1197
1194
|
end
|
1198
1195
|
|
@@ -1200,7 +1197,10 @@ module Sinatra
|
|
1200
1197
|
def error_block!(key, *block_params)
|
1201
1198
|
base = settings
|
1202
1199
|
while base.respond_to?(:errors)
|
1203
|
-
|
1200
|
+
args_array = base.errors[key]
|
1201
|
+
|
1202
|
+
next base = base.superclass unless args_array
|
1203
|
+
|
1204
1204
|
args_array.reverse_each do |args|
|
1205
1205
|
first = args == args_array.first
|
1206
1206
|
args += [block_params]
|
@@ -1208,51 +1208,50 @@ module Sinatra
|
|
1208
1208
|
return resp unless resp.nil? && !first
|
1209
1209
|
end
|
1210
1210
|
end
|
1211
|
-
return false unless key.respond_to?
|
1211
|
+
return false unless key.respond_to?(:superclass) && (key.superclass < Exception)
|
1212
|
+
|
1212
1213
|
error_block!(key.superclass, *block_params)
|
1213
1214
|
end
|
1214
1215
|
|
1215
1216
|
def dump_errors!(boom)
|
1216
|
-
msg = ["#{Time.now.strftime(
|
1217
|
+
msg = ["#{Time.now.strftime('%Y-%m-%d %H:%M:%S')} - #{boom.class} - #{boom.message}:", *boom.backtrace].join("\n\t")
|
1217
1218
|
@env['rack.errors'].puts(msg)
|
1218
1219
|
end
|
1219
1220
|
|
1220
1221
|
class << self
|
1221
1222
|
CALLERS_TO_IGNORE = [ # :nodoc:
|
1222
|
-
|
1223
|
-
/
|
1223
|
+
%r{/sinatra(/(base|main|show_exceptions))?\.rb$}, # all sinatra code
|
1224
|
+
%r{lib/tilt.*\.rb$}, # all tilt code
|
1224
1225
|
/^\(.*\)$/, # generated code
|
1225
|
-
/
|
1226
|
+
%r{rubygems/(custom|core_ext/kernel)_require\.rb$}, # rubygems require hacks
|
1226
1227
|
/active_support/, # active_support require hacks
|
1227
|
-
|
1228
|
+
%r{bundler(/(?:runtime|inline))?\.rb}, # bundler require hacks
|
1228
1229
|
/<internal:/, # internal in ruby >= 1.9.2
|
1229
|
-
/
|
1230
|
-
]
|
1231
|
-
|
1232
|
-
# contrary to what the comment said previously, rubinius never supported this
|
1233
|
-
if defined?(RUBY_IGNORE_CALLERS)
|
1234
|
-
warn "RUBY_IGNORE_CALLERS is deprecated and will no longer be supported by Sinatra 2.0"
|
1235
|
-
CALLERS_TO_IGNORE.concat(RUBY_IGNORE_CALLERS)
|
1236
|
-
end
|
1230
|
+
%r{zeitwerk/kernel\.rb} # Zeitwerk kernel#require decorator
|
1231
|
+
].freeze
|
1237
1232
|
|
1238
1233
|
attr_reader :routes, :filters, :templates, :errors
|
1239
1234
|
|
1235
|
+
def callers_to_ignore
|
1236
|
+
CALLERS_TO_IGNORE
|
1237
|
+
end
|
1238
|
+
|
1240
1239
|
# Removes all routes, filters, middleware and extension hooks from the
|
1241
1240
|
# current class (not routes/filters/... defined by its superclass).
|
1242
1241
|
def reset!
|
1243
1242
|
@conditions = []
|
1244
1243
|
@routes = {}
|
1245
|
-
@filters = {:
|
1244
|
+
@filters = { before: [], after: [] }
|
1246
1245
|
@errors = {}
|
1247
1246
|
@middleware = []
|
1248
1247
|
@prototype = nil
|
1249
1248
|
@extensions = []
|
1250
1249
|
|
1251
|
-
if superclass.respond_to?(:templates)
|
1252
|
-
|
1253
|
-
|
1254
|
-
|
1255
|
-
|
1250
|
+
@templates = if superclass.respond_to?(:templates)
|
1251
|
+
Hash.new { |_hash, key| superclass.templates[key] }
|
1252
|
+
else
|
1253
|
+
{}
|
1254
|
+
end
|
1256
1255
|
end
|
1257
1256
|
|
1258
1257
|
# Extension modules registered on this class and all superclasses.
|
@@ -1276,16 +1275,21 @@ module Sinatra
|
|
1276
1275
|
# Sets an option to the given value. If the value is a proc,
|
1277
1276
|
# the proc will be called every time the option is accessed.
|
1278
1277
|
def set(option, value = (not_set = true), ignore_setter = false, &block)
|
1279
|
-
raise ArgumentError if block
|
1280
|
-
|
1278
|
+
raise ArgumentError if block && !not_set
|
1279
|
+
|
1280
|
+
if block
|
1281
|
+
value = block
|
1282
|
+
not_set = false
|
1283
|
+
end
|
1281
1284
|
|
1282
1285
|
if not_set
|
1283
1286
|
raise ArgumentError unless option.respond_to?(:each)
|
1284
|
-
|
1287
|
+
|
1288
|
+
option.each { |k, v| set(k, v) }
|
1285
1289
|
return self
|
1286
1290
|
end
|
1287
1291
|
|
1288
|
-
if respond_to?("#{option}=")
|
1292
|
+
if respond_to?("#{option}=") && !ignore_setter
|
1289
1293
|
return __send__("#{option}=", value)
|
1290
1294
|
end
|
1291
1295
|
|
@@ -1324,7 +1328,7 @@ module Sinatra
|
|
1324
1328
|
# class, or an HTTP status code to specify which errors should be
|
1325
1329
|
# handled.
|
1326
1330
|
def error(*codes, &block)
|
1327
|
-
args = compile!
|
1331
|
+
args = compile! 'ERROR', /.*/, block
|
1328
1332
|
codes = codes.flat_map(&method(:Array))
|
1329
1333
|
codes << Exception if codes.empty?
|
1330
1334
|
codes << Sinatra::NotFound if codes.include?(404)
|
@@ -1350,7 +1354,7 @@ module Sinatra
|
|
1350
1354
|
# Load embedded templates from the file; uses the caller's __FILE__
|
1351
1355
|
# when no file is specified.
|
1352
1356
|
def inline_templates=(file = nil)
|
1353
|
-
file = (
|
1357
|
+
file = (caller_files.first || File.expand_path($0)) if file.nil? || file == true
|
1354
1358
|
|
1355
1359
|
begin
|
1356
1360
|
io = ::IO.respond_to?(:binread) ? ::IO.binread(file) : ::IO.read(file)
|
@@ -1359,23 +1363,24 @@ module Sinatra
|
|
1359
1363
|
app, data = nil
|
1360
1364
|
end
|
1361
1365
|
|
1362
|
-
|
1363
|
-
|
1364
|
-
|
1365
|
-
|
1366
|
-
|
1367
|
-
|
1368
|
-
|
1369
|
-
|
1370
|
-
|
1371
|
-
|
1372
|
-
|
1373
|
-
|
1374
|
-
|
1375
|
-
|
1376
|
-
|
1377
|
-
|
1378
|
-
|
1366
|
+
return unless data
|
1367
|
+
|
1368
|
+
encoding = if app && app =~ /([^\n]*\n)?#[^\n]*coding: *(\S+)/m
|
1369
|
+
$2
|
1370
|
+
else
|
1371
|
+
settings.default_encoding
|
1372
|
+
end
|
1373
|
+
|
1374
|
+
lines = app.count("\n") + 1
|
1375
|
+
template = nil
|
1376
|
+
force_encoding data, encoding
|
1377
|
+
data.each_line do |line|
|
1378
|
+
lines += 1
|
1379
|
+
if line =~ /^@@\s*(.*\S)\s*$/
|
1380
|
+
template = force_encoding(String.new, encoding)
|
1381
|
+
templates[$1.to_sym] = [template, file, lines]
|
1382
|
+
elsif template
|
1383
|
+
template << line
|
1379
1384
|
end
|
1380
1385
|
end
|
1381
1386
|
end
|
@@ -1384,8 +1389,10 @@ module Sinatra
|
|
1384
1389
|
def mime_type(type, value = nil)
|
1385
1390
|
return type if type.nil?
|
1386
1391
|
return type.to_s if type.to_s.include?('/')
|
1387
|
-
|
1392
|
+
|
1393
|
+
type = ".#{type}" unless type.to_s[0] == '.'
|
1388
1394
|
return Rack::Mime.mime_type(type, nil) unless value
|
1395
|
+
|
1389
1396
|
Rack::Mime::MIME_TYPES[type] = value
|
1390
1397
|
end
|
1391
1398
|
|
@@ -1394,7 +1401,7 @@ module Sinatra
|
|
1394
1401
|
# mime_types :js # => ['application/javascript', 'text/javascript']
|
1395
1402
|
def mime_types(type)
|
1396
1403
|
type = mime_type type
|
1397
|
-
type =~
|
1404
|
+
type =~ %r{^application/(xml|javascript)$} ? [type, "text/#{$1}"] : [type]
|
1398
1405
|
end
|
1399
1406
|
|
1400
1407
|
# Define a before filter; runs before all requests within the same
|
@@ -1423,7 +1430,7 @@ module Sinatra
|
|
1423
1430
|
end
|
1424
1431
|
|
1425
1432
|
def public=(value)
|
1426
|
-
|
1433
|
+
warn_for_deprecation ':public is no longer used to avoid overloading Module#public, use :public_folder or :public_dir instead'
|
1427
1434
|
set(:public_folder, value)
|
1428
1435
|
end
|
1429
1436
|
|
@@ -1445,14 +1452,21 @@ module Sinatra
|
|
1445
1452
|
route('HEAD', path, opts, &block)
|
1446
1453
|
end
|
1447
1454
|
|
1448
|
-
def put(path, opts = {}, &
|
1449
|
-
|
1450
|
-
def
|
1451
|
-
|
1452
|
-
def
|
1453
|
-
|
1454
|
-
def
|
1455
|
-
|
1455
|
+
def put(path, opts = {}, &block) route 'PUT', path, opts, &block end
|
1456
|
+
|
1457
|
+
def post(path, opts = {}, &block) route 'POST', path, opts, &block end
|
1458
|
+
|
1459
|
+
def delete(path, opts = {}, &block) route 'DELETE', path, opts, &block end
|
1460
|
+
|
1461
|
+
def head(path, opts = {}, &block) route 'HEAD', path, opts, &block end
|
1462
|
+
|
1463
|
+
def options(path, opts = {}, &block) route 'OPTIONS', path, opts, &block end
|
1464
|
+
|
1465
|
+
def patch(path, opts = {}, &block) route 'PATCH', path, opts, &block end
|
1466
|
+
|
1467
|
+
def link(path, opts = {}, &block) route 'LINK', path, opts, &block end
|
1468
|
+
|
1469
|
+
def unlink(path, opts = {}, &block) route 'UNLINK', path, opts, &block end
|
1456
1470
|
|
1457
1471
|
# Makes the methods defined in the block and in the Modules given
|
1458
1472
|
# in `extensions` available to the handlers and templates
|
@@ -1492,37 +1506,39 @@ module Sinatra
|
|
1492
1506
|
# Stop the self-hosted server if running.
|
1493
1507
|
def quit!
|
1494
1508
|
return unless running?
|
1509
|
+
|
1495
1510
|
# Use Thin's hard #stop! if available, otherwise just #stop.
|
1496
1511
|
running_server.respond_to?(:stop!) ? running_server.stop! : running_server.stop
|
1497
|
-
|
1512
|
+
warn '== Sinatra has ended his set (crowd applauds)' unless suppress_messages?
|
1498
1513
|
set :running_server, nil
|
1499
1514
|
set :handler_name, nil
|
1500
1515
|
end
|
1501
1516
|
|
1502
|
-
|
1517
|
+
alias stop! quit!
|
1503
1518
|
|
1504
1519
|
# Run the Sinatra app as a self-hosted server using
|
1505
|
-
# Puma, Mongrel, or WEBrick (in that order). If given a block, will call
|
1520
|
+
# Puma, Falcon, Mongrel, or WEBrick (in that order). If given a block, will call
|
1506
1521
|
# with the constructed handler once we have taken the stage.
|
1507
1522
|
def run!(options = {}, &block)
|
1508
1523
|
return if running?
|
1524
|
+
|
1509
1525
|
set options
|
1510
1526
|
handler = Rack::Handler.pick(server)
|
1511
1527
|
handler_name = handler.name.gsub(/.*::/, '')
|
1512
1528
|
server_settings = settings.respond_to?(:server_settings) ? settings.server_settings : {}
|
1513
|
-
server_settings.merge!(:
|
1529
|
+
server_settings.merge!(Port: port, Host: bind)
|
1514
1530
|
|
1515
1531
|
begin
|
1516
1532
|
start_server(handler, server_settings, handler_name, &block)
|
1517
1533
|
rescue Errno::EADDRINUSE
|
1518
|
-
|
1534
|
+
warn "== Someone is already performing on port #{port}!"
|
1519
1535
|
raise
|
1520
1536
|
ensure
|
1521
1537
|
quit!
|
1522
1538
|
end
|
1523
1539
|
end
|
1524
1540
|
|
1525
|
-
|
1541
|
+
alias start! run!
|
1526
1542
|
|
1527
1543
|
# Check whether the self-hosted server is running or not.
|
1528
1544
|
def running?
|
@@ -1540,8 +1556,8 @@ module Sinatra
|
|
1540
1556
|
# Create a new instance of the class fronted by its middleware
|
1541
1557
|
# pipeline. The object is guaranteed to respond to #call but may not be
|
1542
1558
|
# an instance of the class new was called on.
|
1543
|
-
def new(*args, &
|
1544
|
-
instance = new!(*args, &
|
1559
|
+
def new(*args, &block)
|
1560
|
+
instance = new!(*args, &block)
|
1545
1561
|
Wrapper.new(build(instance).to_app, instance)
|
1546
1562
|
end
|
1547
1563
|
ruby2_keywords :new if respond_to?(:ruby2_keywords, true)
|
@@ -1566,12 +1582,6 @@ module Sinatra
|
|
1566
1582
|
cleaned_caller(1).flatten
|
1567
1583
|
end
|
1568
1584
|
|
1569
|
-
# Like caller_files, but containing Arrays rather than strings with the
|
1570
|
-
# first element being the file, and the second being the line.
|
1571
|
-
def caller_locations
|
1572
|
-
cleaned_caller 2
|
1573
|
-
end
|
1574
|
-
|
1575
1585
|
private
|
1576
1586
|
|
1577
1587
|
# Starts the server by running the Rack Handler.
|
@@ -1582,7 +1592,7 @@ module Sinatra
|
|
1582
1592
|
# Run the instance we created:
|
1583
1593
|
handler.run(self, **server_settings) do |server|
|
1584
1594
|
unless suppress_messages?
|
1585
|
-
|
1595
|
+
warn "== Sinatra (v#{Sinatra::VERSION}) has taken the stage on #{port} for #{environment} with backup from #{handler_name}"
|
1586
1596
|
end
|
1587
1597
|
|
1588
1598
|
setup_traps
|
@@ -1599,18 +1609,18 @@ module Sinatra
|
|
1599
1609
|
end
|
1600
1610
|
|
1601
1611
|
def setup_traps
|
1602
|
-
|
1603
|
-
at_exit { quit! }
|
1612
|
+
return unless traps?
|
1604
1613
|
|
1605
|
-
|
1606
|
-
old_handler = trap(signal) do
|
1607
|
-
quit!
|
1608
|
-
old_handler.call if old_handler.respond_to?(:call)
|
1609
|
-
end
|
1610
|
-
end
|
1614
|
+
at_exit { quit! }
|
1611
1615
|
|
1612
|
-
|
1616
|
+
%i[INT TERM].each do |signal|
|
1617
|
+
old_handler = trap(signal) do
|
1618
|
+
quit!
|
1619
|
+
old_handler.call if old_handler.respond_to?(:call)
|
1620
|
+
end
|
1613
1621
|
end
|
1622
|
+
|
1623
|
+
set :traps, false
|
1614
1624
|
end
|
1615
1625
|
|
1616
1626
|
# Dynamically defines a method on settings.
|
@@ -1638,18 +1648,21 @@ module Sinatra
|
|
1638
1648
|
end
|
1639
1649
|
end
|
1640
1650
|
end
|
1641
|
-
|
1651
|
+
alias agent user_agent
|
1642
1652
|
|
1643
1653
|
# Condition for matching mimetypes. Accepts file extensions.
|
1644
1654
|
def provides(*types)
|
1645
1655
|
types.map! { |t| mime_types(t) }
|
1646
1656
|
types.flatten!
|
1647
1657
|
condition do
|
1648
|
-
|
1649
|
-
|
1650
|
-
|
1651
|
-
|
1652
|
-
|
1658
|
+
response_content_type = response['Content-Type']
|
1659
|
+
preferred_type = request.preferred_type(types)
|
1660
|
+
|
1661
|
+
if response_content_type
|
1662
|
+
types.include?(response_content_type) || types.include?(response_content_type[/^[^;]+/])
|
1663
|
+
elsif preferred_type
|
1664
|
+
params = (preferred_type.respond_to?(:params) ? preferred_type.params : {})
|
1665
|
+
content_type(preferred_type, params)
|
1653
1666
|
true
|
1654
1667
|
else
|
1655
1668
|
false
|
@@ -1658,7 +1671,7 @@ module Sinatra
|
|
1658
1671
|
end
|
1659
1672
|
|
1660
1673
|
def route(verb, path, options = {}, &block)
|
1661
|
-
enable :empty_path_info if path ==
|
1674
|
+
enable :empty_path_info if path == '' && empty_path_info.nil?
|
1662
1675
|
signature = compile!(verb, path, block, **options)
|
1663
1676
|
(@routes[verb] ||= []) << signature
|
1664
1677
|
invoke_hook(:route_added, verb, path, block)
|
@@ -1687,12 +1700,13 @@ module Sinatra
|
|
1687
1700
|
pattern = compile(path, route_mustermann_opts)
|
1688
1701
|
method_name = "#{verb} #{path}"
|
1689
1702
|
unbound_method = generate_method(method_name, &block)
|
1690
|
-
conditions
|
1691
|
-
|
1692
|
-
|
1693
|
-
proc { |a,
|
1703
|
+
conditions = @conditions
|
1704
|
+
@conditions = []
|
1705
|
+
wrapper = block.arity.zero? ?
|
1706
|
+
proc { |a, _p| unbound_method.bind(a).call } :
|
1707
|
+
proc { |a, p| unbound_method.bind(a).call(*p) }
|
1694
1708
|
|
1695
|
-
[
|
1709
|
+
[pattern, conditions, wrapper]
|
1696
1710
|
end
|
1697
1711
|
|
1698
1712
|
def compile(path, route_mustermann_opts = {})
|
@@ -1710,7 +1724,7 @@ module Sinatra
|
|
1710
1724
|
end
|
1711
1725
|
|
1712
1726
|
def setup_middleware(builder)
|
1713
|
-
middleware.each { |c,a,b| builder.use(c, *a, &b) }
|
1727
|
+
middleware.each { |c, a, b| builder.use(c, *a, &b) }
|
1714
1728
|
end
|
1715
1729
|
|
1716
1730
|
def setup_logging(builder)
|
@@ -1740,9 +1754,10 @@ module Sinatra
|
|
1740
1754
|
|
1741
1755
|
def setup_protection(builder)
|
1742
1756
|
return unless protection?
|
1757
|
+
|
1743
1758
|
options = Hash === protection ? protection.dup : {}
|
1744
1759
|
options = {
|
1745
|
-
img_src:
|
1760
|
+
img_src: "'self' data:",
|
1746
1761
|
font_src: "'self'"
|
1747
1762
|
}.merge options
|
1748
1763
|
|
@@ -1756,6 +1771,7 @@ module Sinatra
|
|
1756
1771
|
|
1757
1772
|
def setup_sessions(builder)
|
1758
1773
|
return unless sessions?
|
1774
|
+
|
1759
1775
|
options = {}
|
1760
1776
|
options[:secret] = session_secret if session_secret?
|
1761
1777
|
options.merge! sessions.to_hash if sessions.respond_to? :to_hash
|
@@ -1778,15 +1794,15 @@ module Sinatra
|
|
1778
1794
|
end
|
1779
1795
|
|
1780
1796
|
# used for deprecation warnings
|
1781
|
-
def
|
1782
|
-
|
1797
|
+
def warn_for_deprecation(message)
|
1798
|
+
warn message + "\n\tfrom #{cleaned_caller.first.join(':')}"
|
1783
1799
|
end
|
1784
1800
|
|
1785
1801
|
# Like Kernel#caller but excluding certain magic entries
|
1786
1802
|
def cleaned_caller(keep = 3)
|
1787
|
-
caller(1)
|
1788
|
-
map!
|
1789
|
-
reject { |file, *_|
|
1803
|
+
caller(1)
|
1804
|
+
.map! { |line| line.split(/:(?=\d|in )/, 3)[0, keep] }
|
1805
|
+
.reject { |file, *_| callers_to_ignore.any? { |pattern| file =~ pattern } }
|
1790
1806
|
end
|
1791
1807
|
end
|
1792
1808
|
|
@@ -1794,6 +1810,7 @@ module Sinatra
|
|
1794
1810
|
# which is UTF-8 by default
|
1795
1811
|
def self.force_encoding(data, encoding = default_encoding)
|
1796
1812
|
return if data == settings || data.is_a?(Tempfile)
|
1813
|
+
|
1797
1814
|
if data.respond_to? :force_encoding
|
1798
1815
|
data.force_encoding(encoding).encode!
|
1799
1816
|
elsif data.respond_to? :each_value
|
@@ -1804,24 +1821,26 @@ module Sinatra
|
|
1804
1821
|
data
|
1805
1822
|
end
|
1806
1823
|
|
1807
|
-
def force_encoding(*args)
|
1824
|
+
def force_encoding(*args)
|
1825
|
+
settings.force_encoding(*args)
|
1826
|
+
end
|
1808
1827
|
|
1809
1828
|
reset!
|
1810
1829
|
|
1811
1830
|
set :environment, (ENV['APP_ENV'] || ENV['RACK_ENV'] || :development).to_sym
|
1812
|
-
set :raise_errors,
|
1813
|
-
set :dump_errors,
|
1814
|
-
set :show_exceptions,
|
1831
|
+
set :raise_errors, proc { test? }
|
1832
|
+
set :dump_errors, proc { !test? }
|
1833
|
+
set :show_exceptions, proc { development? }
|
1815
1834
|
set :sessions, false
|
1816
|
-
set :session_store, Rack::
|
1835
|
+
set :session_store, Rack::Protection::EncryptedCookie
|
1817
1836
|
set :logging, false
|
1818
1837
|
set :protection, true
|
1819
1838
|
set :method_override, false
|
1820
1839
|
set :use_code, false
|
1821
|
-
set :default_encoding,
|
1840
|
+
set :default_encoding, 'utf-8'
|
1822
1841
|
set :x_cascade, true
|
1823
1842
|
set :add_charset, %w[javascript xml xhtml+xml].map { |t| "application/#{t}" }
|
1824
|
-
settings.add_charset <<
|
1843
|
+
settings.add_charset << %r{^text/}
|
1825
1844
|
set :mustermann_opts, {}
|
1826
1845
|
set :default_content_type, 'text/html'
|
1827
1846
|
|
@@ -1829,14 +1848,15 @@ module Sinatra
|
|
1829
1848
|
begin
|
1830
1849
|
require 'securerandom'
|
1831
1850
|
set :session_secret, SecureRandom.hex(64)
|
1832
|
-
rescue LoadError, NotImplementedError
|
1851
|
+
rescue LoadError, NotImplementedError, RuntimeError
|
1833
1852
|
# SecureRandom raises a NotImplementedError if no random device is available
|
1834
|
-
|
1853
|
+
# RuntimeError raised due to broken openssl backend: https://bugs.ruby-lang.org/issues/19230
|
1854
|
+
set :session_secret, format('%064x', Kernel.rand((2**256) - 1))
|
1835
1855
|
end
|
1836
1856
|
|
1837
1857
|
class << self
|
1838
|
-
|
1839
|
-
|
1858
|
+
alias methodoverride? method_override?
|
1859
|
+
alias methodoverride= method_override=
|
1840
1860
|
end
|
1841
1861
|
|
1842
1862
|
set :run, false # start server via at-exit hook?
|
@@ -1844,21 +1864,17 @@ module Sinatra
|
|
1844
1864
|
set :handler_name, nil
|
1845
1865
|
set :traps, true
|
1846
1866
|
set :server, %w[HTTP webrick]
|
1847
|
-
set :bind,
|
1867
|
+
set :bind, proc { development? ? 'localhost' : '0.0.0.0' }
|
1848
1868
|
set :port, Integer(ENV['PORT'] && !ENV['PORT'].empty? ? ENV['PORT'] : 4567)
|
1849
1869
|
set :quiet, false
|
1850
1870
|
|
1851
1871
|
ruby_engine = defined?(RUBY_ENGINE) && RUBY_ENGINE
|
1852
1872
|
|
1853
|
-
|
1854
|
-
|
1855
|
-
|
1856
|
-
|
1857
|
-
|
1858
|
-
server.unshift 'mongrel' if ruby_engine.nil?
|
1859
|
-
server.unshift 'thin' if ruby_engine != 'jruby'
|
1860
|
-
server.unshift 'trinidad' if ruby_engine == 'jruby'
|
1861
|
-
end
|
1873
|
+
server.unshift 'puma'
|
1874
|
+
server.unshift 'falcon' if ruby_engine != 'jruby'
|
1875
|
+
server.unshift 'mongrel' if ruby_engine.nil?
|
1876
|
+
server.unshift 'thin' if ruby_engine != 'jruby'
|
1877
|
+
server.unshift 'trinidad' if ruby_engine == 'jruby'
|
1862
1878
|
|
1863
1879
|
set :absolute_redirects, true
|
1864
1880
|
set :prefixed_redirects, false
|
@@ -1866,14 +1882,14 @@ module Sinatra
|
|
1866
1882
|
set :strict_paths, true
|
1867
1883
|
|
1868
1884
|
set :app_file, nil
|
1869
|
-
set :root,
|
1870
|
-
set :views,
|
1871
|
-
set :reload_templates,
|
1885
|
+
set :root, proc { app_file && File.expand_path(File.dirname(app_file)) }
|
1886
|
+
set :views, proc { root && File.join(root, 'views') }
|
1887
|
+
set :reload_templates, proc { development? }
|
1872
1888
|
set :lock, false
|
1873
1889
|
set :threaded, true
|
1874
1890
|
|
1875
|
-
set :public_folder,
|
1876
|
-
set :static,
|
1891
|
+
set :public_folder, proc { root && File.join(root, 'public') }
|
1892
|
+
set :static, proc { public_folder && File.exist?(public_folder) }
|
1877
1893
|
set :static_cache_control, false
|
1878
1894
|
|
1879
1895
|
error ::Exception do
|
@@ -1892,7 +1908,7 @@ module Sinatra
|
|
1892
1908
|
error NotFound do
|
1893
1909
|
content_type 'text/html'
|
1894
1910
|
|
1895
|
-
if
|
1911
|
+
if instance_of?(Sinatra::Application)
|
1896
1912
|
code = <<-RUBY.gsub(/^ {12}/, '')
|
1897
1913
|
#{request.request_method.downcase} '#{request.path_info}' do
|
1898
1914
|
"Hello World"
|
@@ -1907,11 +1923,11 @@ module Sinatra
|
|
1907
1923
|
end
|
1908
1924
|
RUBY
|
1909
1925
|
|
1910
|
-
file = settings.app_file.to_s.sub(settings.root.to_s, '').sub(
|
1926
|
+
file = settings.app_file.to_s.sub(settings.root.to_s, '').sub(%r{^/}, '')
|
1911
1927
|
code = "# in #{file}\n#{code}" unless file.empty?
|
1912
1928
|
end
|
1913
1929
|
|
1914
|
-
|
1930
|
+
<<-HTML.gsub(/^ {10}/, '')
|
1915
1931
|
<!DOCTYPE html>
|
1916
1932
|
<html>
|
1917
1933
|
<head>
|
@@ -1923,7 +1939,7 @@ module Sinatra
|
|
1923
1939
|
</head>
|
1924
1940
|
<body>
|
1925
1941
|
<h2>Sinatra doesn’t know this ditty.</h2>
|
1926
|
-
<img src='#{uri
|
1942
|
+
<img src='#{uri '/__sinatra__/404.png'}'>
|
1927
1943
|
<div id="c">
|
1928
1944
|
Try this:
|
1929
1945
|
<pre>#{Rack::Utils.escape_html(code)}</pre>
|
@@ -1943,12 +1959,12 @@ module Sinatra
|
|
1943
1959
|
# top-level. Subclassing Sinatra::Base is highly recommended for
|
1944
1960
|
# modular applications.
|
1945
1961
|
class Application < Base
|
1946
|
-
set :logging,
|
1962
|
+
set :logging, proc { !test? }
|
1947
1963
|
set :method_override, true
|
1948
|
-
set :run,
|
1964
|
+
set :run, proc { !test? }
|
1949
1965
|
set :app_file, nil
|
1950
1966
|
|
1951
|
-
def self.register(*extensions, &block)
|
1967
|
+
def self.register(*extensions, &block) # :nodoc:
|
1952
1968
|
added_methods = extensions.flat_map(&:public_instance_methods)
|
1953
1969
|
Delegator.delegate(*added_methods)
|
1954
1970
|
super(*extensions, &block)
|
@@ -1958,11 +1974,12 @@ module Sinatra
|
|
1958
1974
|
# Sinatra delegation mixin. Mixing this module into an object causes all
|
1959
1975
|
# methods to be delegated to the Sinatra::Application class. Used primarily
|
1960
1976
|
# at the top-level.
|
1961
|
-
module Delegator
|
1977
|
+
module Delegator # :nodoc:
|
1962
1978
|
def self.delegate(*methods)
|
1963
1979
|
methods.each do |method_name|
|
1964
1980
|
define_method(method_name) do |*args, &block|
|
1965
1981
|
return super(*args, &block) if respond_to? method_name
|
1982
|
+
|
1966
1983
|
Delegator.target.send(method_name, *args, &block)
|
1967
1984
|
end
|
1968
1985
|
# ensure keyword argument passing is compatible with ruby >= 2.7
|
@@ -1985,7 +2002,8 @@ module Sinatra
|
|
1985
2002
|
|
1986
2003
|
class Wrapper
|
1987
2004
|
def initialize(stack, instance)
|
1988
|
-
@stack
|
2005
|
+
@stack = stack
|
2006
|
+
@instance = instance
|
1989
2007
|
end
|
1990
2008
|
|
1991
2009
|
def settings
|