sinatra 2.2.3 → 3.0.6

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sinatra might be problematic. Click here for more details.

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'
@@ -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] or not response['Content-Type']
406
- 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'
407
422
  end
408
423
 
409
424
  disposition = opts[:disposition]
410
425
  filename = opts[:filename]
411
- disposition = :attachment if disposition.nil? and filename
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::File.new(File.dirname(settings.app_file))
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, @scheduler, @keep_open = back.to_proc, scheduler, keep_open
442
- @callbacks, @closed = [], false
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
- 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
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.kind_of?(Hash)
533
+ if values.last.is_a?(Hash)
504
534
  hash = values.pop
505
- hash.reject! { |k, v| v == false }
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 ['max-age', 's-maxage'].include? key
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.kind_of?(Hash)
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 => 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 and env['HTTP_IF_MODIFIED_SINCE']
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? or status == 412) and env['HTTP_IF_UNMODIFIED_SINCE']
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 = [:strong, :weak]
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 = {:kind => options} unless Hash === 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, ":strong or :weak expected"
622
+ raise ArgumentError, ':strong or :weak expected'
592
623
  end
593
624
 
594
- value = '"%s"' % value
625
+ value = format('"%s"', value)
595
626
  value = "W/#{value}" if kind == :weak
596
627
  response['ETag'] = value
597
628
 
598
- if success? or status == 304
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
- if env['HTTP_IF_MATCH']
604
- halt 412 unless etag_matches? env['HTTP_IF_MATCH'], new_resource
605
- end
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 => boom
660
- raise boom
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, @params = @params, temp_params
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 (Ignored for `sass` and `less`)
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
- options, template = template, nil if template.is_a?(Hash)
829
- template = Proc.new { block } if template.nil?
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) { |key, v1, v2| v1 }
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 || "./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? or (layout == true && engine_options[:layout] != false)
845
- layout = @default_layout if layout.nil? or layout == true
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
- options = options.merge(:views => views, :layout => false, :eat_errors => eat_errors, :scope => scope).
871
- merge!(layout_options)
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
- if found = File.exist?(file)
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 and not found
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
- caller = settings.caller_locations.first
918
- path = options[:path] || caller[0]
919
- line = options[:line] || caller[1]
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, **kwargs)
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
- fail "downstream app not set" unless @app.respond_to? :call
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
- if routes = base.routes[@request.request_method]
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
- returned_pass_block = process_route(pattern, conditions) do |*args|
1029
- env['sinatra.route'] = "#{@request.request_method} #{pattern}"
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
- # don't wipe out pass_block in superclass
1034
- pass_block = returned_pass_block if returned_pass_block
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? and not settings.empty_path_info?
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.delete("ignore") # TODO: better params handling, maybe turn it into "smart" object or detect changes
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
- if @app
1095
- forward
1096
- else
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(:disposition => nil)
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) { yield }
1108
+ def invoke(&block)
1109
+ res = catch(:halt, &block)
1120
1110
 
1121
- res = [res] if Integer === res or String === res
1122
- if Array === res and Integer === res.first
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 => boom
1150
- invoke { handle_exception!(boom) }
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 => boom
1155
- invoke { handle_exception!(boom) } unless @env['sinatra.error']
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
- if error_params = @env['sinatra.error.params']
1162
- @params = @params.merge(error_params)
1163
- end
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
- if boom.respond_to? :http_status and boom.http_status.between? 400, 599
1167
- status(boom.http_status)
1168
- elsif settings.use_code? and boom.respond_to? :code and boom.code.between? 400, 599
1169
- status(boom.code)
1170
- else
1171
- status(500)
1172
- end
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? and settings.show_exceptions != :after_handler
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 '<h1>' + (not_found? ? 'Not Found' : 'Bad Request') + '</h1>'
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
- raise boom if settings.raise_errors? or settings.show_exceptions?
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
- next base = base.superclass unless args_array = base.errors[key]
1200
+ args_array = base.errors[key]
1201
+
1202
+ next base = base.superclass unless args_array
1203
+
1204
1204
  args_array.reverse_each do |args|
1205
1205
  first = args == args_array.first
1206
1206
  args += [block_params]
@@ -1208,51 +1208,50 @@ module Sinatra
1208
1208
  return resp unless resp.nil? && !first
1209
1209
  end
1210
1210
  end
1211
- return false unless key.respond_to? :superclass and key.superclass < Exception
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("%Y-%m-%d %H:%M:%S")} - #{boom.class} - #{boom.message}:", *boom.backtrace].join("\n\t")
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
- /\/sinatra(\/(base|main|show_exceptions))?\.rb$/, # all sinatra code
1223
- /lib\/tilt.*\.rb$/, # all tilt code
1223
+ %r{/sinatra(/(base|main|show_exceptions))?\.rb$}, # all sinatra code
1224
+ %r{lib/tilt.*\.rb$}, # all tilt code
1224
1225
  /^\(.*\)$/, # generated code
1225
- /rubygems\/(custom|core_ext\/kernel)_require\.rb$/, # rubygems require hacks
1226
+ %r{rubygems/(custom|core_ext/kernel)_require\.rb$}, # rubygems require hacks
1226
1227
  /active_support/, # active_support require hacks
1227
- /bundler(\/(?:runtime|inline))?\.rb/, # bundler require hacks
1228
+ %r{bundler(/(?:runtime|inline))?\.rb}, # bundler require hacks
1228
1229
  /<internal:/, # internal in ruby >= 1.9.2
1229
- /src\/kernel\/bootstrap\/[A-Z]/ # maglev kernel files
1230
- ]
1231
-
1232
- # contrary to what the comment said previously, rubinius never supported this
1233
- if defined?(RUBY_IGNORE_CALLERS)
1234
- warn "RUBY_IGNORE_CALLERS is deprecated and will no longer be supported by Sinatra 2.0"
1235
- CALLERS_TO_IGNORE.concat(RUBY_IGNORE_CALLERS)
1236
- end
1230
+ %r{zeitwerk/kernel\.rb} # Zeitwerk kernel#require decorator
1231
+ ].freeze
1237
1232
 
1238
1233
  attr_reader :routes, :filters, :templates, :errors
1239
1234
 
1235
+ def callers_to_ignore
1236
+ CALLERS_TO_IGNORE
1237
+ end
1238
+
1240
1239
  # Removes all routes, filters, middleware and extension hooks from the
1241
1240
  # current class (not routes/filters/... defined by its superclass).
1242
1241
  def reset!
1243
1242
  @conditions = []
1244
1243
  @routes = {}
1245
- @filters = {:before => [], :after => []}
1244
+ @filters = { before: [], after: [] }
1246
1245
  @errors = {}
1247
1246
  @middleware = []
1248
1247
  @prototype = nil
1249
1248
  @extensions = []
1250
1249
 
1251
- if superclass.respond_to?(:templates)
1252
- @templates = Hash.new { |hash, key| superclass.templates[key] }
1253
- else
1254
- @templates = {}
1255
- end
1250
+ @templates = if superclass.respond_to?(:templates)
1251
+ Hash.new { |_hash, key| superclass.templates[key] }
1252
+ else
1253
+ {}
1254
+ end
1256
1255
  end
1257
1256
 
1258
1257
  # Extension modules registered on this class and all superclasses.
@@ -1276,16 +1275,21 @@ module Sinatra
1276
1275
  # Sets an option to the given value. If the value is a proc,
1277
1276
  # the proc will be called every time the option is accessed.
1278
1277
  def set(option, value = (not_set = true), ignore_setter = false, &block)
1279
- raise ArgumentError if block and !not_set
1280
- value, not_set = block, false if block
1278
+ raise ArgumentError if block && !not_set
1279
+
1280
+ if block
1281
+ value = block
1282
+ not_set = false
1283
+ end
1281
1284
 
1282
1285
  if not_set
1283
1286
  raise ArgumentError unless option.respond_to?(:each)
1284
- option.each { |k,v| set(k, v) }
1287
+
1288
+ option.each { |k, v| set(k, v) }
1285
1289
  return self
1286
1290
  end
1287
1291
 
1288
- if respond_to?("#{option}=") and not ignore_setter
1292
+ if respond_to?("#{option}=") && !ignore_setter
1289
1293
  return __send__("#{option}=", value)
1290
1294
  end
1291
1295
 
@@ -1324,7 +1328,7 @@ module Sinatra
1324
1328
  # class, or an HTTP status code to specify which errors should be
1325
1329
  # handled.
1326
1330
  def error(*codes, &block)
1327
- args = compile! "ERROR", /.*/, block
1331
+ args = compile! 'ERROR', /.*/, block
1328
1332
  codes = codes.flat_map(&method(:Array))
1329
1333
  codes << Exception if codes.empty?
1330
1334
  codes << Sinatra::NotFound if codes.include?(404)
@@ -1350,7 +1354,7 @@ module Sinatra
1350
1354
  # Load embedded templates from the file; uses the caller's __FILE__
1351
1355
  # when no file is specified.
1352
1356
  def inline_templates=(file = nil)
1353
- file = (file.nil? || file == true) ? (caller_files.first || File.expand_path($0)) : file
1357
+ file = (caller_files.first || File.expand_path($0)) if file.nil? || file == true
1354
1358
 
1355
1359
  begin
1356
1360
  io = ::IO.respond_to?(:binread) ? ::IO.binread(file) : ::IO.read(file)
@@ -1359,23 +1363,24 @@ module Sinatra
1359
1363
  app, data = nil
1360
1364
  end
1361
1365
 
1362
- if data
1363
- if app and app =~ /([^\n]*\n)?#[^\n]*coding: *(\S+)/m
1364
- encoding = $2
1365
- else
1366
- encoding = settings.default_encoding
1367
- end
1368
- lines = app.count("\n") + 1
1369
- template = nil
1370
- force_encoding data, encoding
1371
- data.each_line do |line|
1372
- lines += 1
1373
- if line =~ /^@@\s*(.*\S)\s*$/
1374
- template = force_encoding(String.new, encoding)
1375
- templates[$1.to_sym] = [template, file, lines]
1376
- elsif template
1377
- template << line
1378
- end
1366
+ return unless data
1367
+
1368
+ encoding = if app && app =~ /([^\n]*\n)?#[^\n]*coding: *(\S+)/m
1369
+ $2
1370
+ else
1371
+ settings.default_encoding
1372
+ end
1373
+
1374
+ lines = app.count("\n") + 1
1375
+ template = nil
1376
+ force_encoding data, encoding
1377
+ data.each_line do |line|
1378
+ lines += 1
1379
+ if line =~ /^@@\s*(.*\S)\s*$/
1380
+ template = force_encoding(String.new, encoding)
1381
+ templates[$1.to_sym] = [template, file, lines]
1382
+ elsif template
1383
+ template << line
1379
1384
  end
1380
1385
  end
1381
1386
  end
@@ -1384,8 +1389,10 @@ module Sinatra
1384
1389
  def mime_type(type, value = nil)
1385
1390
  return type if type.nil?
1386
1391
  return type.to_s if type.to_s.include?('/')
1387
- type = ".#{type}" unless type.to_s[0] == ?.
1392
+
1393
+ type = ".#{type}" unless type.to_s[0] == '.'
1388
1394
  return Rack::Mime.mime_type(type, nil) unless value
1395
+
1389
1396
  Rack::Mime::MIME_TYPES[type] = value
1390
1397
  end
1391
1398
 
@@ -1394,7 +1401,7 @@ module Sinatra
1394
1401
  # mime_types :js # => ['application/javascript', 'text/javascript']
1395
1402
  def mime_types(type)
1396
1403
  type = mime_type type
1397
- type =~ /^application\/(xml|javascript)$/ ? [type, "text/#$1"] : [type]
1404
+ type =~ %r{^application/(xml|javascript)$} ? [type, "text/#{$1}"] : [type]
1398
1405
  end
1399
1406
 
1400
1407
  # Define a before filter; runs before all requests within the same
@@ -1423,7 +1430,7 @@ module Sinatra
1423
1430
  end
1424
1431
 
1425
1432
  def public=(value)
1426
- warn ":public is no longer used to avoid overloading Module#public, use :public_folder or :public_dir instead"
1433
+ warn_for_deprecation ':public is no longer used to avoid overloading Module#public, use :public_folder or :public_dir instead'
1427
1434
  set(:public_folder, value)
1428
1435
  end
1429
1436
 
@@ -1445,14 +1452,21 @@ module Sinatra
1445
1452
  route('HEAD', path, opts, &block)
1446
1453
  end
1447
1454
 
1448
- def put(path, opts = {}, &bk) route 'PUT', path, opts, &bk end
1449
- def post(path, opts = {}, &bk) route 'POST', path, opts, &bk end
1450
- def delete(path, opts = {}, &bk) route 'DELETE', path, opts, &bk end
1451
- def head(path, opts = {}, &bk) route 'HEAD', path, opts, &bk end
1452
- def options(path, opts = {}, &bk) route 'OPTIONS', path, opts, &bk end
1453
- def patch(path, opts = {}, &bk) route 'PATCH', path, opts, &bk end
1454
- def link(path, opts = {}, &bk) route 'LINK', path, opts, &bk end
1455
- def unlink(path, opts = {}, &bk) route 'UNLINK', path, opts, &bk end
1455
+ def put(path, opts = {}, &block) route 'PUT', path, opts, &block end
1456
+
1457
+ def post(path, opts = {}, &block) route 'POST', path, opts, &block end
1458
+
1459
+ def delete(path, opts = {}, &block) route 'DELETE', path, opts, &block end
1460
+
1461
+ def head(path, opts = {}, &block) route 'HEAD', path, opts, &block end
1462
+
1463
+ def options(path, opts = {}, &block) route 'OPTIONS', path, opts, &block end
1464
+
1465
+ def patch(path, opts = {}, &block) route 'PATCH', path, opts, &block end
1466
+
1467
+ def link(path, opts = {}, &block) route 'LINK', path, opts, &block end
1468
+
1469
+ def unlink(path, opts = {}, &block) route 'UNLINK', path, opts, &block end
1456
1470
 
1457
1471
  # Makes the methods defined in the block and in the Modules given
1458
1472
  # in `extensions` available to the handlers and templates
@@ -1492,37 +1506,39 @@ module Sinatra
1492
1506
  # Stop the self-hosted server if running.
1493
1507
  def quit!
1494
1508
  return unless running?
1509
+
1495
1510
  # Use Thin's hard #stop! if available, otherwise just #stop.
1496
1511
  running_server.respond_to?(:stop!) ? running_server.stop! : running_server.stop
1497
- $stderr.puts "== Sinatra has ended his set (crowd applauds)" unless suppress_messages?
1512
+ warn '== Sinatra has ended his set (crowd applauds)' unless suppress_messages?
1498
1513
  set :running_server, nil
1499
1514
  set :handler_name, nil
1500
1515
  end
1501
1516
 
1502
- alias_method :stop!, :quit!
1517
+ alias stop! quit!
1503
1518
 
1504
1519
  # Run the Sinatra app as a self-hosted server using
1505
- # Puma, Mongrel, or WEBrick (in that order). If given a block, will call
1520
+ # Puma, Falcon, Mongrel, or WEBrick (in that order). If given a block, will call
1506
1521
  # with the constructed handler once we have taken the stage.
1507
1522
  def run!(options = {}, &block)
1508
1523
  return if running?
1524
+
1509
1525
  set options
1510
1526
  handler = Rack::Handler.pick(server)
1511
1527
  handler_name = handler.name.gsub(/.*::/, '')
1512
1528
  server_settings = settings.respond_to?(:server_settings) ? settings.server_settings : {}
1513
- server_settings.merge!(:Port => port, :Host => bind)
1529
+ server_settings.merge!(Port: port, Host: bind)
1514
1530
 
1515
1531
  begin
1516
1532
  start_server(handler, server_settings, handler_name, &block)
1517
1533
  rescue Errno::EADDRINUSE
1518
- $stderr.puts "== Someone is already performing on port #{port}!"
1534
+ warn "== Someone is already performing on port #{port}!"
1519
1535
  raise
1520
1536
  ensure
1521
1537
  quit!
1522
1538
  end
1523
1539
  end
1524
1540
 
1525
- alias_method :start!, :run!
1541
+ alias start! run!
1526
1542
 
1527
1543
  # Check whether the self-hosted server is running or not.
1528
1544
  def running?
@@ -1540,8 +1556,8 @@ module Sinatra
1540
1556
  # Create a new instance of the class fronted by its middleware
1541
1557
  # pipeline. The object is guaranteed to respond to #call but may not be
1542
1558
  # an instance of the class new was called on.
1543
- def new(*args, &bk)
1544
- instance = new!(*args, &bk)
1559
+ def new(*args, &block)
1560
+ instance = new!(*args, &block)
1545
1561
  Wrapper.new(build(instance).to_app, instance)
1546
1562
  end
1547
1563
  ruby2_keywords :new if respond_to?(:ruby2_keywords, true)
@@ -1566,12 +1582,6 @@ module Sinatra
1566
1582
  cleaned_caller(1).flatten
1567
1583
  end
1568
1584
 
1569
- # Like caller_files, but containing Arrays rather than strings with the
1570
- # first element being the file, and the second being the line.
1571
- def caller_locations
1572
- cleaned_caller 2
1573
- end
1574
-
1575
1585
  private
1576
1586
 
1577
1587
  # Starts the server by running the Rack Handler.
@@ -1582,7 +1592,7 @@ module Sinatra
1582
1592
  # Run the instance we created:
1583
1593
  handler.run(self, **server_settings) do |server|
1584
1594
  unless suppress_messages?
1585
- $stderr.puts "== Sinatra (v#{Sinatra::VERSION}) has taken the stage on #{port} for #{environment} with backup from #{handler_name}"
1595
+ warn "== Sinatra (v#{Sinatra::VERSION}) has taken the stage on #{port} for #{environment} with backup from #{handler_name}"
1586
1596
  end
1587
1597
 
1588
1598
  setup_traps
@@ -1599,18 +1609,18 @@ module Sinatra
1599
1609
  end
1600
1610
 
1601
1611
  def setup_traps
1602
- if traps?
1603
- at_exit { quit! }
1612
+ return unless traps?
1604
1613
 
1605
- [:INT, :TERM].each do |signal|
1606
- old_handler = trap(signal) do
1607
- quit!
1608
- old_handler.call if old_handler.respond_to?(:call)
1609
- end
1610
- end
1614
+ at_exit { quit! }
1611
1615
 
1612
- set :traps, false
1616
+ %i[INT TERM].each do |signal|
1617
+ old_handler = trap(signal) do
1618
+ quit!
1619
+ old_handler.call if old_handler.respond_to?(:call)
1620
+ end
1613
1621
  end
1622
+
1623
+ set :traps, false
1614
1624
  end
1615
1625
 
1616
1626
  # Dynamically defines a method on settings.
@@ -1638,18 +1648,21 @@ module Sinatra
1638
1648
  end
1639
1649
  end
1640
1650
  end
1641
- alias_method :agent, :user_agent
1651
+ alias agent user_agent
1642
1652
 
1643
1653
  # Condition for matching mimetypes. Accepts file extensions.
1644
1654
  def provides(*types)
1645
1655
  types.map! { |t| mime_types(t) }
1646
1656
  types.flatten!
1647
1657
  condition do
1648
- if type = response['Content-Type']
1649
- types.include? type or types.include? type[/^[^;]+/]
1650
- elsif type = request.preferred_type(types)
1651
- params = (type.respond_to?(:params) ? type.params : {})
1652
- content_type(type, params)
1658
+ response_content_type = response['Content-Type']
1659
+ preferred_type = request.preferred_type(types)
1660
+
1661
+ if response_content_type
1662
+ types.include?(response_content_type) || types.include?(response_content_type[/^[^;]+/])
1663
+ elsif preferred_type
1664
+ params = (preferred_type.respond_to?(:params) ? preferred_type.params : {})
1665
+ content_type(preferred_type, params)
1653
1666
  true
1654
1667
  else
1655
1668
  false
@@ -1658,7 +1671,7 @@ module Sinatra
1658
1671
  end
1659
1672
 
1660
1673
  def route(verb, path, options = {}, &block)
1661
- enable :empty_path_info if path == "" and empty_path_info.nil?
1674
+ enable :empty_path_info if path == '' && empty_path_info.nil?
1662
1675
  signature = compile!(verb, path, block, **options)
1663
1676
  (@routes[verb] ||= []) << signature
1664
1677
  invoke_hook(:route_added, verb, path, block)
@@ -1687,12 +1700,13 @@ module Sinatra
1687
1700
  pattern = compile(path, route_mustermann_opts)
1688
1701
  method_name = "#{verb} #{path}"
1689
1702
  unbound_method = generate_method(method_name, &block)
1690
- conditions, @conditions = @conditions, []
1691
- wrapper = block.arity != 0 ?
1692
- proc { |a, p| unbound_method.bind(a).call(*p) } :
1693
- proc { |a, p| unbound_method.bind(a).call }
1703
+ conditions = @conditions
1704
+ @conditions = []
1705
+ wrapper = block.arity.zero? ?
1706
+ proc { |a, _p| unbound_method.bind(a).call } :
1707
+ proc { |a, p| unbound_method.bind(a).call(*p) }
1694
1708
 
1695
- [ pattern, conditions, wrapper ]
1709
+ [pattern, conditions, wrapper]
1696
1710
  end
1697
1711
 
1698
1712
  def compile(path, route_mustermann_opts = {})
@@ -1710,7 +1724,7 @@ module Sinatra
1710
1724
  end
1711
1725
 
1712
1726
  def setup_middleware(builder)
1713
- middleware.each { |c,a,b| builder.use(c, *a, &b) }
1727
+ middleware.each { |c, a, b| builder.use(c, *a, &b) }
1714
1728
  end
1715
1729
 
1716
1730
  def setup_logging(builder)
@@ -1740,9 +1754,10 @@ module Sinatra
1740
1754
 
1741
1755
  def setup_protection(builder)
1742
1756
  return unless protection?
1757
+
1743
1758
  options = Hash === protection ? protection.dup : {}
1744
1759
  options = {
1745
- img_src: "'self' data:",
1760
+ img_src: "'self' data:",
1746
1761
  font_src: "'self'"
1747
1762
  }.merge options
1748
1763
 
@@ -1756,6 +1771,7 @@ module Sinatra
1756
1771
 
1757
1772
  def setup_sessions(builder)
1758
1773
  return unless sessions?
1774
+
1759
1775
  options = {}
1760
1776
  options[:secret] = session_secret if session_secret?
1761
1777
  options.merge! sessions.to_hash if sessions.respond_to? :to_hash
@@ -1778,15 +1794,15 @@ module Sinatra
1778
1794
  end
1779
1795
 
1780
1796
  # used for deprecation warnings
1781
- def warn(message)
1782
- super message + "\n\tfrom #{cleaned_caller.first.join(':')}"
1797
+ def warn_for_deprecation(message)
1798
+ warn message + "\n\tfrom #{cleaned_caller.first.join(':')}"
1783
1799
  end
1784
1800
 
1785
1801
  # Like Kernel#caller but excluding certain magic entries
1786
1802
  def cleaned_caller(keep = 3)
1787
- caller(1).
1788
- map! { |line| line.split(/:(?=\d|in )/, 3)[0,keep] }.
1789
- 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 } }
1790
1806
  end
1791
1807
  end
1792
1808
 
@@ -1794,6 +1810,7 @@ module Sinatra
1794
1810
  # which is UTF-8 by default
1795
1811
  def self.force_encoding(data, encoding = default_encoding)
1796
1812
  return if data == settings || data.is_a?(Tempfile)
1813
+
1797
1814
  if data.respond_to? :force_encoding
1798
1815
  data.force_encoding(encoding).encode!
1799
1816
  elsif data.respond_to? :each_value
@@ -1804,24 +1821,26 @@ module Sinatra
1804
1821
  data
1805
1822
  end
1806
1823
 
1807
- def force_encoding(*args) settings.force_encoding(*args) end
1824
+ def force_encoding(*args)
1825
+ settings.force_encoding(*args)
1826
+ end
1808
1827
 
1809
1828
  reset!
1810
1829
 
1811
1830
  set :environment, (ENV['APP_ENV'] || ENV['RACK_ENV'] || :development).to_sym
1812
- set :raise_errors, Proc.new { test? }
1813
- set :dump_errors, Proc.new { !test? }
1814
- set :show_exceptions, Proc.new { development? }
1831
+ set :raise_errors, proc { test? }
1832
+ set :dump_errors, proc { !test? }
1833
+ set :show_exceptions, proc { development? }
1815
1834
  set :sessions, false
1816
- set :session_store, Rack::Session::Cookie
1835
+ set :session_store, Rack::Protection::EncryptedCookie
1817
1836
  set :logging, false
1818
1837
  set :protection, true
1819
1838
  set :method_override, false
1820
1839
  set :use_code, false
1821
- set :default_encoding, "utf-8"
1840
+ set :default_encoding, 'utf-8'
1822
1841
  set :x_cascade, true
1823
1842
  set :add_charset, %w[javascript xml xhtml+xml].map { |t| "application/#{t}" }
1824
- settings.add_charset << /^text\//
1843
+ settings.add_charset << %r{^text/}
1825
1844
  set :mustermann_opts, {}
1826
1845
  set :default_content_type, 'text/html'
1827
1846
 
@@ -1829,14 +1848,15 @@ module Sinatra
1829
1848
  begin
1830
1849
  require 'securerandom'
1831
1850
  set :session_secret, SecureRandom.hex(64)
1832
- rescue LoadError, NotImplementedError
1851
+ rescue LoadError, NotImplementedError, RuntimeError
1833
1852
  # SecureRandom raises a NotImplementedError if no random device is available
1834
- set :session_secret, "%064x" % Kernel.rand(2**256-1)
1853
+ # RuntimeError raised due to broken openssl backend: https://bugs.ruby-lang.org/issues/19230
1854
+ set :session_secret, format('%064x', Kernel.rand((2**256) - 1))
1835
1855
  end
1836
1856
 
1837
1857
  class << self
1838
- alias_method :methodoverride?, :method_override?
1839
- alias_method :methodoverride=, :method_override=
1858
+ alias methodoverride? method_override?
1859
+ alias methodoverride= method_override=
1840
1860
  end
1841
1861
 
1842
1862
  set :run, false # start server via at-exit hook?
@@ -1844,21 +1864,17 @@ module Sinatra
1844
1864
  set :handler_name, nil
1845
1865
  set :traps, true
1846
1866
  set :server, %w[HTTP webrick]
1847
- set :bind, Proc.new { development? ? 'localhost' : '0.0.0.0' }
1867
+ set :bind, proc { development? ? 'localhost' : '0.0.0.0' }
1848
1868
  set :port, Integer(ENV['PORT'] && !ENV['PORT'].empty? ? ENV['PORT'] : 4567)
1849
1869
  set :quiet, false
1850
1870
 
1851
1871
  ruby_engine = defined?(RUBY_ENGINE) && RUBY_ENGINE
1852
1872
 
1853
- if ruby_engine == 'macruby'
1854
- server.unshift 'control_tower'
1855
- else
1856
- server.unshift 'reel'
1857
- server.unshift 'puma'
1858
- server.unshift 'mongrel' if ruby_engine.nil?
1859
- server.unshift 'thin' if ruby_engine != 'jruby'
1860
- server.unshift 'trinidad' if ruby_engine == 'jruby'
1861
- end
1873
+ server.unshift 'puma'
1874
+ server.unshift 'falcon' if ruby_engine != 'jruby'
1875
+ server.unshift 'mongrel' if ruby_engine.nil?
1876
+ server.unshift 'thin' if ruby_engine != 'jruby'
1877
+ server.unshift 'trinidad' if ruby_engine == 'jruby'
1862
1878
 
1863
1879
  set :absolute_redirects, true
1864
1880
  set :prefixed_redirects, false
@@ -1866,14 +1882,14 @@ module Sinatra
1866
1882
  set :strict_paths, true
1867
1883
 
1868
1884
  set :app_file, nil
1869
- set :root, Proc.new { app_file && File.expand_path(File.dirname(app_file)) }
1870
- set :views, Proc.new { root && File.join(root, 'views') }
1871
- set :reload_templates, Proc.new { development? }
1885
+ set :root, proc { app_file && File.expand_path(File.dirname(app_file)) }
1886
+ set :views, proc { root && File.join(root, 'views') }
1887
+ set :reload_templates, proc { development? }
1872
1888
  set :lock, false
1873
1889
  set :threaded, true
1874
1890
 
1875
- set :public_folder, Proc.new { root && File.join(root, 'public') }
1876
- set :static, Proc.new { public_folder && File.exist?(public_folder) }
1891
+ set :public_folder, proc { root && File.join(root, 'public') }
1892
+ set :static, proc { public_folder && File.exist?(public_folder) }
1877
1893
  set :static_cache_control, false
1878
1894
 
1879
1895
  error ::Exception do
@@ -1892,7 +1908,7 @@ module Sinatra
1892
1908
  error NotFound do
1893
1909
  content_type 'text/html'
1894
1910
 
1895
- if self.class == Sinatra::Application
1911
+ if instance_of?(Sinatra::Application)
1896
1912
  code = <<-RUBY.gsub(/^ {12}/, '')
1897
1913
  #{request.request_method.downcase} '#{request.path_info}' do
1898
1914
  "Hello World"
@@ -1907,11 +1923,11 @@ module Sinatra
1907
1923
  end
1908
1924
  RUBY
1909
1925
 
1910
- file = settings.app_file.to_s.sub(settings.root.to_s, '').sub(/^\//, '')
1926
+ file = settings.app_file.to_s.sub(settings.root.to_s, '').sub(%r{^/}, '')
1911
1927
  code = "# in #{file}\n#{code}" unless file.empty?
1912
1928
  end
1913
1929
 
1914
- (<<-HTML).gsub(/^ {10}/, '')
1930
+ <<-HTML.gsub(/^ {10}/, '')
1915
1931
  <!DOCTYPE html>
1916
1932
  <html>
1917
1933
  <head>
@@ -1923,7 +1939,7 @@ module Sinatra
1923
1939
  </head>
1924
1940
  <body>
1925
1941
  <h2>Sinatra doesn’t know this ditty.</h2>
1926
- <img src='#{uri "/__sinatra__/404.png"}'>
1942
+ <img src='#{uri '/__sinatra__/404.png'}'>
1927
1943
  <div id="c">
1928
1944
  Try this:
1929
1945
  <pre>#{Rack::Utils.escape_html(code)}</pre>
@@ -1943,12 +1959,12 @@ module Sinatra
1943
1959
  # top-level. Subclassing Sinatra::Base is highly recommended for
1944
1960
  # modular applications.
1945
1961
  class Application < Base
1946
- set :logging, Proc.new { !test? }
1962
+ set :logging, proc { !test? }
1947
1963
  set :method_override, true
1948
- set :run, Proc.new { !test? }
1964
+ set :run, proc { !test? }
1949
1965
  set :app_file, nil
1950
1966
 
1951
- def self.register(*extensions, &block) #:nodoc:
1967
+ def self.register(*extensions, &block) # :nodoc:
1952
1968
  added_methods = extensions.flat_map(&:public_instance_methods)
1953
1969
  Delegator.delegate(*added_methods)
1954
1970
  super(*extensions, &block)
@@ -1958,11 +1974,12 @@ module Sinatra
1958
1974
  # Sinatra delegation mixin. Mixing this module into an object causes all
1959
1975
  # methods to be delegated to the Sinatra::Application class. Used primarily
1960
1976
  # at the top-level.
1961
- module Delegator #:nodoc:
1977
+ module Delegator # :nodoc:
1962
1978
  def self.delegate(*methods)
1963
1979
  methods.each do |method_name|
1964
1980
  define_method(method_name) do |*args, &block|
1965
1981
  return super(*args, &block) if respond_to? method_name
1982
+
1966
1983
  Delegator.target.send(method_name, *args, &block)
1967
1984
  end
1968
1985
  # ensure keyword argument passing is compatible with ruby >= 2.7
@@ -1985,7 +2002,8 @@ module Sinatra
1985
2002
 
1986
2003
  class Wrapper
1987
2004
  def initialize(stack, instance)
1988
- @stack, @instance = stack, instance
2005
+ @stack = stack
2006
+ @instance = instance
1989
2007
  end
1990
2008
 
1991
2009
  def settings