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.
- checksums.yaml +5 -5
- data/AUTHORS.md +1 -0
- data/CHANGELOG.md +157 -37
- data/CONTRIBUTING.md +7 -7
- data/Gemfile +10 -2
- data/README.de.md +6 -6
- data/README.es.md +733 -352
- data/README.fr.md +6 -6
- data/README.ja.md +22 -22
- data/README.ko.md +6 -6
- data/README.malayalam.md +3141 -0
- data/README.md +75 -56
- data/README.pt-br.md +2359 -332
- data/README.ru.md +834 -563
- data/README.zh.md +82 -20
- data/Rakefile +10 -7
- data/VERSION +1 -0
- data/lib/sinatra/base.rb +51 -55
- data/lib/sinatra/indifferent_hash.rb +65 -15
- data/lib/sinatra/main.rb +30 -11
- data/lib/sinatra/show_exceptions.rb +43 -11
- data/lib/sinatra/version.rb +1 -1
- data/sinatra.gemspec +26 -2
- metadata +16 -7
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
|
-
* 一个包含三个元素的数组: `[状态 (
|
317
|
-
* 一个包含两个元素的数组: `[状态 (
|
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="
|
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="
|
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
|
-
|
1306
|
-
|
1307
|
-
|
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
|
-
|
1327
|
+
$ ruby -e "require 'securerandom'; puts SecureRandom.hex(64)"
|
1328
|
+
99ae8af...snip...ec0f262ac
|
1329
|
+
```
|
1312
1330
|
|
1313
|
-
|
1314
|
-
"value = " << session['value'].inspect
|
1315
|
-
end
|
1331
|
+
#### 生成密钥(小贴士)
|
1316
1332
|
|
1317
|
-
|
1318
|
-
|
1319
|
-
|
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
|
-
|
1323
|
-
然而,每次启动应用时,该密码都会变化,你也可以自己设置该密码,以便所有的应用实例共享:
|
1345
|
+
#### 从环境变量使用密钥
|
1324
1346
|
|
1347
|
+
将Sinatra的SESSION_SECRET环境变量设置为生成的值。在主机的重新启动之间保存这个值。由于这样做的方法会因系统而异,仅供说明之用:
|
1325
1348
|
```
|
1326
|
-
|
1349
|
+
# echo "export SESSION_SECRET=99ae8af...snip...ec0f262ac" >> ~/.bashrc
|
1327
1350
|
```
|
1328
1351
|
|
1329
|
-
|
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
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
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,
|
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, :
|
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
|
-
|
790
|
-
|
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]
|
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
|
-
|
1021
|
+
force_encoding(params)
|
1022
|
+
@params = @params.merge(params) if params.any?
|
1026
1023
|
|
1027
|
-
|
1028
|
-
|
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
|
-
|
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
|
-
|
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$/,
|
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/,
|
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)
|
1249
|
-
define_singleton(option, 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
|
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
|
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
|
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
|
-
#
|
1744
|
-
#
|
1745
|
-
|
1746
|
-
|
1747
|
-
|
1748
|
-
|
1749
|
-
|
1750
|
-
|
1751
|
-
|
1752
|
-
|
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
|
-
|
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
|
-
|
52
|
+
args.map!(&method(:convert_value))
|
53
|
+
|
54
|
+
super(*args)
|
45
55
|
end
|
46
56
|
|
47
57
|
def default(*args)
|
48
|
-
|
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
|
-
|
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
|
-
|
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
|
108
|
-
|
109
|
-
end
|
123
|
+
def slice(*keys)
|
124
|
+
keys.map!(&method(:convert_key))
|
110
125
|
|
111
|
-
|
112
|
-
|
126
|
+
self.class[super(*keys)]
|
127
|
+
end if method_defined?(:slice) # Added in Ruby 2.5
|
113
128
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
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(
|
126
|
-
dup.merge!(
|
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
|
-
|
15
|
-
|
16
|
-
|
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
|
|