sinatra 2.0.0 → 2.0.8.1

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.

Potentially problematic release.


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

data/README.zh.md CHANGED
@@ -239,6 +239,17 @@ end
239
239
  ```
240
240
  顺便一提,除非你禁用了路径遍历攻击防护(见下文),请求路径可能在匹配路由前发生改变。
241
241
 
242
+ 你也可以通过`:mustermann_opt`选项定义[Mustermann](https://github.com/sinatra/mustermann)来匹配路由。
243
+
244
+ ```ruby
245
+ get '\A/posts\z', :mustermann_opts => { :type => :regexp, :check_anchors => false } do
246
+ # matches /posts exactly, with explicit anchoring
247
+ "If you match an anchored pattern clap your hands!"
248
+ end
249
+ ```
250
+
251
+ 它看起来像一个[条件](https://github.com/sinatra/sinatra/blob/master/README.zh.md#%E6%9D%A1%E4%BB%B6),但实际不是!这些选项将被合并到全局的`mustermann_opts`。
252
+
242
253
  ### 条件
243
254
 
244
255
  路由可以包含各种匹配条件,比如 user agent:
@@ -313,8 +324,8 @@ Rack 堆栈中的下一个中间件。大多数情况下,返回值是一个字
313
324
 
314
325
  你可以返回任何对象,该对象要么是一个合理的 Rack 响应,要么是一个 Rack body 对象,要么是 HTTP 状态码:
315
326
 
316
- * 一个包含三个元素的数组: `[状态 (Fixnum), 响应首部 (Hash), 响应主体 (可以响应 #each 方法)]`
317
- * 一个包含两个元素的数组: `[状态 (Fixnum), 响应主体 (可以响应 #each 方法)]`
327
+ * 一个包含三个元素的数组: `[状态 (Integer), 响应首部 (Hash), 响应主体 (可以响应 #each 方法)]`
328
+ * 一个包含两个元素的数组: `[状态 (Integer), 响应主体 (可以响应 #each 方法)]`
318
329
  * 一个响应 `#each` 方法,只传回字符串的对象
319
330
  * 一个代表状态码的数字
320
331
 
@@ -649,7 +660,7 @@ get('/') { markdown :index }
649
660
  <table>
650
661
  <tr>
651
662
  <td>依赖项</td>
652
- <td><a href="http://liquidmarkup.org/" title="liquid">liquid</a></td>
663
+ <td><a href="https://shopify.github.io/liquid/" title="liquid">liquid</a></td>
653
664
  </tr>
654
665
  <tr>
655
666
  <td>文件扩展名</td>
@@ -672,7 +683,7 @@ get('/') { markdown :index }
672
683
  下列任一:
673
684
  <a href="https://github.com/davidfstr/rdiscount" title="RDiscount">RDiscount</a>,
674
685
  <a href="https://github.com/vmg/redcarpet" title="RedCarpet">RedCarpet</a>,
675
- <a href="http://deveiate.org/projects/BlueCloth" title="BlueCloth">BlueCloth</a>,
686
+ <a href="https://github.com/ged/bluecloth" title="bluecloth">BlueCloth</a>,
676
687
  <a href="http://kramdown.gettalong.org/" title="kramdown">kramdown</a>,
677
688
  <a href="https://github.com/bhollis/maruku" title="maruku">maruku</a>
678
689
  </td>
@@ -1302,33 +1313,59 @@ get '/:value' do
1302
1313
  end
1303
1314
  ```
1304
1315
 
1305
- 请注意 `enable :sessions` 实际将所有的数据保存在一个 cookie 中。
1306
- 这可能并不总是你想要的(cookie 中存储大量的数据会增加你的流量)。
1307
- 你可以使用任何 Rack session 中间件:要达到此目的,**不要**使用 `enable :sessions`,
1308
- 而是按照自己的需要引入想使用的中间件:
1316
+ #### 会话加密
1317
+
1318
+ 为提高安全性,cookie 中的会话数据使用`HMAC-SHA1`进行加密。会话加密的最佳实践应当是像`HMAC-SHA1`这样生成大于或等于64字节 (512 bits, 128 hex characters)的随机值。应当避免使用少于32字节(256 bits, 64 hex characters)的随机值。应当使用生成器来创建安全的密钥,而不是拍脑袋决定。
1319
+
1320
+ 默认情况下,Sinatra会生成一个32字节的密钥,但随着应用程序的每次重新启动,它都会发生改变。如果有多个应用程序的实例,使用Sinatra生成密钥,每个实例将有不同的密钥,这可能不是您想要的。
1321
+
1322
+ 为了更好的安全性和可用性,[建议](https://12factor.net/config)生成安全的随机密钥,并将其存储在运行应用程序的每个主机上的环境变量中,以便所有应用程序实例都将共享相同的密钥。并且应该定期更新会话密钥。下面是一些创建64比特密钥的例子:
1323
+
1324
+ #### 生成密钥
1309
1325
 
1310
1326
  ```ruby
1311
- use Rack::Session::Pool, :expire_after => 2592000
1327
+ $ ruby -e "require 'securerandom'; puts SecureRandom.hex(64)"
1328
+ 99ae8af...snip...ec0f262ac
1329
+ ```
1312
1330
 
1313
- get '/' do
1314
- "value = " << session['value'].inspect
1315
- end
1331
+ #### 生成密钥(小贴士)
1316
1332
 
1317
- get '/:value' do
1318
- session['value'] = params['value']
1319
- end
1333
+ MRI Ruby目前认为[sysrandom gem](https://github.com/cryptosphere/sysrandom)使用系统的随机数生成器要比用户态的`OpenSSL`好。
1334
+
1335
+ ```ruby
1336
+ $ gem install sysrandom
1337
+ Building native extensions. This could take a while...
1338
+ Successfully installed sysrandom-1.x
1339
+ 1 gem installed
1340
+
1341
+ $ ruby -e "require 'sysrandom/securerandom'; puts SecureRandom.hex(64)"
1342
+ 99ae8af...snip...ec0f262ac
1320
1343
  ```
1321
1344
 
1322
- 为提高安全性,cookie 中的会话数据会被一个会话密码保护。Sinatra 会为你生成一个随机的密码。
1323
- 然而,每次启动应用时,该密码都会变化,你也可以自己设置该密码,以便所有的应用实例共享:
1345
+ #### 从环境变量使用密钥
1324
1346
 
1347
+ 将Sinatra的SESSION_SECRET环境变量设置为生成的值。在主机的重新启动之间保存这个值。由于这样做的方法会因系统而异,仅供说明之用:
1325
1348
  ```
1326
- set :session_secret, 'super secret'
1349
+ # echo "export SESSION_SECRET=99ae8af...snip...ec0f262ac" >> ~/.bashrc
1327
1350
  ```
1328
1351
 
1329
- 如果你想进一步配置会话,可以在设置 `sessions` 时提供一个选项 hash 作为第二个参数:
1352
+ #### 应用的密钥配置
1330
1353
 
1354
+ 如果SESSION SECRET环境变量不可用,将把应用的随机密钥设置为不安全的。
1355
+
1356
+ 关于[sysrandom gem](https://github.com/cryptosphere/sysrandom)的更多用法:
1357
+
1358
+ ```ruby
1359
+ require 'securerandom'
1360
+ # -or- require 'sysrandom/securerandom'
1361
+ set :session_secret, ENV.fetch('SESSION_SECRET') { SecureRandom.hex(64) }
1331
1362
  ```
1363
+
1364
+ #### 会话配置
1365
+
1366
+ 如果你想进一步配置会话,可以在设置 `sessions` 时提供一个选项 hash 作为第二个参数:
1367
+
1368
+ ```ruby
1332
1369
  set :sessions, :domain => 'foo.com'
1333
1370
  ```
1334
1371
 
@@ -1338,6 +1375,31 @@ set :sessions, :domain => 'foo.com'
1338
1375
  set :sessions, :domain => '.foo.com'
1339
1376
  ```
1340
1377
 
1378
+ #### 选择你自己的会话中间件
1379
+
1380
+ 请注意 `enable :sessions` 实际将所有的数据保存在一个 cookie 中。
1381
+ 这可能并不总是你想要的(cookie 中存储大量的数据会增加你的流量)。
1382
+ 你可以使用任何 Rack session 中间件:要达到此目的,**不要**使用 `enable :sessions`,
1383
+ 而是按照自己的需要引入想使用的中间件:
1384
+
1385
+ ```ruby
1386
+ enable :sessions
1387
+ set :session_store, Rack::Session::Pool
1388
+ ```
1389
+
1390
+ 另一种选择是不要调用enable:sessions,而是像你想要的其他中间件一样加入你的中间件。
1391
+
1392
+ 重要的是要注意,使用此方法时,默认情况下不会启用基于会话的保护。
1393
+
1394
+ 还需要添加Rack中间件:
1395
+
1396
+ ```ruby
1397
+ use Rack::Session::Pool, :expire_after => 2592000
1398
+ use Rack::Protection::RemoteToken
1399
+ use Rack::Protection::SessionHijacking
1400
+ ```
1401
+ 更多[安全防护配置](https://github.com/sinatra/sinatra#configuring-attack-protection)的信息。
1402
+
1341
1403
  ### 中断请求
1342
1404
 
1343
1405
  要想在过滤器或路由中立即中断一个请求:
@@ -1965,7 +2027,7 @@ end
1965
2027
 
1966
2028
  ### 配置攻击防护
1967
2029
 
1968
- Sinatra 使用 [Rack::Protection](https://github.com/sinatra/rack-protection#readme)
2030
+ Sinatra 使用 [Rack::Protection](https://github.com/sinatra/sinatra/tree/master/rack-protection#readme)
1969
2031
  来抵御常见的攻击。你可以轻易地禁用该行为(但这会大大增加应用被攻击的概率)。
1970
2032
 
1971
2033
  ```ruby
data/Rakefile CHANGED
@@ -30,7 +30,7 @@ end
30
30
 
31
31
  Rake::TestTask.new(:test) do |t|
32
32
  t.test_files = FileList['test/*_test.rb']
33
- t.ruby_opts = ['-rubygems'] if defined? Gem
33
+ t.ruby_opts = ['-r rubygems'] if defined? Gem
34
34
  t.ruby_opts << '-I.'
35
35
  t.warning = true
36
36
  end
@@ -41,7 +41,7 @@ Rake::TestTask.new(:"test:core") do |t|
41
41
  readme request response result route_added_hook
42
42
  routing server settings sinatra static templates]
43
43
  t.test_files = core_tests.map {|n| "test/#{n}_test.rb"}
44
- t.ruby_opts = ["-rubygems"] if defined? Gem
44
+ t.ruby_opts = ["-r rubygems"] if defined? Gem
45
45
  t.ruby_opts << "-I."
46
46
  t.warning = true
47
47
  end
@@ -202,11 +202,14 @@ if defined?(Gem)
202
202
 
203
203
  desc "Commits the version to github repository"
204
204
  task :commit_version do
205
- sh <<-SH
206
- sed -i "s/.*VERSION.*/ VERSION = '#{source_version}'/" lib/sinatra/version.rb
207
- sed -i "s/.*VERSION.*/ VERSION = '#{source_version}'/" sinatra-contrib/lib/sinatra/contrib/version.rb
208
- sed -i "s/.*VERSION.*/ VERSION = '#{source_version}'/" rack-protection/lib/rack/protection/version.rb
209
- SH
205
+ %w[
206
+ lib/sinatra
207
+ sinatra-contrib/lib/sinatra/contrib
208
+ rack-protection/lib/rack/protection
209
+ ].each do |path|
210
+ path = File.join(path, 'version.rb')
211
+ File.write(path, File.read(path).sub(/VERSION = '(.+?)'/, "VERSION = '#{source_version}'"))
212
+ end
210
213
 
211
214
  sh <<-SH
212
215
  git commit --allow-empty -a -m '#{source_version} release' &&
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 2.0.8.1
data/lib/sinatra/base.rb CHANGED
@@ -78,7 +78,7 @@ module Sinatra
78
78
  def params
79
79
  super
80
80
  rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
81
- raise BadRequest, "Invalid query parameters: #{e.message}"
81
+ raise BadRequest, "Invalid query parameters: #{Rack::Utils.escape_html(e.message)}"
82
82
  end
83
83
 
84
84
  private
@@ -132,7 +132,7 @@ module Sinatra
132
132
  # http://rubydoc.info/github/rack/rack/master/Rack/Response
133
133
  # http://rubydoc.info/github/rack/rack/master/Rack/Response/Helpers
134
134
  class Response < Rack::Response
135
- DROP_BODY_RESPONSES = [204, 205, 304]
135
+ DROP_BODY_RESPONSES = [204, 304]
136
136
  def initialize(*)
137
137
  super
138
138
  headers['Content-Type'] ||= 'text/html'
@@ -360,7 +360,7 @@ module Sinatra
360
360
  # Set the Content-Disposition to "attachment" with the specified filename,
361
361
  # instructing the user agents to prompt to save.
362
362
  def attachment(filename = nil, disposition = :attachment)
363
- response['Content-Disposition'] = disposition.to_s
363
+ response['Content-Disposition'] = disposition.to_s.dup
364
364
  if filename
365
365
  params = '; filename="%s"' % File.basename(filename)
366
366
  response['Content-Disposition'] << params
@@ -461,7 +461,7 @@ module Sinatra
461
461
  # Specify response freshness policy for HTTP caches (Cache-Control header).
462
462
  # Any number of non-value directives (:public, :private, :no_cache,
463
463
  # :no_store, :must_revalidate, :proxy_revalidate) may be passed along with
464
- # a Hash of value directives (:max_age, :min_stale, :s_maxage).
464
+ # a Hash of value directives (:max_age, :s_maxage).
465
465
  #
466
466
  # cache_control :public, :must_revalidate, :max_age => 60
467
467
  # => Cache-Control: public, must-revalidate, max-age=60
@@ -722,6 +722,7 @@ module Sinatra
722
722
  end
723
723
 
724
724
  def markdown(template, options = {}, locals = {})
725
+ options[:exclude_outvar] = true
725
726
  render :markdown, template, options, locals
726
727
  end
727
728
 
@@ -786,15 +787,8 @@ module Sinatra
786
787
  def find_template(views, name, engine)
787
788
  yield ::File.join(views, "#{name}.#{@preferred_extension}")
788
789
 
789
- if Tilt.respond_to?(:mappings)
790
- Tilt.mappings.each do |ext, engines|
791
- next unless ext != @preferred_extension and engines.include? engine
792
- yield ::File.join(views, "#{name}.#{ext}")
793
- end
794
- else
795
- Tilt.default_mapping.extensions_for(engine).each do |ext|
796
- yield ::File.join(views, "#{name}.#{ext}") unless ext == @preferred_extension
797
- end
790
+ Tilt.default_mapping.extensions_for(engine).each do |ext|
791
+ yield ::File.join(views, "#{name}.#{ext}") unless ext == @preferred_extension
798
792
  end
799
793
  end
800
794
 
@@ -825,10 +819,11 @@ module Sinatra
825
819
  content_type = options.delete(:content_type) || content_type
826
820
  layout_engine = options.delete(:layout_engine) || engine
827
821
  scope = options.delete(:scope) || self
822
+ exclude_outvar = options.delete(:exclude_outvar)
828
823
  options.delete(:layout)
829
824
 
830
825
  # set some defaults
831
- options[:outvar] ||= '@_out_buf'
826
+ options[:outvar] ||= '@_out_buf' unless exclude_outvar
832
827
  options[:default_encoding] ||= settings.default_encoding
833
828
 
834
829
  # compile and render template
@@ -915,6 +910,7 @@ module Sinatra
915
910
 
916
911
  def call!(env) # :nodoc:
917
912
  @env = env
913
+ @params = IndifferentHash.new
918
914
  @request = Request.new(env)
919
915
  @response = Response.new
920
916
  template_cache.clear if settings.reload_templates
@@ -1022,12 +1018,14 @@ module Sinatra
1022
1018
  return unless params = pattern.params(route)
1023
1019
 
1024
1020
  params.delete("ignore") # TODO: better params handling, maybe turn it into "smart" object or detect changes
1025
- original, @params = @params, @params.merge(params) if params.any?
1021
+ force_encoding(params)
1022
+ @params = @params.merge(params) if params.any?
1026
1023
 
1027
- if pattern.is_a? Mustermann::Regular
1028
- captures = pattern.match(route).captures
1024
+ regexp_exists = pattern.is_a?(Mustermann::Regular) || (pattern.respond_to?(:patterns) && pattern.patterns.any? {|subpattern| subpattern.is_a?(Mustermann::Regular)} )
1025
+ if regexp_exists
1026
+ captures = pattern.match(route).captures.map { |c| URI_INSTANCE.unescape(c) if c }
1029
1027
  values += captures
1030
- @params[:captures] = captures
1028
+ @params[:captures] = force_encoding(captures) unless captures.nil? || captures.empty?
1031
1029
  else
1032
1030
  values += params.values.flatten
1033
1031
  end
@@ -1040,7 +1038,8 @@ module Sinatra
1040
1038
  @env['sinatra.error.params'] = @params
1041
1039
  raise
1042
1040
  ensure
1043
- @params = original if original
1041
+ params ||= {}
1042
+ params.each { |k, _| @params.delete(k) } unless @env['sinatra.error.params']
1044
1043
  end
1045
1044
 
1046
1045
  # No matching route was found or all routes passed. The default
@@ -1052,7 +1051,7 @@ module Sinatra
1052
1051
  if @app
1053
1052
  forward
1054
1053
  else
1055
- raise NotFound
1054
+ raise NotFound, "#{request.request_method} #{request.path_info}"
1056
1055
  end
1057
1056
  end
1058
1057
 
@@ -1086,7 +1085,12 @@ module Sinatra
1086
1085
 
1087
1086
  # Dispatch a request with error handling.
1088
1087
  def dispatch!
1089
- force_encoding(@params = IndifferentHash[@request.params])
1088
+ # Avoid passing frozen string in force_encoding
1089
+ @params.merge!(@request.params).each do |key, val|
1090
+ next unless val.respond_to?(:force_encoding)
1091
+ val = val.dup if val.frozen?
1092
+ @params[key] = force_encoding(val)
1093
+ end
1090
1094
 
1091
1095
  invoke do
1092
1096
  static! if settings.static? && (request.get? || request.head?)
@@ -1160,12 +1164,12 @@ module Sinatra
1160
1164
 
1161
1165
  class << self
1162
1166
  CALLERS_TO_IGNORE = [ # :nodoc:
1163
- /\/sinatra(\/(base|main|show_exceptions))?\.rb$/, # all sinatra code
1167
+ /\/sinatra(\/(base|main|show_exceptions))?\.rb$/, # all sinatra code
1164
1168
  /lib\/tilt.*\.rb$/, # all tilt code
1165
1169
  /^\(.*\)$/, # generated code
1166
1170
  /rubygems\/(custom|core_ext\/kernel)_require\.rb$/, # rubygems require hacks
1167
1171
  /active_support/, # active_support require hacks
1168
- /bundler(\/runtime)?\.rb/, # bundler require hacks
1172
+ /bundler(\/(?:runtime|inline))?\.rb/, # bundler require hacks
1169
1173
  /<internal:/, # internal in ruby >= 1.9.2
1170
1174
  /src\/kernel\/bootstrap\/[A-Z]/ # maglev kernel files
1171
1175
  ]
@@ -1245,8 +1249,8 @@ module Sinatra
1245
1249
  end
1246
1250
  end
1247
1251
 
1248
- define_singleton("#{option}=", setter) if setter
1249
- define_singleton(option, getter) if getter
1252
+ define_singleton("#{option}=", setter)
1253
+ define_singleton(option, getter)
1250
1254
  define_singleton("#{option}?", "!!#{option}") unless method_defined? "#{option}?"
1251
1255
  self
1252
1256
  end
@@ -1342,19 +1346,19 @@ module Sinatra
1342
1346
  # context as route handlers and may access/modify the request and
1343
1347
  # response.
1344
1348
  def before(path = /.*/, **options, &block)
1345
- add_filter(:before, path, options, &block)
1349
+ add_filter(:before, path, **options, &block)
1346
1350
  end
1347
1351
 
1348
1352
  # Define an after filter; runs after all requests within the same
1349
1353
  # context as route handlers and may access/modify the request and
1350
1354
  # response.
1351
1355
  def after(path = /.*/, **options, &block)
1352
- add_filter(:after, path, options, &block)
1356
+ add_filter(:after, path, **options, &block)
1353
1357
  end
1354
1358
 
1355
1359
  # add a filter
1356
1360
  def add_filter(type, path = /.*/, **options, &block)
1357
- filters[type] << compile!(type, path, block, options)
1361
+ filters[type] << compile!(type, path, block, **options)
1358
1362
  end
1359
1363
 
1360
1364
  # Add a route condition. The route is considered non-matching when the
@@ -1434,7 +1438,7 @@ module Sinatra
1434
1438
  return unless running?
1435
1439
  # Use Thin's hard #stop! if available, otherwise just #stop.
1436
1440
  running_server.respond_to?(:stop!) ? running_server.stop! : running_server.stop
1437
- $stderr.puts "== Sinatra has ended his set (crowd applauds)" unless supress_messages?
1441
+ $stderr.puts "== Sinatra has ended his set (crowd applauds)" unless suppress_messages?
1438
1442
  set :running_server, nil
1439
1443
  set :handler_name, nil
1440
1444
  end
@@ -1520,7 +1524,7 @@ module Sinatra
1520
1524
  prototype
1521
1525
  # Run the instance we created:
1522
1526
  handler.run(self, server_settings) do |server|
1523
- unless supress_messages?
1527
+ unless suppress_messages?
1524
1528
  $stderr.puts "== Sinatra (v#{Sinatra::VERSION}) has taken the stage on #{port} for #{environment} with backup from #{handler_name}"
1525
1529
  end
1526
1530
 
@@ -1533,7 +1537,7 @@ module Sinatra
1533
1537
  end
1534
1538
  end
1535
1539
 
1536
- def supress_messages?
1540
+ def suppress_messages?
1537
1541
  handler_name =~ /cgi/i || quiet
1538
1542
  end
1539
1543
 
@@ -1598,7 +1602,7 @@ module Sinatra
1598
1602
 
1599
1603
  def route(verb, path, options = {}, &block)
1600
1604
  enable :empty_path_info if path == "" and empty_path_info.nil?
1601
- signature = compile!(verb, path, block, options)
1605
+ signature = compile!(verb, path, block, **options)
1602
1606
  (@routes[verb] ||= []) << signature
1603
1607
  invoke_hook(:route_added, verb, path, block)
1604
1608
  signature
@@ -1635,7 +1639,7 @@ module Sinatra
1635
1639
  end
1636
1640
 
1637
1641
  def compile(path, route_mustermann_opts = {})
1638
- Mustermann.new(path, mustermann_opts.merge(route_mustermann_opts))
1642
+ Mustermann.new(path, **mustermann_opts.merge(route_mustermann_opts))
1639
1643
  end
1640
1644
 
1641
1645
  def setup_default_middleware(builder)
@@ -1740,29 +1744,22 @@ module Sinatra
1740
1744
  end
1741
1745
  end
1742
1746
 
1743
- # Fixes encoding issues by
1744
- # * defaulting to UTF-8
1745
- # * casting params to Encoding.default_external
1746
- #
1747
- # The latter might not be necessary if Rack handles it one day.
1748
- # Keep an eye on Rack's LH #100.
1749
- def force_encoding(*args) settings.force_encoding(*args) end
1750
- if defined? Encoding
1751
- def self.force_encoding(data, encoding = default_encoding)
1752
- return if data == settings || data.is_a?(Tempfile)
1753
- if data.respond_to? :force_encoding
1754
- data.force_encoding(encoding).encode!
1755
- elsif data.respond_to? :each_value
1756
- data.each_value { |v| force_encoding(v, encoding) }
1757
- elsif data.respond_to? :each
1758
- data.each { |v| force_encoding(v, encoding) }
1759
- end
1760
- data
1747
+ # Force data to specified encoding. It defaults to settings.default_encoding
1748
+ # which is UTF-8 by default
1749
+ def self.force_encoding(data, encoding = default_encoding)
1750
+ return if data == settings || data.is_a?(Tempfile)
1751
+ if data.respond_to? :force_encoding
1752
+ data.force_encoding(encoding).encode!
1753
+ elsif data.respond_to? :each_value
1754
+ data.each_value { |v| force_encoding(v, encoding) }
1755
+ elsif data.respond_to? :each
1756
+ data.each { |v| force_encoding(v, encoding) }
1761
1757
  end
1762
- else
1763
- def self.force_encoding(data, *) data end
1758
+ data
1764
1759
  end
1765
1760
 
1761
+ def force_encoding(*args) settings.force_encoding(*args) end
1762
+
1766
1763
  reset!
1767
1764
 
1768
1765
  set :environment, (ENV['APP_ENV'] || ENV['RACK_ENV'] || :development).to_sym
@@ -1810,10 +1807,9 @@ module Sinatra
1810
1807
  server.unshift 'control_tower'
1811
1808
  else
1812
1809
  server.unshift 'reel'
1810
+ server.unshift 'puma'
1813
1811
  server.unshift 'mongrel' if ruby_engine.nil?
1814
- server.unshift 'puma' if ruby_engine != 'rbx'
1815
1812
  server.unshift 'thin' if ruby_engine != 'jruby'
1816
- server.unshift 'puma' if ruby_engine == 'rbx'
1817
1813
  server.unshift 'trinidad' if ruby_engine == 'jruby'
1818
1814
  end
1819
1815
 
@@ -1,4 +1,12 @@
1
1
  # frozen_string_literal: true
2
+ $stderr.puts <<EOF if !Hash.method_defined?(:slice) && !$LOAD_PATH.grep(%r{gems/activesupport}).empty? && ENV['SINATRA_ACTIVESUPPORT_WARNING'] != 'false'
3
+ WARNING: If you plan to load any of ActiveSupport's core extensions to Hash, be
4
+ sure to do so *before* loading Sinatra::Application or Sinatra::Base. If not,
5
+ you may disregard this warning.
6
+
7
+ Set SINATRA_ACTIVESUPPORT_WARNING=false in the environment to hide this warning.
8
+ EOF
9
+
2
10
  module Sinatra
3
11
  # A poor man's ActiveSupport::HashWithIndifferentAccess, with all the Rails-y
4
12
  # stuff removed.
@@ -41,11 +49,15 @@ module Sinatra
41
49
  end
42
50
 
43
51
  def initialize(*args)
44
- super(*args.map(&method(:convert_value)))
52
+ args.map!(&method(:convert_value))
53
+
54
+ super(*args)
45
55
  end
46
56
 
47
57
  def default(*args)
48
- super(*args.map(&method(:convert_key)))
58
+ args.map!(&method(:convert_key))
59
+
60
+ super(*args)
49
61
  end
50
62
 
51
63
  def default=(value)
@@ -61,7 +73,9 @@ module Sinatra
61
73
  end
62
74
 
63
75
  def fetch(key, *args)
64
- super(convert_key(key), *args.map(&method(:convert_value)))
76
+ args.map!(&method(:convert_value))
77
+
78
+ super(convert_key(key), *args)
65
79
  end
66
80
 
67
81
  def [](key)
@@ -101,20 +115,34 @@ module Sinatra
101
115
  end if method_defined?(:dig) # Added in Ruby 2.3
102
116
 
103
117
  def fetch_values(*keys)
104
- super(*keys.map(&method(:convert_key)))
118
+ keys.map!(&method(:convert_key))
119
+
120
+ super(*keys)
105
121
  end if method_defined?(:fetch_values) # Added in Ruby 2.3
106
122
 
107
- def values_at(*keys)
108
- super(*keys.map(&method(:convert_key)))
109
- end
123
+ def slice(*keys)
124
+ keys.map!(&method(:convert_key))
110
125
 
111
- def merge!(other_hash)
112
- return super if other_hash.is_a?(self.class)
126
+ self.class[super(*keys)]
127
+ end if method_defined?(:slice) # Added in Ruby 2.5
113
128
 
114
- other_hash.each_pair do |key, value|
115
- key = convert_key(key)
116
- value = yield(key, self[key], value) if block_given? && key?(key)
117
- self[key] = convert_value(value)
129
+ def values_at(*keys)
130
+ keys.map!(&method(:convert_key))
131
+
132
+ super(*keys)
133
+ end
134
+
135
+ def merge!(*other_hashes)
136
+ other_hashes.each do |other_hash|
137
+ if other_hash.is_a?(self.class)
138
+ super(other_hash)
139
+ else
140
+ other_hash.each_pair do |key, value|
141
+ key = convert_key(key)
142
+ value = yield(key, self[key], value) if block_given? && key?(key)
143
+ self[key] = convert_value(value)
144
+ end
145
+ end
118
146
  end
119
147
 
120
148
  self
@@ -122,14 +150,36 @@ module Sinatra
122
150
 
123
151
  alias_method :update, :merge!
124
152
 
125
- def merge(other_hash, &block)
126
- dup.merge!(other_hash, &block)
153
+ def merge(*other_hashes, &block)
154
+ dup.merge!(*other_hashes, &block)
127
155
  end
128
156
 
129
157
  def replace(other_hash)
130
158
  super(other_hash.is_a?(self.class) ? other_hash : self.class[other_hash])
131
159
  end
132
160
 
161
+ if method_defined?(:transform_values!) # Added in Ruby 2.4
162
+ def transform_values(&block)
163
+ dup.transform_values!(&block)
164
+ end
165
+
166
+ def transform_values!
167
+ super
168
+ super(&method(:convert_value))
169
+ end
170
+ end
171
+
172
+ if method_defined?(:transform_keys!) # Added in Ruby 2.5
173
+ def transform_keys(&block)
174
+ dup.transform_keys!(&block)
175
+ end
176
+
177
+ def transform_keys!
178
+ super
179
+ super(&method(:convert_key))
180
+ end
181
+ end
182
+
133
183
  private
134
184
 
135
185
  def convert_key(key)
data/lib/sinatra/main.rb CHANGED
@@ -1,6 +1,30 @@
1
- require 'sinatra/base'
2
-
3
1
  module Sinatra
2
+ ParamsConfig = {}
3
+
4
+ if ARGV.any?
5
+ require 'optparse'
6
+ parser = OptionParser.new { |op|
7
+ op.on('-p port', 'set the port (default is 4567)') { |val| ParamsConfig[:port] = Integer(val) }
8
+ op.on('-s server', 'specify rack server/handler (default is thin)') { |val| ParamsConfig[:server] = val }
9
+ op.on('-q', 'turn on quiet mode (default is off)') { ParamsConfig[:quiet] = true }
10
+ op.on('-x', 'turn on the mutex lock (default is off)') { ParamsConfig[:lock] = true }
11
+ op.on('-e env', 'set the environment (default is development)') do |val|
12
+ ENV['RACK_ENV'] = val
13
+ ParamsConfig[:environment] = val.to_sym
14
+ end
15
+ op.on('-o addr', "set the host (default is (env == 'development' ? 'localhost' : '0.0.0.0'))") do |val|
16
+ ParamsConfig[:bind] = val
17
+ end
18
+ }
19
+ begin
20
+ parser.parse!(ARGV.dup)
21
+ rescue => evar
22
+ ParamsConfig[:optparse_error] = evar
23
+ end
24
+ end
25
+
26
+ require 'sinatra/base'
27
+
4
28
  class Application < Base
5
29
 
6
30
  # we assume that the first file that requires 'sinatra' is the
@@ -11,18 +35,13 @@ module Sinatra
11
35
  set :run, Proc.new { File.expand_path($0) == File.expand_path(app_file) }
12
36
 
13
37
  if run? && ARGV.any?
14
- require 'optparse'
15
- OptionParser.new { |op|
16
- op.on('-p port', 'set the port (default is 4567)') { |val| set :port, Integer(val) }
17
- op.on('-o addr', "set the host (default is #{bind})") { |val| set :bind, val }
18
- op.on('-e env', 'set the environment (default is development)') { |val| set :environment, val.to_sym }
19
- op.on('-s server', 'specify rack server/handler (default is thin)') { |val| set :server, val }
20
- op.on('-q', 'turn on quiet mode (default is off)') { set :quiet, true }
21
- op.on('-x', 'turn on the mutex lock (default is off)') { set :lock, true }
22
- }.parse!(ARGV.dup)
38
+ error = ParamsConfig.delete(:optparse_error)
39
+ raise error if error
40
+ ParamsConfig.each { |k, v| set k, v }
23
41
  end
24
42
  end
25
43
 
44
+ remove_const(:ParamsConfig)
26
45
  at_exit { Application.run! if $!.nil? && Application.run? }
27
46
  end
28
47