sinatra 1.4.8 → 2.0.8.1
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.
- checksums.yaml +5 -5
- data/AUTHORS.md +1 -0
- data/CHANGELOG.md +238 -54
- data/CONTRIBUTING.md +8 -8
- data/Gemfile +47 -47
- data/LICENSE +4 -1
- data/MAINTENANCE.md +42 -0
- data/README.de.md +650 -442
- data/README.es.md +738 -357
- data/README.fr.md +15 -15
- data/README.hu.md +37 -3
- data/README.ja.md +124 -66
- data/README.ko.md +14 -14
- data/README.malayalam.md +3141 -0
- data/README.md +530 -403
- data/README.pt-br.md +2361 -334
- data/README.pt-pt.md +2 -2
- data/README.ru.md +856 -607
- data/README.zh.md +90 -28
- data/Rakefile +77 -51
- data/SECURITY.md +35 -0
- data/VERSION +1 -0
- data/lib/sinatra/base.rb +177 -239
- data/lib/sinatra/indifferent_hash.rb +200 -0
- data/lib/sinatra/main.rb +30 -10
- data/lib/sinatra/show_exceptions.rb +102 -62
- data/lib/sinatra/version.rb +1 -1
- data/sinatra.gemspec +44 -8
- metadata +41 -166
- data/lib/sinatra/ext.rb +0 -17
- data/test/asciidoctor_test.rb +0 -72
- data/test/base_test.rb +0 -167
- data/test/builder_test.rb +0 -91
- data/test/coffee_test.rb +0 -96
- data/test/compile_test.rb +0 -183
- data/test/contest.rb +0 -91
- data/test/creole_test.rb +0 -65
- data/test/delegator_test.rb +0 -160
- data/test/encoding_test.rb +0 -20
- data/test/erb_test.rb +0 -116
- data/test/extensions_test.rb +0 -98
- data/test/filter_test.rb +0 -487
- data/test/haml_test.rb +0 -109
- data/test/helper.rb +0 -132
- data/test/helpers_test.rb +0 -1917
- data/test/integration/app.rb +0 -79
- data/test/integration_helper.rb +0 -236
- data/test/integration_test.rb +0 -104
- data/test/less_test.rb +0 -69
- data/test/liquid_test.rb +0 -77
- data/test/mapped_error_test.rb +0 -285
- data/test/markaby_test.rb +0 -80
- data/test/markdown_test.rb +0 -85
- data/test/mediawiki_test.rb +0 -68
- data/test/middleware_test.rb +0 -68
- data/test/nokogiri_test.rb +0 -67
- data/test/public/favicon.ico +0 -0
- data/test/public/hello+world.txt +0 -1
- data/test/rabl_test.rb +0 -89
- data/test/rack_test.rb +0 -45
- data/test/radius_test.rb +0 -59
- data/test/rdoc_test.rb +0 -66
- data/test/readme_test.rb +0 -130
- data/test/request_test.rb +0 -100
- data/test/response_test.rb +0 -63
- data/test/result_test.rb +0 -76
- data/test/route_added_hook_test.rb +0 -59
- data/test/routing_test.rb +0 -1456
- data/test/sass_test.rb +0 -115
- data/test/scss_test.rb +0 -88
- data/test/server_test.rb +0 -56
- data/test/settings_test.rb +0 -582
- data/test/sinatra_test.rb +0 -12
- data/test/slim_test.rb +0 -102
- data/test/static_test.rb +0 -266
- data/test/streaming_test.rb +0 -149
- data/test/stylus_test.rb +0 -90
- data/test/templates_test.rb +0 -382
- data/test/textile_test.rb +0 -65
- data/test/views/a/in_a.str +0 -1
- data/test/views/ascii.erb +0 -2
- data/test/views/b/in_b.str +0 -1
- data/test/views/calc.html.erb +0 -1
- data/test/views/error.builder +0 -3
- data/test/views/error.erb +0 -3
- data/test/views/error.haml +0 -3
- data/test/views/error.sass +0 -2
- data/test/views/explicitly_nested.str +0 -1
- data/test/views/foo/hello.test +0 -1
- data/test/views/hello.asciidoc +0 -1
- data/test/views/hello.builder +0 -1
- data/test/views/hello.coffee +0 -1
- data/test/views/hello.creole +0 -1
- data/test/views/hello.erb +0 -1
- data/test/views/hello.haml +0 -1
- data/test/views/hello.less +0 -5
- data/test/views/hello.liquid +0 -1
- data/test/views/hello.mab +0 -1
- data/test/views/hello.md +0 -1
- data/test/views/hello.mediawiki +0 -1
- data/test/views/hello.nokogiri +0 -1
- data/test/views/hello.rabl +0 -2
- data/test/views/hello.radius +0 -1
- data/test/views/hello.rdoc +0 -1
- data/test/views/hello.sass +0 -2
- data/test/views/hello.scss +0 -3
- data/test/views/hello.slim +0 -1
- data/test/views/hello.str +0 -1
- data/test/views/hello.styl +0 -2
- data/test/views/hello.test +0 -1
- data/test/views/hello.textile +0 -1
- data/test/views/hello.wlang +0 -1
- data/test/views/hello.yajl +0 -1
- data/test/views/layout2.builder +0 -3
- data/test/views/layout2.erb +0 -2
- data/test/views/layout2.haml +0 -2
- data/test/views/layout2.liquid +0 -2
- data/test/views/layout2.mab +0 -2
- data/test/views/layout2.nokogiri +0 -3
- data/test/views/layout2.rabl +0 -3
- data/test/views/layout2.radius +0 -2
- data/test/views/layout2.slim +0 -3
- data/test/views/layout2.str +0 -2
- data/test/views/layout2.test +0 -1
- data/test/views/layout2.wlang +0 -2
- data/test/views/nested.str +0 -1
- data/test/views/utf8.erb +0 -2
- data/test/wlang_test.rb +0 -87
- 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
|
@@ -69,6 +75,12 @@ module Sinatra
|
|
69
75
|
request_method == "UNLINK"
|
70
76
|
end
|
71
77
|
|
78
|
+
def params
|
79
|
+
super
|
80
|
+
rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
|
81
|
+
raise BadRequest, "Invalid query parameters: #{Rack::Utils.escape_html(e.message)}"
|
82
|
+
end
|
83
|
+
|
72
84
|
private
|
73
85
|
|
74
86
|
class AcceptEntry
|
@@ -120,7 +132,7 @@ module Sinatra
|
|
120
132
|
# http://rubydoc.info/github/rack/rack/master/Rack/Response
|
121
133
|
# http://rubydoc.info/github/rack/rack/master/Rack/Response/Helpers
|
122
134
|
class Response < Rack::Response
|
123
|
-
DROP_BODY_RESPONSES = [204,
|
135
|
+
DROP_BODY_RESPONSES = [204, 304]
|
124
136
|
def initialize(*)
|
125
137
|
super
|
126
138
|
headers['Content-Type'] ||= 'text/html'
|
@@ -151,7 +163,7 @@ module Sinatra
|
|
151
163
|
if calculate_content_length?
|
152
164
|
# if some other code has already set Content-Length, don't muck with it
|
153
165
|
# currently, this would be the static file-handler
|
154
|
-
headers["Content-Length"] = body.inject(0) { |l, p| l +
|
166
|
+
headers["Content-Length"] = body.inject(0) { |l, p| l + p.bytesize }.to_s
|
155
167
|
end
|
156
168
|
|
157
169
|
[status.to_i, headers, result]
|
@@ -221,6 +233,10 @@ module Sinatra
|
|
221
233
|
end
|
222
234
|
end
|
223
235
|
|
236
|
+
class BadRequest < TypeError #:nodoc:
|
237
|
+
def http_status; 400 end
|
238
|
+
end
|
239
|
+
|
224
240
|
class NotFound < NameError #:nodoc:
|
225
241
|
def http_status; 404 end
|
226
242
|
end
|
@@ -229,7 +245,7 @@ module Sinatra
|
|
229
245
|
module Helpers
|
230
246
|
# Set or retrieve the response status code.
|
231
247
|
def status(value = nil)
|
232
|
-
response.status = value if value
|
248
|
+
response.status = Rack::Utils.status_code(value) if value
|
233
249
|
response.status
|
234
250
|
end
|
235
251
|
|
@@ -240,7 +256,11 @@ module Sinatra
|
|
240
256
|
def block.each; yield(call) end
|
241
257
|
response.body = block
|
242
258
|
elsif value
|
243
|
-
|
259
|
+
# Rack 2.0 returns a Rack::File::Iterator here instead of
|
260
|
+
# Rack::File as it was in the previous API.
|
261
|
+
unless request.head? || value.is_a?(Rack::File::Iterator) || value.is_a?(Stream)
|
262
|
+
headers.delete 'Content-Length'
|
263
|
+
end
|
244
264
|
response.body = value
|
245
265
|
else
|
246
266
|
response.body
|
@@ -264,8 +284,8 @@ module Sinatra
|
|
264
284
|
# Generates the absolute URI for a given path in the app.
|
265
285
|
# Takes Rack routers and reverse proxies into account.
|
266
286
|
def uri(addr = nil, absolute = true, add_script_name = true)
|
267
|
-
return addr if addr =~ /\A[
|
268
|
-
uri = [host =
|
287
|
+
return addr if addr =~ /\A[a-z][a-z0-9\+\.\-]*:/i
|
288
|
+
uri = [host = String.new]
|
269
289
|
if absolute
|
270
290
|
host << "http#{'s' if request.secure?}://"
|
271
291
|
if request.forwarded? or request.port != (request.secure? ? 443 : 80)
|
@@ -339,8 +359,8 @@ module Sinatra
|
|
339
359
|
|
340
360
|
# Set the Content-Disposition to "attachment" with the specified filename,
|
341
361
|
# instructing the user agents to prompt to save.
|
342
|
-
def attachment(filename = nil, disposition =
|
343
|
-
response['Content-Disposition'] = disposition.to_s
|
362
|
+
def attachment(filename = nil, disposition = :attachment)
|
363
|
+
response['Content-Disposition'] = disposition.to_s.dup
|
344
364
|
if filename
|
345
365
|
params = '; filename="%s"' % File.basename(filename)
|
346
366
|
response['Content-Disposition'] << params
|
@@ -357,19 +377,19 @@ module Sinatra
|
|
357
377
|
|
358
378
|
disposition = opts[:disposition]
|
359
379
|
filename = opts[:filename]
|
360
|
-
disposition =
|
361
|
-
filename = path
|
380
|
+
disposition = :attachment if disposition.nil? and filename
|
381
|
+
filename = path if filename.nil?
|
362
382
|
attachment(filename, disposition) if disposition
|
363
383
|
|
364
384
|
last_modified opts[:last_modified] if opts[:last_modified]
|
365
385
|
|
366
|
-
file
|
367
|
-
file.
|
368
|
-
|
386
|
+
file = Rack::File.new(File.dirname(settings.app_file))
|
387
|
+
result = file.serving(request, path)
|
388
|
+
|
369
389
|
result[1].each { |k,v| headers[k] ||= v }
|
370
390
|
headers['Content-Length'] = result[1]['Content-Length']
|
371
391
|
opts[:status] &&= Integer(opts[:status])
|
372
|
-
halt opts[:status] || result[0], result[2]
|
392
|
+
halt (opts[:status] || result[0]), result[2]
|
373
393
|
rescue Errno::ENOENT
|
374
394
|
not_found
|
375
395
|
end
|
@@ -394,7 +414,7 @@ module Sinatra
|
|
394
414
|
def close
|
395
415
|
return if closed?
|
396
416
|
@closed = true
|
397
|
-
@scheduler.schedule { @callbacks.each { |c| c.call }}
|
417
|
+
@scheduler.schedule { @callbacks.each { |c| c.call } }
|
398
418
|
end
|
399
419
|
|
400
420
|
def each(&front)
|
@@ -441,7 +461,7 @@ module Sinatra
|
|
441
461
|
# Specify response freshness policy for HTTP caches (Cache-Control header).
|
442
462
|
# Any number of non-value directives (:public, :private, :no_cache,
|
443
463
|
# :no_store, :must_revalidate, :proxy_revalidate) may be passed along with
|
444
|
-
# a Hash of value directives (:max_age, :
|
464
|
+
# a Hash of value directives (:max_age, :s_maxage).
|
445
465
|
#
|
446
466
|
# cache_control :public, :must_revalidate, :max_age => 60
|
447
467
|
# => Cache-Control: public, must-revalidate, max-age=60
|
@@ -451,8 +471,8 @@ module Sinatra
|
|
451
471
|
def cache_control(*values)
|
452
472
|
if values.last.kind_of?(Hash)
|
453
473
|
hash = values.pop
|
454
|
-
hash.reject! { |k,v| v == false }
|
455
|
-
hash.reject! { |k,v| values << k if v == true }
|
474
|
+
hash.reject! { |k, v| v == false }
|
475
|
+
hash.reject! { |k, v| values << k if v == true }
|
456
476
|
else
|
457
477
|
hash = {}
|
458
478
|
end
|
@@ -460,7 +480,7 @@ module Sinatra
|
|
460
480
|
values.map! { |value| value.to_s.tr('_','-') }
|
461
481
|
hash.each do |key, value|
|
462
482
|
key = key.to_s.tr('_', '-')
|
463
|
-
value = value.to_i if
|
483
|
+
value = value.to_i if ['max-age', 's-maxage'].include? key
|
464
484
|
values << "#{key}=#{value}"
|
465
485
|
end
|
466
486
|
|
@@ -473,7 +493,7 @@ module Sinatra
|
|
473
493
|
# "values" arguments are passed to the #cache_control helper:
|
474
494
|
#
|
475
495
|
# expires 500, :public, :must_revalidate
|
476
|
-
# => Cache-Control: public, must-revalidate, max-age=
|
496
|
+
# => Cache-Control: public, must-revalidate, max-age=500
|
477
497
|
# => Expires: Mon, 08 Jun 2009 08:50:17 GMT
|
478
498
|
#
|
479
499
|
def expires(amount, *values)
|
@@ -590,25 +610,20 @@ module Sinatra
|
|
590
610
|
status == 404
|
591
611
|
end
|
592
612
|
|
613
|
+
# whether or not the status is set to 400
|
614
|
+
def bad_request?
|
615
|
+
status == 400
|
616
|
+
end
|
617
|
+
|
593
618
|
# Generates a Time object from the given value.
|
594
619
|
# Used by #expires and #last_modified.
|
595
620
|
def time_for(value)
|
596
|
-
if value.
|
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
|
621
|
+
if value.is_a? Numeric
|
609
622
|
Time.at value
|
610
|
-
|
623
|
+
elsif value.respond_to? :to_s
|
611
624
|
Time.parse value.to_s
|
625
|
+
else
|
626
|
+
value.to_time
|
612
627
|
end
|
613
628
|
rescue ArgumentError => boom
|
614
629
|
raise boom
|
@@ -692,7 +707,7 @@ module Sinatra
|
|
692
707
|
render :less, template, options, locals
|
693
708
|
end
|
694
709
|
|
695
|
-
def stylus(template, options={}, locals={})
|
710
|
+
def stylus(template, options = {}, locals = {})
|
696
711
|
options.merge! :layout => false, :default_content_type => :css
|
697
712
|
render :styl, template, options, locals
|
698
713
|
end
|
@@ -707,6 +722,7 @@ module Sinatra
|
|
707
722
|
end
|
708
723
|
|
709
724
|
def markdown(template, options = {}, locals = {})
|
725
|
+
options[:exclude_outvar] = true
|
710
726
|
render :markdown, template, options, locals
|
711
727
|
end
|
712
728
|
|
@@ -771,15 +787,8 @@ module Sinatra
|
|
771
787
|
def find_template(views, name, engine)
|
772
788
|
yield ::File.join(views, "#{name}.#{@preferred_extension}")
|
773
789
|
|
774
|
-
|
775
|
-
|
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
|
790
|
+
Tilt.default_mapping.extensions_for(engine).each do |ext|
|
791
|
+
yield ::File.join(views, "#{name}.#{ext}") unless ext == @preferred_extension
|
783
792
|
end
|
784
793
|
end
|
785
794
|
|
@@ -794,7 +803,7 @@ module Sinatra
|
|
794
803
|
|
795
804
|
def render(engine, data, options = {}, locals = {}, &block)
|
796
805
|
# merge app-level options
|
797
|
-
engine_options
|
806
|
+
engine_options = settings.respond_to?(engine) ? settings.send(engine) : {}
|
798
807
|
options.merge!(engine_options) { |key, v1, v2| v1 }
|
799
808
|
|
800
809
|
# extract generic options
|
@@ -806,13 +815,15 @@ module Sinatra
|
|
806
815
|
layout = engine_options[:layout] if layout.nil? or (layout == true && engine_options[:layout] != false)
|
807
816
|
layout = @default_layout if layout.nil? or layout == true
|
808
817
|
layout_options = options.delete(:layout_options) || {}
|
809
|
-
content_type = options.delete(:
|
818
|
+
content_type = options.delete(:default_content_type)
|
819
|
+
content_type = options.delete(:content_type) || content_type
|
810
820
|
layout_engine = options.delete(:layout_engine) || engine
|
811
821
|
scope = options.delete(:scope) || self
|
822
|
+
exclude_outvar = options.delete(:exclude_outvar)
|
812
823
|
options.delete(:layout)
|
813
824
|
|
814
825
|
# set some defaults
|
815
|
-
options[:outvar]
|
826
|
+
options[:outvar] ||= '@_out_buf' unless exclude_outvar
|
816
827
|
options[:default_encoding] ||= settings.default_encoding
|
817
828
|
|
818
829
|
# compile and render template
|
@@ -863,7 +874,9 @@ module Sinatra
|
|
863
874
|
end
|
864
875
|
when Proc, String
|
865
876
|
body = data.is_a?(String) ? Proc.new { data } : data
|
866
|
-
|
877
|
+
caller = settings.caller_locations.first
|
878
|
+
path = options[:path] || caller[0]
|
879
|
+
line = options[:line] || caller[1]
|
867
880
|
template.new(path, line.to_i, options, &body)
|
868
881
|
else
|
869
882
|
raise ArgumentError, "Sorry, don't know how to render #{data.inspect}."
|
@@ -878,7 +891,7 @@ module Sinatra
|
|
878
891
|
include Helpers
|
879
892
|
include Templates
|
880
893
|
|
881
|
-
URI_INSTANCE = URI
|
894
|
+
URI_INSTANCE = URI::Parser.new
|
882
895
|
|
883
896
|
attr_accessor :app, :env, :request, :response, :params
|
884
897
|
attr_reader :template_cache
|
@@ -897,11 +910,10 @@ module Sinatra
|
|
897
910
|
|
898
911
|
def call!(env) # :nodoc:
|
899
912
|
@env = env
|
913
|
+
@params = IndifferentHash.new
|
900
914
|
@request = Request.new(env)
|
901
915
|
@response = Response.new
|
902
|
-
@params = indifferent_params(@request.params)
|
903
916
|
template_cache.clear if settings.reload_templates
|
904
|
-
force_encoding(@params)
|
905
917
|
|
906
918
|
@response['Content-Type'] = nil
|
907
919
|
invoke { dispatch! }
|
@@ -969,9 +981,9 @@ module Sinatra
|
|
969
981
|
# Run routes defined on the class and all superclasses.
|
970
982
|
def route!(base = settings, pass_block = nil)
|
971
983
|
if routes = base.routes[@request.request_method]
|
972
|
-
routes.each do |pattern,
|
973
|
-
returned_pass_block = process_route(pattern,
|
974
|
-
env['sinatra.route'] =
|
984
|
+
routes.each do |pattern, conditions, block|
|
985
|
+
returned_pass_block = process_route(pattern, conditions) do |*args|
|
986
|
+
env['sinatra.route'] = "#{@request.request_method} #{pattern}"
|
975
987
|
route_eval { block[*args] }
|
976
988
|
end
|
977
989
|
|
@@ -999,23 +1011,35 @@ module Sinatra
|
|
999
1011
|
# Revert params afterwards.
|
1000
1012
|
#
|
1001
1013
|
# Returns pass block.
|
1002
|
-
def process_route(pattern,
|
1014
|
+
def process_route(pattern, conditions, block = nil, values = [])
|
1003
1015
|
route = @request.path_info
|
1004
1016
|
route = '/' if route.empty? and not settings.empty_path_info?
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1017
|
+
route = route[0..-2] if !settings.strict_paths? && route != '/' && route.end_with?('/')
|
1018
|
+
return unless params = pattern.params(route)
|
1019
|
+
|
1020
|
+
params.delete("ignore") # TODO: better params handling, maybe turn it into "smart" object or detect changes
|
1021
|
+
force_encoding(params)
|
1022
|
+
@params = @params.merge(params) if params.any?
|
1023
|
+
|
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 }
|
1027
|
+
values += captures
|
1028
|
+
@params[:captures] = force_encoding(captures) unless captures.nil? || captures.empty?
|
1029
|
+
else
|
1030
|
+
values += params.values.flatten
|
1011
1031
|
end
|
1012
1032
|
|
1013
1033
|
catch(:pass) do
|
1014
1034
|
conditions.each { |c| throw :pass if c.bind(self).call == false }
|
1015
1035
|
block ? block[self, values] : yield(self, values)
|
1016
1036
|
end
|
1037
|
+
rescue
|
1038
|
+
@env['sinatra.error.params'] = @params
|
1039
|
+
raise
|
1017
1040
|
ensure
|
1018
|
-
|
1041
|
+
params ||= {}
|
1042
|
+
params.each { |k, _| @params.delete(k) } unless @env['sinatra.error.params']
|
1019
1043
|
end
|
1020
1044
|
|
1021
1045
|
# No matching route was found or all routes passed. The default
|
@@ -1027,7 +1051,7 @@ module Sinatra
|
|
1027
1051
|
if @app
|
1028
1052
|
forward
|
1029
1053
|
else
|
1030
|
-
raise NotFound
|
1054
|
+
raise NotFound, "#{request.request_method} #{request.path_info}"
|
1031
1055
|
end
|
1032
1056
|
end
|
1033
1057
|
|
@@ -1043,28 +1067,10 @@ module Sinatra
|
|
1043
1067
|
send_file path, options.merge(:disposition => nil)
|
1044
1068
|
end
|
1045
1069
|
|
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
1070
|
# Run the block with 'throw :halt' support and apply result to the response.
|
1066
1071
|
def invoke
|
1067
1072
|
res = catch(:halt) { yield }
|
1073
|
+
|
1068
1074
|
res = [res] if Integer === res or String === res
|
1069
1075
|
if Array === res and Integer === res.first
|
1070
1076
|
res = res.dup
|
@@ -1079,6 +1085,13 @@ module Sinatra
|
|
1079
1085
|
|
1080
1086
|
# Dispatch a request with error handling.
|
1081
1087
|
def dispatch!
|
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
|
1094
|
+
|
1082
1095
|
invoke do
|
1083
1096
|
static! if settings.static? && (request.get? || request.head?)
|
1084
1097
|
filter! :before
|
@@ -1096,6 +1109,9 @@ module Sinatra
|
|
1096
1109
|
|
1097
1110
|
# Error handling during requests.
|
1098
1111
|
def handle_exception!(boom)
|
1112
|
+
if error_params = @env['sinatra.error.params']
|
1113
|
+
@params = @params.merge(error_params)
|
1114
|
+
end
|
1099
1115
|
@env['sinatra.error'] = boom
|
1100
1116
|
|
1101
1117
|
if boom.respond_to? :http_status
|
@@ -1108,14 +1124,15 @@ module Sinatra
|
|
1108
1124
|
|
1109
1125
|
status(500) unless status.between? 400, 599
|
1110
1126
|
|
1127
|
+
boom_message = boom.message if boom.message && boom.message != boom.class.name
|
1111
1128
|
if server_error?
|
1112
1129
|
dump_errors! boom if settings.dump_errors?
|
1113
1130
|
raise boom if settings.show_exceptions? and settings.show_exceptions != :after_handler
|
1114
|
-
|
1115
|
-
|
1116
|
-
if not_found?
|
1131
|
+
elsif not_found?
|
1117
1132
|
headers['X-Cascade'] = 'pass' if settings.x_cascade?
|
1118
|
-
body '<h1>Not Found</h1>'
|
1133
|
+
body boom_message || '<h1>Not Found</h1>'
|
1134
|
+
elsif bad_request?
|
1135
|
+
body boom_message || '<h1>Bad Request</h1>'
|
1119
1136
|
end
|
1120
1137
|
|
1121
1138
|
res = error_block!(boom.class, boom) || error_block!(status, boom)
|
@@ -1147,12 +1164,12 @@ module Sinatra
|
|
1147
1164
|
|
1148
1165
|
class << self
|
1149
1166
|
CALLERS_TO_IGNORE = [ # :nodoc:
|
1150
|
-
/\/sinatra(\/(base|main|show_exceptions))?\.rb$/,
|
1167
|
+
/\/sinatra(\/(base|main|show_exceptions))?\.rb$/, # all sinatra code
|
1151
1168
|
/lib\/tilt.*\.rb$/, # all tilt code
|
1152
1169
|
/^\(.*\)$/, # generated code
|
1153
1170
|
/rubygems\/(custom|core_ext\/kernel)_require\.rb$/, # rubygems require hacks
|
1154
1171
|
/active_support/, # active_support require hacks
|
1155
|
-
/bundler(\/runtime)?\.rb/,
|
1172
|
+
/bundler(\/(?:runtime|inline))?\.rb/, # bundler require hacks
|
1156
1173
|
/<internal:/, # internal in ruby >= 1.9.2
|
1157
1174
|
/src\/kernel\/bootstrap\/[A-Z]/ # maglev kernel files
|
1158
1175
|
]
|
@@ -1177,7 +1194,7 @@ module Sinatra
|
|
1177
1194
|
@extensions = []
|
1178
1195
|
|
1179
1196
|
if superclass.respond_to?(:templates)
|
1180
|
-
@templates = Hash.new { |hash,key| superclass.templates[key] }
|
1197
|
+
@templates = Hash.new { |hash, key| superclass.templates[key] }
|
1181
1198
|
else
|
1182
1199
|
@templates = {}
|
1183
1200
|
end
|
@@ -1232,8 +1249,8 @@ module Sinatra
|
|
1232
1249
|
end
|
1233
1250
|
end
|
1234
1251
|
|
1235
|
-
define_singleton("#{option}=", setter)
|
1236
|
-
define_singleton(option, getter)
|
1252
|
+
define_singleton("#{option}=", setter)
|
1253
|
+
define_singleton(option, getter)
|
1237
1254
|
define_singleton("#{option}?", "!!#{option}") unless method_defined? "#{option}?"
|
1238
1255
|
self
|
1239
1256
|
end
|
@@ -1252,16 +1269,16 @@ module Sinatra
|
|
1252
1269
|
# class, or an HTTP status code to specify which errors should be
|
1253
1270
|
# handled.
|
1254
1271
|
def error(*codes, &block)
|
1255
|
-
args = compile! "ERROR",
|
1256
|
-
codes = codes.
|
1272
|
+
args = compile! "ERROR", /.*/, block
|
1273
|
+
codes = codes.flat_map(&method(:Array))
|
1257
1274
|
codes << Exception if codes.empty?
|
1275
|
+
codes << Sinatra::NotFound if codes.include?(404)
|
1258
1276
|
codes.each { |c| (@errors[c] ||= []) << args }
|
1259
1277
|
end
|
1260
1278
|
|
1261
1279
|
# Sugar for `error(404) { ... }`
|
1262
1280
|
def not_found(&block)
|
1263
1281
|
error(404, &block)
|
1264
|
-
error(Sinatra::NotFound, &block)
|
1265
1282
|
end
|
1266
1283
|
|
1267
1284
|
# Define a named template. The block must return the template source.
|
@@ -1299,7 +1316,7 @@ module Sinatra
|
|
1299
1316
|
data.each_line do |line|
|
1300
1317
|
lines += 1
|
1301
1318
|
if line =~ /^@@\s*(.*\S)\s*$/
|
1302
|
-
template = force_encoding(
|
1319
|
+
template = force_encoding(String.new, encoding)
|
1303
1320
|
templates[$1.to_sym] = [template, file, lines]
|
1304
1321
|
elsif template
|
1305
1322
|
template << line
|
@@ -1328,21 +1345,20 @@ module Sinatra
|
|
1328
1345
|
# Define a before filter; runs before all requests within the same
|
1329
1346
|
# context as route handlers and may access/modify the request and
|
1330
1347
|
# response.
|
1331
|
-
def before(path =
|
1332
|
-
add_filter(:before, path, options, &block)
|
1348
|
+
def before(path = /.*/, **options, &block)
|
1349
|
+
add_filter(:before, path, **options, &block)
|
1333
1350
|
end
|
1334
1351
|
|
1335
1352
|
# Define an after filter; runs after all requests within the same
|
1336
1353
|
# context as route handlers and may access/modify the request and
|
1337
1354
|
# response.
|
1338
|
-
def after(path =
|
1339
|
-
add_filter(:after, path, options, &block)
|
1355
|
+
def after(path = /.*/, **options, &block)
|
1356
|
+
add_filter(:after, path, **options, &block)
|
1340
1357
|
end
|
1341
1358
|
|
1342
1359
|
# add a filter
|
1343
|
-
def add_filter(type, path =
|
1344
|
-
|
1345
|
-
filters[type] << compile!(type, path || //, block, options)
|
1360
|
+
def add_filter(type, path = /.*/, **options, &block)
|
1361
|
+
filters[type] << compile!(type, path, block, **options)
|
1346
1362
|
end
|
1347
1363
|
|
1348
1364
|
# Add a route condition. The route is considered non-matching when the
|
@@ -1422,7 +1438,7 @@ module Sinatra
|
|
1422
1438
|
return unless running?
|
1423
1439
|
# Use Thin's hard #stop! if available, otherwise just #stop.
|
1424
1440
|
running_server.respond_to?(:stop!) ? running_server.stop! : running_server.stop
|
1425
|
-
$stderr.puts "== Sinatra has ended his set (crowd applauds)" unless
|
1441
|
+
$stderr.puts "== Sinatra has ended his set (crowd applauds)" unless suppress_messages?
|
1426
1442
|
set :running_server, nil
|
1427
1443
|
set :handler_name, nil
|
1428
1444
|
end
|
@@ -1503,8 +1519,12 @@ module Sinatra
|
|
1503
1519
|
|
1504
1520
|
# Starts the server by running the Rack Handler.
|
1505
1521
|
def start_server(handler, server_settings, handler_name)
|
1522
|
+
# Ensure we initialize middleware before startup, to match standard Rack
|
1523
|
+
# behavior, by ensuring an instance exists:
|
1524
|
+
prototype
|
1525
|
+
# Run the instance we created:
|
1506
1526
|
handler.run(self, server_settings) do |server|
|
1507
|
-
unless
|
1527
|
+
unless suppress_messages?
|
1508
1528
|
$stderr.puts "== Sinatra (v#{Sinatra::VERSION}) has taken the stage on #{port} for #{environment} with backup from #{handler_name}"
|
1509
1529
|
end
|
1510
1530
|
|
@@ -1517,6 +1537,10 @@ module Sinatra
|
|
1517
1537
|
end
|
1518
1538
|
end
|
1519
1539
|
|
1540
|
+
def suppress_messages?
|
1541
|
+
handler_name =~ /cgi/i || quiet
|
1542
|
+
end
|
1543
|
+
|
1520
1544
|
def setup_traps
|
1521
1545
|
if traps?
|
1522
1546
|
at_exit { quit! }
|
@@ -1534,8 +1558,7 @@ module Sinatra
|
|
1534
1558
|
|
1535
1559
|
# Dynamically defines a method on settings.
|
1536
1560
|
def define_singleton(name, content = Proc.new)
|
1537
|
-
|
1538
|
-
(class << self; self; end).class_eval do
|
1561
|
+
singleton_class.class_eval do
|
1539
1562
|
undef_method(name) if method_defined? name
|
1540
1563
|
String === content ? class_eval("def #{name}() #{content}; end") : define_method(name, &content)
|
1541
1564
|
end
|
@@ -1578,10 +1601,8 @@ module Sinatra
|
|
1578
1601
|
end
|
1579
1602
|
|
1580
1603
|
def route(verb, path, options = {}, &block)
|
1581
|
-
# Because of self.options.host
|
1582
|
-
host_name(options.delete(:host)) if options.key?(:host)
|
1583
1604
|
enable :empty_path_info if path == "" and empty_path_info.nil?
|
1584
|
-
signature = compile!(verb, path, block, options)
|
1605
|
+
signature = compile!(verb, path, block, **options)
|
1585
1606
|
(@routes[verb] ||= []) << signature
|
1586
1607
|
invoke_hook(:route_added, verb, path, block)
|
1587
1608
|
signature
|
@@ -1592,115 +1613,33 @@ module Sinatra
|
|
1592
1613
|
end
|
1593
1614
|
|
1594
1615
|
def generate_method(method_name, &block)
|
1595
|
-
method_name = method_name.to_sym
|
1596
1616
|
define_method(method_name, &block)
|
1597
1617
|
method = instance_method method_name
|
1598
1618
|
remove_method method_name
|
1599
1619
|
method
|
1600
1620
|
end
|
1601
1621
|
|
1602
|
-
def compile!(verb, path, block, options
|
1622
|
+
def compile!(verb, path, block, **options)
|
1623
|
+
# Because of self.options.host
|
1624
|
+
host_name(options.delete(:host)) if options.key?(:host)
|
1625
|
+
# Pass Mustermann opts to compile()
|
1626
|
+
route_mustermann_opts = options.key?(:mustermann_opts) ? options.delete(:mustermann_opts) : {}.freeze
|
1627
|
+
|
1603
1628
|
options.each_pair { |option, args| send(option, *args) }
|
1629
|
+
|
1630
|
+
pattern = compile(path, route_mustermann_opts)
|
1604
1631
|
method_name = "#{verb} #{path}"
|
1605
1632
|
unbound_method = generate_method(method_name, &block)
|
1606
|
-
pattern, keys = compile path
|
1607
1633
|
conditions, @conditions = @conditions, []
|
1608
|
-
|
1609
1634
|
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)
|
1650
|
-
|
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
|
1673
|
-
end
|
1674
|
-
|
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
|
1635
|
+
proc { |a, p| unbound_method.bind(a).call(*p) } :
|
1636
|
+
proc { |a, p| unbound_method.bind(a).call }
|
1681
1637
|
|
1682
|
-
|
1683
|
-
[Regexp.escape(enc), URI_INSTANCE.escape(char, /./)]
|
1638
|
+
[ pattern, conditions, wrapper ]
|
1684
1639
|
end
|
1685
1640
|
|
1686
|
-
def
|
1687
|
-
|
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
|
1641
|
+
def compile(path, route_mustermann_opts = {})
|
1642
|
+
Mustermann.new(path, **mustermann_opts.merge(route_mustermann_opts))
|
1704
1643
|
end
|
1705
1644
|
|
1706
1645
|
def setup_default_middleware(builder)
|
@@ -1745,10 +1684,16 @@ module Sinatra
|
|
1745
1684
|
def setup_protection(builder)
|
1746
1685
|
return unless protection?
|
1747
1686
|
options = Hash === protection ? protection.dup : {}
|
1748
|
-
|
1749
|
-
|
1750
|
-
|
1687
|
+
options = {
|
1688
|
+
img_src: "'self' data:",
|
1689
|
+
font_src: "'self'"
|
1690
|
+
}.merge options
|
1691
|
+
|
1692
|
+
protect_session = options.fetch(:session) { sessions? }
|
1693
|
+
options[:without_session] = !protect_session
|
1694
|
+
|
1751
1695
|
options[:reaction] ||= :drop_session
|
1696
|
+
|
1752
1697
|
builder.use Rack::Protection, options
|
1753
1698
|
end
|
1754
1699
|
|
@@ -1757,7 +1702,7 @@ module Sinatra
|
|
1757
1702
|
options = {}
|
1758
1703
|
options[:secret] = session_secret if session_secret?
|
1759
1704
|
options.merge! sessions.to_hash if sessions.respond_to? :to_hash
|
1760
|
-
builder.use
|
1705
|
+
builder.use session_store, options
|
1761
1706
|
end
|
1762
1707
|
|
1763
1708
|
def detect_rack_handler
|
@@ -1766,8 +1711,6 @@ module Sinatra
|
|
1766
1711
|
begin
|
1767
1712
|
return Rack::Handler.get(server_name.to_s)
|
1768
1713
|
rescue LoadError, NameError
|
1769
|
-
rescue ArgumentError
|
1770
|
-
Sinatra::Ext.get_handler(server_name.to_s)
|
1771
1714
|
end
|
1772
1715
|
end
|
1773
1716
|
fail "Server handler (#{servers.join(',')}) not found."
|
@@ -1801,36 +1744,30 @@ module Sinatra
|
|
1801
1744
|
end
|
1802
1745
|
end
|
1803
1746
|
|
1804
|
-
#
|
1805
|
-
#
|
1806
|
-
|
1807
|
-
|
1808
|
-
|
1809
|
-
|
1810
|
-
|
1811
|
-
|
1812
|
-
|
1813
|
-
|
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
|
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) }
|
1822
1757
|
end
|
1823
|
-
|
1824
|
-
def self.force_encoding(data, *) data end
|
1758
|
+
data
|
1825
1759
|
end
|
1826
1760
|
|
1761
|
+
def force_encoding(*args) settings.force_encoding(*args) end
|
1762
|
+
|
1827
1763
|
reset!
|
1828
1764
|
|
1829
|
-
set :environment, (ENV['RACK_ENV'] || :development).to_sym
|
1765
|
+
set :environment, (ENV['APP_ENV'] || ENV['RACK_ENV'] || :development).to_sym
|
1830
1766
|
set :raise_errors, Proc.new { test? }
|
1831
1767
|
set :dump_errors, Proc.new { !test? }
|
1832
1768
|
set :show_exceptions, Proc.new { development? }
|
1833
1769
|
set :sessions, false
|
1770
|
+
set :session_store, Rack::Session::Cookie
|
1834
1771
|
set :logging, false
|
1835
1772
|
set :protection, true
|
1836
1773
|
set :method_override, false
|
@@ -1839,6 +1776,7 @@ module Sinatra
|
|
1839
1776
|
set :x_cascade, true
|
1840
1777
|
set :add_charset, %w[javascript xml xhtml+xml].map { |t| "application/#{t}" }
|
1841
1778
|
settings.add_charset << /^text\//
|
1779
|
+
set :mustermann_opts, {}
|
1842
1780
|
|
1843
1781
|
# explicitly generating a session secret eagerly to play nice with preforking
|
1844
1782
|
begin
|
@@ -1861,6 +1799,7 @@ module Sinatra
|
|
1861
1799
|
set :server, %w[HTTP webrick]
|
1862
1800
|
set :bind, Proc.new { development? ? 'localhost' : '0.0.0.0' }
|
1863
1801
|
set :port, Integer(ENV['PORT'] && !ENV['PORT'].empty? ? ENV['PORT'] : 4567)
|
1802
|
+
set :quiet, false
|
1864
1803
|
|
1865
1804
|
ruby_engine = defined?(RUBY_ENGINE) && RUBY_ENGINE
|
1866
1805
|
|
@@ -1868,16 +1807,16 @@ module Sinatra
|
|
1868
1807
|
server.unshift 'control_tower'
|
1869
1808
|
else
|
1870
1809
|
server.unshift 'reel'
|
1810
|
+
server.unshift 'puma'
|
1871
1811
|
server.unshift 'mongrel' if ruby_engine.nil?
|
1872
|
-
server.unshift 'puma' if ruby_engine != 'rbx'
|
1873
1812
|
server.unshift 'thin' if ruby_engine != 'jruby'
|
1874
|
-
server.unshift 'puma' if ruby_engine == 'rbx'
|
1875
1813
|
server.unshift 'trinidad' if ruby_engine == 'jruby'
|
1876
1814
|
end
|
1877
1815
|
|
1878
1816
|
set :absolute_redirects, true
|
1879
1817
|
set :prefixed_redirects, false
|
1880
1818
|
set :empty_path_info, nil
|
1819
|
+
set :strict_paths, true
|
1881
1820
|
|
1882
1821
|
set :app_file, nil
|
1883
1822
|
set :root, Proc.new { app_file && File.expand_path(File.dirname(app_file)) }
|
@@ -1936,7 +1875,7 @@ module Sinatra
|
|
1936
1875
|
</style>
|
1937
1876
|
</head>
|
1938
1877
|
<body>
|
1939
|
-
<h2>Sinatra doesn
|
1878
|
+
<h2>Sinatra doesn’t know this ditty.</h2>
|
1940
1879
|
<img src='#{uri "/__sinatra__/404.png"}'>
|
1941
1880
|
<div id="c">
|
1942
1881
|
Try this:
|
@@ -1957,14 +1896,13 @@ module Sinatra
|
|
1957
1896
|
# top-level. Subclassing Sinatra::Base is highly recommended for
|
1958
1897
|
# modular applications.
|
1959
1898
|
class Application < Base
|
1960
|
-
set :logging, Proc.new { !
|
1899
|
+
set :logging, Proc.new { !test? }
|
1961
1900
|
set :method_override, true
|
1962
|
-
set :run, Proc.new { !
|
1963
|
-
set :session_secret, Proc.new { super() unless development? }
|
1901
|
+
set :run, Proc.new { !test? }
|
1964
1902
|
set :app_file, nil
|
1965
1903
|
|
1966
1904
|
def self.register(*extensions, &block) #:nodoc:
|
1967
|
-
added_methods = extensions.
|
1905
|
+
added_methods = extensions.flat_map(&:public_instance_methods)
|
1968
1906
|
Delegator.delegate(*added_methods)
|
1969
1907
|
super(*extensions, &block)
|
1970
1908
|
end
|