sinatra 2.2.0 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
- # http://rubydoc.info/github/rack/rack/master/Rack/Request
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 = /(?:(?:\w+|\*)\/(?:\w+(?:\.|\-|\+)?|\*)*)\s*(?:;#{HEADER_PARAM})*/
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'] ||= begin
32
- if @env.include? 'HTTP_ACCEPT' and @env['HTTP_ACCEPT'].to_s != ''
33
- @env['HTTP_ACCEPT'].to_s.scan(HEADER_VALUE_WITH_PARAMS).
34
- map! { |e| AcceptEntry.new(e) }.sort
35
- else
36
- [AcceptEntry.new('*/*')]
37
- end
38
- end
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? "HTTP_X_FORWARDED_HOST"
59
+ @env.include? 'HTTP_X_FORWARDED_HOST'
59
60
  end
60
61
 
61
62
  def safe?
62
- get? or head? or options? or trace?
63
+ get? || head? || options? || trace?
63
64
  end
64
65
 
65
66
  def idempotent?
66
- safe? or put? or delete? or link? or unlink?
67
+ safe? || put? || delete? || link? || unlink?
67
68
  end
68
69
 
69
70
  def link?
70
- request_method == "LINK"
71
+ request_method == 'LINK'
71
72
  end
72
73
 
73
74
  def unlink?
74
- request_method == "UNLINK"
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 = Hash[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 <=> self.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
- [ @q, -@type.count('*'), @params.size ]
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 or to_str.respond_to?(*args)
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 = Hash[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.has_key?(k) || @params[k] == v }
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
- # http://rubydoc.info/github/rack/rack/master/Rack/Response
161
- # http://rubydoc.info/github/rack/rack/master/Rack/Response/Helpers
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 "Content-Length"
179
- headers.delete "Content-Type"
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["Content-Length"] = body.map(&:bytesize).reduce(0, :+).to_s
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["Content-Type"] and not headers["Content-Length"] and Array === body
200
+ headers['Content-Type'] && !headers['Content-Length'] && (Array === body)
200
201
  end
201
202
 
202
203
  def drop_content_info?
203
- informational? or drop_body?
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, callback = app.call(env), env['async.callback']
219
- return result unless callback and async?(*result)
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, status, headers, body)
228
- return unless body.respond_to? :close and env.include? 'async.close'
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, "only supports EventMachine at the moment" unless defined? EventMachine
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, headers, body)
243
+ def async?(status, _headers, body)
239
244
  return true if status == -1
240
- body.respond_to? :callback and body.respond_to? :errback
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
- alias call_without_check call unless method_defined? :call_without_check
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 BadRequest < TypeError #:nodoc:
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 < NameError #:nodoc:
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::File::Iterator) || value.is_a?(Stream)
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' and env["REQUEST_METHOD"] != 'GET'
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\+\.\-]*:/i
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? or request.port != (request.secure? ? 443 : 80)
316
- host << request.host_with_port
317
- else
318
- host << request.host
319
- end
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 ? addr : request.path_info).to_s
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
- code, body = 500, code.to_str if code.respond_to? :to_str
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
- fail "Unknown media type: %p" % type if mime_type.nil?
382
+ raise format('Unknown media type: %p', type) if mime_type.nil?
383
+
369
384
  mime_type = mime_type.dup
370
- unless params.include? :charset or settings.add_charset.all? { |p| not p === mime_type }
385
+ unless params.include?(:charset) || settings.add_charset.all? { |p| !(p === mime_type) }
371
386
  params[:charset] = params.delete('charset') || settings.default_encoding
372
387
  end
373
388
  params.delete :charset if mime_type.include? 'charset'
@@ -381,36 +396,43 @@ module Sinatra
381
396
  response['Content-Type'] = mime_type
382
397
  end
383
398
 
399
+ # https://html.spec.whatwg.org/#multipart-form-data
400
+ MULTIPART_FORM_DATA_REPLACEMENT_TABLE = {
401
+ '"' => '%22',
402
+ "\r" => '%0D',
403
+ "\n" => '%0A'
404
+ }.freeze
405
+
384
406
  # Set the Content-Disposition to "attachment" with the specified filename,
385
407
  # instructing the user agents to prompt to save.
386
408
  def attachment(filename = nil, disposition = :attachment)
387
409
  response['Content-Disposition'] = disposition.to_s.dup
388
- if filename
389
- params = '; filename="%s"' % File.basename(filename)
390
- response['Content-Disposition'] << params
391
- ext = File.extname(filename)
392
- content_type(ext) unless response['Content-Type'] or ext.empty?
393
- end
410
+ return unless filename
411
+
412
+ params = format('; filename="%s"', File.basename(filename).gsub(/["\r\n]/, MULTIPART_FORM_DATA_REPLACEMENT_TABLE))
413
+ response['Content-Disposition'] << params
414
+ ext = File.extname(filename)
415
+ content_type(ext) unless response['Content-Type'] || ext.empty?
394
416
  end
395
417
 
396
418
  # Use the contents of the file at +path+ as the response body.
397
419
  def send_file(path, opts = {})
398
- if opts[:type] or not response['Content-Type']
399
- content_type opts[:type] || File.extname(path), :default => 'application/octet-stream'
420
+ if opts[:type] || !response['Content-Type']
421
+ content_type opts[:type] || File.extname(path), default: 'application/octet-stream'
400
422
  end
401
423
 
402
424
  disposition = opts[:disposition]
403
425
  filename = opts[:filename]
404
- disposition = :attachment if disposition.nil? and filename
426
+ disposition = :attachment if disposition.nil? && filename
405
427
  filename = path if filename.nil?
406
428
  attachment(filename, disposition) if disposition
407
429
 
408
430
  last_modified opts[:last_modified] if opts[:last_modified]
409
431
 
410
- file = Rack::File.new(File.dirname(settings.app_file))
432
+ file = Rack::Files.new(File.dirname(settings.app_file))
411
433
  result = file.serving(request, path)
412
434
 
413
- result[1].each { |k,v| headers[k] ||= v }
435
+ result[1].each { |k, v| headers[k] ||= v }
414
436
  headers['Content-Length'] = result[1]['Content-Length']
415
437
  opts[:status] &&= Integer(opts[:status])
416
438
  halt (opts[:status] || result[0]), result[2]
@@ -431,12 +453,16 @@ module Sinatra
431
453
  def self.defer(*) yield end
432
454
 
433
455
  def initialize(scheduler = self.class, keep_open = false, &back)
434
- @back, @scheduler, @keep_open = back.to_proc, scheduler, keep_open
435
- @callbacks, @closed = [], false
456
+ @back = back.to_proc
457
+ @scheduler = scheduler
458
+ @keep_open = keep_open
459
+ @callbacks = []
460
+ @closed = false
436
461
  end
437
462
 
438
463
  def close
439
464
  return if closed?
465
+
440
466
  @closed = true
441
467
  @scheduler.schedule { @callbacks.each { |c| c.call } }
442
468
  end
@@ -448,8 +474,9 @@ module Sinatra
448
474
  @back.call(self)
449
475
  rescue Exception => e
450
476
  @scheduler.schedule { raise e }
477
+ ensure
478
+ close unless @keep_open
451
479
  end
452
- close unless @keep_open
453
480
  end
454
481
  end
455
482
 
@@ -460,6 +487,7 @@ module Sinatra
460
487
 
461
488
  def callback(&block)
462
489
  return yield if closed?
490
+
463
491
  @callbacks << block
464
492
  end
465
493
 
@@ -479,7 +507,16 @@ module Sinatra
479
507
  def stream(keep_open = false)
480
508
  scheduler = env['async.callback'] ? EventMachine : Stream
481
509
  current = @params.dup
482
- body Stream.new(scheduler, keep_open) { |out| with_params(current) { yield(out) } }
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
483
520
  end
484
521
 
485
522
  # Specify response freshness policy for HTTP caches (Cache-Control header).
@@ -493,18 +530,18 @@ module Sinatra
493
530
  # See RFC 2616 / 14.9 for more on standard cache control directives:
494
531
  # http://tools.ietf.org/html/rfc2616#section-14.9.1
495
532
  def cache_control(*values)
496
- if values.last.kind_of?(Hash)
533
+ if values.last.is_a?(Hash)
497
534
  hash = values.pop
498
- hash.reject! { |k, v| v == false }
535
+ hash.reject! { |_k, v| v == false }
499
536
  hash.reject! { |k, v| values << k if v == true }
500
537
  else
501
538
  hash = {}
502
539
  end
503
540
 
504
- values.map! { |value| value.to_s.tr('_','-') }
541
+ values.map! { |value| value.to_s.tr('_', '-') }
505
542
  hash.each do |key, value|
506
543
  key = key.to_s.tr('_', '-')
507
- value = value.to_i if ['max-age', 's-maxage'].include? key
544
+ value = value.to_i if %w[max-age s-maxage].include? key
508
545
  values << "#{key}=#{value}"
509
546
  end
510
547
 
@@ -521,7 +558,7 @@ module Sinatra
521
558
  # => Expires: Mon, 08 Jun 2009 08:50:17 GMT
522
559
  #
523
560
  def expires(amount, *values)
524
- values << {} unless values.last.kind_of?(Hash)
561
+ values << {} unless values.last.is_a?(Hash)
525
562
 
526
563
  if amount.is_a? Integer
527
564
  time = Time.now + amount.to_i
@@ -531,7 +568,7 @@ module Sinatra
531
568
  max_age = time - Time.now
532
569
  end
533
570
 
534
- values.last.merge!(:max_age => max_age)
571
+ values.last.merge!(max_age: max_age) { |_key, v1, v2| v1 || v2 }
535
572
  cache_control(*values)
536
573
 
537
574
  response['Expires'] = time.httpdate
@@ -546,17 +583,18 @@ module Sinatra
546
583
  # with a '304 Not Modified' response.
547
584
  def last_modified(time)
548
585
  return unless time
586
+
549
587
  time = time_for time
550
588
  response['Last-Modified'] = time.httpdate
551
589
  return if env['HTTP_IF_NONE_MATCH']
552
590
 
553
- if status == 200 and env['HTTP_IF_MODIFIED_SINCE']
591
+ if (status == 200) && env['HTTP_IF_MODIFIED_SINCE']
554
592
  # compare based on seconds since epoch
555
593
  since = Time.httpdate(env['HTTP_IF_MODIFIED_SINCE']).to_i
556
594
  halt 304 if since >= time.to_i
557
595
  end
558
596
 
559
- if (success? or status == 412) and env['HTTP_IF_UNMODIFIED_SINCE']
597
+ if (success? || (status == 412)) && env['HTTP_IF_UNMODIFIED_SINCE']
560
598
  # compare based on seconds since epoch
561
599
  since = Time.httpdate(env['HTTP_IF_UNMODIFIED_SINCE']).to_i
562
600
  halt 412 if since < time.to_i
@@ -564,7 +602,7 @@ module Sinatra
564
602
  rescue ArgumentError
565
603
  end
566
604
 
567
- ETAG_KINDS = [:strong, :weak]
605
+ ETAG_KINDS = %i[strong weak].freeze
568
606
  # Set the response entity tag (HTTP 'ETag' header) and halt if conditional
569
607
  # GET matches. The +value+ argument is an identifier that uniquely
570
608
  # identifies the current version of the resource. The +kind+ argument
@@ -576,27 +614,31 @@ module Sinatra
576
614
  # GET or HEAD, a '304 Not Modified' response is sent.
577
615
  def etag(value, options = {})
578
616
  # Before touching this code, please double check RFC 2616 14.24 and 14.26.
579
- options = {:kind => options} unless Hash === options
617
+ options = { kind: options } unless Hash === options
580
618
  kind = options[:kind] || :strong
581
619
  new_resource = options.fetch(:new_resource) { request.post? }
582
620
 
583
621
  unless ETAG_KINDS.include?(kind)
584
- raise ArgumentError, ":strong or :weak expected"
622
+ raise ArgumentError, ':strong or :weak expected'
585
623
  end
586
624
 
587
- value = '"%s"' % value
625
+ value = format('"%s"', value)
588
626
  value = "W/#{value}" if kind == :weak
589
627
  response['ETag'] = value
590
628
 
591
- if success? or status == 304
592
- if etag_matches? env['HTTP_IF_NONE_MATCH'], new_resource
593
- halt(request.safe? ? 304 : 412)
594
- end
629
+ return unless success? || status == 304
595
630
 
596
- if env['HTTP_IF_MATCH']
597
- halt 412 unless etag_matches? env['HTTP_IF_MATCH'], new_resource
598
- end
631
+ if etag_matches?(env['HTTP_IF_NONE_MATCH'], new_resource)
632
+ halt(request.safe? ? 304 : 412)
599
633
  end
634
+
635
+ if env['HTTP_IF_MATCH']
636
+ return if etag_matches?(env['HTTP_IF_MATCH'], new_resource)
637
+
638
+ halt 412
639
+ end
640
+
641
+ nil
600
642
  end
601
643
 
602
644
  # Sugar for redirect (example: redirect back)
@@ -649,8 +691,8 @@ module Sinatra
649
691
  else
650
692
  value.to_time
651
693
  end
652
- rescue ArgumentError => boom
653
- raise boom
694
+ rescue ArgumentError => e
695
+ raise e
654
696
  rescue Exception
655
697
  raise ArgumentError, "unable to convert #{value.inspect} to a Time object"
656
698
  end
@@ -660,11 +702,13 @@ module Sinatra
660
702
  # Helper method checking if a ETag value list includes the current ETag.
661
703
  def etag_matches?(list, new_resource = request.post?)
662
704
  return !new_resource if list == '*'
705
+
663
706
  list.to_s.split(/\s*,\s*/).include? response['ETag']
664
707
  end
665
708
 
666
709
  def with_params(temp_params)
667
- original, @params = @params, temp_params
710
+ original = @params
711
+ @params = temp_params
668
712
  yield
669
713
  ensure
670
714
  @params = original if original
@@ -682,7 +726,7 @@ module Sinatra
682
726
  # Possible options are:
683
727
  # :content_type The content type to use, same arguments as content_type.
684
728
  # :layout If set to something falsy, no layout is rendered, otherwise
685
- # the specified layout is used (Ignored for `sass` and `less`)
729
+ # the specified layout is used (Ignored for `sass`)
686
730
  # :layout_engine Engine to use for rendering the layout.
687
731
  # :locals A hash with local variables that should be available
688
732
  # in the template
@@ -704,36 +748,24 @@ module Sinatra
704
748
  render(:erb, template, options, locals, &block)
705
749
  end
706
750
 
707
- def erubis(template, options = {}, locals = {})
708
- warn "Sinatra::Templates#erubis is deprecated and will be removed, use #erb instead.\n" \
709
- "If you have Erubis installed, it will be used automatically."
710
- render :erubis, template, options, locals
711
- end
712
-
713
751
  def haml(template, options = {}, locals = {}, &block)
714
752
  render(:haml, template, options, locals, &block)
715
753
  end
716
754
 
717
755
  def sass(template, options = {}, locals = {})
718
- options.merge! :layout => false, :default_content_type => :css
756
+ options[:default_content_type] = :css
757
+ options[:exclude_outvar] = true
758
+ options[:layout] = nil
719
759
  render :sass, template, options, locals
720
760
  end
721
761
 
722
762
  def scss(template, options = {}, locals = {})
723
- options.merge! :layout => false, :default_content_type => :css
763
+ options[:default_content_type] = :css
764
+ options[:exclude_outvar] = true
765
+ options[:layout] = nil
724
766
  render :scss, template, options, locals
725
767
  end
726
768
 
727
- def less(template, options = {}, locals = {})
728
- options.merge! :layout => false, :default_content_type => :css
729
- render :less, template, options, locals
730
- end
731
-
732
- def stylus(template, options = {}, locals = {})
733
- options.merge! :layout => false, :default_content_type => :css
734
- render :styl, template, options, locals
735
- end
736
-
737
769
  def builder(template = nil, options = {}, locals = {}, &block)
738
770
  options[:default_content_type] = :xml
739
771
  render_ruby(:builder, template, options, locals, &block)
@@ -748,10 +780,6 @@ module Sinatra
748
780
  render :markdown, template, options, locals
749
781
  end
750
782
 
751
- def textile(template, options = {}, locals = {})
752
- render :textile, template, options, locals
753
- end
754
-
755
783
  def rdoc(template, options = {}, locals = {})
756
784
  render :rdoc, template, options, locals
757
785
  end
@@ -760,19 +788,10 @@ module Sinatra
760
788
  render :asciidoc, template, options, locals
761
789
  end
762
790
 
763
- def radius(template, options = {}, locals = {})
764
- render :radius, template, options, locals
765
- end
766
-
767
791
  def markaby(template = nil, options = {}, locals = {}, &block)
768
792
  render_ruby(:mab, template, options, locals, &block)
769
793
  end
770
794
 
771
- def coffee(template, options = {}, locals = {})
772
- options.merge! :layout => false, :default_content_type => :js
773
- render :coffee, template, options, locals
774
- end
775
-
776
795
  def nokogiri(template = nil, options = {}, locals = {}, &block)
777
796
  options[:default_content_type] = :xml
778
797
  render_ruby(:nokogiri, template, options, locals, &block)
@@ -782,18 +801,6 @@ module Sinatra
782
801
  render(:slim, template, options, locals, &block)
783
802
  end
784
803
 
785
- def creole(template, options = {}, locals = {})
786
- render :creole, template, options, locals
787
- end
788
-
789
- def mediawiki(template, options = {}, locals = {})
790
- render :mediawiki, template, options, locals
791
- end
792
-
793
- def wlang(template, options = {}, locals = {}, &block)
794
- render(:wlang, template, options, locals, &block)
795
- end
796
-
797
804
  def yajl(template, options = {}, locals = {})
798
805
  options[:default_content_type] = :json
799
806
  render :yajl, template, options, locals
@@ -818,24 +825,27 @@ module Sinatra
818
825
 
819
826
  # logic shared between builder and nokogiri
820
827
  def render_ruby(engine, template, options = {}, locals = {}, &block)
821
- options, template = template, nil if template.is_a?(Hash)
822
- template = Proc.new { block } if template.nil?
828
+ if template.is_a?(Hash)
829
+ options = template
830
+ template = nil
831
+ end
832
+ template = proc { block } if template.nil?
823
833
  render engine, template, options, locals
824
834
  end
825
835
 
826
836
  def render(engine, data, options = {}, locals = {}, &block)
827
837
  # merge app-level options
828
838
  engine_options = settings.respond_to?(engine) ? settings.send(engine) : {}
829
- options.merge!(engine_options) { |key, v1, v2| v1 }
839
+ options.merge!(engine_options) { |_key, v1, _v2| v1 }
830
840
 
831
841
  # extract generic options
832
842
  locals = options.delete(:locals) || locals || {}
833
- views = options.delete(:views) || settings.views || "./views"
843
+ views = options.delete(:views) || settings.views || './views'
834
844
  layout = options[:layout]
835
845
  layout = false if layout.nil? && options.include?(:layout)
836
846
  eat_errors = layout.nil?
837
- layout = engine_options[:layout] if layout.nil? or (layout == true && engine_options[:layout] != false)
838
- layout = @default_layout if layout.nil? or layout == true
847
+ layout = engine_options[:layout] if layout.nil? || (layout == true && engine_options[:layout] != false)
848
+ layout = @default_layout if layout.nil? || (layout == true)
839
849
  layout_options = options.delete(:layout_options) || {}
840
850
  content_type = options.delete(:default_content_type)
841
851
  content_type = options.delete(:content_type) || content_type
@@ -860,12 +870,17 @@ module Sinatra
860
870
 
861
871
  # render layout
862
872
  if layout
863
- options = options.merge(:views => views, :layout => false, :eat_errors => eat_errors, :scope => scope).
864
- merge!(layout_options)
873
+ extra_options = { views: views, layout: false, eat_errors: eat_errors, scope: scope }
874
+ options = options.merge(extra_options).merge!(layout_options)
875
+
865
876
  catch(:layout_missing) { return render(layout_engine, layout, options, locals) { output } }
866
877
  end
867
878
 
868
- output.extend(ContentTyped).content_type = content_type if content_type
879
+ if content_type
880
+ # sass-embedded returns a frozen string
881
+ output = +output
882
+ output.extend(ContentTyped).content_type = content_type
883
+ end
869
884
  output
870
885
  end
871
886
 
@@ -886,12 +901,13 @@ module Sinatra
886
901
  @preferred_extension = engine.to_s
887
902
  find_template(views, data, template) do |file|
888
903
  path ||= file # keep the initial path rather than the last one
889
- if found = File.exist?(file)
904
+ found = File.exist?(file)
905
+ if found
890
906
  path = file
891
907
  break
892
908
  end
893
909
  end
894
- throw :layout_missing if eat_errors and not found
910
+ throw :layout_missing if eat_errors && !found
895
911
  template.new(path, 1, options)
896
912
  end
897
913
  end
@@ -907,13 +923,44 @@ module Sinatra
907
923
  end
908
924
 
909
925
  def compile_block_template(template, options, &body)
910
- caller = settings.caller_locations.first
911
- path = options[:path] || caller[0]
912
- line = options[:line] || caller[1]
926
+ first_location = caller_locations.first
927
+ path = first_location.path
928
+ line = first_location.lineno
929
+ path = options[:path] || path
930
+ line = options[:line] || line
913
931
  template.new(path, line.to_i, options, &body)
914
932
  end
915
933
  end
916
934
 
935
+ # Extremely simple template cache implementation.
936
+ # * Not thread-safe.
937
+ # * Size is unbounded.
938
+ # * Keys are not copied defensively, and should not be modified after
939
+ # being passed to #fetch. More specifically, the values returned by
940
+ # key#hash and key#eql? should not change.
941
+ #
942
+ # Implementation copied from Tilt::Cache.
943
+ class TemplateCache
944
+ def initialize
945
+ @cache = {}
946
+ end
947
+
948
+ # Caches a value for key, or returns the previously cached value.
949
+ # If a value has been previously cached for key then it is
950
+ # returned. Otherwise, block is yielded to and its return value
951
+ # which may be nil, is cached under key and returned.
952
+ def fetch(*key)
953
+ @cache.fetch(key) do
954
+ @cache[key] = yield
955
+ end
956
+ end
957
+
958
+ # Clears the cache.
959
+ def clear
960
+ @cache = {}
961
+ end
962
+ end
963
+
917
964
  # Base class for all Sinatra applications and middleware.
918
965
  class Base
919
966
  include Rack::Utils
@@ -925,10 +972,10 @@ module Sinatra
925
972
  attr_accessor :app, :env, :request, :response, :params
926
973
  attr_reader :template_cache
927
974
 
928
- def initialize(app = nil, **kwargs)
975
+ def initialize(app = nil, **_kwargs)
929
976
  super()
930
977
  @app = app
931
- @template_cache = Tilt::Cache.new
978
+ @template_cache = TemplateCache.new
932
979
  @pinned_response = nil # whether a before! filter pinned the content-type
933
980
  yield self if block_given?
934
981
  end
@@ -952,7 +999,7 @@ module Sinatra
952
999
  unless @response['Content-Type']
953
1000
  if Array === body && body[0].respond_to?(:content_type)
954
1001
  content_type body[0].content_type
955
- elsif default = settings.default_content_type
1002
+ elsif (default = settings.default_content_type)
956
1003
  content_type default
957
1004
  end
958
1005
  end
@@ -970,12 +1017,6 @@ module Sinatra
970
1017
  self.class.settings
971
1018
  end
972
1019
 
973
- def options
974
- warn "Sinatra::Base#options is deprecated and will be removed, " \
975
- "use #settings instead."
976
- settings
977
- end
978
-
979
1020
  # Exit the current block, halts any further processing
980
1021
  # of the request, and returns the specified response.
981
1022
  def halt(*response)
@@ -992,7 +1033,8 @@ module Sinatra
992
1033
 
993
1034
  # Forward the request to the downstream app -- middleware only.
994
1035
  def forward
995
- fail "downstream app not set" unless @app.respond_to? :call
1036
+ raise 'downstream app not set' unless @app.respond_to? :call
1037
+
996
1038
  status, headers, body = @app.call env
997
1039
  @response.status = status
998
1040
  @response.body = body
@@ -1014,18 +1056,18 @@ module Sinatra
1014
1056
 
1015
1057
  # Run routes defined on the class and all superclasses.
1016
1058
  def route!(base = settings, pass_block = nil)
1017
- if routes = base.routes[@request.request_method]
1018
- routes.each do |pattern, conditions, block|
1019
- response.delete_header('Content-Type') unless @pinned_response
1059
+ routes = base.routes[@request.request_method]
1020
1060
 
1021
- returned_pass_block = process_route(pattern, conditions) do |*args|
1022
- env['sinatra.route'] = "#{@request.request_method} #{pattern}"
1023
- route_eval { block[*args] }
1024
- end
1061
+ routes&.each do |pattern, conditions, block|
1062
+ response.delete_header('Content-Type') unless @pinned_response
1025
1063
 
1026
- # don't wipe out pass_block in superclass
1027
- pass_block = returned_pass_block if returned_pass_block
1064
+ returned_pass_block = process_route(pattern, conditions) do |*args|
1065
+ env['sinatra.route'] = "#{@request.request_method} #{pattern}"
1066
+ route_eval { block[*args] }
1028
1067
  end
1068
+
1069
+ # don't wipe out pass_block in superclass
1070
+ pass_block = returned_pass_block if returned_pass_block
1029
1071
  end
1030
1072
 
1031
1073
  # Run routes defined in superclass.
@@ -1049,15 +1091,17 @@ module Sinatra
1049
1091
  # Returns pass block.
1050
1092
  def process_route(pattern, conditions, block = nil, values = [])
1051
1093
  route = @request.path_info
1052
- route = '/' if route.empty? and not settings.empty_path_info?
1094
+ route = '/' if route.empty? && !settings.empty_path_info?
1053
1095
  route = route[0..-2] if !settings.strict_paths? && route != '/' && route.end_with?('/')
1054
- return unless params = pattern.params(route)
1055
1096
 
1056
- params.delete("ignore") # TODO: better params handling, maybe turn it into "smart" object or detect changes
1097
+ params = pattern.params(route)
1098
+ return unless params
1099
+
1100
+ params.delete('ignore') # TODO: better params handling, maybe turn it into "smart" object or detect changes
1057
1101
  force_encoding(params)
1058
- @params = @params.merge(params) if params.any?
1102
+ @params = @params.merge(params) { |_k, v1, v2| v2 || v1 } if params.any?
1059
1103
 
1060
- regexp_exists = pattern.is_a?(Mustermann::Regular) || (pattern.respond_to?(:patterns) && pattern.patterns.any? {|subpattern| subpattern.is_a?(Mustermann::Regular)} )
1104
+ regexp_exists = pattern.is_a?(Mustermann::Regular) || (pattern.respond_to?(:patterns) && pattern.patterns.any? { |subpattern| subpattern.is_a?(Mustermann::Regular) })
1061
1105
  if regexp_exists
1062
1106
  captures = pattern.match(route).captures.map { |c| URI_INSTANCE.unescape(c) if c }
1063
1107
  values += captures
@@ -1070,7 +1114,7 @@ module Sinatra
1070
1114
  conditions.each { |c| throw :pass if c.bind(self).call == false }
1071
1115
  block ? block[self, values] : yield(self, values)
1072
1116
  end
1073
- rescue
1117
+ rescue StandardError
1074
1118
  @env['sinatra.error.params'] = @params
1075
1119
  raise
1076
1120
  ensure
@@ -1084,35 +1128,35 @@ module Sinatra
1084
1128
  # a NotFound exception. Subclasses can override this method to perform
1085
1129
  # custom route miss logic.
1086
1130
  def route_missing
1087
- if @app
1088
- forward
1089
- else
1090
- raise NotFound, "#{request.request_method} #{request.path_info}"
1091
- end
1131
+ raise NotFound unless @app
1132
+
1133
+ forward
1092
1134
  end
1093
1135
 
1094
1136
  # Attempt to serve static files from public directory. Throws :halt when
1095
1137
  # a matching file is found, returns nil otherwise.
1096
1138
  def static!(options = {})
1097
1139
  return if (public_dir = settings.public_folder).nil?
1140
+
1098
1141
  path = "#{public_dir}#{URI_INSTANCE.unescape(request.path_info)}"
1099
1142
  return unless valid_path?(path)
1100
1143
 
1101
1144
  path = File.expand_path(path)
1102
- return unless path.start_with?(File.expand_path(public_dir) + '/')
1145
+ return unless path.start_with?("#{File.expand_path(public_dir)}/")
1146
+
1103
1147
  return unless File.file?(path)
1104
1148
 
1105
1149
  env['sinatra.static_file'] = path
1106
1150
  cache_control(*settings.static_cache_control) if settings.static_cache_control?
1107
- send_file path, options.merge(:disposition => nil)
1151
+ send_file path, options.merge(disposition: nil)
1108
1152
  end
1109
1153
 
1110
1154
  # Run the block with 'throw :halt' support and apply result to the response.
1111
- def invoke
1112
- res = catch(:halt) { yield }
1155
+ def invoke(&block)
1156
+ res = catch(:halt, &block)
1113
1157
 
1114
- res = [res] if Integer === res or String === res
1115
- if Array === res and Integer === res.first
1158
+ res = [res] if (Integer === res) || (String === res)
1159
+ if (Array === res) && (Integer === res.first)
1116
1160
  res = res.dup
1117
1161
  status(res.shift)
1118
1162
  body(res.pop)
@@ -1128,6 +1172,7 @@ module Sinatra
1128
1172
  # Avoid passing frozen string in force_encoding
1129
1173
  @params.merge!(@request.params).each do |key, val|
1130
1174
  next unless val.respond_to?(:force_encoding)
1175
+
1131
1176
  val = val.dup if val.frozen?
1132
1177
  @params[key] = force_encoding(val)
1133
1178
  end
@@ -1139,39 +1184,43 @@ module Sinatra
1139
1184
  end
1140
1185
  route!
1141
1186
  end
1142
- rescue ::Exception => boom
1143
- invoke { handle_exception!(boom) }
1187
+ rescue ::Exception => e
1188
+ invoke { handle_exception!(e) }
1144
1189
  ensure
1145
1190
  begin
1146
1191
  filter! :after unless env['sinatra.static_file']
1147
- rescue ::Exception => boom
1148
- invoke { handle_exception!(boom) } unless @env['sinatra.error']
1192
+ rescue ::Exception => e
1193
+ invoke { handle_exception!(e) } unless @env['sinatra.error']
1149
1194
  end
1150
1195
  end
1151
1196
 
1152
1197
  # Error handling during requests.
1153
1198
  def handle_exception!(boom)
1154
- if error_params = @env['sinatra.error.params']
1155
- @params = @params.merge(error_params)
1156
- end
1199
+ error_params = @env['sinatra.error.params']
1200
+
1201
+ @params = @params.merge(error_params) if error_params
1202
+
1157
1203
  @env['sinatra.error'] = boom
1158
1204
 
1159
- if boom.respond_to? :http_status and boom.http_status.between? 400, 599
1160
- status(boom.http_status)
1161
- elsif settings.use_code? and boom.respond_to? :code and boom.code.between? 400, 599
1162
- status(boom.code)
1163
- else
1164
- status(500)
1165
- end
1205
+ http_status = if boom.is_a? Sinatra::Error
1206
+ if boom.respond_to? :http_status
1207
+ boom.http_status
1208
+ elsif settings.use_code? && boom.respond_to?(:code)
1209
+ boom.code
1210
+ end
1211
+ end
1212
+
1213
+ http_status = 500 unless http_status&.between?(400, 599)
1214
+ status(http_status)
1166
1215
 
1167
1216
  if server_error?
1168
1217
  dump_errors! boom if settings.dump_errors?
1169
- raise boom if settings.show_exceptions? and settings.show_exceptions != :after_handler
1218
+ raise boom if settings.show_exceptions? && (settings.show_exceptions != :after_handler)
1170
1219
  elsif not_found?
1171
1220
  headers['X-Cascade'] = 'pass' if settings.x_cascade?
1172
1221
  end
1173
1222
 
1174
- if res = error_block!(boom.class, boom) || error_block!(status, boom)
1223
+ if (res = error_block!(boom.class, boom) || error_block!(status, boom))
1175
1224
  return res
1176
1225
  end
1177
1226
 
@@ -1180,12 +1229,14 @@ module Sinatra
1180
1229
  body Rack::Utils.escape_html(boom.message)
1181
1230
  else
1182
1231
  content_type 'text/html'
1183
- body '<h1>' + (not_found? ? 'Not Found' : 'Bad Request') + '</h1>'
1232
+ body "<h1>#{not_found? ? 'Not Found' : 'Bad Request'}</h1>"
1184
1233
  end
1185
1234
  end
1186
1235
 
1187
1236
  return unless server_error?
1188
- raise boom if settings.raise_errors? or settings.show_exceptions?
1237
+
1238
+ raise boom if settings.raise_errors? || settings.show_exceptions?
1239
+
1189
1240
  error_block! Exception, boom
1190
1241
  end
1191
1242
 
@@ -1193,7 +1244,10 @@ module Sinatra
1193
1244
  def error_block!(key, *block_params)
1194
1245
  base = settings
1195
1246
  while base.respond_to?(:errors)
1196
- next base = base.superclass unless args_array = base.errors[key]
1247
+ args_array = base.errors[key]
1248
+
1249
+ next base = base.superclass unless args_array
1250
+
1197
1251
  args_array.reverse_each do |args|
1198
1252
  first = args == args_array.first
1199
1253
  args += [block_params]
@@ -1201,51 +1255,50 @@ module Sinatra
1201
1255
  return resp unless resp.nil? && !first
1202
1256
  end
1203
1257
  end
1204
- return false unless key.respond_to? :superclass and key.superclass < Exception
1258
+ return false unless key.respond_to?(:superclass) && (key.superclass < Exception)
1259
+
1205
1260
  error_block!(key.superclass, *block_params)
1206
1261
  end
1207
1262
 
1208
1263
  def dump_errors!(boom)
1209
- msg = ["#{Time.now.strftime("%Y-%m-%d %H:%M:%S")} - #{boom.class} - #{boom.message}:", *boom.backtrace].join("\n\t")
1264
+ msg = ["#{Time.now.strftime('%Y-%m-%d %H:%M:%S')} - #{boom.class} - #{boom.message}:", *boom.backtrace].join("\n\t")
1210
1265
  @env['rack.errors'].puts(msg)
1211
1266
  end
1212
1267
 
1213
1268
  class << self
1214
1269
  CALLERS_TO_IGNORE = [ # :nodoc:
1215
- /\/sinatra(\/(base|main|show_exceptions))?\.rb$/, # all sinatra code
1216
- /lib\/tilt.*\.rb$/, # all tilt code
1270
+ %r{/sinatra(/(base|main|show_exceptions))?\.rb$}, # all sinatra code
1271
+ %r{lib/tilt.*\.rb$}, # all tilt code
1217
1272
  /^\(.*\)$/, # generated code
1218
- /rubygems\/(custom|core_ext\/kernel)_require\.rb$/, # rubygems require hacks
1273
+ %r{rubygems/(custom|core_ext/kernel)_require\.rb$}, # rubygems require hacks
1219
1274
  /active_support/, # active_support require hacks
1220
- /bundler(\/(?:runtime|inline))?\.rb/, # bundler require hacks
1275
+ %r{bundler(/(?:runtime|inline))?\.rb}, # bundler require hacks
1221
1276
  /<internal:/, # internal in ruby >= 1.9.2
1222
- /src\/kernel\/bootstrap\/[A-Z]/ # maglev kernel files
1223
- ]
1277
+ %r{zeitwerk/kernel\.rb} # Zeitwerk kernel#require decorator
1278
+ ].freeze
1224
1279
 
1225
- # contrary to what the comment said previously, rubinius never supported this
1226
- if defined?(RUBY_IGNORE_CALLERS)
1227
- warn "RUBY_IGNORE_CALLERS is deprecated and will no longer be supported by Sinatra 2.0"
1228
- CALLERS_TO_IGNORE.concat(RUBY_IGNORE_CALLERS)
1229
- end
1280
+ attr_reader :routes, :filters, :templates, :errors, :on_start_callback, :on_stop_callback
1230
1281
 
1231
- attr_reader :routes, :filters, :templates, :errors
1282
+ def callers_to_ignore
1283
+ CALLERS_TO_IGNORE
1284
+ end
1232
1285
 
1233
1286
  # Removes all routes, filters, middleware and extension hooks from the
1234
1287
  # current class (not routes/filters/... defined by its superclass).
1235
1288
  def reset!
1236
1289
  @conditions = []
1237
1290
  @routes = {}
1238
- @filters = {:before => [], :after => []}
1291
+ @filters = { before: [], after: [] }
1239
1292
  @errors = {}
1240
1293
  @middleware = []
1241
1294
  @prototype = nil
1242
1295
  @extensions = []
1243
1296
 
1244
- if superclass.respond_to?(:templates)
1245
- @templates = Hash.new { |hash, key| superclass.templates[key] }
1246
- else
1247
- @templates = {}
1248
- end
1297
+ @templates = if superclass.respond_to?(:templates)
1298
+ Hash.new { |_hash, key| superclass.templates[key] }
1299
+ else
1300
+ {}
1301
+ end
1249
1302
  end
1250
1303
 
1251
1304
  # Extension modules registered on this class and all superclasses.
@@ -1269,16 +1322,21 @@ module Sinatra
1269
1322
  # Sets an option to the given value. If the value is a proc,
1270
1323
  # the proc will be called every time the option is accessed.
1271
1324
  def set(option, value = (not_set = true), ignore_setter = false, &block)
1272
- raise ArgumentError if block and !not_set
1273
- value, not_set = block, false if block
1325
+ raise ArgumentError if block && !not_set
1326
+
1327
+ if block
1328
+ value = block
1329
+ not_set = false
1330
+ end
1274
1331
 
1275
1332
  if not_set
1276
1333
  raise ArgumentError unless option.respond_to?(:each)
1277
- option.each { |k,v| set(k, v) }
1334
+
1335
+ option.each { |k, v| set(k, v) }
1278
1336
  return self
1279
1337
  end
1280
1338
 
1281
- if respond_to?("#{option}=") and not ignore_setter
1339
+ if respond_to?("#{option}=") && !ignore_setter
1282
1340
  return __send__("#{option}=", value)
1283
1341
  end
1284
1342
 
@@ -1317,7 +1375,7 @@ module Sinatra
1317
1375
  # class, or an HTTP status code to specify which errors should be
1318
1376
  # handled.
1319
1377
  def error(*codes, &block)
1320
- args = compile! "ERROR", /.*/, block
1378
+ args = compile! 'ERROR', /.*/, block
1321
1379
  codes = codes.flat_map(&method(:Array))
1322
1380
  codes << Exception if codes.empty?
1323
1381
  codes << Sinatra::NotFound if codes.include?(404)
@@ -1343,7 +1401,7 @@ module Sinatra
1343
1401
  # Load embedded templates from the file; uses the caller's __FILE__
1344
1402
  # when no file is specified.
1345
1403
  def inline_templates=(file = nil)
1346
- file = (file.nil? || file == true) ? (caller_files.first || File.expand_path($0)) : file
1404
+ file = (caller_files.first || File.expand_path($0)) if file.nil? || file == true
1347
1405
 
1348
1406
  begin
1349
1407
  io = ::IO.respond_to?(:binread) ? ::IO.binread(file) : ::IO.read(file)
@@ -1352,23 +1410,24 @@ module Sinatra
1352
1410
  app, data = nil
1353
1411
  end
1354
1412
 
1355
- if data
1356
- if app and app =~ /([^\n]*\n)?#[^\n]*coding: *(\S+)/m
1357
- encoding = $2
1358
- else
1359
- encoding = settings.default_encoding
1360
- end
1361
- lines = app.count("\n") + 1
1362
- template = nil
1363
- force_encoding data, encoding
1364
- data.each_line do |line|
1365
- lines += 1
1366
- if line =~ /^@@\s*(.*\S)\s*$/
1367
- template = force_encoding(String.new, encoding)
1368
- templates[$1.to_sym] = [template, file, lines]
1369
- elsif template
1370
- template << line
1371
- end
1413
+ return unless data
1414
+
1415
+ encoding = if app && app =~ /([^\n]*\n)?#[^\n]*coding: *(\S+)/m
1416
+ $2
1417
+ else
1418
+ settings.default_encoding
1419
+ end
1420
+
1421
+ lines = app.count("\n") + 1
1422
+ template = nil
1423
+ force_encoding data, encoding
1424
+ data.each_line do |line|
1425
+ lines += 1
1426
+ if line =~ /^@@\s*(.*\S)\s*$/
1427
+ template = force_encoding(String.new, encoding)
1428
+ templates[$1.to_sym] = [template, file, lines]
1429
+ elsif template
1430
+ template << line
1372
1431
  end
1373
1432
  end
1374
1433
  end
@@ -1377,8 +1436,10 @@ module Sinatra
1377
1436
  def mime_type(type, value = nil)
1378
1437
  return type if type.nil?
1379
1438
  return type.to_s if type.to_s.include?('/')
1380
- type = ".#{type}" unless type.to_s[0] == ?.
1439
+
1440
+ type = ".#{type}" unless type.to_s[0] == '.'
1381
1441
  return Rack::Mime.mime_type(type, nil) unless value
1442
+
1382
1443
  Rack::Mime::MIME_TYPES[type] = value
1383
1444
  end
1384
1445
 
@@ -1387,7 +1448,7 @@ module Sinatra
1387
1448
  # mime_types :js # => ['application/javascript', 'text/javascript']
1388
1449
  def mime_types(type)
1389
1450
  type = mime_type type
1390
- type =~ /^application\/(xml|javascript)$/ ? [type, "text/#$1"] : [type]
1451
+ type =~ %r{^application/(xml|javascript)$} ? [type, "text/#{$1}"] : [type]
1391
1452
  end
1392
1453
 
1393
1454
  # Define a before filter; runs before all requests within the same
@@ -1409,6 +1470,14 @@ module Sinatra
1409
1470
  filters[type] << compile!(type, path, block, **options)
1410
1471
  end
1411
1472
 
1473
+ def on_start(&on_start_callback)
1474
+ @on_start_callback = on_start_callback
1475
+ end
1476
+
1477
+ def on_stop(&on_stop_callback)
1478
+ @on_stop_callback = on_stop_callback
1479
+ end
1480
+
1412
1481
  # Add a route condition. The route is considered non-matching when the
1413
1482
  # block returns false.
1414
1483
  def condition(name = "#{caller.first[/`.*'/]} condition", &block)
@@ -1416,7 +1485,7 @@ module Sinatra
1416
1485
  end
1417
1486
 
1418
1487
  def public=(value)
1419
- warn ":public is no longer used to avoid overloading Module#public, use :public_folder or :public_dir instead"
1488
+ warn_for_deprecation ':public is no longer used to avoid overloading Module#public, use :public_folder or :public_dir instead'
1420
1489
  set(:public_folder, value)
1421
1490
  end
1422
1491
 
@@ -1438,14 +1507,21 @@ module Sinatra
1438
1507
  route('HEAD', path, opts, &block)
1439
1508
  end
1440
1509
 
1441
- def put(path, opts = {}, &bk) route 'PUT', path, opts, &bk end
1442
- def post(path, opts = {}, &bk) route 'POST', path, opts, &bk end
1443
- def delete(path, opts = {}, &bk) route 'DELETE', path, opts, &bk end
1444
- def head(path, opts = {}, &bk) route 'HEAD', path, opts, &bk end
1445
- def options(path, opts = {}, &bk) route 'OPTIONS', path, opts, &bk end
1446
- def patch(path, opts = {}, &bk) route 'PATCH', path, opts, &bk end
1447
- def link(path, opts = {}, &bk) route 'LINK', path, opts, &bk end
1448
- def unlink(path, opts = {}, &bk) route 'UNLINK', path, opts, &bk end
1510
+ def put(path, opts = {}, &block) route 'PUT', path, opts, &block end
1511
+
1512
+ def post(path, opts = {}, &block) route 'POST', path, opts, &block end
1513
+
1514
+ def delete(path, opts = {}, &block) route 'DELETE', path, opts, &block end
1515
+
1516
+ def head(path, opts = {}, &block) route 'HEAD', path, opts, &block end
1517
+
1518
+ def options(path, opts = {}, &block) route 'OPTIONS', path, opts, &block end
1519
+
1520
+ def patch(path, opts = {}, &block) route 'PATCH', path, opts, &block end
1521
+
1522
+ def link(path, opts = {}, &block) route 'LINK', path, opts, &block end
1523
+
1524
+ def unlink(path, opts = {}, &block) route 'UNLINK', path, opts, &block end
1449
1525
 
1450
1526
  # Makes the methods defined in the block and in the Modules given
1451
1527
  # in `extensions` available to the handlers and templates
@@ -1485,37 +1561,41 @@ module Sinatra
1485
1561
  # Stop the self-hosted server if running.
1486
1562
  def quit!
1487
1563
  return unless running?
1564
+
1488
1565
  # Use Thin's hard #stop! if available, otherwise just #stop.
1489
1566
  running_server.respond_to?(:stop!) ? running_server.stop! : running_server.stop
1490
- $stderr.puts "== Sinatra has ended his set (crowd applauds)" unless suppress_messages?
1567
+ warn '== Sinatra has ended his set (crowd applauds)' unless suppress_messages?
1491
1568
  set :running_server, nil
1492
1569
  set :handler_name, nil
1570
+
1571
+ on_stop_callback.call unless on_stop_callback.nil?
1493
1572
  end
1494
1573
 
1495
- alias_method :stop!, :quit!
1574
+ alias stop! quit!
1496
1575
 
1497
1576
  # Run the Sinatra app as a self-hosted server using
1498
- # Puma, Mongrel, or WEBrick (in that order). If given a block, will call
1577
+ # Puma, Falcon, or WEBrick (in that order). If given a block, will call
1499
1578
  # with the constructed handler once we have taken the stage.
1500
1579
  def run!(options = {}, &block)
1501
1580
  return if running?
1581
+
1502
1582
  set options
1503
1583
  handler = Rack::Handler.pick(server)
1504
1584
  handler_name = handler.name.gsub(/.*::/, '')
1505
1585
  server_settings = settings.respond_to?(:server_settings) ? settings.server_settings : {}
1506
- server_settings.merge!(:Port => port, :Host => bind)
1586
+ server_settings.merge!(Port: port, Host: bind)
1507
1587
 
1508
1588
  begin
1509
1589
  start_server(handler, server_settings, handler_name, &block)
1510
1590
  rescue Errno::EADDRINUSE
1511
- $stderr.puts "== Someone is already performing on port #{port}!"
1591
+ warn "== Someone is already performing on port #{port}!"
1512
1592
  raise
1513
1593
  ensure
1514
1594
  quit!
1515
1595
  end
1516
1596
  end
1517
1597
 
1518
- alias_method :start!, :run!
1598
+ alias start! run!
1519
1599
 
1520
1600
  # Check whether the self-hosted server is running or not.
1521
1601
  def running?
@@ -1533,10 +1613,11 @@ module Sinatra
1533
1613
  # Create a new instance of the class fronted by its middleware
1534
1614
  # pipeline. The object is guaranteed to respond to #call but may not be
1535
1615
  # an instance of the class new was called on.
1536
- def new(*args, **kwargs, &bk)
1537
- instance = new!(*args, **kwargs, &bk)
1616
+ def new(*args, &block)
1617
+ instance = new!(*args, &block)
1538
1618
  Wrapper.new(build(instance).to_app, instance)
1539
1619
  end
1620
+ ruby2_keywords :new if respond_to?(:ruby2_keywords, true)
1540
1621
 
1541
1622
  # Creates a Rack::Builder instance with all the middleware set up and
1542
1623
  # the given +app+ as end point.
@@ -1558,12 +1639,6 @@ module Sinatra
1558
1639
  cleaned_caller(1).flatten
1559
1640
  end
1560
1641
 
1561
- # Like caller_files, but containing Arrays rather than strings with the
1562
- # first element being the file, and the second being the line.
1563
- def caller_locations
1564
- cleaned_caller 2
1565
- end
1566
-
1567
1642
  private
1568
1643
 
1569
1644
  # Starts the server by running the Rack Handler.
@@ -1574,14 +1649,14 @@ module Sinatra
1574
1649
  # Run the instance we created:
1575
1650
  handler.run(self, **server_settings) do |server|
1576
1651
  unless suppress_messages?
1577
- $stderr.puts "== Sinatra (v#{Sinatra::VERSION}) has taken the stage on #{port} for #{environment} with backup from #{handler_name}"
1652
+ warn "== Sinatra (v#{Sinatra::VERSION}) has taken the stage on #{port} for #{environment} with backup from #{handler_name}"
1578
1653
  end
1579
1654
 
1580
1655
  setup_traps
1581
1656
  set :running_server, server
1582
1657
  set :handler_name, handler_name
1583
1658
  server.threaded = settings.threaded if server.respond_to? :threaded=
1584
-
1659
+ on_start_callback.call unless on_start_callback.nil?
1585
1660
  yield server if block_given?
1586
1661
  end
1587
1662
  end
@@ -1591,18 +1666,18 @@ module Sinatra
1591
1666
  end
1592
1667
 
1593
1668
  def setup_traps
1594
- if traps?
1595
- at_exit { quit! }
1669
+ return unless traps?
1596
1670
 
1597
- [:INT, :TERM].each do |signal|
1598
- old_handler = trap(signal) do
1599
- quit!
1600
- old_handler.call if old_handler.respond_to?(:call)
1601
- end
1602
- end
1671
+ at_exit { quit! }
1603
1672
 
1604
- set :traps, false
1673
+ %i[INT TERM].each do |signal|
1674
+ old_handler = trap(signal) do
1675
+ quit!
1676
+ old_handler.call if old_handler.respond_to?(:call)
1677
+ end
1605
1678
  end
1679
+
1680
+ set :traps, false
1606
1681
  end
1607
1682
 
1608
1683
  # Dynamically defines a method on settings.
@@ -1630,18 +1705,21 @@ module Sinatra
1630
1705
  end
1631
1706
  end
1632
1707
  end
1633
- alias_method :agent, :user_agent
1708
+ alias agent user_agent
1634
1709
 
1635
1710
  # Condition for matching mimetypes. Accepts file extensions.
1636
1711
  def provides(*types)
1637
1712
  types.map! { |t| mime_types(t) }
1638
1713
  types.flatten!
1639
1714
  condition do
1640
- if type = response['Content-Type']
1641
- types.include? type or types.include? type[/^[^;]+/]
1642
- elsif type = request.preferred_type(types)
1643
- params = (type.respond_to?(:params) ? type.params : {})
1644
- content_type(type, params)
1715
+ response_content_type = response['Content-Type']
1716
+ preferred_type = request.preferred_type(types)
1717
+
1718
+ if response_content_type
1719
+ types.include?(response_content_type) || types.include?(response_content_type[/^[^;]+/])
1720
+ elsif preferred_type
1721
+ params = (preferred_type.respond_to?(:params) ? preferred_type.params : {})
1722
+ content_type(preferred_type, params)
1645
1723
  true
1646
1724
  else
1647
1725
  false
@@ -1650,7 +1728,7 @@ module Sinatra
1650
1728
  end
1651
1729
 
1652
1730
  def route(verb, path, options = {}, &block)
1653
- enable :empty_path_info if path == "" and empty_path_info.nil?
1731
+ enable :empty_path_info if path == '' && empty_path_info.nil?
1654
1732
  signature = compile!(verb, path, block, **options)
1655
1733
  (@routes[verb] ||= []) << signature
1656
1734
  invoke_hook(:route_added, verb, path, block)
@@ -1679,12 +1757,13 @@ module Sinatra
1679
1757
  pattern = compile(path, route_mustermann_opts)
1680
1758
  method_name = "#{verb} #{path}"
1681
1759
  unbound_method = generate_method(method_name, &block)
1682
- conditions, @conditions = @conditions, []
1683
- wrapper = block.arity != 0 ?
1684
- proc { |a, p| unbound_method.bind(a).call(*p) } :
1685
- proc { |a, p| unbound_method.bind(a).call }
1760
+ conditions = @conditions
1761
+ @conditions = []
1762
+ wrapper = block.arity.zero? ?
1763
+ proc { |a, _p| unbound_method.bind(a).call } :
1764
+ proc { |a, p| unbound_method.bind(a).call(*p) }
1686
1765
 
1687
- [ pattern, conditions, wrapper ]
1766
+ [pattern, conditions, wrapper]
1688
1767
  end
1689
1768
 
1690
1769
  def compile(path, route_mustermann_opts = {})
@@ -1702,7 +1781,7 @@ module Sinatra
1702
1781
  end
1703
1782
 
1704
1783
  def setup_middleware(builder)
1705
- middleware.each { |c,a,b| builder.use(c, *a, &b) }
1784
+ middleware.each { |c, a, b| builder.use(c, *a, &b) }
1706
1785
  end
1707
1786
 
1708
1787
  def setup_logging(builder)
@@ -1732,9 +1811,10 @@ module Sinatra
1732
1811
 
1733
1812
  def setup_protection(builder)
1734
1813
  return unless protection?
1814
+
1735
1815
  options = Hash === protection ? protection.dup : {}
1736
1816
  options = {
1737
- img_src: "'self' data:",
1817
+ img_src: "'self' data:",
1738
1818
  font_src: "'self'"
1739
1819
  }.merge options
1740
1820
 
@@ -1748,6 +1828,7 @@ module Sinatra
1748
1828
 
1749
1829
  def setup_sessions(builder)
1750
1830
  return unless sessions?
1831
+
1751
1832
  options = {}
1752
1833
  options[:secret] = session_secret if session_secret?
1753
1834
  options.merge! sessions.to_hash if sessions.respond_to? :to_hash
@@ -1770,15 +1851,15 @@ module Sinatra
1770
1851
  end
1771
1852
 
1772
1853
  # used for deprecation warnings
1773
- def warn(message)
1774
- super message + "\n\tfrom #{cleaned_caller.first.join(':')}"
1854
+ def warn_for_deprecation(message)
1855
+ warn message + "\n\tfrom #{cleaned_caller.first.join(':')}"
1775
1856
  end
1776
1857
 
1777
1858
  # Like Kernel#caller but excluding certain magic entries
1778
1859
  def cleaned_caller(keep = 3)
1779
- caller(1).
1780
- map! { |line| line.split(/:(?=\d|in )/, 3)[0,keep] }.
1781
- reject { |file, *_| CALLERS_TO_IGNORE.any? { |pattern| file =~ pattern } }
1860
+ caller(1)
1861
+ .map! { |line| line.split(/:(?=\d|in )/, 3)[0, keep] }
1862
+ .reject { |file, *_| callers_to_ignore.any? { |pattern| file =~ pattern } }
1782
1863
  end
1783
1864
  end
1784
1865
 
@@ -1786,6 +1867,7 @@ module Sinatra
1786
1867
  # which is UTF-8 by default
1787
1868
  def self.force_encoding(data, encoding = default_encoding)
1788
1869
  return if data == settings || data.is_a?(Tempfile)
1870
+
1789
1871
  if data.respond_to? :force_encoding
1790
1872
  data.force_encoding(encoding).encode!
1791
1873
  elsif data.respond_to? :each_value
@@ -1796,24 +1878,26 @@ module Sinatra
1796
1878
  data
1797
1879
  end
1798
1880
 
1799
- def force_encoding(*args) settings.force_encoding(*args) end
1881
+ def force_encoding(*args)
1882
+ settings.force_encoding(*args)
1883
+ end
1800
1884
 
1801
1885
  reset!
1802
1886
 
1803
1887
  set :environment, (ENV['APP_ENV'] || ENV['RACK_ENV'] || :development).to_sym
1804
- set :raise_errors, Proc.new { test? }
1805
- set :dump_errors, Proc.new { !test? }
1806
- set :show_exceptions, Proc.new { development? }
1888
+ set :raise_errors, proc { test? }
1889
+ set :dump_errors, proc { !test? }
1890
+ set :show_exceptions, proc { development? }
1807
1891
  set :sessions, false
1808
- set :session_store, Rack::Session::Cookie
1892
+ set :session_store, Rack::Protection::EncryptedCookie
1809
1893
  set :logging, false
1810
1894
  set :protection, true
1811
1895
  set :method_override, false
1812
1896
  set :use_code, false
1813
- set :default_encoding, "utf-8"
1897
+ set :default_encoding, 'utf-8'
1814
1898
  set :x_cascade, true
1815
1899
  set :add_charset, %w[javascript xml xhtml+xml].map { |t| "application/#{t}" }
1816
- settings.add_charset << /^text\//
1900
+ settings.add_charset << %r{^text/}
1817
1901
  set :mustermann_opts, {}
1818
1902
  set :default_content_type, 'text/html'
1819
1903
 
@@ -1821,14 +1905,15 @@ module Sinatra
1821
1905
  begin
1822
1906
  require 'securerandom'
1823
1907
  set :session_secret, SecureRandom.hex(64)
1824
- rescue LoadError, NotImplementedError
1908
+ rescue LoadError, NotImplementedError, RuntimeError
1825
1909
  # SecureRandom raises a NotImplementedError if no random device is available
1826
- set :session_secret, "%064x" % Kernel.rand(2**256-1)
1910
+ # RuntimeError raised due to broken openssl backend: https://bugs.ruby-lang.org/issues/19230
1911
+ set :session_secret, format('%064x', Kernel.rand((2**256) - 1))
1827
1912
  end
1828
1913
 
1829
1914
  class << self
1830
- alias_method :methodoverride?, :method_override?
1831
- alias_method :methodoverride=, :method_override=
1915
+ alias methodoverride? method_override?
1916
+ alias methodoverride= method_override=
1832
1917
  end
1833
1918
 
1834
1919
  set :run, false # start server via at-exit hook?
@@ -1836,21 +1921,16 @@ module Sinatra
1836
1921
  set :handler_name, nil
1837
1922
  set :traps, true
1838
1923
  set :server, %w[HTTP webrick]
1839
- set :bind, Proc.new { development? ? 'localhost' : '0.0.0.0' }
1924
+ set :bind, proc { development? ? 'localhost' : '0.0.0.0' }
1840
1925
  set :port, Integer(ENV['PORT'] && !ENV['PORT'].empty? ? ENV['PORT'] : 4567)
1841
1926
  set :quiet, false
1842
1927
 
1843
1928
  ruby_engine = defined?(RUBY_ENGINE) && RUBY_ENGINE
1844
1929
 
1845
- if ruby_engine == 'macruby'
1846
- server.unshift 'control_tower'
1847
- else
1848
- server.unshift 'reel'
1849
- server.unshift 'puma'
1850
- server.unshift 'mongrel' if ruby_engine.nil?
1851
- server.unshift 'thin' if ruby_engine != 'jruby'
1852
- server.unshift 'trinidad' if ruby_engine == 'jruby'
1853
- end
1930
+ server.unshift 'thin' if ruby_engine != 'jruby'
1931
+ server.unshift 'falcon' if ruby_engine != 'jruby'
1932
+ server.unshift 'trinidad' if ruby_engine == 'jruby'
1933
+ server.unshift 'puma'
1854
1934
 
1855
1935
  set :absolute_redirects, true
1856
1936
  set :prefixed_redirects, false
@@ -1858,14 +1938,14 @@ module Sinatra
1858
1938
  set :strict_paths, true
1859
1939
 
1860
1940
  set :app_file, nil
1861
- set :root, Proc.new { app_file && File.expand_path(File.dirname(app_file)) }
1862
- set :views, Proc.new { root && File.join(root, 'views') }
1863
- set :reload_templates, Proc.new { development? }
1941
+ set :root, proc { app_file && File.expand_path(File.dirname(app_file)) }
1942
+ set :views, proc { root && File.join(root, 'views') }
1943
+ set :reload_templates, proc { development? }
1864
1944
  set :lock, false
1865
1945
  set :threaded, true
1866
1946
 
1867
- set :public_folder, Proc.new { root && File.join(root, 'public') }
1868
- set :static, Proc.new { public_folder && File.exist?(public_folder) }
1947
+ set :public_folder, proc { root && File.join(root, 'public') }
1948
+ set :static, proc { public_folder && File.exist?(public_folder) }
1869
1949
  set :static_cache_control, false
1870
1950
 
1871
1951
  error ::Exception do
@@ -1884,7 +1964,7 @@ module Sinatra
1884
1964
  error NotFound do
1885
1965
  content_type 'text/html'
1886
1966
 
1887
- if self.class == Sinatra::Application
1967
+ if instance_of?(Sinatra::Application)
1888
1968
  code = <<-RUBY.gsub(/^ {12}/, '')
1889
1969
  #{request.request_method.downcase} '#{request.path_info}' do
1890
1970
  "Hello World"
@@ -1899,11 +1979,11 @@ module Sinatra
1899
1979
  end
1900
1980
  RUBY
1901
1981
 
1902
- file = settings.app_file.to_s.sub(settings.root.to_s, '').sub(/^\//, '')
1982
+ file = settings.app_file.to_s.sub(settings.root.to_s, '').sub(%r{^/}, '')
1903
1983
  code = "# in #{file}\n#{code}" unless file.empty?
1904
1984
  end
1905
1985
 
1906
- (<<-HTML).gsub(/^ {10}/, '')
1986
+ <<-HTML.gsub(/^ {10}/, '')
1907
1987
  <!DOCTYPE html>
1908
1988
  <html>
1909
1989
  <head>
@@ -1915,7 +1995,7 @@ module Sinatra
1915
1995
  </head>
1916
1996
  <body>
1917
1997
  <h2>Sinatra doesn’t know this ditty.</h2>
1918
- <img src='#{uri "/__sinatra__/404.png"}'>
1998
+ <img src='#{uri '/__sinatra__/404.png'}'>
1919
1999
  <div id="c">
1920
2000
  Try this:
1921
2001
  <pre>#{Rack::Utils.escape_html(code)}</pre>
@@ -1935,12 +2015,12 @@ module Sinatra
1935
2015
  # top-level. Subclassing Sinatra::Base is highly recommended for
1936
2016
  # modular applications.
1937
2017
  class Application < Base
1938
- set :logging, Proc.new { !test? }
2018
+ set :logging, proc { !test? }
1939
2019
  set :method_override, true
1940
- set :run, Proc.new { !test? }
2020
+ set :run, proc { !test? }
1941
2021
  set :app_file, nil
1942
2022
 
1943
- def self.register(*extensions, &block) #:nodoc:
2023
+ def self.register(*extensions, &block) # :nodoc:
1944
2024
  added_methods = extensions.flat_map(&:public_instance_methods)
1945
2025
  Delegator.delegate(*added_methods)
1946
2026
  super(*extensions, &block)
@@ -1950,11 +2030,12 @@ module Sinatra
1950
2030
  # Sinatra delegation mixin. Mixing this module into an object causes all
1951
2031
  # methods to be delegated to the Sinatra::Application class. Used primarily
1952
2032
  # at the top-level.
1953
- module Delegator #:nodoc:
2033
+ module Delegator # :nodoc:
1954
2034
  def self.delegate(*methods)
1955
2035
  methods.each do |method_name|
1956
2036
  define_method(method_name) do |*args, &block|
1957
2037
  return super(*args, &block) if respond_to? method_name
2038
+
1958
2039
  Delegator.target.send(method_name, *args, &block)
1959
2040
  end
1960
2041
  # ensure keyword argument passing is compatible with ruby >= 2.7
@@ -1966,7 +2047,7 @@ module Sinatra
1966
2047
  delegate :get, :patch, :put, :post, :delete, :head, :options, :link, :unlink,
1967
2048
  :template, :layout, :before, :after, :error, :not_found, :configure,
1968
2049
  :set, :mime_type, :enable, :disable, :use, :development?, :test?,
1969
- :production?, :helpers, :settings, :register
2050
+ :production?, :helpers, :settings, :register, :on_start, :on_stop
1970
2051
 
1971
2052
  class << self
1972
2053
  attr_accessor :target
@@ -1977,7 +2058,8 @@ module Sinatra
1977
2058
 
1978
2059
  class Wrapper
1979
2060
  def initialize(stack, instance)
1980
- @stack, @instance = stack, instance
2061
+ @stack = stack
2062
+ @instance = instance
1981
2063
  end
1982
2064
 
1983
2065
  def settings