sinatra 1.4.8 → 2.1.0

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.

Files changed (133) hide show
  1. checksums.yaml +5 -5
  2. data/AUTHORS.md +1 -0
  3. data/CHANGELOG.md +272 -54
  4. data/CONTRIBUTING.md +8 -8
  5. data/Gemfile +48 -47
  6. data/LICENSE +4 -1
  7. data/MAINTENANCE.md +42 -0
  8. data/README.de.md +651 -443
  9. data/README.es.md +738 -357
  10. data/README.fr.md +197 -100
  11. data/README.hu.md +40 -6
  12. data/README.ja.md +125 -67
  13. data/README.ko.md +15 -15
  14. data/README.malayalam.md +3141 -0
  15. data/README.md +592 -432
  16. data/README.pt-br.md +2362 -335
  17. data/README.pt-pt.md +5 -5
  18. data/README.ru.md +857 -608
  19. data/README.zh.md +91 -29
  20. data/Rakefile +77 -51
  21. data/SECURITY.md +35 -0
  22. data/VERSION +1 -0
  23. data/examples/chat.rb +2 -1
  24. data/examples/rainbows.conf +3 -0
  25. data/examples/rainbows.rb +20 -0
  26. data/examples/stream.ru +4 -4
  27. data/lib/sinatra/base.rb +243 -265
  28. data/lib/sinatra/indifferent_hash.rb +200 -0
  29. data/lib/sinatra/main.rb +30 -10
  30. data/lib/sinatra/show_exceptions.rb +67 -62
  31. data/lib/sinatra/version.rb +1 -1
  32. data/sinatra.gemspec +44 -8
  33. metadata +43 -167
  34. data/lib/sinatra/ext.rb +0 -17
  35. data/test/asciidoctor_test.rb +0 -72
  36. data/test/base_test.rb +0 -167
  37. data/test/builder_test.rb +0 -91
  38. data/test/coffee_test.rb +0 -96
  39. data/test/compile_test.rb +0 -183
  40. data/test/contest.rb +0 -91
  41. data/test/creole_test.rb +0 -65
  42. data/test/delegator_test.rb +0 -160
  43. data/test/encoding_test.rb +0 -20
  44. data/test/erb_test.rb +0 -116
  45. data/test/extensions_test.rb +0 -98
  46. data/test/filter_test.rb +0 -487
  47. data/test/haml_test.rb +0 -109
  48. data/test/helper.rb +0 -132
  49. data/test/helpers_test.rb +0 -1917
  50. data/test/integration/app.rb +0 -79
  51. data/test/integration_helper.rb +0 -236
  52. data/test/integration_test.rb +0 -104
  53. data/test/less_test.rb +0 -69
  54. data/test/liquid_test.rb +0 -77
  55. data/test/mapped_error_test.rb +0 -285
  56. data/test/markaby_test.rb +0 -80
  57. data/test/markdown_test.rb +0 -85
  58. data/test/mediawiki_test.rb +0 -68
  59. data/test/middleware_test.rb +0 -68
  60. data/test/nokogiri_test.rb +0 -67
  61. data/test/public/favicon.ico +0 -0
  62. data/test/public/hello+world.txt +0 -1
  63. data/test/rabl_test.rb +0 -89
  64. data/test/rack_test.rb +0 -45
  65. data/test/radius_test.rb +0 -59
  66. data/test/rdoc_test.rb +0 -66
  67. data/test/readme_test.rb +0 -130
  68. data/test/request_test.rb +0 -100
  69. data/test/response_test.rb +0 -63
  70. data/test/result_test.rb +0 -76
  71. data/test/route_added_hook_test.rb +0 -59
  72. data/test/routing_test.rb +0 -1456
  73. data/test/sass_test.rb +0 -115
  74. data/test/scss_test.rb +0 -88
  75. data/test/server_test.rb +0 -56
  76. data/test/settings_test.rb +0 -582
  77. data/test/sinatra_test.rb +0 -12
  78. data/test/slim_test.rb +0 -102
  79. data/test/static_test.rb +0 -266
  80. data/test/streaming_test.rb +0 -149
  81. data/test/stylus_test.rb +0 -90
  82. data/test/templates_test.rb +0 -382
  83. data/test/textile_test.rb +0 -65
  84. data/test/views/a/in_a.str +0 -1
  85. data/test/views/ascii.erb +0 -2
  86. data/test/views/b/in_b.str +0 -1
  87. data/test/views/calc.html.erb +0 -1
  88. data/test/views/error.builder +0 -3
  89. data/test/views/error.erb +0 -3
  90. data/test/views/error.haml +0 -3
  91. data/test/views/error.sass +0 -2
  92. data/test/views/explicitly_nested.str +0 -1
  93. data/test/views/foo/hello.test +0 -1
  94. data/test/views/hello.asciidoc +0 -1
  95. data/test/views/hello.builder +0 -1
  96. data/test/views/hello.coffee +0 -1
  97. data/test/views/hello.creole +0 -1
  98. data/test/views/hello.erb +0 -1
  99. data/test/views/hello.haml +0 -1
  100. data/test/views/hello.less +0 -5
  101. data/test/views/hello.liquid +0 -1
  102. data/test/views/hello.mab +0 -1
  103. data/test/views/hello.md +0 -1
  104. data/test/views/hello.mediawiki +0 -1
  105. data/test/views/hello.nokogiri +0 -1
  106. data/test/views/hello.rabl +0 -2
  107. data/test/views/hello.radius +0 -1
  108. data/test/views/hello.rdoc +0 -1
  109. data/test/views/hello.sass +0 -2
  110. data/test/views/hello.scss +0 -3
  111. data/test/views/hello.slim +0 -1
  112. data/test/views/hello.str +0 -1
  113. data/test/views/hello.styl +0 -2
  114. data/test/views/hello.test +0 -1
  115. data/test/views/hello.textile +0 -1
  116. data/test/views/hello.wlang +0 -1
  117. data/test/views/hello.yajl +0 -1
  118. data/test/views/layout2.builder +0 -3
  119. data/test/views/layout2.erb +0 -2
  120. data/test/views/layout2.haml +0 -2
  121. data/test/views/layout2.liquid +0 -2
  122. data/test/views/layout2.mab +0 -2
  123. data/test/views/layout2.nokogiri +0 -3
  124. data/test/views/layout2.rabl +0 -3
  125. data/test/views/layout2.radius +0 -2
  126. data/test/views/layout2.slim +0 -3
  127. data/test/views/layout2.str +0 -2
  128. data/test/views/layout2.test +0 -1
  129. data/test/views/layout2.wlang +0 -2
  130. data/test/views/nested.str +0 -1
  131. data/test/views/utf8.erb +0 -2
  132. data/test/wlang_test.rb +0 -87
  133. data/test/yajl_test.rb +0 -86
data/lib/sinatra/base.rb CHANGED
@@ -1,7 +1,13 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+
1
4
  # external dependencies
2
5
  require 'rack'
3
6
  require 'tilt'
4
7
  require 'rack/protection'
8
+ require 'mustermann'
9
+ require 'mustermann/sinatra'
10
+ require 'mustermann/regular'
5
11
 
6
12
  # stdlib dependencies
7
13
  require 'thread'
@@ -9,8 +15,8 @@ require 'time'
9
15
  require 'uri'
10
16
 
11
17
  # other files we need
18
+ require 'sinatra/indifferent_hash'
12
19
  require 'sinatra/show_exceptions'
13
- require 'sinatra/ext'
14
20
  require 'sinatra/version'
15
21
 
16
22
  module Sinatra
@@ -37,12 +43,11 @@ module Sinatra
37
43
  end
38
44
 
39
45
  def preferred_type(*types)
40
- accepts = accept # just evaluate once
41
- return accepts.first if types.empty?
46
+ return accept.first if types.empty?
42
47
  types.flatten!
43
- return types.first if accepts.empty?
44
- accepts.detect do |pattern|
45
- type = types.detect { |t| File.fnmatch(pattern, t) }
48
+ return types.first if accept.empty?
49
+ accept.detect do |accept_header|
50
+ type = types.detect { |t| MimeTypeEntry.new(t).accepts?(accept_header) }
46
51
  return type if type
47
52
  end
48
53
  end
@@ -69,7 +74,11 @@ module Sinatra
69
74
  request_method == "UNLINK"
70
75
  end
71
76
 
72
- private
77
+ def params
78
+ super
79
+ rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
80
+ raise BadRequest, "Invalid query parameters: #{Rack::Utils.escape_html(e.message)}"
81
+ end
73
82
 
74
83
  class AcceptEntry
75
84
  attr_accessor :params
@@ -113,6 +122,35 @@ module Sinatra
113
122
  to_str.send(*args, &block)
114
123
  end
115
124
  end
125
+
126
+ class MimeTypeEntry
127
+ attr_reader :params
128
+
129
+ def initialize(entry)
130
+ params = entry.scan(HEADER_PARAM).map! do |s|
131
+ key, value = s.strip.split('=', 2)
132
+ value = value[1..-2].gsub(/\\(.)/, '\1') if value.start_with?('"')
133
+ [key, value]
134
+ end
135
+
136
+ @type = entry[/[^;]+/].delete(' ')
137
+ @params = Hash[params]
138
+ end
139
+
140
+ def accepts?(entry)
141
+ File.fnmatch(entry, self) && matches_params?(entry.params)
142
+ end
143
+
144
+ def to_str
145
+ @type
146
+ end
147
+
148
+ def matches_params?(params)
149
+ return true if @params.empty?
150
+
151
+ params.all? { |k,v| !@params.has_key?(k) || @params[k] == v }
152
+ end
153
+ end
116
154
  end
117
155
 
118
156
  # The response object. See Rack::Response and Rack::Response::Helpers for
@@ -120,11 +158,7 @@ module Sinatra
120
158
  # http://rubydoc.info/github/rack/rack/master/Rack/Response
121
159
  # http://rubydoc.info/github/rack/rack/master/Rack/Response/Helpers
122
160
  class Response < Rack::Response
123
- DROP_BODY_RESPONSES = [204, 205, 304]
124
- def initialize(*)
125
- super
126
- headers['Content-Type'] ||= 'text/html'
127
- end
161
+ DROP_BODY_RESPONSES = [204, 304]
128
162
 
129
163
  def body=(value)
130
164
  value = value.body while Rack::Response === value
@@ -151,7 +185,7 @@ module Sinatra
151
185
  if calculate_content_length?
152
186
  # if some other code has already set Content-Length, don't muck with it
153
187
  # currently, this would be the static file-handler
154
- headers["Content-Length"] = body.inject(0) { |l, p| l + Rack::Utils.bytesize(p) }.to_s
188
+ headers["Content-Length"] = body.map(&:bytesize).reduce(0, :+).to_s
155
189
  end
156
190
 
157
191
  [status.to_i, headers, result]
@@ -172,7 +206,7 @@ module Sinatra
172
206
  end
173
207
  end
174
208
 
175
- # Some Rack handlers (Thin, Rainbows!) implement an extended body object protocol, however,
209
+ # Some Rack handlers (Rainbows!) implement an extended body object protocol, however,
176
210
  # some middleware (namely Rack::Lint) will break it by not mirroring the methods in question.
177
211
  # This middleware will detect an extended body object and will make sure it reaches the
178
212
  # handler directly. We do this here, so our middleware and middleware set up by the app will
@@ -221,6 +255,10 @@ module Sinatra
221
255
  end
222
256
  end
223
257
 
258
+ class BadRequest < TypeError #:nodoc:
259
+ def http_status; 400 end
260
+ end
261
+
224
262
  class NotFound < NameError #:nodoc:
225
263
  def http_status; 404 end
226
264
  end
@@ -229,7 +267,7 @@ module Sinatra
229
267
  module Helpers
230
268
  # Set or retrieve the response status code.
231
269
  def status(value = nil)
232
- response.status = value if value
270
+ response.status = Rack::Utils.status_code(value) if value
233
271
  response.status
234
272
  end
235
273
 
@@ -240,7 +278,11 @@ module Sinatra
240
278
  def block.each; yield(call) end
241
279
  response.body = block
242
280
  elsif value
243
- headers.delete 'Content-Length' unless request.head? || value.is_a?(Rack::File) || value.is_a?(Stream)
281
+ # Rack 2.0 returns a Rack::File::Iterator here instead of
282
+ # Rack::File as it was in the previous API.
283
+ unless request.head? || value.is_a?(Rack::File::Iterator) || value.is_a?(Stream)
284
+ headers.delete 'Content-Length'
285
+ end
244
286
  response.body = value
245
287
  else
246
288
  response.body
@@ -264,8 +306,8 @@ module Sinatra
264
306
  # Generates the absolute URI for a given path in the app.
265
307
  # Takes Rack routers and reverse proxies into account.
266
308
  def uri(addr = nil, absolute = true, add_script_name = true)
267
- return addr if addr =~ /\A[A-z][A-z0-9\+\.\-]*:/
268
- uri = [host = ""]
309
+ return addr if addr =~ /\A[a-z][a-z0-9\+\.\-]*:/i
310
+ uri = [host = String.new]
269
311
  if absolute
270
312
  host << "http#{'s' if request.secure?}://"
271
313
  if request.forwarded? or request.port != (request.secure? ? 443 : 80)
@@ -339,8 +381,8 @@ module Sinatra
339
381
 
340
382
  # Set the Content-Disposition to "attachment" with the specified filename,
341
383
  # instructing the user agents to prompt to save.
342
- def attachment(filename = nil, disposition = 'attachment')
343
- response['Content-Disposition'] = disposition.to_s
384
+ def attachment(filename = nil, disposition = :attachment)
385
+ response['Content-Disposition'] = disposition.to_s.dup
344
386
  if filename
345
387
  params = '; filename="%s"' % File.basename(filename)
346
388
  response['Content-Disposition'] << params
@@ -357,19 +399,19 @@ module Sinatra
357
399
 
358
400
  disposition = opts[:disposition]
359
401
  filename = opts[:filename]
360
- disposition = 'attachment' if disposition.nil? and filename
361
- filename = path if filename.nil?
402
+ disposition = :attachment if disposition.nil? and filename
403
+ filename = path if filename.nil?
362
404
  attachment(filename, disposition) if disposition
363
405
 
364
406
  last_modified opts[:last_modified] if opts[:last_modified]
365
407
 
366
- file = Rack::File.new nil
367
- file.path = path
368
- result = file.serving env
408
+ file = Rack::File.new(File.dirname(settings.app_file))
409
+ result = file.serving(request, path)
410
+
369
411
  result[1].each { |k,v| headers[k] ||= v }
370
412
  headers['Content-Length'] = result[1]['Content-Length']
371
413
  opts[:status] &&= Integer(opts[:status])
372
- halt opts[:status] || result[0], result[2]
414
+ halt (opts[:status] || result[0]), result[2]
373
415
  rescue Errno::ENOENT
374
416
  not_found
375
417
  end
@@ -394,7 +436,7 @@ module Sinatra
394
436
  def close
395
437
  return if closed?
396
438
  @closed = true
397
- @scheduler.schedule { @callbacks.each { |c| c.call }}
439
+ @scheduler.schedule { @callbacks.each { |c| c.call } }
398
440
  end
399
441
 
400
442
  def each(&front)
@@ -431,7 +473,7 @@ module Sinatra
431
473
  #
432
474
  # The close parameter specifies whether Stream#close should be called
433
475
  # after the block has been executed. This is only relevant for evented
434
- # servers like Thin or Rainbows.
476
+ # servers like Rainbows.
435
477
  def stream(keep_open = false)
436
478
  scheduler = env['async.callback'] ? EventMachine : Stream
437
479
  current = @params.dup
@@ -441,7 +483,7 @@ module Sinatra
441
483
  # Specify response freshness policy for HTTP caches (Cache-Control header).
442
484
  # Any number of non-value directives (:public, :private, :no_cache,
443
485
  # :no_store, :must_revalidate, :proxy_revalidate) may be passed along with
444
- # a Hash of value directives (:max_age, :min_stale, :s_max_age).
486
+ # a Hash of value directives (:max_age, :s_maxage).
445
487
  #
446
488
  # cache_control :public, :must_revalidate, :max_age => 60
447
489
  # => Cache-Control: public, must-revalidate, max-age=60
@@ -451,8 +493,8 @@ module Sinatra
451
493
  def cache_control(*values)
452
494
  if values.last.kind_of?(Hash)
453
495
  hash = values.pop
454
- hash.reject! { |k,v| v == false }
455
- hash.reject! { |k,v| values << k if v == true }
496
+ hash.reject! { |k, v| v == false }
497
+ hash.reject! { |k, v| values << k if v == true }
456
498
  else
457
499
  hash = {}
458
500
  end
@@ -460,7 +502,7 @@ module Sinatra
460
502
  values.map! { |value| value.to_s.tr('_','-') }
461
503
  hash.each do |key, value|
462
504
  key = key.to_s.tr('_', '-')
463
- value = value.to_i if key == "max-age"
505
+ value = value.to_i if ['max-age', 's-maxage'].include? key
464
506
  values << "#{key}=#{value}"
465
507
  end
466
508
 
@@ -473,7 +515,7 @@ module Sinatra
473
515
  # "values" arguments are passed to the #cache_control helper:
474
516
  #
475
517
  # expires 500, :public, :must_revalidate
476
- # => Cache-Control: public, must-revalidate, max-age=60
518
+ # => Cache-Control: public, must-revalidate, max-age=500
477
519
  # => Expires: Mon, 08 Jun 2009 08:50:17 GMT
478
520
  #
479
521
  def expires(amount, *values)
@@ -590,25 +632,20 @@ module Sinatra
590
632
  status == 404
591
633
  end
592
634
 
635
+ # whether or not the status is set to 400
636
+ def bad_request?
637
+ status == 400
638
+ end
639
+
593
640
  # Generates a Time object from the given value.
594
641
  # Used by #expires and #last_modified.
595
642
  def time_for(value)
596
- if value.respond_to? :to_time
597
- value.to_time
598
- elsif value.is_a? Time
599
- value
600
- elsif value.respond_to? :new_offset
601
- # DateTime#to_time does the same on 1.9
602
- d = value.new_offset 0
603
- t = Time.utc d.year, d.mon, d.mday, d.hour, d.min, d.sec + d.sec_fraction
604
- t.getlocal
605
- elsif value.respond_to? :mday
606
- # Date#to_time does the same on 1.9
607
- Time.local(value.year, value.mon, value.mday)
608
- elsif value.is_a? Numeric
643
+ if value.is_a? Numeric
609
644
  Time.at value
610
- else
645
+ elsif value.respond_to? :to_s
611
646
  Time.parse value.to_s
647
+ else
648
+ value.to_time
612
649
  end
613
650
  rescue ArgumentError => boom
614
651
  raise boom
@@ -632,8 +669,6 @@ module Sinatra
632
669
  end
633
670
  end
634
671
 
635
- private
636
-
637
672
  # Template rendering methods. Each method takes the name of a template
638
673
  # to render as a Symbol and returns a String with the rendered output,
639
674
  # as well as an optional hash with additional options.
@@ -692,7 +727,7 @@ module Sinatra
692
727
  render :less, template, options, locals
693
728
  end
694
729
 
695
- def stylus(template, options={}, locals={})
730
+ def stylus(template, options = {}, locals = {})
696
731
  options.merge! :layout => false, :default_content_type => :css
697
732
  render :styl, template, options, locals
698
733
  end
@@ -707,6 +742,7 @@ module Sinatra
707
742
  end
708
743
 
709
744
  def markdown(template, options = {}, locals = {})
745
+ options[:exclude_outvar] = true
710
746
  render :markdown, template, options, locals
711
747
  end
712
748
 
@@ -771,15 +807,8 @@ module Sinatra
771
807
  def find_template(views, name, engine)
772
808
  yield ::File.join(views, "#{name}.#{@preferred_extension}")
773
809
 
774
- if Tilt.respond_to?(:mappings)
775
- Tilt.mappings.each do |ext, engines|
776
- next unless ext != @preferred_extension and engines.include? engine
777
- yield ::File.join(views, "#{name}.#{ext}")
778
- end
779
- else
780
- Tilt.default_mapping.extensions_for(engine).each do |ext|
781
- yield ::File.join(views, "#{name}.#{ext}") unless ext == @preferred_extension
782
- end
810
+ Tilt.default_mapping.extensions_for(engine).each do |ext|
811
+ yield ::File.join(views, "#{name}.#{ext}") unless ext == @preferred_extension
783
812
  end
784
813
  end
785
814
 
@@ -794,7 +823,7 @@ module Sinatra
794
823
 
795
824
  def render(engine, data, options = {}, locals = {}, &block)
796
825
  # merge app-level options
797
- engine_options = settings.respond_to?(engine) ? settings.send(engine) : {}
826
+ engine_options = settings.respond_to?(engine) ? settings.send(engine) : {}
798
827
  options.merge!(engine_options) { |key, v1, v2| v1 }
799
828
 
800
829
  # extract generic options
@@ -806,13 +835,15 @@ module Sinatra
806
835
  layout = engine_options[:layout] if layout.nil? or (layout == true && engine_options[:layout] != false)
807
836
  layout = @default_layout if layout.nil? or layout == true
808
837
  layout_options = options.delete(:layout_options) || {}
809
- content_type = options.delete(:content_type) || options.delete(:default_content_type)
838
+ content_type = options.delete(:default_content_type)
839
+ content_type = options.delete(:content_type) || content_type
810
840
  layout_engine = options.delete(:layout_engine) || engine
811
841
  scope = options.delete(:scope) || self
842
+ exclude_outvar = options.delete(:exclude_outvar)
812
843
  options.delete(:layout)
813
844
 
814
845
  # set some defaults
815
- options[:outvar] ||= '@_out_buf'
846
+ options[:outvar] ||= '@_out_buf' unless exclude_outvar
816
847
  options[:default_encoding] ||= settings.default_encoding
817
848
 
818
849
  # compile and render template
@@ -863,7 +894,9 @@ module Sinatra
863
894
  end
864
895
  when Proc, String
865
896
  body = data.is_a?(String) ? Proc.new { data } : data
866
- path, line = settings.caller_locations.first
897
+ caller = settings.caller_locations.first
898
+ path = options[:path] || caller[0]
899
+ line = options[:line] || caller[1]
867
900
  template.new(path, line.to_i, options, &body)
868
901
  else
869
902
  raise ArgumentError, "Sorry, don't know how to render #{data.inspect}."
@@ -878,7 +911,7 @@ module Sinatra
878
911
  include Helpers
879
912
  include Templates
880
913
 
881
- URI_INSTANCE = URI.const_defined?(:Parser) ? URI::Parser.new : URI
914
+ URI_INSTANCE = URI::Parser.new
882
915
 
883
916
  attr_accessor :app, :env, :request, :response, :params
884
917
  attr_reader :template_cache
@@ -887,6 +920,7 @@ module Sinatra
887
920
  super()
888
921
  @app = app
889
922
  @template_cache = Tilt::Cache.new
923
+ @pinned_response = nil # whether a before! filter pinned the content-type
890
924
  yield self if block_given?
891
925
  end
892
926
 
@@ -897,21 +931,19 @@ module Sinatra
897
931
 
898
932
  def call!(env) # :nodoc:
899
933
  @env = env
934
+ @params = IndifferentHash.new
900
935
  @request = Request.new(env)
901
936
  @response = Response.new
902
- @params = indifferent_params(@request.params)
903
937
  template_cache.clear if settings.reload_templates
904
- force_encoding(@params)
905
938
 
906
- @response['Content-Type'] = nil
907
939
  invoke { dispatch! }
908
940
  invoke { error_block!(response.status) } unless @env['sinatra.error']
909
941
 
910
942
  unless @response['Content-Type']
911
- if Array === body and body[0].respond_to? :content_type
943
+ if Array === body && body[0].respond_to?(:content_type)
912
944
  content_type body[0].content_type
913
- else
914
- content_type :html
945
+ elsif default = settings.default_content_type
946
+ content_type default
915
947
  end
916
948
  end
917
949
 
@@ -961,17 +993,23 @@ module Sinatra
961
993
  private
962
994
 
963
995
  # Run filters defined on the class and all superclasses.
996
+ # Accepts an optional block to call after each filter is applied.
964
997
  def filter!(type, base = settings)
965
998
  filter! type, base.superclass if base.superclass.respond_to?(:filters)
966
- base.filters[type].each { |args| process_route(*args) }
999
+ base.filters[type].each do |args|
1000
+ result = process_route(*args)
1001
+ yield result if block_given?
1002
+ end
967
1003
  end
968
1004
 
969
1005
  # Run routes defined on the class and all superclasses.
970
1006
  def route!(base = settings, pass_block = nil)
971
1007
  if routes = base.routes[@request.request_method]
972
- routes.each do |pattern, keys, conditions, block|
973
- returned_pass_block = process_route(pattern, keys, conditions) do |*args|
974
- env['sinatra.route'] = block.instance_variable_get(:@route_name)
1008
+ routes.each do |pattern, conditions, block|
1009
+ @response.delete_header('Content-Type') unless @pinned_response
1010
+
1011
+ returned_pass_block = process_route(pattern, conditions) do |*args|
1012
+ env['sinatra.route'] = "#{@request.request_method} #{pattern}"
975
1013
  route_eval { block[*args] }
976
1014
  end
977
1015
 
@@ -999,23 +1037,35 @@ module Sinatra
999
1037
  # Revert params afterwards.
1000
1038
  #
1001
1039
  # Returns pass block.
1002
- def process_route(pattern, keys, conditions, block = nil, values = [])
1040
+ def process_route(pattern, conditions, block = nil, values = [])
1003
1041
  route = @request.path_info
1004
1042
  route = '/' if route.empty? and not settings.empty_path_info?
1005
- return unless match = pattern.match(route)
1006
- values += match.captures.map! { |v| force_encoding URI_INSTANCE.unescape(v) if v }
1007
-
1008
- if values.any?
1009
- original, @params = params, params.merge('splat' => [], 'captures' => values)
1010
- keys.zip(values) { |k,v| Array === @params[k] ? @params[k] << v : @params[k] = v if v }
1043
+ route = route[0..-2] if !settings.strict_paths? && route != '/' && route.end_with?('/')
1044
+ return unless params = pattern.params(route)
1045
+
1046
+ params.delete("ignore") # TODO: better params handling, maybe turn it into "smart" object or detect changes
1047
+ force_encoding(params)
1048
+ @params = @params.merge(params) if params.any?
1049
+
1050
+ regexp_exists = pattern.is_a?(Mustermann::Regular) || (pattern.respond_to?(:patterns) && pattern.patterns.any? {|subpattern| subpattern.is_a?(Mustermann::Regular)} )
1051
+ if regexp_exists
1052
+ captures = pattern.match(route).captures.map { |c| URI_INSTANCE.unescape(c) if c }
1053
+ values += captures
1054
+ @params[:captures] = force_encoding(captures) unless captures.nil? || captures.empty?
1055
+ else
1056
+ values += params.values.flatten
1011
1057
  end
1012
1058
 
1013
1059
  catch(:pass) do
1014
1060
  conditions.each { |c| throw :pass if c.bind(self).call == false }
1015
1061
  block ? block[self, values] : yield(self, values)
1016
1062
  end
1063
+ rescue
1064
+ @env['sinatra.error.params'] = @params
1065
+ raise
1017
1066
  ensure
1018
- @params = original if original
1067
+ params ||= {}
1068
+ params.each { |k, _| @params.delete(k) } unless @env['sinatra.error.params']
1019
1069
  end
1020
1070
 
1021
1071
  # No matching route was found or all routes passed. The default
@@ -1027,7 +1077,7 @@ module Sinatra
1027
1077
  if @app
1028
1078
  forward
1029
1079
  else
1030
- raise NotFound
1080
+ raise NotFound, "#{request.request_method} #{request.path_info}"
1031
1081
  end
1032
1082
  end
1033
1083
 
@@ -1035,7 +1085,10 @@ module Sinatra
1035
1085
  # a matching file is found, returns nil otherwise.
1036
1086
  def static!(options = {})
1037
1087
  return if (public_dir = settings.public_folder).nil?
1038
- path = File.expand_path("#{public_dir}#{URI_INSTANCE.unescape(request.path_info)}" )
1088
+ path = "#{public_dir}#{URI_INSTANCE.unescape(request.path_info)}"
1089
+ return unless valid_path?(path)
1090
+
1091
+ path = File.expand_path(path)
1039
1092
  return unless File.file?(path)
1040
1093
 
1041
1094
  env['sinatra.static_file'] = path
@@ -1043,28 +1096,10 @@ module Sinatra
1043
1096
  send_file path, options.merge(:disposition => nil)
1044
1097
  end
1045
1098
 
1046
- # Enable string or symbol key access to the nested params hash.
1047
- def indifferent_params(object)
1048
- case object
1049
- when Hash
1050
- new_hash = indifferent_hash
1051
- object.each { |key, value| new_hash[key] = indifferent_params(value) }
1052
- new_hash
1053
- when Array
1054
- object.map { |item| indifferent_params(item) }
1055
- else
1056
- object
1057
- end
1058
- end
1059
-
1060
- # Creates a Hash with indifferent access.
1061
- def indifferent_hash
1062
- Hash.new {|hash,key| hash[key.to_s] if Symbol === key }
1063
- end
1064
-
1065
1099
  # Run the block with 'throw :halt' support and apply result to the response.
1066
1100
  def invoke
1067
1101
  res = catch(:halt) { yield }
1102
+
1068
1103
  res = [res] if Integer === res or String === res
1069
1104
  if Array === res and Integer === res.first
1070
1105
  res = res.dup
@@ -1079,9 +1114,18 @@ module Sinatra
1079
1114
 
1080
1115
  # Dispatch a request with error handling.
1081
1116
  def dispatch!
1117
+ # Avoid passing frozen string in force_encoding
1118
+ @params.merge!(@request.params).each do |key, val|
1119
+ next unless val.respond_to?(:force_encoding)
1120
+ val = val.dup if val.frozen?
1121
+ @params[key] = force_encoding(val)
1122
+ end
1123
+
1082
1124
  invoke do
1083
1125
  static! if settings.static? && (request.get? || request.head?)
1084
- filter! :before
1126
+ filter! :before do
1127
+ @pinned_response = !@response['Content-Type'].nil?
1128
+ end
1085
1129
  route!
1086
1130
  end
1087
1131
  rescue ::Exception => boom
@@ -1096,6 +1140,9 @@ module Sinatra
1096
1140
 
1097
1141
  # Error handling during requests.
1098
1142
  def handle_exception!(boom)
1143
+ if error_params = @env['sinatra.error.params']
1144
+ @params = @params.merge(error_params)
1145
+ end
1099
1146
  @env['sinatra.error'] = boom
1100
1147
 
1101
1148
  if boom.respond_to? :http_status
@@ -1111,15 +1158,24 @@ module Sinatra
1111
1158
  if server_error?
1112
1159
  dump_errors! boom if settings.dump_errors?
1113
1160
  raise boom if settings.show_exceptions? and settings.show_exceptions != :after_handler
1161
+ elsif not_found?
1162
+ headers['X-Cascade'] = 'pass' if settings.x_cascade?
1114
1163
  end
1115
1164
 
1116
- if not_found?
1117
- headers['X-Cascade'] = 'pass' if settings.x_cascade?
1118
- body '<h1>Not Found</h1>'
1165
+ if res = error_block!(boom.class, boom) || error_block!(status, boom)
1166
+ return res
1167
+ end
1168
+
1169
+ if not_found? || bad_request?
1170
+ if boom.message && boom.message != boom.class.name
1171
+ body boom.message
1172
+ else
1173
+ content_type 'text/html'
1174
+ body '<h1>' + (not_found? ? 'Not Found' : 'Bad Request') + '</h1>'
1175
+ end
1119
1176
  end
1120
1177
 
1121
- res = error_block!(boom.class, boom) || error_block!(status, boom)
1122
- return res if res or not server_error?
1178
+ return unless server_error?
1123
1179
  raise boom if settings.raise_errors? or settings.show_exceptions?
1124
1180
  error_block! Exception, boom
1125
1181
  end
@@ -1147,12 +1203,12 @@ module Sinatra
1147
1203
 
1148
1204
  class << self
1149
1205
  CALLERS_TO_IGNORE = [ # :nodoc:
1150
- /\/sinatra(\/(base|main|show_exceptions))?\.rb$/, # all sinatra code
1206
+ /\/sinatra(\/(base|main|show_exceptions))?\.rb$/, # all sinatra code
1151
1207
  /lib\/tilt.*\.rb$/, # all tilt code
1152
1208
  /^\(.*\)$/, # generated code
1153
1209
  /rubygems\/(custom|core_ext\/kernel)_require\.rb$/, # rubygems require hacks
1154
1210
  /active_support/, # active_support require hacks
1155
- /bundler(\/runtime)?\.rb/, # bundler require hacks
1211
+ /bundler(\/(?:runtime|inline))?\.rb/, # bundler require hacks
1156
1212
  /<internal:/, # internal in ruby >= 1.9.2
1157
1213
  /src\/kernel\/bootstrap\/[A-Z]/ # maglev kernel files
1158
1214
  ]
@@ -1177,7 +1233,7 @@ module Sinatra
1177
1233
  @extensions = []
1178
1234
 
1179
1235
  if superclass.respond_to?(:templates)
1180
- @templates = Hash.new { |hash,key| superclass.templates[key] }
1236
+ @templates = Hash.new { |hash, key| superclass.templates[key] }
1181
1237
  else
1182
1238
  @templates = {}
1183
1239
  end
@@ -1232,8 +1288,8 @@ module Sinatra
1232
1288
  end
1233
1289
  end
1234
1290
 
1235
- define_singleton("#{option}=", setter) if setter
1236
- define_singleton(option, getter) if getter
1291
+ define_singleton("#{option}=", setter)
1292
+ define_singleton(option, getter)
1237
1293
  define_singleton("#{option}?", "!!#{option}") unless method_defined? "#{option}?"
1238
1294
  self
1239
1295
  end
@@ -1252,16 +1308,16 @@ module Sinatra
1252
1308
  # class, or an HTTP status code to specify which errors should be
1253
1309
  # handled.
1254
1310
  def error(*codes, &block)
1255
- args = compile! "ERROR", //, block
1256
- codes = codes.map { |c| Array(c) }.flatten
1311
+ args = compile! "ERROR", /.*/, block
1312
+ codes = codes.flat_map(&method(:Array))
1257
1313
  codes << Exception if codes.empty?
1314
+ codes << Sinatra::NotFound if codes.include?(404)
1258
1315
  codes.each { |c| (@errors[c] ||= []) << args }
1259
1316
  end
1260
1317
 
1261
1318
  # Sugar for `error(404) { ... }`
1262
1319
  def not_found(&block)
1263
1320
  error(404, &block)
1264
- error(Sinatra::NotFound, &block)
1265
1321
  end
1266
1322
 
1267
1323
  # Define a named template. The block must return the template source.
@@ -1299,7 +1355,7 @@ module Sinatra
1299
1355
  data.each_line do |line|
1300
1356
  lines += 1
1301
1357
  if line =~ /^@@\s*(.*\S)\s*$/
1302
- template = force_encoding('', encoding)
1358
+ template = force_encoding(String.new, encoding)
1303
1359
  templates[$1.to_sym] = [template, file, lines]
1304
1360
  elsif template
1305
1361
  template << line
@@ -1328,21 +1384,20 @@ module Sinatra
1328
1384
  # Define a before filter; runs before all requests within the same
1329
1385
  # context as route handlers and may access/modify the request and
1330
1386
  # response.
1331
- def before(path = nil, options = {}, &block)
1332
- add_filter(:before, path, options, &block)
1387
+ def before(path = /.*/, **options, &block)
1388
+ add_filter(:before, path, **options, &block)
1333
1389
  end
1334
1390
 
1335
1391
  # Define an after filter; runs after all requests within the same
1336
1392
  # context as route handlers and may access/modify the request and
1337
1393
  # response.
1338
- def after(path = nil, options = {}, &block)
1339
- add_filter(:after, path, options, &block)
1394
+ def after(path = /.*/, **options, &block)
1395
+ add_filter(:after, path, **options, &block)
1340
1396
  end
1341
1397
 
1342
1398
  # add a filter
1343
- def add_filter(type, path = nil, options = {}, &block)
1344
- path, options = //, path if path.respond_to?(:each_pair)
1345
- filters[type] << compile!(type, path || //, block, options)
1399
+ def add_filter(type, path = /.*/, **options, &block)
1400
+ filters[type] << compile!(type, path, block, **options)
1346
1401
  end
1347
1402
 
1348
1403
  # Add a route condition. The route is considered non-matching when the
@@ -1387,7 +1442,7 @@ module Sinatra
1387
1442
  # in `extensions` available to the handlers and templates
1388
1443
  def helpers(*extensions, &block)
1389
1444
  class_eval(&block) if block_given?
1390
- include(*extensions) if extensions.any?
1445
+ prepend(*extensions) if extensions.any?
1391
1446
  end
1392
1447
 
1393
1448
  # Register an extension. Alternatively take a block from which an
@@ -1422,7 +1477,7 @@ module Sinatra
1422
1477
  return unless running?
1423
1478
  # Use Thin's hard #stop! if available, otherwise just #stop.
1424
1479
  running_server.respond_to?(:stop!) ? running_server.stop! : running_server.stop
1425
- $stderr.puts "== Sinatra has ended his set (crowd applauds)" unless handler_name =~/cgi/i
1480
+ $stderr.puts "== Sinatra has ended his set (crowd applauds)" unless suppress_messages?
1426
1481
  set :running_server, nil
1427
1482
  set :handler_name, nil
1428
1483
  end
@@ -1430,7 +1485,7 @@ module Sinatra
1430
1485
  alias_method :stop!, :quit!
1431
1486
 
1432
1487
  # Run the Sinatra app as a self-hosted server using
1433
- # Thin, Puma, Mongrel, or WEBrick (in that order). If given a block, will call
1488
+ # Puma, Mongrel, or WEBrick (in that order). If given a block, will call
1434
1489
  # with the constructed handler once we have taken the stage.
1435
1490
  def run!(options = {}, &block)
1436
1491
  return if running?
@@ -1503,8 +1558,12 @@ module Sinatra
1503
1558
 
1504
1559
  # Starts the server by running the Rack Handler.
1505
1560
  def start_server(handler, server_settings, handler_name)
1506
- handler.run(self, server_settings) do |server|
1507
- unless handler_name =~ /cgi/i
1561
+ # Ensure we initialize middleware before startup, to match standard Rack
1562
+ # behavior, by ensuring an instance exists:
1563
+ prototype
1564
+ # Run the instance we created:
1565
+ handler.run(self, **server_settings) do |server|
1566
+ unless suppress_messages?
1508
1567
  $stderr.puts "== Sinatra (v#{Sinatra::VERSION}) has taken the stage on #{port} for #{environment} with backup from #{handler_name}"
1509
1568
  end
1510
1569
 
@@ -1517,6 +1576,10 @@ module Sinatra
1517
1576
  end
1518
1577
  end
1519
1578
 
1579
+ def suppress_messages?
1580
+ handler_name =~ /cgi/i || quiet
1581
+ end
1582
+
1520
1583
  def setup_traps
1521
1584
  if traps?
1522
1585
  at_exit { quit! }
@@ -1534,8 +1597,7 @@ module Sinatra
1534
1597
 
1535
1598
  # Dynamically defines a method on settings.
1536
1599
  def define_singleton(name, content = Proc.new)
1537
- # replace with call to singleton_class once we're 1.9 only
1538
- (class << self; self; end).class_eval do
1600
+ singleton_class.class_eval do
1539
1601
  undef_method(name) if method_defined? name
1540
1602
  String === content ? class_eval("def #{name}() #{content}; end") : define_method(name, &content)
1541
1603
  end
@@ -1578,10 +1640,8 @@ module Sinatra
1578
1640
  end
1579
1641
 
1580
1642
  def route(verb, path, options = {}, &block)
1581
- # Because of self.options.host
1582
- host_name(options.delete(:host)) if options.key?(:host)
1583
1643
  enable :empty_path_info if path == "" and empty_path_info.nil?
1584
- signature = compile!(verb, path, block, options)
1644
+ signature = compile!(verb, path, block, **options)
1585
1645
  (@routes[verb] ||= []) << signature
1586
1646
  invoke_hook(:route_added, verb, path, block)
1587
1647
  signature
@@ -1592,115 +1652,33 @@ module Sinatra
1592
1652
  end
1593
1653
 
1594
1654
  def generate_method(method_name, &block)
1595
- method_name = method_name.to_sym
1596
1655
  define_method(method_name, &block)
1597
1656
  method = instance_method method_name
1598
1657
  remove_method method_name
1599
1658
  method
1600
1659
  end
1601
1660
 
1602
- def compile!(verb, path, block, options = {})
1661
+ def compile!(verb, path, block, **options)
1662
+ # Because of self.options.host
1663
+ host_name(options.delete(:host)) if options.key?(:host)
1664
+ # Pass Mustermann opts to compile()
1665
+ route_mustermann_opts = options.key?(:mustermann_opts) ? options.delete(:mustermann_opts) : {}.freeze
1666
+
1603
1667
  options.each_pair { |option, args| send(option, *args) }
1668
+
1669
+ pattern = compile(path, route_mustermann_opts)
1604
1670
  method_name = "#{verb} #{path}"
1605
1671
  unbound_method = generate_method(method_name, &block)
1606
- pattern, keys = compile path
1607
1672
  conditions, @conditions = @conditions, []
1608
-
1609
1673
  wrapper = block.arity != 0 ?
1610
- proc { |a,p| unbound_method.bind(a).call(*p) } :
1611
- proc { |a,p| unbound_method.bind(a).call }
1612
- wrapper.instance_variable_set(:@route_name, method_name)
1613
-
1614
- [ pattern, keys, conditions, wrapper ]
1615
- end
1616
-
1617
- def compile(path)
1618
- if path.respond_to? :to_str
1619
- keys = []
1620
-
1621
- # Split the path into pieces in between forward slashes.
1622
- # A negative number is given as the second argument of path.split
1623
- # because with this number, the method does not ignore / at the end
1624
- # and appends an empty string at the end of the return value.
1625
- #
1626
- segments = path.split('/', -1).map! do |segment|
1627
- ignore = []
1628
-
1629
- # Special character handling.
1630
- #
1631
- pattern = segment.to_str.gsub(/[^\?\%\\\/\:\*\w]|:(?!\w)/) do |c|
1632
- ignore << escaped(c).join if c.match(/[\.@]/)
1633
- patt = encoded(c)
1634
- patt.gsub(/%[\da-fA-F]{2}/) do |match|
1635
- match.split(//).map! { |char| char == char.downcase ? char : "[#{char}#{char.downcase}]" }.join
1636
- end
1637
- end
1638
-
1639
- ignore = ignore.uniq.join
1640
-
1641
- # Key handling.
1642
- #
1643
- pattern.gsub(/((:\w+)|\*)/) do |match|
1644
- if match == "*"
1645
- keys << 'splat'
1646
- "(.*?)"
1647
- else
1648
- keys << $2[1..-1]
1649
- ignore_pattern = safe_ignore(ignore)
1674
+ proc { |a, p| unbound_method.bind(a).call(*p) } :
1675
+ proc { |a, p| unbound_method.bind(a).call }
1650
1676
 
1651
- ignore_pattern
1652
- end
1653
- end
1654
- end
1655
-
1656
- # Special case handling.
1657
- #
1658
- if last_segment = segments[-1] and last_segment.match(/\[\^\\\./)
1659
- parts = last_segment.rpartition(/\[\^\\\./)
1660
- parts[1] = '[^'
1661
- segments[-1] = parts.join
1662
- end
1663
- [/\A#{segments.join('/')}\z/, keys]
1664
- elsif path.respond_to?(:keys) && path.respond_to?(:match)
1665
- [path, path.keys]
1666
- elsif path.respond_to?(:names) && path.respond_to?(:match)
1667
- [path, path.names]
1668
- elsif path.respond_to? :match
1669
- [path, []]
1670
- else
1671
- raise TypeError, path
1672
- end
1677
+ [ pattern, conditions, wrapper ]
1673
1678
  end
1674
1679
 
1675
- def encoded(char)
1676
- enc = URI_INSTANCE.escape(char)
1677
- enc = "(?:#{escaped(char, enc).join('|')})" if enc == char
1678
- enc = "(?:#{enc}|#{encoded('+')})" if char == " "
1679
- enc
1680
- end
1681
-
1682
- def escaped(char, enc = URI_INSTANCE.escape(char))
1683
- [Regexp.escape(enc), URI_INSTANCE.escape(char, /./)]
1684
- end
1685
-
1686
- def safe_ignore(ignore)
1687
- unsafe_ignore = []
1688
- ignore = ignore.gsub(/%[\da-fA-F]{2}/) do |hex|
1689
- unsafe_ignore << hex[1..2]
1690
- ''
1691
- end
1692
- unsafe_patterns = unsafe_ignore.map! do |unsafe|
1693
- chars = unsafe.split(//).map! do |char|
1694
- char == char.downcase ? char : char + char.downcase
1695
- end
1696
-
1697
- "|(?:%[^#{chars[0]}].|%[#{chars[0]}][^#{chars[1]}])"
1698
- end
1699
- if unsafe_patterns.length > 0
1700
- "((?:[^#{ignore}/?#%]#{unsafe_patterns.join()})+)"
1701
- else
1702
- "([^#{ignore}/?#]+)"
1703
- end
1680
+ def compile(path, route_mustermann_opts = {})
1681
+ Mustermann.new(path, **mustermann_opts.merge(route_mustermann_opts))
1704
1682
  end
1705
1683
 
1706
1684
  def setup_default_middleware(builder)
@@ -1745,10 +1723,16 @@ module Sinatra
1745
1723
  def setup_protection(builder)
1746
1724
  return unless protection?
1747
1725
  options = Hash === protection ? protection.dup : {}
1748
- protect_session = options.fetch(:session) { sessions? }
1749
- options[:except] = Array options[:except]
1750
- options[:except] += [:session_hijacking, :remote_token] unless protect_session
1726
+ options = {
1727
+ img_src: "'self' data:",
1728
+ font_src: "'self'"
1729
+ }.merge options
1730
+
1731
+ protect_session = options.fetch(:session) { sessions? }
1732
+ options[:without_session] = !protect_session
1733
+
1751
1734
  options[:reaction] ||= :drop_session
1735
+
1752
1736
  builder.use Rack::Protection, options
1753
1737
  end
1754
1738
 
@@ -1757,7 +1741,7 @@ module Sinatra
1757
1741
  options = {}
1758
1742
  options[:secret] = session_secret if session_secret?
1759
1743
  options.merge! sessions.to_hash if sessions.respond_to? :to_hash
1760
- builder.use Rack::Session::Cookie, options
1744
+ builder.use session_store, options
1761
1745
  end
1762
1746
 
1763
1747
  def detect_rack_handler
@@ -1766,8 +1750,6 @@ module Sinatra
1766
1750
  begin
1767
1751
  return Rack::Handler.get(server_name.to_s)
1768
1752
  rescue LoadError, NameError
1769
- rescue ArgumentError
1770
- Sinatra::Ext.get_handler(server_name.to_s)
1771
1753
  end
1772
1754
  end
1773
1755
  fail "Server handler (#{servers.join(',')}) not found."
@@ -1801,36 +1783,30 @@ module Sinatra
1801
1783
  end
1802
1784
  end
1803
1785
 
1804
- # Fixes encoding issues by
1805
- # * defaulting to UTF-8
1806
- # * casting params to Encoding.default_external
1807
- #
1808
- # The latter might not be necessary if Rack handles it one day.
1809
- # Keep an eye on Rack's LH #100.
1810
- def force_encoding(*args) settings.force_encoding(*args) end
1811
- if defined? Encoding
1812
- def self.force_encoding(data, encoding = default_encoding)
1813
- return if data == settings || data.is_a?(Tempfile)
1814
- if data.respond_to? :force_encoding
1815
- data.force_encoding(encoding).encode!
1816
- elsif data.respond_to? :each_value
1817
- data.each_value { |v| force_encoding(v, encoding) }
1818
- elsif data.respond_to? :each
1819
- data.each { |v| force_encoding(v, encoding) }
1820
- end
1821
- data
1786
+ # Force data to specified encoding. It defaults to settings.default_encoding
1787
+ # which is UTF-8 by default
1788
+ def self.force_encoding(data, encoding = default_encoding)
1789
+ return if data == settings || data.is_a?(Tempfile)
1790
+ if data.respond_to? :force_encoding
1791
+ data.force_encoding(encoding).encode!
1792
+ elsif data.respond_to? :each_value
1793
+ data.each_value { |v| force_encoding(v, encoding) }
1794
+ elsif data.respond_to? :each
1795
+ data.each { |v| force_encoding(v, encoding) }
1822
1796
  end
1823
- else
1824
- def self.force_encoding(data, *) data end
1797
+ data
1825
1798
  end
1826
1799
 
1800
+ def force_encoding(*args) settings.force_encoding(*args) end
1801
+
1827
1802
  reset!
1828
1803
 
1829
- set :environment, (ENV['RACK_ENV'] || :development).to_sym
1804
+ set :environment, (ENV['APP_ENV'] || ENV['RACK_ENV'] || :development).to_sym
1830
1805
  set :raise_errors, Proc.new { test? }
1831
1806
  set :dump_errors, Proc.new { !test? }
1832
1807
  set :show_exceptions, Proc.new { development? }
1833
1808
  set :sessions, false
1809
+ set :session_store, Rack::Session::Cookie
1834
1810
  set :logging, false
1835
1811
  set :protection, true
1836
1812
  set :method_override, false
@@ -1839,6 +1815,8 @@ module Sinatra
1839
1815
  set :x_cascade, true
1840
1816
  set :add_charset, %w[javascript xml xhtml+xml].map { |t| "application/#{t}" }
1841
1817
  settings.add_charset << /^text\//
1818
+ set :mustermann_opts, {}
1819
+ set :default_content_type, 'text/html'
1842
1820
 
1843
1821
  # explicitly generating a session secret eagerly to play nice with preforking
1844
1822
  begin
@@ -1861,6 +1839,7 @@ module Sinatra
1861
1839
  set :server, %w[HTTP webrick]
1862
1840
  set :bind, Proc.new { development? ? 'localhost' : '0.0.0.0' }
1863
1841
  set :port, Integer(ENV['PORT'] && !ENV['PORT'].empty? ? ENV['PORT'] : 4567)
1842
+ set :quiet, false
1864
1843
 
1865
1844
  ruby_engine = defined?(RUBY_ENGINE) && RUBY_ENGINE
1866
1845
 
@@ -1868,16 +1847,16 @@ module Sinatra
1868
1847
  server.unshift 'control_tower'
1869
1848
  else
1870
1849
  server.unshift 'reel'
1850
+ server.unshift 'puma'
1871
1851
  server.unshift 'mongrel' if ruby_engine.nil?
1872
- server.unshift 'puma' if ruby_engine != 'rbx'
1873
1852
  server.unshift 'thin' if ruby_engine != 'jruby'
1874
- server.unshift 'puma' if ruby_engine == 'rbx'
1875
1853
  server.unshift 'trinidad' if ruby_engine == 'jruby'
1876
1854
  end
1877
1855
 
1878
1856
  set :absolute_redirects, true
1879
1857
  set :prefixed_redirects, false
1880
1858
  set :empty_path_info, nil
1859
+ set :strict_paths, true
1881
1860
 
1882
1861
  set :app_file, nil
1883
1862
  set :root, Proc.new { app_file && File.expand_path(File.dirname(app_file)) }
@@ -1898,7 +1877,7 @@ module Sinatra
1898
1877
 
1899
1878
  configure :development do
1900
1879
  get '/__sinatra__/:image.png' do
1901
- filename = File.dirname(__FILE__) + "/images/#{params[:image].to_i}.png"
1880
+ filename = __dir__ + "/images/#{params[:image].to_i}.png"
1902
1881
  content_type :png
1903
1882
  send_file filename
1904
1883
  end
@@ -1936,7 +1915,7 @@ module Sinatra
1936
1915
  </style>
1937
1916
  </head>
1938
1917
  <body>
1939
- <h2>Sinatra doesn&rsquo;t know this ditty.</h2>
1918
+ <h2>Sinatra doesnt know this ditty.</h2>
1940
1919
  <img src='#{uri "/__sinatra__/404.png"}'>
1941
1920
  <div id="c">
1942
1921
  Try this:
@@ -1957,14 +1936,13 @@ module Sinatra
1957
1936
  # top-level. Subclassing Sinatra::Base is highly recommended for
1958
1937
  # modular applications.
1959
1938
  class Application < Base
1960
- set :logging, Proc.new { ! test? }
1939
+ set :logging, Proc.new { !test? }
1961
1940
  set :method_override, true
1962
- set :run, Proc.new { ! test? }
1963
- set :session_secret, Proc.new { super() unless development? }
1941
+ set :run, Proc.new { !test? }
1964
1942
  set :app_file, nil
1965
1943
 
1966
1944
  def self.register(*extensions, &block) #:nodoc:
1967
- added_methods = extensions.map {|m| m.public_instance_methods }.flatten
1945
+ added_methods = extensions.flat_map(&:public_instance_methods)
1968
1946
  Delegator.delegate(*added_methods)
1969
1947
  super(*extensions, &block)
1970
1948
  end