sinatra 2.2.3 → 3.0.6

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'
@@ -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