sinatra 3.0.5 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/sinatra/base.rb CHANGED
@@ -2,8 +2,13 @@
2
2
 
3
3
  # external dependencies
4
4
  require 'rack'
5
+ begin
6
+ require 'rackup'
7
+ rescue LoadError
8
+ end
5
9
  require 'tilt'
6
10
  require 'rack/protection'
11
+ require 'rack/session'
7
12
  require 'mustermann'
8
13
  require 'mustermann/sinatra'
9
14
  require 'mustermann/regular'
@@ -19,7 +24,7 @@ require 'sinatra/version'
19
24
 
20
25
  module Sinatra
21
26
  # The request object. See Rack::Request for more info:
22
- # http://rubydoc.info/github/rack/rack/master/Rack/Request
27
+ # https://rubydoc.info/github/rack/rack/main/Rack/Request
23
28
  class Request < Rack::Request
24
29
  HEADER_PARAM = /\s*[\w.]+=(?:[\w.]+|"(?:[^"\\]|\\.)*")?\s*/.freeze
25
30
  HEADER_VALUE_WITH_PARAMS = %r{(?:(?:\w+|\*)/(?:\w+(?:\.|-|\+)?|\*)*)\s*(?:;#{HEADER_PARAM})*}.freeze
@@ -158,8 +163,8 @@ module Sinatra
158
163
 
159
164
  # The response object. See Rack::Response and Rack::Response::Helpers for
160
165
  # more info:
161
- # http://rubydoc.info/github/rack/rack/master/Rack/Response
162
- # http://rubydoc.info/github/rack/rack/master/Rack/Response/Helpers
166
+ # https://rubydoc.info/github/rack/rack/main/Rack/Response
167
+ # https://rubydoc.info/github/rack/rack/main/Rack/Response/Helpers
163
168
  class Response < Rack::Response
164
169
  DROP_BODY_RESPONSES = [204, 304].freeze
165
170
 
@@ -176,8 +181,8 @@ module Sinatra
176
181
  result = body
177
182
 
178
183
  if drop_content_info?
179
- headers.delete 'Content-Length'
180
- headers.delete 'Content-Type'
184
+ headers.delete 'content-length'
185
+ headers.delete 'content-type'
181
186
  end
182
187
 
183
188
  if drop_body?
@@ -186,9 +191,9 @@ module Sinatra
186
191
  end
187
192
 
188
193
  if calculate_content_length?
189
- # if some other code has already set Content-Length, don't muck with it
194
+ # if some other code has already set content-length, don't muck with it
190
195
  # currently, this would be the static file-handler
191
- headers['Content-Length'] = body.map(&:bytesize).reduce(0, :+).to_s
196
+ headers['content-length'] = body.map(&:bytesize).reduce(0, :+).to_s
192
197
  end
193
198
 
194
199
  [status, headers, result]
@@ -197,7 +202,7 @@ module Sinatra
197
202
  private
198
203
 
199
204
  def calculate_content_length?
200
- headers['Content-Type'] && !headers['Content-Length'] && (Array === body)
205
+ headers['content-type'] && !headers['content-length'] && (Array === body)
201
206
  end
202
207
 
203
208
  def drop_content_info?
@@ -209,7 +214,7 @@ module Sinatra
209
214
  end
210
215
  end
211
216
 
212
- # Some Rack handlers (Rainbows!) implement an extended body object protocol, however,
217
+ # Some Rack handlers implement an extended body object protocol, however,
213
218
  # some middleware (namely Rack::Lint) will break it by not mirroring the methods in question.
214
219
  # This middleware will detect an extended body object and will make sure it reaches the
215
220
  # handler directly. We do this here, so our middleware and middleware set up by the app will
@@ -289,10 +294,8 @@ module Sinatra
289
294
  def block.each; yield(call) end
290
295
  response.body = block
291
296
  elsif value
292
- # Rack 2.0 returns a Rack::File::Iterator here instead of
293
- # Rack::File as it was in the previous API.
294
- unless request.head? || value.is_a?(Rack::File::Iterator) || value.is_a?(Stream)
295
- headers.delete 'Content-Length'
297
+ unless request.head? || value.is_a?(Rack::Files::Iterator) || value.is_a?(Stream)
298
+ headers.delete 'content-length'
296
299
  end
297
300
  response.body = value
298
301
  else
@@ -302,7 +305,10 @@ module Sinatra
302
305
 
303
306
  # Halt processing and redirect to the URI provided.
304
307
  def redirect(uri, *args)
305
- if (env['HTTP_VERSION'] == 'HTTP/1.1') && (env['REQUEST_METHOD'] != 'GET')
308
+ # SERVER_PROTOCOL is required in Rack 3, fall back to HTTP_VERSION
309
+ # for servers not updated for Rack 3 (like Puma 5)
310
+ http_version = env['SERVER_PROTOCOL'] || env['HTTP_VERSION']
311
+ if (http_version == 'HTTP/1.1') && (env['REQUEST_METHOD'] != 'GET')
306
312
  status 303
307
313
  else
308
314
  status 302
@@ -317,7 +323,7 @@ module Sinatra
317
323
  # Generates the absolute URI for a given path in the app.
318
324
  # Takes Rack routers and reverse proxies into account.
319
325
  def uri(addr = nil, absolute = true, add_script_name = true)
320
- return addr if addr =~ /\A[a-z][a-z0-9+.\-]*:/i
326
+ return addr if addr.to_s =~ /\A[a-z][a-z0-9+.\-]*:/i
321
327
 
322
328
  uri = [host = String.new]
323
329
  if absolute
@@ -372,10 +378,10 @@ module Sinatra
372
378
  Base.mime_type(type)
373
379
  end
374
380
 
375
- # Set the Content-Type of the response body given a media type or file
381
+ # Set the content-type of the response body given a media type or file
376
382
  # extension.
377
383
  def content_type(type = nil, params = {})
378
- return response['Content-Type'] unless type
384
+ return response['content-type'] unless type
379
385
 
380
386
  default = params.delete :default
381
387
  mime_type = mime_type(type) || default
@@ -393,7 +399,7 @@ module Sinatra
393
399
  "#{key}=#{val}"
394
400
  end.join(', ')
395
401
  end
396
- response['Content-Type'] = mime_type
402
+ response['content-type'] = mime_type
397
403
  end
398
404
 
399
405
  # https://html.spec.whatwg.org/#multipart-form-data
@@ -412,12 +418,12 @@ module Sinatra
412
418
  params = format('; filename="%s"', File.basename(filename).gsub(/["\r\n]/, MULTIPART_FORM_DATA_REPLACEMENT_TABLE))
413
419
  response['Content-Disposition'] << params
414
420
  ext = File.extname(filename)
415
- content_type(ext) unless response['Content-Type'] || ext.empty?
421
+ content_type(ext) unless response['content-type'] || ext.empty?
416
422
  end
417
423
 
418
424
  # Use the contents of the file at +path+ as the response body.
419
425
  def send_file(path, opts = {})
420
- if opts[:type] || !response['Content-Type']
426
+ if opts[:type] || !response['content-type']
421
427
  content_type opts[:type] || File.extname(path), default: 'application/octet-stream'
422
428
  end
423
429
 
@@ -429,11 +435,11 @@ module Sinatra
429
435
 
430
436
  last_modified opts[:last_modified] if opts[:last_modified]
431
437
 
432
- file = Rack::File.new(File.dirname(settings.app_file))
438
+ file = Rack::Files.new(File.dirname(settings.app_file))
433
439
  result = file.serving(request, path)
434
440
 
435
441
  result[1].each { |k, v| headers[k] ||= v }
436
- headers['Content-Length'] = result[1]['Content-Length']
442
+ headers['content-length'] = result[1]['content-length']
437
443
  opts[:status] &&= Integer(opts[:status])
438
444
  halt (opts[:status] || result[0]), result[2]
439
445
  rescue Errno::ENOENT
@@ -474,8 +480,9 @@ module Sinatra
474
480
  @back.call(self)
475
481
  rescue Exception => e
476
482
  @scheduler.schedule { raise e }
483
+ ensure
484
+ close unless @keep_open
477
485
  end
478
- close unless @keep_open
479
486
  end
480
487
  end
481
488
 
@@ -501,12 +508,20 @@ module Sinatra
501
508
  # the response body have not yet been generated.
502
509
  #
503
510
  # The close parameter specifies whether Stream#close should be called
504
- # after the block has been executed. This is only relevant for evented
505
- # servers like Rainbows.
511
+ # after the block has been executed.
506
512
  def stream(keep_open = false)
507
513
  scheduler = env['async.callback'] ? EventMachine : Stream
508
514
  current = @params.dup
509
- body Stream.new(scheduler, keep_open) { |out| with_params(current) { yield(out) } }
515
+ stream = if scheduler == Stream && keep_open
516
+ Stream.new(scheduler, false) do |out|
517
+ until out.closed?
518
+ with_params(current) { yield(out) }
519
+ end
520
+ end
521
+ else
522
+ Stream.new(scheduler, keep_open) { |out| with_params(current) { yield(out) } }
523
+ end
524
+ body stream
510
525
  end
511
526
 
512
527
  # Specify response freshness policy for HTTP caches (Cache-Control header).
@@ -716,7 +731,7 @@ module Sinatra
716
731
  # Possible options are:
717
732
  # :content_type The content type to use, same arguments as content_type.
718
733
  # :layout If set to something falsy, no layout is rendered, otherwise
719
- # the specified layout is used
734
+ # the specified layout is used (Ignored for `sass`)
720
735
  # :layout_engine Engine to use for rendering the layout.
721
736
  # :locals A hash with local variables that should be available
722
737
  # in the template
@@ -742,6 +757,20 @@ module Sinatra
742
757
  render(:haml, template, options, locals, &block)
743
758
  end
744
759
 
760
+ def sass(template, options = {}, locals = {})
761
+ options[:default_content_type] = :css
762
+ options[:exclude_outvar] = true
763
+ options[:layout] = nil
764
+ render :sass, template, options, locals
765
+ end
766
+
767
+ def scss(template, options = {}, locals = {})
768
+ options[:default_content_type] = :css
769
+ options[:exclude_outvar] = true
770
+ options[:layout] = nil
771
+ render :scss, template, options, locals
772
+ end
773
+
745
774
  def builder(template = nil, options = {}, locals = {}, &block)
746
775
  options[:default_content_type] = :xml
747
776
  render_ruby(:builder, template, options, locals, &block)
@@ -852,7 +881,11 @@ module Sinatra
852
881
  catch(:layout_missing) { return render(layout_engine, layout, options, locals) { output } }
853
882
  end
854
883
 
855
- output.extend(ContentTyped).content_type = content_type if content_type
884
+ if content_type
885
+ # sass-embedded returns a frozen string
886
+ output = +output
887
+ output.extend(ContentTyped).content_type = content_type
888
+ end
856
889
  output
857
890
  end
858
891
 
@@ -904,6 +937,35 @@ module Sinatra
904
937
  end
905
938
  end
906
939
 
940
+ # Extremely simple template cache implementation.
941
+ # * Not thread-safe.
942
+ # * Size is unbounded.
943
+ # * Keys are not copied defensively, and should not be modified after
944
+ # being passed to #fetch. More specifically, the values returned by
945
+ # key#hash and key#eql? should not change.
946
+ #
947
+ # Implementation copied from Tilt::Cache.
948
+ class TemplateCache
949
+ def initialize
950
+ @cache = {}
951
+ end
952
+
953
+ # Caches a value for key, or returns the previously cached value.
954
+ # If a value has been previously cached for key then it is
955
+ # returned. Otherwise, block is yielded to and its return value
956
+ # which may be nil, is cached under key and returned.
957
+ def fetch(*key)
958
+ @cache.fetch(key) do
959
+ @cache[key] = yield
960
+ end
961
+ end
962
+
963
+ # Clears the cache.
964
+ def clear
965
+ @cache = {}
966
+ end
967
+ end
968
+
907
969
  # Base class for all Sinatra applications and middleware.
908
970
  class Base
909
971
  include Rack::Utils
@@ -918,7 +980,7 @@ module Sinatra
918
980
  def initialize(app = nil, **_kwargs)
919
981
  super()
920
982
  @app = app
921
- @template_cache = Tilt::Cache.new
983
+ @template_cache = TemplateCache.new
922
984
  @pinned_response = nil # whether a before! filter pinned the content-type
923
985
  yield self if block_given?
924
986
  end
@@ -939,7 +1001,7 @@ module Sinatra
939
1001
  invoke { dispatch! }
940
1002
  invoke { error_block!(response.status) } unless @env['sinatra.error']
941
1003
 
942
- unless @response['Content-Type']
1004
+ unless @response['content-type']
943
1005
  if Array === body && body[0].respond_to?(:content_type)
944
1006
  content_type body[0].content_type
945
1007
  elsif (default = settings.default_content_type)
@@ -1002,7 +1064,7 @@ module Sinatra
1002
1064
  routes = base.routes[@request.request_method]
1003
1065
 
1004
1066
  routes&.each do |pattern, conditions, block|
1005
- response.delete_header('Content-Type') unless @pinned_response
1067
+ response.delete_header('content-type') unless @pinned_response
1006
1068
 
1007
1069
  returned_pass_block = process_route(pattern, conditions) do |*args|
1008
1070
  env['sinatra.route'] = "#{@request.request_method} #{pattern}"
@@ -1123,7 +1185,7 @@ module Sinatra
1123
1185
  invoke do
1124
1186
  static! if settings.static? && (request.get? || request.head?)
1125
1187
  filter! :before do
1126
- @pinned_response = !response['Content-Type'].nil?
1188
+ @pinned_response = !response['content-type'].nil?
1127
1189
  end
1128
1190
  route!
1129
1191
  end
@@ -1204,7 +1266,19 @@ module Sinatra
1204
1266
  end
1205
1267
 
1206
1268
  def dump_errors!(boom)
1207
- msg = ["#{Time.now.strftime('%Y-%m-%d %H:%M:%S')} - #{boom.class} - #{boom.message}:", *boom.backtrace].join("\n\t")
1269
+ if boom.respond_to?(:detailed_message)
1270
+ msg = boom.detailed_message(highlight: false)
1271
+ if msg =~ /\A(.*?)(?: \(#{ Regexp.quote(boom.class.to_s) }\))?\n/
1272
+ msg = $1
1273
+ additional_msg = $'.lines(chomp: true)
1274
+ else
1275
+ additional_msg = []
1276
+ end
1277
+ else
1278
+ msg = boom.message
1279
+ additional_msg = []
1280
+ end
1281
+ msg = ["#{Time.now.strftime('%Y-%m-%d %H:%M:%S')} - #{boom.class} - #{msg}:", *additional_msg, *boom.backtrace].join("\n\t")
1208
1282
  @env['rack.errors'].puts(msg)
1209
1283
  end
1210
1284
 
@@ -1213,6 +1287,7 @@ module Sinatra
1213
1287
  %r{/sinatra(/(base|main|show_exceptions))?\.rb$}, # all sinatra code
1214
1288
  %r{lib/tilt.*\.rb$}, # all tilt code
1215
1289
  /^\(.*\)$/, # generated code
1290
+ /\/bundled_gems.rb$/, # ruby >= 3.3 with bundler >= 2.5
1216
1291
  %r{rubygems/(custom|core_ext/kernel)_require\.rb$}, # rubygems require hacks
1217
1292
  /active_support/, # active_support require hacks
1218
1293
  %r{bundler(/(?:runtime|inline))?\.rb}, # bundler require hacks
@@ -1220,7 +1295,7 @@ module Sinatra
1220
1295
  %r{zeitwerk/kernel\.rb} # Zeitwerk kernel#require decorator
1221
1296
  ].freeze
1222
1297
 
1223
- attr_reader :routes, :filters, :templates, :errors
1298
+ attr_reader :routes, :filters, :templates, :errors, :on_start_callback, :on_stop_callback
1224
1299
 
1225
1300
  def callers_to_ignore
1226
1301
  CALLERS_TO_IGNORE
@@ -1391,7 +1466,13 @@ module Sinatra
1391
1466
  # mime_types :js # => ['application/javascript', 'text/javascript']
1392
1467
  def mime_types(type)
1393
1468
  type = mime_type type
1394
- type =~ %r{^application/(xml|javascript)$} ? [type, "text/#{$1}"] : [type]
1469
+ if type =~ %r{^application/(xml|javascript)$}
1470
+ [type, "text/#{$1}"]
1471
+ elsif type =~ %r{^text/(xml|javascript)$}
1472
+ [type, "application/#{$1}"]
1473
+ else
1474
+ [type]
1475
+ end
1395
1476
  end
1396
1477
 
1397
1478
  # Define a before filter; runs before all requests within the same
@@ -1413,6 +1494,14 @@ module Sinatra
1413
1494
  filters[type] << compile!(type, path, block, **options)
1414
1495
  end
1415
1496
 
1497
+ def on_start(&on_start_callback)
1498
+ @on_start_callback = on_start_callback
1499
+ end
1500
+
1501
+ def on_stop(&on_stop_callback)
1502
+ @on_stop_callback = on_stop_callback
1503
+ end
1504
+
1416
1505
  # Add a route condition. The route is considered non-matching when the
1417
1506
  # block returns false.
1418
1507
  def condition(name = "#{caller.first[/`.*'/]} condition", &block)
@@ -1502,18 +1591,37 @@ module Sinatra
1502
1591
  warn '== Sinatra has ended his set (crowd applauds)' unless suppress_messages?
1503
1592
  set :running_server, nil
1504
1593
  set :handler_name, nil
1594
+
1595
+ on_stop_callback.call unless on_stop_callback.nil?
1505
1596
  end
1506
1597
 
1507
1598
  alias stop! quit!
1508
1599
 
1509
1600
  # Run the Sinatra app as a self-hosted server using
1510
- # Puma, Falcon, Mongrel, or WEBrick (in that order). If given a block, will call
1601
+ # Puma, Falcon, or WEBrick (in that order). If given a block, will call
1511
1602
  # with the constructed handler once we have taken the stage.
1512
1603
  def run!(options = {}, &block)
1604
+ unless defined?(Rackup::Handler)
1605
+ rackup_warning = <<~MISSING_RACKUP
1606
+ Sinatra could not start, the "rackup" gem was not found!
1607
+
1608
+ Add it to your bundle with:
1609
+
1610
+ bundle add rackup
1611
+
1612
+ or install it with:
1613
+
1614
+ gem install rackup
1615
+
1616
+ MISSING_RACKUP
1617
+ warn rackup_warning
1618
+ exit 1
1619
+ end
1620
+
1513
1621
  return if running?
1514
1622
 
1515
1623
  set options
1516
- handler = Rack::Handler.pick(server)
1624
+ handler = Rackup::Handler.pick(server)
1517
1625
  handler_name = handler.name.gsub(/.*::/, '')
1518
1626
  server_settings = settings.respond_to?(:server_settings) ? settings.server_settings : {}
1519
1627
  server_settings.merge!(Port: port, Host: bind)
@@ -1589,7 +1697,7 @@ module Sinatra
1589
1697
  set :running_server, server
1590
1698
  set :handler_name, handler_name
1591
1699
  server.threaded = settings.threaded if server.respond_to? :threaded=
1592
-
1700
+ on_start_callback.call unless on_start_callback.nil?
1593
1701
  yield server if block_given?
1594
1702
  end
1595
1703
  end
@@ -1645,7 +1753,7 @@ module Sinatra
1645
1753
  types.map! { |t| mime_types(t) }
1646
1754
  types.flatten!
1647
1755
  condition do
1648
- response_content_type = response['Content-Type']
1756
+ response_content_type = response['content-type']
1649
1757
  preferred_type = request.preferred_type(types)
1650
1758
 
1651
1759
  if response_content_type
@@ -1822,7 +1930,7 @@ module Sinatra
1822
1930
  set :dump_errors, proc { !test? }
1823
1931
  set :show_exceptions, proc { development? }
1824
1932
  set :sessions, false
1825
- set :session_store, Rack::Protection::EncryptedCookie
1933
+ set :session_store, Rack::Session::Cookie
1826
1934
  set :logging, false
1827
1935
  set :protection, true
1828
1936
  set :method_override, false
@@ -1838,8 +1946,9 @@ module Sinatra
1838
1946
  begin
1839
1947
  require 'securerandom'
1840
1948
  set :session_secret, SecureRandom.hex(64)
1841
- rescue LoadError, NotImplementedError
1949
+ rescue LoadError, NotImplementedError, RuntimeError
1842
1950
  # SecureRandom raises a NotImplementedError if no random device is available
1951
+ # RuntimeError raised due to broken openssl backend: https://bugs.ruby-lang.org/issues/19230
1843
1952
  set :session_secret, format('%064x', Kernel.rand((2**256) - 1))
1844
1953
  end
1845
1954
 
@@ -1859,11 +1968,10 @@ module Sinatra
1859
1968
 
1860
1969
  ruby_engine = defined?(RUBY_ENGINE) && RUBY_ENGINE
1861
1970
 
1862
- server.unshift 'puma'
1863
- server.unshift 'falcon' if ruby_engine != 'jruby'
1864
- server.unshift 'mongrel' if ruby_engine.nil?
1865
1971
  server.unshift 'thin' if ruby_engine != 'jruby'
1972
+ server.unshift 'falcon' if ruby_engine != 'jruby'
1866
1973
  server.unshift 'trinidad' if ruby_engine == 'jruby'
1974
+ server.unshift 'puma'
1867
1975
 
1868
1976
  set :absolute_redirects, true
1869
1977
  set :prefixed_redirects, false
@@ -1928,7 +2036,7 @@ module Sinatra
1928
2036
  </head>
1929
2037
  <body>
1930
2038
  <h2>Sinatra doesn’t know this ditty.</h2>
1931
- <img src='#{uri '/__sinatra__/404.png'}'>
2039
+ <img src='#{request.script_name}/__sinatra__/404.png'>
1932
2040
  <div id="c">
1933
2041
  Try this:
1934
2042
  <pre>#{Rack::Utils.escape_html(code)}</pre>
@@ -1980,7 +2088,7 @@ module Sinatra
1980
2088
  delegate :get, :patch, :put, :post, :delete, :head, :options, :link, :unlink,
1981
2089
  :template, :layout, :before, :after, :error, :not_found, :configure,
1982
2090
  :set, :mime_type, :enable, :disable, :use, :development?, :test?,
1983
- :production?, :helpers, :settings, :register
2091
+ :production?, :helpers, :settings, :register, :on_start, :on_stop
1984
2092
 
1985
2093
  class << self
1986
2094
  attr_accessor :target
@@ -21,7 +21,8 @@ module Sinatra
21
21
  # writing interface (calling e.g. <tt>[]=</tt>, <tt>merge</tt>). This mapping
22
22
  # belongs to the public interface. For example, given:
23
23
  #
24
- # hash = Sinatra::IndifferentHash.new(:a=>1)
24
+ # hash = Sinatra::IndifferentHash.new
25
+ # hash[:a] = 1
25
26
  #
26
27
  # You are guaranteed that the key is returned as a string:
27
28
  #
@@ -29,7 +30,8 @@ module Sinatra
29
30
  #
30
31
  # Technically other types of keys are accepted:
31
32
  #
32
- # hash = Sinatra::IndifferentHash.new(:a=>1)
33
+ # hash = Sinatra::IndifferentHash
34
+ # hash[:a] = 1
33
35
  # hash[0] = 0
34
36
  # hash # => { "a"=>1, 0=>0 }
35
37
  #
@@ -41,12 +43,6 @@ module Sinatra
41
43
  new.merge!(Hash[*args])
42
44
  end
43
45
 
44
- def initialize(*args)
45
- args.map!(&method(:convert_value))
46
-
47
- super(*args)
48
- end
49
-
50
46
  def default(*args)
51
47
  args.map!(&method(:convert_key))
52
48
 
@@ -186,6 +182,12 @@ module Sinatra
186
182
  dup.tap(&:compact!)
187
183
  end
188
184
 
185
+ def except(*keys)
186
+ keys.map!(&method(:convert_key))
187
+
188
+ super(*keys)
189
+ end if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.0")
190
+
189
191
  private
190
192
 
191
193
  def convert_key(key)
@@ -38,8 +38,8 @@ module Sinatra
38
38
  [
39
39
  500,
40
40
  {
41
- 'Content-Type' => content_type,
42
- 'Content-Length' => body.bytesize.to_s
41
+ 'content-type' => content_type,
42
+ 'content-length' => body.bytesize.to_s
43
43
  },
44
44
  [body]
45
45
  ]
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sinatra
4
- VERSION = '3.0.4'
4
+ VERSION = '4.0.0'
5
5
  end
data/sinatra.gemspec CHANGED
@@ -36,19 +36,18 @@ RubyGems 2.0 or newer is required to protect against public gem pushes. You can
36
36
 
37
37
  s.metadata = {
38
38
  'source_code_uri' => 'https://github.com/sinatra/sinatra',
39
- 'changelog_uri' => 'https://github.com/sinatra/sinatra/blob/master/CHANGELOG.md',
39
+ 'changelog_uri' => 'https://github.com/sinatra/sinatra/blob/main/CHANGELOG.md',
40
40
  'homepage_uri' => 'http://sinatrarb.com/',
41
41
  'bug_tracker_uri' => 'https://github.com/sinatra/sinatra/issues',
42
42
  'mailing_list_uri' => 'http://groups.google.com/group/sinatrarb',
43
43
  'documentation_uri' => 'https://www.rubydoc.info/gems/sinatra'
44
44
  }
45
45
 
46
- s.required_ruby_version = '>= 2.6.0'
46
+ s.required_ruby_version = '>= 2.7.8'
47
47
 
48
48
  s.add_dependency 'mustermann', '~> 3.0'
49
- s.add_dependency 'rack', '~> 2.2', '>= 2.2.4'
49
+ s.add_dependency 'rack', '>= 3.0.0', '< 4'
50
+ s.add_dependency 'rack-session', '>= 2.0.0', '< 3'
50
51
  s.add_dependency 'rack-protection', version
51
52
  s.add_dependency 'tilt', '~> 2.0'
52
-
53
- s.add_development_dependency 'rack-test', '~> 2'
54
53
  end