sinatra 2.0.0 → 2.2.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.
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 +258 -37
- data/CONTRIBUTING.md +7 -7
- data/Gemfile +15 -6
- data/MAINTENANCE.md +2 -15
- data/README.de.md +22 -22
- data/README.es.md +772 -362
- data/README.fr.md +188 -91
- data/README.hu.md +3 -3
- data/README.ja.md +84 -54
- data/README.ko.md +7 -7
- data/README.malayalam.md +3141 -0
- data/README.md +165 -113
- data/README.pt-br.md +2366 -339
- data/README.pt-pt.md +3 -3
- data/README.ru.md +835 -564
- data/README.zh.md +83 -21
- data/Rakefile +10 -7
- data/VERSION +1 -0
- data/examples/chat.rb +2 -1
- data/examples/rainbows.conf +3 -0
- data/examples/rainbows.rb +20 -0
- data/examples/stream.ru +4 -4
- data/lib/sinatra/base.rb +160 -123
- data/lib/sinatra/indifferent_hash.rb +79 -15
- data/lib/sinatra/main.rb +30 -11
- data/lib/sinatra/show_exceptions.rb +8 -11
- data/lib/sinatra/version.rb +1 -1
- data/sinatra.gemspec +25 -7
- metadata +20 -22
data/lib/sinatra/base.rb
CHANGED
@@ -43,12 +43,11 @@ module Sinatra
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def preferred_type(*types)
|
46
|
-
|
47
|
-
return accepts.first if types.empty?
|
46
|
+
return accept.first if types.empty?
|
48
47
|
types.flatten!
|
49
|
-
return types.first if
|
50
|
-
|
51
|
-
type = types.detect { |t|
|
48
|
+
return types.first if accept.empty?
|
49
|
+
accept.detect do |accept_header|
|
50
|
+
type = types.detect { |t| MimeTypeEntry.new(t).accepts?(accept_header) }
|
52
51
|
return type if type
|
53
52
|
end
|
54
53
|
end
|
@@ -78,11 +77,11 @@ module Sinatra
|
|
78
77
|
def params
|
79
78
|
super
|
80
79
|
rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
|
81
|
-
raise BadRequest, "Invalid query parameters: #{e.message}"
|
80
|
+
raise BadRequest, "Invalid query parameters: #{Rack::Utils.escape_html(e.message)}"
|
81
|
+
rescue EOFError => e
|
82
|
+
raise BadRequest, "Invalid multipart/form-data: #{Rack::Utils.escape_html(e.message)}"
|
82
83
|
end
|
83
84
|
|
84
|
-
private
|
85
|
-
|
86
85
|
class AcceptEntry
|
87
86
|
attr_accessor :params
|
88
87
|
attr_reader :entry
|
@@ -125,6 +124,35 @@ module Sinatra
|
|
125
124
|
to_str.send(*args, &block)
|
126
125
|
end
|
127
126
|
end
|
127
|
+
|
128
|
+
class MimeTypeEntry
|
129
|
+
attr_reader :params
|
130
|
+
|
131
|
+
def initialize(entry)
|
132
|
+
params = entry.scan(HEADER_PARAM).map! do |s|
|
133
|
+
key, value = s.strip.split('=', 2)
|
134
|
+
value = value[1..-2].gsub(/\\(.)/, '\1') if value.start_with?('"')
|
135
|
+
[key, value]
|
136
|
+
end
|
137
|
+
|
138
|
+
@type = entry[/[^;]+/].delete(' ')
|
139
|
+
@params = Hash[params]
|
140
|
+
end
|
141
|
+
|
142
|
+
def accepts?(entry)
|
143
|
+
File.fnmatch(entry, self) && matches_params?(entry.params)
|
144
|
+
end
|
145
|
+
|
146
|
+
def to_str
|
147
|
+
@type
|
148
|
+
end
|
149
|
+
|
150
|
+
def matches_params?(params)
|
151
|
+
return true if @params.empty?
|
152
|
+
|
153
|
+
params.all? { |k,v| !@params.has_key?(k) || @params[k] == v }
|
154
|
+
end
|
155
|
+
end
|
128
156
|
end
|
129
157
|
|
130
158
|
# The response object. See Rack::Response and Rack::Response::Helpers for
|
@@ -132,11 +160,7 @@ module Sinatra
|
|
132
160
|
# http://rubydoc.info/github/rack/rack/master/Rack/Response
|
133
161
|
# http://rubydoc.info/github/rack/rack/master/Rack/Response/Helpers
|
134
162
|
class Response < Rack::Response
|
135
|
-
DROP_BODY_RESPONSES = [204,
|
136
|
-
def initialize(*)
|
137
|
-
super
|
138
|
-
headers['Content-Type'] ||= 'text/html'
|
139
|
-
end
|
163
|
+
DROP_BODY_RESPONSES = [204, 304]
|
140
164
|
|
141
165
|
def body=(value)
|
142
166
|
value = value.body while Rack::Response === value
|
@@ -163,10 +187,10 @@ module Sinatra
|
|
163
187
|
if calculate_content_length?
|
164
188
|
# if some other code has already set Content-Length, don't muck with it
|
165
189
|
# currently, this would be the static file-handler
|
166
|
-
headers["Content-Length"] = body.
|
190
|
+
headers["Content-Length"] = body.map(&:bytesize).reduce(0, :+).to_s
|
167
191
|
end
|
168
192
|
|
169
|
-
[status
|
193
|
+
[status, headers, result]
|
170
194
|
end
|
171
195
|
|
172
196
|
private
|
@@ -176,15 +200,15 @@ module Sinatra
|
|
176
200
|
end
|
177
201
|
|
178
202
|
def drop_content_info?
|
179
|
-
|
203
|
+
informational? or drop_body?
|
180
204
|
end
|
181
205
|
|
182
206
|
def drop_body?
|
183
|
-
DROP_BODY_RESPONSES.include?(status
|
207
|
+
DROP_BODY_RESPONSES.include?(status)
|
184
208
|
end
|
185
209
|
end
|
186
210
|
|
187
|
-
# Some Rack handlers (
|
211
|
+
# Some Rack handlers (Rainbows!) implement an extended body object protocol, however,
|
188
212
|
# some middleware (namely Rack::Lint) will break it by not mirroring the methods in question.
|
189
213
|
# This middleware will detect an extended body object and will make sure it reaches the
|
190
214
|
# handler directly. We do this here, so our middleware and middleware set up by the app will
|
@@ -360,7 +384,7 @@ module Sinatra
|
|
360
384
|
# Set the Content-Disposition to "attachment" with the specified filename,
|
361
385
|
# instructing the user agents to prompt to save.
|
362
386
|
def attachment(filename = nil, disposition = :attachment)
|
363
|
-
response['Content-Disposition'] = disposition.to_s
|
387
|
+
response['Content-Disposition'] = disposition.to_s.dup
|
364
388
|
if filename
|
365
389
|
params = '; filename="%s"' % File.basename(filename)
|
366
390
|
response['Content-Disposition'] << params
|
@@ -451,7 +475,7 @@ module Sinatra
|
|
451
475
|
#
|
452
476
|
# The close parameter specifies whether Stream#close should be called
|
453
477
|
# after the block has been executed. This is only relevant for evented
|
454
|
-
# servers like
|
478
|
+
# servers like Rainbows.
|
455
479
|
def stream(keep_open = false)
|
456
480
|
scheduler = env['async.callback'] ? EventMachine : Stream
|
457
481
|
current = @params.dup
|
@@ -461,7 +485,7 @@ module Sinatra
|
|
461
485
|
# Specify response freshness policy for HTTP caches (Cache-Control header).
|
462
486
|
# Any number of non-value directives (:public, :private, :no_cache,
|
463
487
|
# :no_store, :must_revalidate, :proxy_revalidate) may be passed along with
|
464
|
-
# a Hash of value directives (:max_age, :
|
488
|
+
# a Hash of value directives (:max_age, :s_maxage).
|
465
489
|
#
|
466
490
|
# cache_control :public, :must_revalidate, :max_age => 60
|
467
491
|
# => Cache-Control: public, must-revalidate, max-age=60
|
@@ -647,8 +671,6 @@ module Sinatra
|
|
647
671
|
end
|
648
672
|
end
|
649
673
|
|
650
|
-
private
|
651
|
-
|
652
674
|
# Template rendering methods. Each method takes the name of a template
|
653
675
|
# to render as a Symbol and returns a String with the rendered output,
|
654
676
|
# as well as an optional hash with additional options.
|
@@ -722,6 +744,7 @@ module Sinatra
|
|
722
744
|
end
|
723
745
|
|
724
746
|
def markdown(template, options = {}, locals = {})
|
747
|
+
options[:exclude_outvar] = true
|
725
748
|
render :markdown, template, options, locals
|
726
749
|
end
|
727
750
|
|
@@ -786,15 +809,8 @@ module Sinatra
|
|
786
809
|
def find_template(views, name, engine)
|
787
810
|
yield ::File.join(views, "#{name}.#{@preferred_extension}")
|
788
811
|
|
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
|
812
|
+
Tilt.default_mapping.extensions_for(engine).each do |ext|
|
813
|
+
yield ::File.join(views, "#{name}.#{ext}") unless ext == @preferred_extension
|
798
814
|
end
|
799
815
|
end
|
800
816
|
|
@@ -825,10 +841,11 @@ module Sinatra
|
|
825
841
|
content_type = options.delete(:content_type) || content_type
|
826
842
|
layout_engine = options.delete(:layout_engine) || engine
|
827
843
|
scope = options.delete(:scope) || self
|
844
|
+
exclude_outvar = options.delete(:exclude_outvar)
|
828
845
|
options.delete(:layout)
|
829
846
|
|
830
847
|
# set some defaults
|
831
|
-
options[:outvar]
|
848
|
+
options[:outvar] ||= '@_out_buf' unless exclude_outvar
|
832
849
|
options[:default_encoding] ||= settings.default_encoding
|
833
850
|
|
834
851
|
# compile and render template
|
@@ -854,12 +871,12 @@ module Sinatra
|
|
854
871
|
|
855
872
|
def compile_template(engine, data, options, views)
|
856
873
|
eat_errors = options.delete :eat_errors
|
857
|
-
|
858
|
-
|
859
|
-
raise "Template engine not found: #{engine}" if template.nil?
|
874
|
+
template = Tilt[engine]
|
875
|
+
raise "Template engine not found: #{engine}" if template.nil?
|
860
876
|
|
861
|
-
|
862
|
-
|
877
|
+
case data
|
878
|
+
when Symbol
|
879
|
+
template_cache.fetch engine, data, options, views do
|
863
880
|
body, path, line = settings.templates[data]
|
864
881
|
if body
|
865
882
|
body = body.call if body.respond_to?(:call)
|
@@ -877,17 +894,24 @@ module Sinatra
|
|
877
894
|
throw :layout_missing if eat_errors and not found
|
878
895
|
template.new(path, 1, options)
|
879
896
|
end
|
880
|
-
when Proc, String
|
881
|
-
body = data.is_a?(String) ? Proc.new { data } : data
|
882
|
-
caller = settings.caller_locations.first
|
883
|
-
path = options[:path] || caller[0]
|
884
|
-
line = options[:line] || caller[1]
|
885
|
-
template.new(path, line.to_i, options, &body)
|
886
|
-
else
|
887
|
-
raise ArgumentError, "Sorry, don't know how to render #{data.inspect}."
|
888
897
|
end
|
898
|
+
when Proc
|
899
|
+
compile_block_template(template, options, &data)
|
900
|
+
when String
|
901
|
+
template_cache.fetch engine, data, options, views do
|
902
|
+
compile_block_template(template, options) { data }
|
903
|
+
end
|
904
|
+
else
|
905
|
+
raise ArgumentError, "Sorry, don't know how to render #{data.inspect}."
|
889
906
|
end
|
890
907
|
end
|
908
|
+
|
909
|
+
def compile_block_template(template, options, &body)
|
910
|
+
caller = settings.caller_locations.first
|
911
|
+
path = options[:path] || caller[0]
|
912
|
+
line = options[:line] || caller[1]
|
913
|
+
template.new(path, line.to_i, options, &body)
|
914
|
+
end
|
891
915
|
end
|
892
916
|
|
893
917
|
# Base class for all Sinatra applications and middleware.
|
@@ -901,10 +925,11 @@ module Sinatra
|
|
901
925
|
attr_accessor :app, :env, :request, :response, :params
|
902
926
|
attr_reader :template_cache
|
903
927
|
|
904
|
-
def initialize(app = nil)
|
928
|
+
def initialize(app = nil, **kwargs)
|
905
929
|
super()
|
906
930
|
@app = app
|
907
931
|
@template_cache = Tilt::Cache.new
|
932
|
+
@pinned_response = nil # whether a before! filter pinned the content-type
|
908
933
|
yield self if block_given?
|
909
934
|
end
|
910
935
|
|
@@ -915,19 +940,20 @@ module Sinatra
|
|
915
940
|
|
916
941
|
def call!(env) # :nodoc:
|
917
942
|
@env = env
|
943
|
+
@params = IndifferentHash.new
|
918
944
|
@request = Request.new(env)
|
919
945
|
@response = Response.new
|
946
|
+
@pinned_response = nil
|
920
947
|
template_cache.clear if settings.reload_templates
|
921
948
|
|
922
|
-
@response['Content-Type'] = nil
|
923
949
|
invoke { dispatch! }
|
924
950
|
invoke { error_block!(response.status) } unless @env['sinatra.error']
|
925
951
|
|
926
952
|
unless @response['Content-Type']
|
927
|
-
if Array === body
|
953
|
+
if Array === body && body[0].respond_to?(:content_type)
|
928
954
|
content_type body[0].content_type
|
929
|
-
|
930
|
-
content_type
|
955
|
+
elsif default = settings.default_content_type
|
956
|
+
content_type default
|
931
957
|
end
|
932
958
|
end
|
933
959
|
|
@@ -977,15 +1003,21 @@ module Sinatra
|
|
977
1003
|
private
|
978
1004
|
|
979
1005
|
# Run filters defined on the class and all superclasses.
|
980
|
-
|
981
|
-
|
982
|
-
base.
|
1006
|
+
# Accepts an optional block to call after each filter is applied.
|
1007
|
+
def filter!(type, base = settings, &block)
|
1008
|
+
filter!(type, base.superclass, &block) if base.superclass.respond_to?(:filters)
|
1009
|
+
base.filters[type].each do |args|
|
1010
|
+
result = process_route(*args)
|
1011
|
+
block.call(result) if block_given?
|
1012
|
+
end
|
983
1013
|
end
|
984
1014
|
|
985
1015
|
# Run routes defined on the class and all superclasses.
|
986
1016
|
def route!(base = settings, pass_block = nil)
|
987
1017
|
if routes = base.routes[@request.request_method]
|
988
1018
|
routes.each do |pattern, conditions, block|
|
1019
|
+
response.delete_header('Content-Type') unless @pinned_response
|
1020
|
+
|
989
1021
|
returned_pass_block = process_route(pattern, conditions) do |*args|
|
990
1022
|
env['sinatra.route'] = "#{@request.request_method} #{pattern}"
|
991
1023
|
route_eval { block[*args] }
|
@@ -1022,12 +1054,14 @@ module Sinatra
|
|
1022
1054
|
return unless params = pattern.params(route)
|
1023
1055
|
|
1024
1056
|
params.delete("ignore") # TODO: better params handling, maybe turn it into "smart" object or detect changes
|
1025
|
-
|
1057
|
+
force_encoding(params)
|
1058
|
+
@params = @params.merge(params) if params.any?
|
1026
1059
|
|
1027
|
-
|
1028
|
-
|
1060
|
+
regexp_exists = pattern.is_a?(Mustermann::Regular) || (pattern.respond_to?(:patterns) && pattern.patterns.any? {|subpattern| subpattern.is_a?(Mustermann::Regular)} )
|
1061
|
+
if regexp_exists
|
1062
|
+
captures = pattern.match(route).captures.map { |c| URI_INSTANCE.unescape(c) if c }
|
1029
1063
|
values += captures
|
1030
|
-
@params[:captures] = captures
|
1064
|
+
@params[:captures] = force_encoding(captures) unless captures.nil? || captures.empty?
|
1031
1065
|
else
|
1032
1066
|
values += params.values.flatten
|
1033
1067
|
end
|
@@ -1040,7 +1074,8 @@ module Sinatra
|
|
1040
1074
|
@env['sinatra.error.params'] = @params
|
1041
1075
|
raise
|
1042
1076
|
ensure
|
1043
|
-
|
1077
|
+
params ||= {}
|
1078
|
+
params.each { |k, _| @params.delete(k) } unless @env['sinatra.error.params']
|
1044
1079
|
end
|
1045
1080
|
|
1046
1081
|
# No matching route was found or all routes passed. The default
|
@@ -1052,7 +1087,7 @@ module Sinatra
|
|
1052
1087
|
if @app
|
1053
1088
|
forward
|
1054
1089
|
else
|
1055
|
-
raise NotFound
|
1090
|
+
raise NotFound, "#{request.request_method} #{request.path_info}"
|
1056
1091
|
end
|
1057
1092
|
end
|
1058
1093
|
|
@@ -1060,7 +1095,11 @@ module Sinatra
|
|
1060
1095
|
# a matching file is found, returns nil otherwise.
|
1061
1096
|
def static!(options = {})
|
1062
1097
|
return if (public_dir = settings.public_folder).nil?
|
1063
|
-
path =
|
1098
|
+
path = "#{public_dir}#{URI_INSTANCE.unescape(request.path_info)}"
|
1099
|
+
return unless valid_path?(path)
|
1100
|
+
|
1101
|
+
path = File.expand_path(path)
|
1102
|
+
return unless path.start_with?(File.expand_path(public_dir) + '/')
|
1064
1103
|
return unless File.file?(path)
|
1065
1104
|
|
1066
1105
|
env['sinatra.static_file'] = path
|
@@ -1086,11 +1125,18 @@ module Sinatra
|
|
1086
1125
|
|
1087
1126
|
# Dispatch a request with error handling.
|
1088
1127
|
def dispatch!
|
1089
|
-
|
1128
|
+
# Avoid passing frozen string in force_encoding
|
1129
|
+
@params.merge!(@request.params).each do |key, val|
|
1130
|
+
next unless val.respond_to?(:force_encoding)
|
1131
|
+
val = val.dup if val.frozen?
|
1132
|
+
@params[key] = force_encoding(val)
|
1133
|
+
end
|
1090
1134
|
|
1091
1135
|
invoke do
|
1092
1136
|
static! if settings.static? && (request.get? || request.head?)
|
1093
|
-
filter! :before
|
1137
|
+
filter! :before do
|
1138
|
+
@pinned_response = !response['Content-Type'].nil?
|
1139
|
+
end
|
1094
1140
|
route!
|
1095
1141
|
end
|
1096
1142
|
rescue ::Exception => boom
|
@@ -1110,7 +1156,7 @@ module Sinatra
|
|
1110
1156
|
end
|
1111
1157
|
@env['sinatra.error'] = boom
|
1112
1158
|
|
1113
|
-
if boom.respond_to? :http_status
|
1159
|
+
if boom.respond_to? :http_status and boom.http_status.between? 400, 599
|
1114
1160
|
status(boom.http_status)
|
1115
1161
|
elsif settings.use_code? and boom.respond_to? :code and boom.code.between? 400, 599
|
1116
1162
|
status(boom.code)
|
@@ -1118,21 +1164,27 @@ module Sinatra
|
|
1118
1164
|
status(500)
|
1119
1165
|
end
|
1120
1166
|
|
1121
|
-
status(500) unless status.between? 400, 599
|
1122
|
-
|
1123
|
-
boom_message = boom.message if boom.message && boom.message != boom.class.name
|
1124
1167
|
if server_error?
|
1125
1168
|
dump_errors! boom if settings.dump_errors?
|
1126
1169
|
raise boom if settings.show_exceptions? and settings.show_exceptions != :after_handler
|
1127
1170
|
elsif not_found?
|
1128
1171
|
headers['X-Cascade'] = 'pass' if settings.x_cascade?
|
1129
|
-
body boom_message || '<h1>Not Found</h1>'
|
1130
|
-
elsif bad_request?
|
1131
|
-
body boom_message || '<h1>Bad Request</h1>'
|
1132
1172
|
end
|
1133
1173
|
|
1134
|
-
res = error_block!(boom.class, boom) || error_block!(status, boom)
|
1135
|
-
|
1174
|
+
if res = error_block!(boom.class, boom) || error_block!(status, boom)
|
1175
|
+
return res
|
1176
|
+
end
|
1177
|
+
|
1178
|
+
if not_found? || bad_request?
|
1179
|
+
if boom.message && boom.message != boom.class.name
|
1180
|
+
body Rack::Utils.escape_html(boom.message)
|
1181
|
+
else
|
1182
|
+
content_type 'text/html'
|
1183
|
+
body '<h1>' + (not_found? ? 'Not Found' : 'Bad Request') + '</h1>'
|
1184
|
+
end
|
1185
|
+
end
|
1186
|
+
|
1187
|
+
return unless server_error?
|
1136
1188
|
raise boom if settings.raise_errors? or settings.show_exceptions?
|
1137
1189
|
error_block! Exception, boom
|
1138
1190
|
end
|
@@ -1160,12 +1212,12 @@ module Sinatra
|
|
1160
1212
|
|
1161
1213
|
class << self
|
1162
1214
|
CALLERS_TO_IGNORE = [ # :nodoc:
|
1163
|
-
/\/sinatra(\/(base|main|show_exceptions))?\.rb$/,
|
1215
|
+
/\/sinatra(\/(base|main|show_exceptions))?\.rb$/, # all sinatra code
|
1164
1216
|
/lib\/tilt.*\.rb$/, # all tilt code
|
1165
1217
|
/^\(.*\)$/, # generated code
|
1166
1218
|
/rubygems\/(custom|core_ext\/kernel)_require\.rb$/, # rubygems require hacks
|
1167
1219
|
/active_support/, # active_support require hacks
|
1168
|
-
/bundler(\/runtime)?\.rb/,
|
1220
|
+
/bundler(\/(?:runtime|inline))?\.rb/, # bundler require hacks
|
1169
1221
|
/<internal:/, # internal in ruby >= 1.9.2
|
1170
1222
|
/src\/kernel\/bootstrap\/[A-Z]/ # maglev kernel files
|
1171
1223
|
]
|
@@ -1245,8 +1297,8 @@ module Sinatra
|
|
1245
1297
|
end
|
1246
1298
|
end
|
1247
1299
|
|
1248
|
-
define_singleton("#{option}=", setter)
|
1249
|
-
define_singleton(option, getter)
|
1300
|
+
define_singleton("#{option}=", setter)
|
1301
|
+
define_singleton(option, getter)
|
1250
1302
|
define_singleton("#{option}?", "!!#{option}") unless method_defined? "#{option}?"
|
1251
1303
|
self
|
1252
1304
|
end
|
@@ -1342,19 +1394,19 @@ module Sinatra
|
|
1342
1394
|
# context as route handlers and may access/modify the request and
|
1343
1395
|
# response.
|
1344
1396
|
def before(path = /.*/, **options, &block)
|
1345
|
-
add_filter(:before, path, options, &block)
|
1397
|
+
add_filter(:before, path, **options, &block)
|
1346
1398
|
end
|
1347
1399
|
|
1348
1400
|
# Define an after filter; runs after all requests within the same
|
1349
1401
|
# context as route handlers and may access/modify the request and
|
1350
1402
|
# response.
|
1351
1403
|
def after(path = /.*/, **options, &block)
|
1352
|
-
add_filter(:after, path, options, &block)
|
1404
|
+
add_filter(:after, path, **options, &block)
|
1353
1405
|
end
|
1354
1406
|
|
1355
1407
|
# add a filter
|
1356
1408
|
def add_filter(type, path = /.*/, **options, &block)
|
1357
|
-
filters[type] << compile!(type, path, block, options)
|
1409
|
+
filters[type] << compile!(type, path, block, **options)
|
1358
1410
|
end
|
1359
1411
|
|
1360
1412
|
# Add a route condition. The route is considered non-matching when the
|
@@ -1428,13 +1480,14 @@ module Sinatra
|
|
1428
1480
|
@prototype = nil
|
1429
1481
|
@middleware << [middleware, args, block]
|
1430
1482
|
end
|
1483
|
+
ruby2_keywords(:use) if respond_to?(:ruby2_keywords, true)
|
1431
1484
|
|
1432
1485
|
# Stop the self-hosted server if running.
|
1433
1486
|
def quit!
|
1434
1487
|
return unless running?
|
1435
1488
|
# Use Thin's hard #stop! if available, otherwise just #stop.
|
1436
1489
|
running_server.respond_to?(:stop!) ? running_server.stop! : running_server.stop
|
1437
|
-
$stderr.puts "== Sinatra has ended his set (crowd applauds)" unless
|
1490
|
+
$stderr.puts "== Sinatra has ended his set (crowd applauds)" unless suppress_messages?
|
1438
1491
|
set :running_server, nil
|
1439
1492
|
set :handler_name, nil
|
1440
1493
|
end
|
@@ -1442,12 +1495,12 @@ module Sinatra
|
|
1442
1495
|
alias_method :stop!, :quit!
|
1443
1496
|
|
1444
1497
|
# Run the Sinatra app as a self-hosted server using
|
1445
|
-
#
|
1498
|
+
# Puma, Mongrel, or WEBrick (in that order). If given a block, will call
|
1446
1499
|
# with the constructed handler once we have taken the stage.
|
1447
1500
|
def run!(options = {}, &block)
|
1448
1501
|
return if running?
|
1449
1502
|
set options
|
1450
|
-
handler =
|
1503
|
+
handler = Rack::Handler.pick(server)
|
1451
1504
|
handler_name = handler.name.gsub(/.*::/, '')
|
1452
1505
|
server_settings = settings.respond_to?(:server_settings) ? settings.server_settings : {}
|
1453
1506
|
server_settings.merge!(:Port => port, :Host => bind)
|
@@ -1480,8 +1533,8 @@ module Sinatra
|
|
1480
1533
|
# Create a new instance of the class fronted by its middleware
|
1481
1534
|
# pipeline. The object is guaranteed to respond to #call but may not be
|
1482
1535
|
# an instance of the class new was called on.
|
1483
|
-
def new(*args, &bk)
|
1484
|
-
instance = new!(*args, &bk)
|
1536
|
+
def new(*args, **kwargs, &bk)
|
1537
|
+
instance = new!(*args, **kwargs, &bk)
|
1485
1538
|
Wrapper.new(build(instance).to_app, instance)
|
1486
1539
|
end
|
1487
1540
|
|
@@ -1519,8 +1572,8 @@ module Sinatra
|
|
1519
1572
|
# behavior, by ensuring an instance exists:
|
1520
1573
|
prototype
|
1521
1574
|
# Run the instance we created:
|
1522
|
-
handler.run(self, server_settings) do |server|
|
1523
|
-
unless
|
1575
|
+
handler.run(self, **server_settings) do |server|
|
1576
|
+
unless suppress_messages?
|
1524
1577
|
$stderr.puts "== Sinatra (v#{Sinatra::VERSION}) has taken the stage on #{port} for #{environment} with backup from #{handler_name}"
|
1525
1578
|
end
|
1526
1579
|
|
@@ -1533,7 +1586,7 @@ module Sinatra
|
|
1533
1586
|
end
|
1534
1587
|
end
|
1535
1588
|
|
1536
|
-
def
|
1589
|
+
def suppress_messages?
|
1537
1590
|
handler_name =~ /cgi/i || quiet
|
1538
1591
|
end
|
1539
1592
|
|
@@ -1598,7 +1651,7 @@ module Sinatra
|
|
1598
1651
|
|
1599
1652
|
def route(verb, path, options = {}, &block)
|
1600
1653
|
enable :empty_path_info if path == "" and empty_path_info.nil?
|
1601
|
-
signature = compile!(verb, path, block, options)
|
1654
|
+
signature = compile!(verb, path, block, **options)
|
1602
1655
|
(@routes[verb] ||= []) << signature
|
1603
1656
|
invoke_hook(:route_added, verb, path, block)
|
1604
1657
|
signature
|
@@ -1635,7 +1688,7 @@ module Sinatra
|
|
1635
1688
|
end
|
1636
1689
|
|
1637
1690
|
def compile(path, route_mustermann_opts = {})
|
1638
|
-
Mustermann.new(path, mustermann_opts.merge(route_mustermann_opts))
|
1691
|
+
Mustermann.new(path, **mustermann_opts.merge(route_mustermann_opts))
|
1639
1692
|
end
|
1640
1693
|
|
1641
1694
|
def setup_default_middleware(builder)
|
@@ -1701,17 +1754,6 @@ module Sinatra
|
|
1701
1754
|
builder.use session_store, options
|
1702
1755
|
end
|
1703
1756
|
|
1704
|
-
def detect_rack_handler
|
1705
|
-
servers = Array(server)
|
1706
|
-
servers.each do |server_name|
|
1707
|
-
begin
|
1708
|
-
return Rack::Handler.get(server_name.to_s)
|
1709
|
-
rescue LoadError, NameError
|
1710
|
-
end
|
1711
|
-
end
|
1712
|
-
fail "Server handler (#{servers.join(',')}) not found."
|
1713
|
-
end
|
1714
|
-
|
1715
1757
|
def inherited(subclass)
|
1716
1758
|
subclass.reset!
|
1717
1759
|
subclass.set :app_file, caller_files.first unless subclass.app_file?
|
@@ -1740,29 +1782,22 @@ module Sinatra
|
|
1740
1782
|
end
|
1741
1783
|
end
|
1742
1784
|
|
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
|
1785
|
+
# Force data to specified encoding. It defaults to settings.default_encoding
|
1786
|
+
# which is UTF-8 by default
|
1787
|
+
def self.force_encoding(data, encoding = default_encoding)
|
1788
|
+
return if data == settings || data.is_a?(Tempfile)
|
1789
|
+
if data.respond_to? :force_encoding
|
1790
|
+
data.force_encoding(encoding).encode!
|
1791
|
+
elsif data.respond_to? :each_value
|
1792
|
+
data.each_value { |v| force_encoding(v, encoding) }
|
1793
|
+
elsif data.respond_to? :each
|
1794
|
+
data.each { |v| force_encoding(v, encoding) }
|
1761
1795
|
end
|
1762
|
-
|
1763
|
-
def self.force_encoding(data, *) data end
|
1796
|
+
data
|
1764
1797
|
end
|
1765
1798
|
|
1799
|
+
def force_encoding(*args) settings.force_encoding(*args) end
|
1800
|
+
|
1766
1801
|
reset!
|
1767
1802
|
|
1768
1803
|
set :environment, (ENV['APP_ENV'] || ENV['RACK_ENV'] || :development).to_sym
|
@@ -1780,6 +1815,7 @@ module Sinatra
|
|
1780
1815
|
set :add_charset, %w[javascript xml xhtml+xml].map { |t| "application/#{t}" }
|
1781
1816
|
settings.add_charset << /^text\//
|
1782
1817
|
set :mustermann_opts, {}
|
1818
|
+
set :default_content_type, 'text/html'
|
1783
1819
|
|
1784
1820
|
# explicitly generating a session secret eagerly to play nice with preforking
|
1785
1821
|
begin
|
@@ -1810,10 +1846,9 @@ module Sinatra
|
|
1810
1846
|
server.unshift 'control_tower'
|
1811
1847
|
else
|
1812
1848
|
server.unshift 'reel'
|
1849
|
+
server.unshift 'puma'
|
1813
1850
|
server.unshift 'mongrel' if ruby_engine.nil?
|
1814
|
-
server.unshift 'puma' if ruby_engine != 'rbx'
|
1815
1851
|
server.unshift 'thin' if ruby_engine != 'jruby'
|
1816
|
-
server.unshift 'puma' if ruby_engine == 'rbx'
|
1817
1852
|
server.unshift 'trinidad' if ruby_engine == 'jruby'
|
1818
1853
|
end
|
1819
1854
|
|
@@ -1841,7 +1876,7 @@ module Sinatra
|
|
1841
1876
|
|
1842
1877
|
configure :development do
|
1843
1878
|
get '/__sinatra__/:image.png' do
|
1844
|
-
filename =
|
1879
|
+
filename = __dir__ + "/images/#{params[:image].to_i}.png"
|
1845
1880
|
content_type :png
|
1846
1881
|
send_file filename
|
1847
1882
|
end
|
@@ -1922,6 +1957,8 @@ module Sinatra
|
|
1922
1957
|
return super(*args, &block) if respond_to? method_name
|
1923
1958
|
Delegator.target.send(method_name, *args, &block)
|
1924
1959
|
end
|
1960
|
+
# ensure keyword argument passing is compatible with ruby >= 2.7
|
1961
|
+
ruby2_keywords(method_name) if respond_to?(:ruby2_keywords, true)
|
1925
1962
|
private method_name
|
1926
1963
|
end
|
1927
1964
|
end
|