sinatra 2.2.4 → 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 +99 -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 +350 -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,32 +1208,27 @@ 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
|
|
@@ -1246,17 +1241,17 @@ module Sinatra
|
|
1246
1241
|
def reset!
|
1247
1242
|
@conditions = []
|
1248
1243
|
@routes = {}
|
1249
|
-
@filters = {:
|
1244
|
+
@filters = { before: [], after: [] }
|
1250
1245
|
@errors = {}
|
1251
1246
|
@middleware = []
|
1252
1247
|
@prototype = nil
|
1253
1248
|
@extensions = []
|
1254
1249
|
|
1255
|
-
if superclass.respond_to?(:templates)
|
1256
|
-
|
1257
|
-
|
1258
|
-
|
1259
|
-
|
1250
|
+
@templates = if superclass.respond_to?(:templates)
|
1251
|
+
Hash.new { |_hash, key| superclass.templates[key] }
|
1252
|
+
else
|
1253
|
+
{}
|
1254
|
+
end
|
1260
1255
|
end
|
1261
1256
|
|
1262
1257
|
# Extension modules registered on this class and all superclasses.
|
@@ -1280,16 +1275,21 @@ module Sinatra
|
|
1280
1275
|
# Sets an option to the given value. If the value is a proc,
|
1281
1276
|
# the proc will be called every time the option is accessed.
|
1282
1277
|
def set(option, value = (not_set = true), ignore_setter = false, &block)
|
1283
|
-
raise ArgumentError if block
|
1284
|
-
|
1278
|
+
raise ArgumentError if block && !not_set
|
1279
|
+
|
1280
|
+
if block
|
1281
|
+
value = block
|
1282
|
+
not_set = false
|
1283
|
+
end
|
1285
1284
|
|
1286
1285
|
if not_set
|
1287
1286
|
raise ArgumentError unless option.respond_to?(:each)
|
1288
|
-
|
1287
|
+
|
1288
|
+
option.each { |k, v| set(k, v) }
|
1289
1289
|
return self
|
1290
1290
|
end
|
1291
1291
|
|
1292
|
-
if respond_to?("#{option}=")
|
1292
|
+
if respond_to?("#{option}=") && !ignore_setter
|
1293
1293
|
return __send__("#{option}=", value)
|
1294
1294
|
end
|
1295
1295
|
|
@@ -1328,7 +1328,7 @@ module Sinatra
|
|
1328
1328
|
# class, or an HTTP status code to specify which errors should be
|
1329
1329
|
# handled.
|
1330
1330
|
def error(*codes, &block)
|
1331
|
-
args = compile!
|
1331
|
+
args = compile! 'ERROR', /.*/, block
|
1332
1332
|
codes = codes.flat_map(&method(:Array))
|
1333
1333
|
codes << Exception if codes.empty?
|
1334
1334
|
codes << Sinatra::NotFound if codes.include?(404)
|
@@ -1354,7 +1354,7 @@ module Sinatra
|
|
1354
1354
|
# Load embedded templates from the file; uses the caller's __FILE__
|
1355
1355
|
# when no file is specified.
|
1356
1356
|
def inline_templates=(file = nil)
|
1357
|
-
file = (
|
1357
|
+
file = (caller_files.first || File.expand_path($0)) if file.nil? || file == true
|
1358
1358
|
|
1359
1359
|
begin
|
1360
1360
|
io = ::IO.respond_to?(:binread) ? ::IO.binread(file) : ::IO.read(file)
|
@@ -1363,23 +1363,24 @@ module Sinatra
|
|
1363
1363
|
app, data = nil
|
1364
1364
|
end
|
1365
1365
|
|
1366
|
-
|
1367
|
-
|
1368
|
-
|
1369
|
-
|
1370
|
-
|
1371
|
-
|
1372
|
-
|
1373
|
-
|
1374
|
-
|
1375
|
-
|
1376
|
-
|
1377
|
-
|
1378
|
-
|
1379
|
-
|
1380
|
-
|
1381
|
-
|
1382
|
-
|
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
|
1383
1384
|
end
|
1384
1385
|
end
|
1385
1386
|
end
|
@@ -1388,8 +1389,10 @@ module Sinatra
|
|
1388
1389
|
def mime_type(type, value = nil)
|
1389
1390
|
return type if type.nil?
|
1390
1391
|
return type.to_s if type.to_s.include?('/')
|
1391
|
-
|
1392
|
+
|
1393
|
+
type = ".#{type}" unless type.to_s[0] == '.'
|
1392
1394
|
return Rack::Mime.mime_type(type, nil) unless value
|
1395
|
+
|
1393
1396
|
Rack::Mime::MIME_TYPES[type] = value
|
1394
1397
|
end
|
1395
1398
|
|
@@ -1398,7 +1401,7 @@ module Sinatra
|
|
1398
1401
|
# mime_types :js # => ['application/javascript', 'text/javascript']
|
1399
1402
|
def mime_types(type)
|
1400
1403
|
type = mime_type type
|
1401
|
-
type =~
|
1404
|
+
type =~ %r{^application/(xml|javascript)$} ? [type, "text/#{$1}"] : [type]
|
1402
1405
|
end
|
1403
1406
|
|
1404
1407
|
# Define a before filter; runs before all requests within the same
|
@@ -1427,7 +1430,7 @@ module Sinatra
|
|
1427
1430
|
end
|
1428
1431
|
|
1429
1432
|
def public=(value)
|
1430
|
-
|
1433
|
+
warn_for_deprecation ':public is no longer used to avoid overloading Module#public, use :public_folder or :public_dir instead'
|
1431
1434
|
set(:public_folder, value)
|
1432
1435
|
end
|
1433
1436
|
|
@@ -1449,14 +1452,21 @@ module Sinatra
|
|
1449
1452
|
route('HEAD', path, opts, &block)
|
1450
1453
|
end
|
1451
1454
|
|
1452
|
-
def put(path, opts = {}, &
|
1453
|
-
|
1454
|
-
def
|
1455
|
-
|
1456
|
-
def
|
1457
|
-
|
1458
|
-
def
|
1459
|
-
|
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
|
1460
1470
|
|
1461
1471
|
# Makes the methods defined in the block and in the Modules given
|
1462
1472
|
# in `extensions` available to the handlers and templates
|
@@ -1496,37 +1506,39 @@ module Sinatra
|
|
1496
1506
|
# Stop the self-hosted server if running.
|
1497
1507
|
def quit!
|
1498
1508
|
return unless running?
|
1509
|
+
|
1499
1510
|
# Use Thin's hard #stop! if available, otherwise just #stop.
|
1500
1511
|
running_server.respond_to?(:stop!) ? running_server.stop! : running_server.stop
|
1501
|
-
|
1512
|
+
warn '== Sinatra has ended his set (crowd applauds)' unless suppress_messages?
|
1502
1513
|
set :running_server, nil
|
1503
1514
|
set :handler_name, nil
|
1504
1515
|
end
|
1505
1516
|
|
1506
|
-
|
1517
|
+
alias stop! quit!
|
1507
1518
|
|
1508
1519
|
# Run the Sinatra app as a self-hosted server using
|
1509
|
-
# 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
|
1510
1521
|
# with the constructed handler once we have taken the stage.
|
1511
1522
|
def run!(options = {}, &block)
|
1512
1523
|
return if running?
|
1524
|
+
|
1513
1525
|
set options
|
1514
1526
|
handler = Rack::Handler.pick(server)
|
1515
1527
|
handler_name = handler.name.gsub(/.*::/, '')
|
1516
1528
|
server_settings = settings.respond_to?(:server_settings) ? settings.server_settings : {}
|
1517
|
-
server_settings.merge!(:
|
1529
|
+
server_settings.merge!(Port: port, Host: bind)
|
1518
1530
|
|
1519
1531
|
begin
|
1520
1532
|
start_server(handler, server_settings, handler_name, &block)
|
1521
1533
|
rescue Errno::EADDRINUSE
|
1522
|
-
|
1534
|
+
warn "== Someone is already performing on port #{port}!"
|
1523
1535
|
raise
|
1524
1536
|
ensure
|
1525
1537
|
quit!
|
1526
1538
|
end
|
1527
1539
|
end
|
1528
1540
|
|
1529
|
-
|
1541
|
+
alias start! run!
|
1530
1542
|
|
1531
1543
|
# Check whether the self-hosted server is running or not.
|
1532
1544
|
def running?
|
@@ -1544,8 +1556,8 @@ module Sinatra
|
|
1544
1556
|
# Create a new instance of the class fronted by its middleware
|
1545
1557
|
# pipeline. The object is guaranteed to respond to #call but may not be
|
1546
1558
|
# an instance of the class new was called on.
|
1547
|
-
def new(*args, &
|
1548
|
-
instance = new!(*args, &
|
1559
|
+
def new(*args, &block)
|
1560
|
+
instance = new!(*args, &block)
|
1549
1561
|
Wrapper.new(build(instance).to_app, instance)
|
1550
1562
|
end
|
1551
1563
|
ruby2_keywords :new if respond_to?(:ruby2_keywords, true)
|
@@ -1570,12 +1582,6 @@ module Sinatra
|
|
1570
1582
|
cleaned_caller(1).flatten
|
1571
1583
|
end
|
1572
1584
|
|
1573
|
-
# Like caller_files, but containing Arrays rather than strings with the
|
1574
|
-
# first element being the file, and the second being the line.
|
1575
|
-
def caller_locations
|
1576
|
-
cleaned_caller 2
|
1577
|
-
end
|
1578
|
-
|
1579
1585
|
private
|
1580
1586
|
|
1581
1587
|
# Starts the server by running the Rack Handler.
|
@@ -1586,7 +1592,7 @@ module Sinatra
|
|
1586
1592
|
# Run the instance we created:
|
1587
1593
|
handler.run(self, **server_settings) do |server|
|
1588
1594
|
unless suppress_messages?
|
1589
|
-
|
1595
|
+
warn "== Sinatra (v#{Sinatra::VERSION}) has taken the stage on #{port} for #{environment} with backup from #{handler_name}"
|
1590
1596
|
end
|
1591
1597
|
|
1592
1598
|
setup_traps
|
@@ -1603,18 +1609,18 @@ module Sinatra
|
|
1603
1609
|
end
|
1604
1610
|
|
1605
1611
|
def setup_traps
|
1606
|
-
|
1607
|
-
at_exit { quit! }
|
1612
|
+
return unless traps?
|
1608
1613
|
|
1609
|
-
|
1610
|
-
old_handler = trap(signal) do
|
1611
|
-
quit!
|
1612
|
-
old_handler.call if old_handler.respond_to?(:call)
|
1613
|
-
end
|
1614
|
-
end
|
1614
|
+
at_exit { quit! }
|
1615
1615
|
|
1616
|
-
|
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
|
1617
1621
|
end
|
1622
|
+
|
1623
|
+
set :traps, false
|
1618
1624
|
end
|
1619
1625
|
|
1620
1626
|
# Dynamically defines a method on settings.
|
@@ -1642,18 +1648,21 @@ module Sinatra
|
|
1642
1648
|
end
|
1643
1649
|
end
|
1644
1650
|
end
|
1645
|
-
|
1651
|
+
alias agent user_agent
|
1646
1652
|
|
1647
1653
|
# Condition for matching mimetypes. Accepts file extensions.
|
1648
1654
|
def provides(*types)
|
1649
1655
|
types.map! { |t| mime_types(t) }
|
1650
1656
|
types.flatten!
|
1651
1657
|
condition do
|
1652
|
-
|
1653
|
-
|
1654
|
-
|
1655
|
-
|
1656
|
-
|
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)
|
1657
1666
|
true
|
1658
1667
|
else
|
1659
1668
|
false
|
@@ -1662,7 +1671,7 @@ module Sinatra
|
|
1662
1671
|
end
|
1663
1672
|
|
1664
1673
|
def route(verb, path, options = {}, &block)
|
1665
|
-
enable :empty_path_info if path ==
|
1674
|
+
enable :empty_path_info if path == '' && empty_path_info.nil?
|
1666
1675
|
signature = compile!(verb, path, block, **options)
|
1667
1676
|
(@routes[verb] ||= []) << signature
|
1668
1677
|
invoke_hook(:route_added, verb, path, block)
|
@@ -1691,12 +1700,13 @@ module Sinatra
|
|
1691
1700
|
pattern = compile(path, route_mustermann_opts)
|
1692
1701
|
method_name = "#{verb} #{path}"
|
1693
1702
|
unbound_method = generate_method(method_name, &block)
|
1694
|
-
conditions
|
1695
|
-
|
1696
|
-
|
1697
|
-
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) }
|
1698
1708
|
|
1699
|
-
[
|
1709
|
+
[pattern, conditions, wrapper]
|
1700
1710
|
end
|
1701
1711
|
|
1702
1712
|
def compile(path, route_mustermann_opts = {})
|
@@ -1714,7 +1724,7 @@ module Sinatra
|
|
1714
1724
|
end
|
1715
1725
|
|
1716
1726
|
def setup_middleware(builder)
|
1717
|
-
middleware.each { |c,a,b| builder.use(c, *a, &b) }
|
1727
|
+
middleware.each { |c, a, b| builder.use(c, *a, &b) }
|
1718
1728
|
end
|
1719
1729
|
|
1720
1730
|
def setup_logging(builder)
|
@@ -1744,9 +1754,10 @@ module Sinatra
|
|
1744
1754
|
|
1745
1755
|
def setup_protection(builder)
|
1746
1756
|
return unless protection?
|
1757
|
+
|
1747
1758
|
options = Hash === protection ? protection.dup : {}
|
1748
1759
|
options = {
|
1749
|
-
img_src:
|
1760
|
+
img_src: "'self' data:",
|
1750
1761
|
font_src: "'self'"
|
1751
1762
|
}.merge options
|
1752
1763
|
|
@@ -1760,6 +1771,7 @@ module Sinatra
|
|
1760
1771
|
|
1761
1772
|
def setup_sessions(builder)
|
1762
1773
|
return unless sessions?
|
1774
|
+
|
1763
1775
|
options = {}
|
1764
1776
|
options[:secret] = session_secret if session_secret?
|
1765
1777
|
options.merge! sessions.to_hash if sessions.respond_to? :to_hash
|
@@ -1782,15 +1794,15 @@ module Sinatra
|
|
1782
1794
|
end
|
1783
1795
|
|
1784
1796
|
# used for deprecation warnings
|
1785
|
-
def
|
1786
|
-
|
1797
|
+
def warn_for_deprecation(message)
|
1798
|
+
warn message + "\n\tfrom #{cleaned_caller.first.join(':')}"
|
1787
1799
|
end
|
1788
1800
|
|
1789
1801
|
# Like Kernel#caller but excluding certain magic entries
|
1790
1802
|
def cleaned_caller(keep = 3)
|
1791
|
-
caller(1)
|
1792
|
-
map!
|
1793
|
-
reject { |file, *_| callers_to_ignore.any? { |pattern| file =~ pattern } }
|
1803
|
+
caller(1)
|
1804
|
+
.map! { |line| line.split(/:(?=\d|in )/, 3)[0, keep] }
|
1805
|
+
.reject { |file, *_| callers_to_ignore.any? { |pattern| file =~ pattern } }
|
1794
1806
|
end
|
1795
1807
|
end
|
1796
1808
|
|
@@ -1798,6 +1810,7 @@ module Sinatra
|
|
1798
1810
|
# which is UTF-8 by default
|
1799
1811
|
def self.force_encoding(data, encoding = default_encoding)
|
1800
1812
|
return if data == settings || data.is_a?(Tempfile)
|
1813
|
+
|
1801
1814
|
if data.respond_to? :force_encoding
|
1802
1815
|
data.force_encoding(encoding).encode!
|
1803
1816
|
elsif data.respond_to? :each_value
|
@@ -1808,24 +1821,26 @@ module Sinatra
|
|
1808
1821
|
data
|
1809
1822
|
end
|
1810
1823
|
|
1811
|
-
def force_encoding(*args)
|
1824
|
+
def force_encoding(*args)
|
1825
|
+
settings.force_encoding(*args)
|
1826
|
+
end
|
1812
1827
|
|
1813
1828
|
reset!
|
1814
1829
|
|
1815
1830
|
set :environment, (ENV['APP_ENV'] || ENV['RACK_ENV'] || :development).to_sym
|
1816
|
-
set :raise_errors,
|
1817
|
-
set :dump_errors,
|
1818
|
-
set :show_exceptions,
|
1831
|
+
set :raise_errors, proc { test? }
|
1832
|
+
set :dump_errors, proc { !test? }
|
1833
|
+
set :show_exceptions, proc { development? }
|
1819
1834
|
set :sessions, false
|
1820
|
-
set :session_store, Rack::
|
1835
|
+
set :session_store, Rack::Protection::EncryptedCookie
|
1821
1836
|
set :logging, false
|
1822
1837
|
set :protection, true
|
1823
1838
|
set :method_override, false
|
1824
1839
|
set :use_code, false
|
1825
|
-
set :default_encoding,
|
1840
|
+
set :default_encoding, 'utf-8'
|
1826
1841
|
set :x_cascade, true
|
1827
1842
|
set :add_charset, %w[javascript xml xhtml+xml].map { |t| "application/#{t}" }
|
1828
|
-
settings.add_charset <<
|
1843
|
+
settings.add_charset << %r{^text/}
|
1829
1844
|
set :mustermann_opts, {}
|
1830
1845
|
set :default_content_type, 'text/html'
|
1831
1846
|
|
@@ -1833,14 +1848,15 @@ module Sinatra
|
|
1833
1848
|
begin
|
1834
1849
|
require 'securerandom'
|
1835
1850
|
set :session_secret, SecureRandom.hex(64)
|
1836
|
-
rescue LoadError, NotImplementedError
|
1851
|
+
rescue LoadError, NotImplementedError, RuntimeError
|
1837
1852
|
# SecureRandom raises a NotImplementedError if no random device is available
|
1838
|
-
|
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))
|
1839
1855
|
end
|
1840
1856
|
|
1841
1857
|
class << self
|
1842
|
-
|
1843
|
-
|
1858
|
+
alias methodoverride? method_override?
|
1859
|
+
alias methodoverride= method_override=
|
1844
1860
|
end
|
1845
1861
|
|
1846
1862
|
set :run, false # start server via at-exit hook?
|
@@ -1848,21 +1864,17 @@ module Sinatra
|
|
1848
1864
|
set :handler_name, nil
|
1849
1865
|
set :traps, true
|
1850
1866
|
set :server, %w[HTTP webrick]
|
1851
|
-
set :bind,
|
1867
|
+
set :bind, proc { development? ? 'localhost' : '0.0.0.0' }
|
1852
1868
|
set :port, Integer(ENV['PORT'] && !ENV['PORT'].empty? ? ENV['PORT'] : 4567)
|
1853
1869
|
set :quiet, false
|
1854
1870
|
|
1855
1871
|
ruby_engine = defined?(RUBY_ENGINE) && RUBY_ENGINE
|
1856
1872
|
|
1857
|
-
|
1858
|
-
|
1859
|
-
|
1860
|
-
|
1861
|
-
|
1862
|
-
server.unshift 'mongrel' if ruby_engine.nil?
|
1863
|
-
server.unshift 'thin' if ruby_engine != 'jruby'
|
1864
|
-
server.unshift 'trinidad' if ruby_engine == 'jruby'
|
1865
|
-
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'
|
1866
1878
|
|
1867
1879
|
set :absolute_redirects, true
|
1868
1880
|
set :prefixed_redirects, false
|
@@ -1870,14 +1882,14 @@ module Sinatra
|
|
1870
1882
|
set :strict_paths, true
|
1871
1883
|
|
1872
1884
|
set :app_file, nil
|
1873
|
-
set :root,
|
1874
|
-
set :views,
|
1875
|
-
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? }
|
1876
1888
|
set :lock, false
|
1877
1889
|
set :threaded, true
|
1878
1890
|
|
1879
|
-
set :public_folder,
|
1880
|
-
set :static,
|
1891
|
+
set :public_folder, proc { root && File.join(root, 'public') }
|
1892
|
+
set :static, proc { public_folder && File.exist?(public_folder) }
|
1881
1893
|
set :static_cache_control, false
|
1882
1894
|
|
1883
1895
|
error ::Exception do
|
@@ -1896,7 +1908,7 @@ module Sinatra
|
|
1896
1908
|
error NotFound do
|
1897
1909
|
content_type 'text/html'
|
1898
1910
|
|
1899
|
-
if
|
1911
|
+
if instance_of?(Sinatra::Application)
|
1900
1912
|
code = <<-RUBY.gsub(/^ {12}/, '')
|
1901
1913
|
#{request.request_method.downcase} '#{request.path_info}' do
|
1902
1914
|
"Hello World"
|
@@ -1911,11 +1923,11 @@ module Sinatra
|
|
1911
1923
|
end
|
1912
1924
|
RUBY
|
1913
1925
|
|
1914
|
-
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{^/}, '')
|
1915
1927
|
code = "# in #{file}\n#{code}" unless file.empty?
|
1916
1928
|
end
|
1917
1929
|
|
1918
|
-
|
1930
|
+
<<-HTML.gsub(/^ {10}/, '')
|
1919
1931
|
<!DOCTYPE html>
|
1920
1932
|
<html>
|
1921
1933
|
<head>
|
@@ -1927,7 +1939,7 @@ module Sinatra
|
|
1927
1939
|
</head>
|
1928
1940
|
<body>
|
1929
1941
|
<h2>Sinatra doesn’t know this ditty.</h2>
|
1930
|
-
<img src='#{uri
|
1942
|
+
<img src='#{uri '/__sinatra__/404.png'}'>
|
1931
1943
|
<div id="c">
|
1932
1944
|
Try this:
|
1933
1945
|
<pre>#{Rack::Utils.escape_html(code)}</pre>
|
@@ -1947,12 +1959,12 @@ module Sinatra
|
|
1947
1959
|
# top-level. Subclassing Sinatra::Base is highly recommended for
|
1948
1960
|
# modular applications.
|
1949
1961
|
class Application < Base
|
1950
|
-
set :logging,
|
1962
|
+
set :logging, proc { !test? }
|
1951
1963
|
set :method_override, true
|
1952
|
-
set :run,
|
1964
|
+
set :run, proc { !test? }
|
1953
1965
|
set :app_file, nil
|
1954
1966
|
|
1955
|
-
def self.register(*extensions, &block)
|
1967
|
+
def self.register(*extensions, &block) # :nodoc:
|
1956
1968
|
added_methods = extensions.flat_map(&:public_instance_methods)
|
1957
1969
|
Delegator.delegate(*added_methods)
|
1958
1970
|
super(*extensions, &block)
|
@@ -1962,11 +1974,12 @@ module Sinatra
|
|
1962
1974
|
# Sinatra delegation mixin. Mixing this module into an object causes all
|
1963
1975
|
# methods to be delegated to the Sinatra::Application class. Used primarily
|
1964
1976
|
# at the top-level.
|
1965
|
-
module Delegator
|
1977
|
+
module Delegator # :nodoc:
|
1966
1978
|
def self.delegate(*methods)
|
1967
1979
|
methods.each do |method_name|
|
1968
1980
|
define_method(method_name) do |*args, &block|
|
1969
1981
|
return super(*args, &block) if respond_to? method_name
|
1982
|
+
|
1970
1983
|
Delegator.target.send(method_name, *args, &block)
|
1971
1984
|
end
|
1972
1985
|
# ensure keyword argument passing is compatible with ruby >= 2.7
|
@@ -1989,7 +2002,8 @@ module Sinatra
|
|
1989
2002
|
|
1990
2003
|
class Wrapper
|
1991
2004
|
def initialize(stack, instance)
|
1992
|
-
@stack
|
2005
|
+
@stack = stack
|
2006
|
+
@instance = instance
|
1993
2007
|
end
|
1994
2008
|
|
1995
2009
|
def settings
|