sinatra 1.3.0.d → 1.3.0.e
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.
- data/.gitignore +6 -0
- data/.yardopts +4 -0
- data/CHANGES +80 -3
- data/Gemfile +18 -12
- data/README.de.rdoc +189 -381
- data/README.es.rdoc +193 -316
- data/README.fr.rdoc +327 -475
- data/README.jp.rdoc +7 -1
- data/README.rdoc +132 -101
- data/README.ru.rdoc +3 -3
- data/README.zh.rdoc +2 -2
- data/Rakefile +19 -27
- data/lib/sinatra/base.rb +186 -262
- data/lib/sinatra/version.rb +3 -0
- data/sinatra.gemspec +12 -128
- data/test/base_test.rb +1 -1
- data/test/builder_test.rb +1 -1
- data/test/coffee_test.rb +1 -1
- data/test/creole_test.rb +1 -1
- data/test/delegator_test.rb +9 -7
- data/test/encoding_test.rb +1 -1
- data/test/erb_test.rb +1 -1
- data/test/extensions_test.rb +1 -1
- data/test/filter_test.rb +7 -4
- data/test/haml_test.rb +1 -1
- data/test/helper.rb +16 -1
- data/test/helpers_test.rb +12 -1
- data/test/less_test.rb +1 -1
- data/test/liquid_test.rb +1 -1
- data/test/mapped_error_test.rb +44 -1
- data/test/markaby_test.rb +1 -1
- data/test/markdown_test.rb +1 -1
- data/test/middleware_test.rb +1 -1
- data/test/nokogiri_test.rb +1 -1
- data/test/radius_test.rb +1 -1
- data/test/rdoc_test.rb +2 -2
- data/test/readme_test.rb +136 -0
- data/test/request_test.rb +1 -1
- data/test/response_test.rb +13 -3
- data/test/result_test.rb +3 -3
- data/test/route_added_hook_test.rb +1 -1
- data/test/routing_test.rb +9 -2
- data/test/sass_test.rb +1 -1
- data/test/scss_test.rb +1 -1
- data/test/server_test.rb +1 -1
- data/test/settings_test.rb +55 -22
- data/test/sinatra_test.rb +1 -1
- data/test/slim_test.rb +1 -1
- data/test/static_test.rb +22 -1
- data/test/templates_test.rb +1 -1
- data/test/textile_test.rb +1 -1
- metadata +47 -59
- data/lib/sinatra/rack.rb +0 -44
data/README.ru.rdoc
CHANGED
@@ -1547,7 +1547,7 @@ DSL верхнего уровня "загрязняет" пространств
|
|
1547
1547
|
Rack-совместимый сервер приложений.
|
1548
1548
|
|
1549
1549
|
# config.ru
|
1550
|
-
require 'my_app'
|
1550
|
+
require './my_app'
|
1551
1551
|
run MyApp
|
1552
1552
|
|
1553
1553
|
Запускаем:
|
@@ -1567,7 +1567,7 @@ Rack-совместимый сервер приложений.
|
|
1567
1567
|
|
1568
1568
|
И соответствующий <tt>config.ru</tt>:
|
1569
1569
|
|
1570
|
-
require 'app'
|
1570
|
+
require './app'
|
1571
1571
|
run Sinatra::Application
|
1572
1572
|
|
1573
1573
|
=== Когда использовать config.ru?
|
@@ -1738,7 +1738,7 @@ Sinatra приложения могут быть запущены напряму
|
|
1738
1738
|
языки шаблонов, работает.
|
1739
1739
|
|
1740
1740
|
[ JRuby ]
|
1741
|
-
JRuby официально поддерживается (JRuby >= 1.6.
|
1741
|
+
JRuby официально поддерживается (JRuby >= 1.6.1). Нет никаких проблем с
|
1742
1742
|
использованием альтернативных шаблонов. Тем не менее, если вы выбираете
|
1743
1743
|
JRuby, то, пожалуйста, посмотрите на JRuby Rack-сервера, так как Thin не
|
1744
1744
|
поддерживается полностью на JRuby. Поддержка расширений на C в JRuby все
|
data/README.zh.rdoc
CHANGED
@@ -1513,7 +1513,7 @@ Sinatra::Base子类可用的方法实际上就是通过顶层 DSL
|
|
1513
1513
|
或者使用一个 <tt>config.ru</tt>,允许你使用任何Rack处理器:
|
1514
1514
|
|
1515
1515
|
# config.ru
|
1516
|
-
require 'my_app'
|
1516
|
+
require './my_app'
|
1517
1517
|
run MyApp
|
1518
1518
|
|
1519
1519
|
运行:
|
@@ -1533,7 +1533,7 @@ Sinatra::Base子类可用的方法实际上就是通过顶层 DSL
|
|
1533
1533
|
|
1534
1534
|
加入相应的 <tt>config.ru</tt>:
|
1535
1535
|
|
1536
|
-
require 'app'
|
1536
|
+
require './app'
|
1537
1537
|
run Sinatra::Application
|
1538
1538
|
|
1539
1539
|
=== 什么时候用 config.ru?
|
data/Rakefile
CHANGED
@@ -10,8 +10,8 @@ CLEAN.include "**/*.rbc"
|
|
10
10
|
|
11
11
|
def source_version
|
12
12
|
@source_version ||= begin
|
13
|
-
|
14
|
-
|
13
|
+
load './lib/sinatra/version.rb'
|
14
|
+
Sinatra::VERSION
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
@@ -37,6 +37,16 @@ Rake::TestTask.new(:test) do |t|
|
|
37
37
|
t.ruby_opts << '-I.'
|
38
38
|
end
|
39
39
|
|
40
|
+
Rake::TestTask.new(:"test:core") do |t|
|
41
|
+
core_tests = %w[base delegator encoding extensions filter
|
42
|
+
helpers mapped_error middleware radius rdoc
|
43
|
+
readme request response result route_added_hook
|
44
|
+
routing server settings sinatra static templates]
|
45
|
+
t.test_files = core_tests.map {|n| "test/#{n}_test.rb"}
|
46
|
+
t.ruby_opts = ["-rubygems"] if defined? Gem
|
47
|
+
t.ruby_opts << "-I."
|
48
|
+
end
|
49
|
+
|
40
50
|
# Rcov ================================================================
|
41
51
|
|
42
52
|
namespace :test do
|
@@ -90,18 +100,22 @@ task :thanks, [:release,:backports] do |t, a|
|
|
90
100
|
"(based on commits included in #{a.release}, but not in #{a.backports})"
|
91
101
|
end
|
92
102
|
|
93
|
-
|
94
|
-
|
103
|
+
desc "list of authors"
|
104
|
+
task :authors, [:commit_range, :format, :sep] do |t, a|
|
105
|
+
a.with_defaults :format => "%s (%d)", :sep => ", "
|
95
106
|
authors = Hash.new { |h,k| h[k] = 0 }
|
96
107
|
blake = "Blake Mizerany"
|
108
|
+
overall = 0
|
97
109
|
mapping = {
|
98
110
|
"blake.mizerany@gmail.com" => blake, "bmizerany" => blake,
|
99
111
|
"a_user@mac.com" => blake, "ichverstehe" => "Harry Vangberg",
|
100
112
|
"Wu Jiang (nouse)" => "Wu Jiang" }
|
101
|
-
`git shortlog -s`.lines.map do |line|
|
113
|
+
`git shortlog -s #{a.commit_range}`.lines.map do |line|
|
102
114
|
num, name = line.split("\t", 2).map(&:strip)
|
103
115
|
authors[mapping[name] || name] += num.to_i
|
116
|
+
overall += num.to_i
|
104
117
|
end
|
118
|
+
puts "#{overall} commits by #{authors.count} authors:"
|
105
119
|
puts authors.sort_by { |n,c| -c }.map { |e| a.format % e }.join(a.sep)
|
106
120
|
end
|
107
121
|
|
@@ -143,28 +157,6 @@ if defined?(Gem)
|
|
143
157
|
SH
|
144
158
|
end
|
145
159
|
|
146
|
-
task 'sinatra.gemspec' => FileList['{lib,test,compat}/**','Rakefile','CHANGES','*.rdoc'] do |f|
|
147
|
-
# read spec file and split out manifest section
|
148
|
-
spec = File.read(f.name)
|
149
|
-
head, manifest, tail = spec.split(" # = MANIFEST =\n")
|
150
|
-
# replace version and date
|
151
|
-
head.sub!(/\.version = '.*'/, ".version = '#{source_version}'")
|
152
|
-
head.sub!(/\.date = '.*'/, ".date = '#{Date.today.to_s}'")
|
153
|
-
# determine file list from git ls-files
|
154
|
-
files = `git ls-files`.
|
155
|
-
split("\n").
|
156
|
-
sort.
|
157
|
-
reject{ |file| file =~ /^\./ }.
|
158
|
-
reject { |file| file =~ /^doc/ }.
|
159
|
-
map{ |file| " #{file}" }.
|
160
|
-
join("\n")
|
161
|
-
# piece file back together and write...
|
162
|
-
manifest = " s.files = %w[\n#{files}\n ]\n"
|
163
|
-
spec = [head,manifest,tail].join(" # = MANIFEST =\n")
|
164
|
-
File.open(f.name, 'w') { |io| io.write(spec) }
|
165
|
-
puts "updated #{f.name}"
|
166
|
-
end
|
167
|
-
|
168
160
|
task 'release' => ['test', package('.gem')] do
|
169
161
|
sh <<-SH
|
170
162
|
gem install #{package('.gem')} --local &&
|
data/lib/sinatra/base.rb
CHANGED
@@ -1,13 +1,17 @@
|
|
1
|
+
# external dependencies
|
2
|
+
require 'rack'
|
3
|
+
require 'tilt'
|
4
|
+
|
5
|
+
# stdlib dependencies
|
1
6
|
require 'thread'
|
2
7
|
require 'time'
|
3
8
|
require 'uri'
|
4
|
-
|
9
|
+
|
10
|
+
# other files we need
|
5
11
|
require 'sinatra/showexceptions'
|
6
|
-
require '
|
12
|
+
require 'sinatra/version'
|
7
13
|
|
8
14
|
module Sinatra
|
9
|
-
VERSION = '1.3.0.d'
|
10
|
-
|
11
15
|
# The request object. See Rack::Request for more info:
|
12
16
|
# http://rack.rubyforge.org/doc/classes/Rack/Request.html
|
13
17
|
class Request < Rack::Request
|
@@ -35,15 +39,6 @@ module Sinatra
|
|
35
39
|
@env.include? "HTTP_X_FORWARDED_HOST"
|
36
40
|
end
|
37
41
|
|
38
|
-
def route
|
39
|
-
@route ||= Rack::Utils.unescape(path_info)
|
40
|
-
end
|
41
|
-
|
42
|
-
def path_info=(value)
|
43
|
-
@route = nil
|
44
|
-
super
|
45
|
-
end
|
46
|
-
|
47
42
|
private
|
48
43
|
|
49
44
|
def accept_entry(entry)
|
@@ -59,20 +54,20 @@ module Sinatra
|
|
59
54
|
# http://rack.rubyforge.org/doc/classes/Rack/Response.html
|
60
55
|
# http://rack.rubyforge.org/doc/classes/Rack/Response/Helpers.html
|
61
56
|
class Response < Rack::Response
|
57
|
+
def body=(value)
|
58
|
+
value = value.body while value.respond_to? :body and value.body != value
|
59
|
+
@body = value.respond_to?(:to_str) ? [value.to_str] : value
|
60
|
+
end
|
61
|
+
|
62
|
+
def each
|
63
|
+
block_given? ? super : enum_for(:each)
|
64
|
+
end
|
65
|
+
|
62
66
|
def finish
|
63
|
-
|
64
|
-
|
65
|
-
header.delete "Content-Type"
|
66
|
-
[status.to_i, header.to_hash, []]
|
67
|
-
else
|
68
|
-
body = @body || []
|
69
|
-
body = [body] if body.respond_to? :to_str
|
70
|
-
if body.respond_to?(:to_ary)
|
71
|
-
header["Content-Length"] = body.to_ary.
|
72
|
-
inject(0) { |len, part| len + Rack::Utils.bytesize(part) }.to_s
|
73
|
-
end
|
74
|
-
[status.to_i, header.to_hash, body]
|
67
|
+
if body.respond_to? :to_ary and not [204, 304].include?(status.to_i)
|
68
|
+
headers["Content-Length"] = body.inject(0) { |l, p| l + Rack::Utils.bytesize(p) }.to_s
|
75
69
|
end
|
70
|
+
super
|
76
71
|
end
|
77
72
|
end
|
78
73
|
|
@@ -117,9 +112,7 @@ module Sinatra
|
|
117
112
|
return addr if addr =~ /\A[A-z][A-z0-9\+\.\-]*:/
|
118
113
|
uri = [host = ""]
|
119
114
|
if absolute
|
120
|
-
host <<
|
121
|
-
host << 's' if request.secure?
|
122
|
-
host << "://"
|
115
|
+
host << "http#{'s' if request.secure?}://"
|
123
116
|
if request.forwarded? or request.port != (request.secure? ? 443 : 80)
|
124
117
|
host << request.host_with_port
|
125
118
|
else
|
@@ -198,9 +191,6 @@ module Sinatra
|
|
198
191
|
|
199
192
|
# Use the contents of the file at +path+ as the response body.
|
200
193
|
def send_file(path, opts={})
|
201
|
-
stat = File.stat(path)
|
202
|
-
last_modified(opts[:last_modified] || stat.mtime)
|
203
|
-
|
204
194
|
if opts[:type] or not response['Content-Type']
|
205
195
|
content_type opts[:type] || File.extname(path), :default => 'application/octet-stream'
|
206
196
|
end
|
@@ -211,90 +201,17 @@ module Sinatra
|
|
211
201
|
response['Content-Disposition'] = 'inline'
|
212
202
|
end
|
213
203
|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
response['Content-Length'] = (r.end - r.begin + 1).to_s
|
222
|
-
halt 206, sf
|
223
|
-
else
|
224
|
-
response['Content-Length'] ||= file_length.to_s
|
225
|
-
halt sf
|
226
|
-
end
|
204
|
+
last_modified opts[:last_modified] if opts[:last_modified]
|
205
|
+
|
206
|
+
file = Rack::File.new nil
|
207
|
+
file.path = path
|
208
|
+
result = file.serving env
|
209
|
+
result[1].each { |k,v| headers[k] ||= v }
|
210
|
+
halt result[0], result[2]
|
227
211
|
rescue Errno::ENOENT
|
228
212
|
not_found
|
229
213
|
end
|
230
214
|
|
231
|
-
# Rack response body used to deliver static files. The file contents are
|
232
|
-
# generated iteratively in 8K chunks.
|
233
|
-
class StaticFile < ::File #:nodoc:
|
234
|
-
alias_method :to_path, :path
|
235
|
-
|
236
|
-
attr_accessor :range # a Range or nil
|
237
|
-
|
238
|
-
# Checks for byte-ranges in the request and sets self.range appropriately.
|
239
|
-
# Returns false if the ranges are unsatisfiable and the request should return 416.
|
240
|
-
def parse_ranges(env, size)
|
241
|
-
#r = Rack::Utils::byte_ranges(env, size) # TODO: not available yet in released Rack
|
242
|
-
r = byte_ranges(env, size)
|
243
|
-
return false if r == [] # Unsatisfiable; report error
|
244
|
-
@range = r[0] if r && r.length == 1 # Ignore multiple-range requests for now
|
245
|
-
return true
|
246
|
-
end
|
247
|
-
|
248
|
-
# TODO: Copied from the new method Rack::Utils::byte_ranges; this method can be removed once
|
249
|
-
# a version of Rack with that method is released and Sinatra can depend on it.
|
250
|
-
def byte_ranges(env, size)
|
251
|
-
# See <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35>
|
252
|
-
http_range = env['HTTP_RANGE']
|
253
|
-
return nil unless http_range
|
254
|
-
ranges = []
|
255
|
-
http_range.split(/,\s*/).each do |range_spec|
|
256
|
-
matches = range_spec.match(/bytes=(\d*)-(\d*)/)
|
257
|
-
return nil unless matches
|
258
|
-
r0,r1 = matches[1], matches[2]
|
259
|
-
if r0.empty?
|
260
|
-
return nil if r1.empty?
|
261
|
-
# suffix-byte-range-spec, represents trailing suffix of file
|
262
|
-
r0 = [size - r1.to_i, 0].max
|
263
|
-
r1 = size - 1
|
264
|
-
else
|
265
|
-
r0 = r0.to_i
|
266
|
-
if r1.empty?
|
267
|
-
r1 = size - 1
|
268
|
-
else
|
269
|
-
r1 = r1.to_i
|
270
|
-
return nil if r1 < r0 # backwards range is syntactically invalid
|
271
|
-
r1 = size-1 if r1 >= size
|
272
|
-
end
|
273
|
-
end
|
274
|
-
ranges << (r0..r1) if r0 <= r1
|
275
|
-
end
|
276
|
-
ranges
|
277
|
-
end
|
278
|
-
|
279
|
-
CHUNK_SIZE = 8192
|
280
|
-
|
281
|
-
def each
|
282
|
-
if @range
|
283
|
-
self.pos = @range.begin
|
284
|
-
length = @range.end - @range.begin + 1
|
285
|
-
while length > 0 && (buf = read([CHUNK_SIZE,length].min))
|
286
|
-
yield buf
|
287
|
-
length -= buf.length
|
288
|
-
end
|
289
|
-
else
|
290
|
-
rewind
|
291
|
-
while buf = read(CHUNK_SIZE)
|
292
|
-
yield buf
|
293
|
-
end
|
294
|
-
end
|
295
|
-
end
|
296
|
-
end
|
297
|
-
|
298
215
|
# Specify response freshness policy for HTTP caches (Cache-Control header).
|
299
216
|
# Any number of non-value directives (:public, :private, :no_cache,
|
300
217
|
# :no_store, :must_revalidate, :proxy_revalidate) may be passed along with
|
@@ -375,8 +292,8 @@ module Sinatra
|
|
375
292
|
# When the current request includes an 'If-None-Match' header with a
|
376
293
|
# matching etag, execution is immediately halted. If the request method is
|
377
294
|
# GET or HEAD, a '304 Not Modified' response is sent.
|
378
|
-
def etag(value, kind
|
379
|
-
raise
|
295
|
+
def etag(value, kind = :strong)
|
296
|
+
raise ArgumentError, ":strong or :weak expected" unless [:strong,:weak].include?(kind)
|
380
297
|
value = '"%s"' % value
|
381
298
|
value = 'W/' + value if kind == :weak
|
382
299
|
response['ETag'] = value
|
@@ -446,6 +363,11 @@ module Sinatra
|
|
446
363
|
attr_accessor :content_type
|
447
364
|
end
|
448
365
|
|
366
|
+
def initialize
|
367
|
+
super
|
368
|
+
@default_layout = :layout
|
369
|
+
end
|
370
|
+
|
449
371
|
def erb(template, options={}, locals={})
|
450
372
|
render :erb, template, options, locals
|
451
373
|
end
|
@@ -549,7 +471,6 @@ module Sinatra
|
|
549
471
|
# extract generic options
|
550
472
|
locals = options.delete(:locals) || locals || {}
|
551
473
|
views = options.delete(:views) || settings.views || "./views"
|
552
|
-
@default_layout = :layout if @default_layout.nil?
|
553
474
|
layout = options.delete(:layout)
|
554
475
|
eat_errors = layout.nil?
|
555
476
|
layout = @default_layout if layout.nil? or layout == true
|
@@ -620,6 +541,7 @@ module Sinatra
|
|
620
541
|
attr_reader :template_cache
|
621
542
|
|
622
543
|
def initialize(app=nil)
|
544
|
+
super()
|
623
545
|
@app = app
|
624
546
|
@template_cache = Tilt::Cache.new
|
625
547
|
yield self if block_given?
|
@@ -638,15 +560,15 @@ module Sinatra
|
|
638
560
|
@response = Response.new
|
639
561
|
@params = indifferent_params(@request.params)
|
640
562
|
template_cache.clear if settings.reload_templates
|
641
|
-
force_encoding(@request.route)
|
642
563
|
force_encoding(@params)
|
643
564
|
|
644
565
|
@response['Content-Type'] = nil
|
645
566
|
invoke { dispatch! }
|
646
567
|
invoke { error_block!(response.status) }
|
568
|
+
|
647
569
|
unless @response['Content-Type']
|
648
|
-
if body.respond_to?
|
649
|
-
content_type body.
|
570
|
+
if body.respond_to? :to_ary and body[0].respond_to? :content_type
|
571
|
+
content_type body[0].content_type
|
650
572
|
else
|
651
573
|
content_type :html
|
652
574
|
end
|
@@ -667,7 +589,7 @@ module Sinatra
|
|
667
589
|
|
668
590
|
def options
|
669
591
|
warn "Sinatra::Base#options is deprecated and will be removed, " \
|
670
|
-
"use #settings
|
592
|
+
"use #settings instead.\n\tfrom #{caller.first}"
|
671
593
|
settings
|
672
594
|
end
|
673
595
|
|
@@ -699,15 +621,15 @@ module Sinatra
|
|
699
621
|
# Run filters defined on the class and all superclasses.
|
700
622
|
def filter!(type, base = settings)
|
701
623
|
filter! type, base.superclass if base.superclass.respond_to?(:filters)
|
702
|
-
base.filters[type].each { |
|
624
|
+
base.filters[type].each { |args| process_route(*args) }
|
703
625
|
end
|
704
626
|
|
705
627
|
# Run routes defined on the class and all superclasses.
|
706
628
|
def route!(base = settings, pass_block=nil)
|
707
629
|
if routes = base.routes[@request.request_method]
|
708
630
|
routes.each do |pattern, keys, conditions, block|
|
709
|
-
pass_block = process_route(pattern, keys, conditions) do
|
710
|
-
route_eval
|
631
|
+
pass_block = process_route(pattern, keys, conditions) do |*args|
|
632
|
+
route_eval { block[*args] }
|
711
633
|
end
|
712
634
|
end
|
713
635
|
end
|
@@ -722,8 +644,8 @@ module Sinatra
|
|
722
644
|
end
|
723
645
|
|
724
646
|
# Run a route block and throw :halt with the result.
|
725
|
-
def route_eval
|
726
|
-
throw :halt,
|
647
|
+
def route_eval
|
648
|
+
throw :halt, yield
|
727
649
|
end
|
728
650
|
|
729
651
|
# If the current request matches pattern and conditions, fill params
|
@@ -731,12 +653,12 @@ module Sinatra
|
|
731
653
|
# Revert params afterwards.
|
732
654
|
#
|
733
655
|
# Returns pass block.
|
734
|
-
def process_route(pattern, keys, conditions)
|
656
|
+
def process_route(pattern, keys, conditions, block = nil)
|
735
657
|
@original_params ||= @params
|
736
|
-
route = @request.
|
658
|
+
route = @request.path_info
|
737
659
|
route = '/' if route.empty? and not settings.empty_path_info?
|
738
660
|
if match = pattern.match(route)
|
739
|
-
values = match.captures.to_a
|
661
|
+
values = match.captures.to_a.map { |v| force_encoding URI.decode(v) if v }
|
740
662
|
params =
|
741
663
|
if keys.any?
|
742
664
|
keys.zip(values).inject({}) do |hash,(k,v)|
|
@@ -755,9 +677,8 @@ module Sinatra
|
|
755
677
|
@params = @original_params.merge(params)
|
756
678
|
@block_params = values
|
757
679
|
catch(:pass) do
|
758
|
-
conditions.each { |
|
759
|
-
|
760
|
-
yield
|
680
|
+
conditions.each { |c| throw :pass if c.bind(self).call == false }
|
681
|
+
block ? block[self, @block_params] : yield(self, @block_params)
|
761
682
|
end
|
762
683
|
end
|
763
684
|
ensure
|
@@ -787,6 +708,7 @@ module Sinatra
|
|
787
708
|
return unless path.start_with?(public_dir) and File.file?(path)
|
788
709
|
|
789
710
|
env['sinatra.static_file'] = path
|
711
|
+
cache_control *settings.static_cache_control if settings.static_cache_control?
|
790
712
|
send_file path, :disposition => nil
|
791
713
|
end
|
792
714
|
|
@@ -805,36 +727,16 @@ module Sinatra
|
|
805
727
|
end
|
806
728
|
|
807
729
|
# Run the block with 'throw :halt' support and apply result to the response.
|
808
|
-
def invoke
|
809
|
-
res = catch(:halt) {
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
if Fixnum === res.first
|
818
|
-
if res.length == 3
|
819
|
-
@response.status, headers, body = res
|
820
|
-
@response.body = body if body
|
821
|
-
headers.each { |k, v| @response.headers[k] = v } if headers
|
822
|
-
elsif res.length == 2
|
823
|
-
@response.status = res.first
|
824
|
-
@response.body = res.last
|
825
|
-
else
|
826
|
-
raise TypeError, "#{res.inspect} not supported"
|
827
|
-
end
|
828
|
-
else
|
829
|
-
@response.body = res
|
830
|
-
end
|
831
|
-
when res.respond_to?(:each)
|
832
|
-
@response.body = res
|
833
|
-
when (100..599) === res
|
834
|
-
@response.status = res
|
730
|
+
def invoke
|
731
|
+
res = catch(:halt) { yield }
|
732
|
+
res = [res] if Fixnum === res or String === res
|
733
|
+
if Array === res and Fixnum === res.first
|
734
|
+
status(res.shift)
|
735
|
+
body(res.pop)
|
736
|
+
headers(*res)
|
737
|
+
elsif res.respond_to? :each
|
738
|
+
body res
|
835
739
|
end
|
836
|
-
|
837
|
-
res
|
838
740
|
end
|
839
741
|
|
840
742
|
# Dispatch a request with error handling.
|
@@ -842,60 +744,45 @@ module Sinatra
|
|
842
744
|
static! if settings.static? && (request.get? || request.head?)
|
843
745
|
filter! :before
|
844
746
|
route!
|
845
|
-
rescue NotFound => boom
|
846
|
-
handle_not_found!(boom)
|
847
747
|
rescue ::Exception => boom
|
848
748
|
handle_exception!(boom)
|
849
749
|
ensure
|
850
750
|
filter! :after unless env['sinatra.static_file']
|
851
751
|
end
|
852
752
|
|
853
|
-
# Special treatment for 404s in order to play nice with cascades.
|
854
|
-
def handle_not_found!(boom)
|
855
|
-
@env['sinatra.error'] = boom
|
856
|
-
@response.status = 404
|
857
|
-
@response.headers['X-Cascade'] = 'pass'
|
858
|
-
@response.body = ['<h1>Not Found</h1>']
|
859
|
-
error_block! boom.class, NotFound
|
860
|
-
end
|
861
|
-
|
862
753
|
# Error handling during requests.
|
863
754
|
def handle_exception!(boom)
|
864
755
|
@env['sinatra.error'] = boom
|
865
|
-
|
866
756
|
dump_errors!(boom) if settings.dump_errors?
|
867
757
|
raise boom if settings.show_exceptions? and settings.show_exceptions != :after_handler
|
758
|
+
@response.status = boom.respond_to?(:code) ? Integer(boom.code) : 500
|
759
|
+
|
760
|
+
if @response.status == 404
|
761
|
+
@response.headers['X-Cascade'] = 'pass'
|
762
|
+
@response.body = ['<h1>Not Found</h1>']
|
763
|
+
end
|
868
764
|
|
869
|
-
@response.status = 500
|
870
765
|
if res = error_block!(boom.class)
|
871
766
|
res
|
872
|
-
elsif
|
873
|
-
raise boom
|
874
|
-
|
875
|
-
error_block!(Exception)
|
767
|
+
elsif @response.status >= 500
|
768
|
+
raise boom if settings.raise_errors? or settings.show_exceptions?
|
769
|
+
error_block! Exception
|
876
770
|
end
|
877
771
|
end
|
878
772
|
|
879
773
|
# Find an custom error block for the key(s) specified.
|
880
|
-
def error_block!(
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
# found a handler, eval and return result
|
886
|
-
return instance_eval(&block)
|
887
|
-
else
|
888
|
-
base = base.superclass
|
889
|
-
end
|
890
|
-
end
|
774
|
+
def error_block!(key)
|
775
|
+
base = settings
|
776
|
+
while base.respond_to?(:errors)
|
777
|
+
next base = base.superclass unless args = base.errors[key]
|
778
|
+
return process_route(*args)
|
891
779
|
end
|
892
|
-
|
893
|
-
|
780
|
+
return false unless key.respond_to? :superclass and key.superclass < Exception
|
781
|
+
error_block! key.superclass
|
894
782
|
end
|
895
783
|
|
896
784
|
def dump_errors!(boom)
|
897
|
-
msg = ["#{boom.class} - #{boom.message}:",
|
898
|
-
*boom.backtrace].join("\n ")
|
785
|
+
msg = ["#{boom.class} - #{boom.message}:", *boom.backtrace].join("\n\t")
|
899
786
|
@env['rack.errors'].puts(msg)
|
900
787
|
end
|
901
788
|
|
@@ -940,20 +827,43 @@ module Sinatra
|
|
940
827
|
|
941
828
|
# Sets an option to the given value. If the value is a proc,
|
942
829
|
# the proc will be called every time the option is accessed.
|
943
|
-
def set(option, value = (not_set = true), &block)
|
830
|
+
def set(option, value = (not_set = true), ignore_setter = false, &block)
|
944
831
|
raise ArgumentError if block and !not_set
|
945
|
-
value = block if block
|
946
|
-
|
947
|
-
|
948
|
-
metadef("#{option}?") { !!__send__(option) }
|
949
|
-
metadef("#{option}=") { |val| metadef(option, &Proc.new{val}) }
|
950
|
-
elsif not_set
|
832
|
+
value, not_set = block, false if block
|
833
|
+
|
834
|
+
if not_set
|
951
835
|
raise ArgumentError unless option.respond_to?(:each)
|
952
836
|
option.each { |k,v| set(k, v) }
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
|
837
|
+
return self
|
838
|
+
end
|
839
|
+
|
840
|
+
if respond_to?("#{option}=") and not ignore_setter
|
841
|
+
return __send__("#{option}=", value)
|
842
|
+
end
|
843
|
+
|
844
|
+
setter = proc { |val| set option, val, true }
|
845
|
+
getter = proc { value }
|
846
|
+
|
847
|
+
case value
|
848
|
+
when Proc
|
849
|
+
getter = value
|
850
|
+
when Symbol, Fixnum, FalseClass, TrueClass, NilClass
|
851
|
+
# we have a lot of enable and disable calls, let's optimize those
|
852
|
+
class_eval "def self.#{option}() #{value.inspect} end"
|
853
|
+
getter = nil
|
854
|
+
when Hash
|
855
|
+
setter = proc do |val|
|
856
|
+
val = value.merge val if Hash === val
|
857
|
+
set option, val, true
|
858
|
+
end
|
859
|
+
end
|
860
|
+
|
861
|
+
(class << self; self; end).class_eval do
|
862
|
+
define_method("#{option}=", &setter) if setter
|
863
|
+
define_method(option, &getter) if getter
|
864
|
+
unless method_defined? "#{option}?"
|
865
|
+
class_eval "def #{option}?() !!#{option} end"
|
866
|
+
end
|
957
867
|
end
|
958
868
|
self
|
959
869
|
end
|
@@ -971,8 +881,11 @@ module Sinatra
|
|
971
881
|
# Define a custom error handler. Optionally takes either an Exception
|
972
882
|
# class, or an HTTP status code to specify which errors should be
|
973
883
|
# handled.
|
974
|
-
def error(codes
|
975
|
-
|
884
|
+
def error(*codes, &block)
|
885
|
+
args = compile! "ERROR", //, block
|
886
|
+
codes = codes.map { |c| Array(c) }.flatten
|
887
|
+
codes << Exception if codes.empty?
|
888
|
+
codes.each { |c| @errors[c] = args }
|
976
889
|
end
|
977
890
|
|
978
891
|
# Sugar for `error(404) { ... }`
|
@@ -1056,18 +969,14 @@ module Sinatra
|
|
1056
969
|
|
1057
970
|
# add a filter
|
1058
971
|
def add_filter(type, path = nil, options = {}, &block)
|
1059
|
-
return filters[type] << block unless path
|
1060
972
|
path, options = //, path if path.respond_to?(:each_pair)
|
1061
|
-
|
1062
|
-
add_filter(type) do
|
1063
|
-
process_route(*arguments) { instance_eval(&block) }
|
1064
|
-
end
|
973
|
+
filters[type] << compile!(type, path || //, block, options)
|
1065
974
|
end
|
1066
975
|
|
1067
976
|
# Add a route condition. The route is considered non-matching when the
|
1068
977
|
# block returns false.
|
1069
|
-
def condition(&block)
|
1070
|
-
@conditions << block
|
978
|
+
def condition(name = "#{caller.first[/`.*'/]} condition", &block)
|
979
|
+
@conditions << generate_method(name, &block)
|
1071
980
|
end
|
1072
981
|
|
1073
982
|
private
|
@@ -1127,51 +1036,47 @@ module Sinatra
|
|
1127
1036
|
# Because of self.options.host
|
1128
1037
|
host_name(options.delete(:host)) if options.key?(:host)
|
1129
1038
|
enable :empty_path_info if path == "" and empty_path_info.nil?
|
1130
|
-
|
1131
|
-
block, pattern, keys, conditions = compile! verb, path, block, options
|
1039
|
+
(@routes[verb] ||= []) << compile!(verb, path, block, options)
|
1132
1040
|
invoke_hook(:route_added, verb, path, block)
|
1133
|
-
|
1134
|
-
(@routes[verb] ||= []).
|
1135
|
-
push([pattern, keys, conditions, block]).last
|
1136
1041
|
end
|
1137
1042
|
|
1138
1043
|
def invoke_hook(name, *args)
|
1139
1044
|
extensions.each { |e| e.send(name, *args) if e.respond_to?(name) }
|
1140
1045
|
end
|
1141
1046
|
|
1047
|
+
def generate_method(method_name, &block)
|
1048
|
+
define_method(method_name, &block)
|
1049
|
+
method = instance_method method_name
|
1050
|
+
remove_method method_name
|
1051
|
+
method
|
1052
|
+
end
|
1053
|
+
|
1142
1054
|
def compile!(verb, path, block, options = {})
|
1143
1055
|
options.each_pair { |option, args| send(option, *args) }
|
1144
|
-
method_name
|
1145
|
-
|
1146
|
-
|
1147
|
-
unbound_method = instance_method method_name
|
1148
|
-
pattern, keys = compile(path)
|
1056
|
+
method_name = "#{verb} #{path}"
|
1057
|
+
unbound_method = generate_method(method_name, &block)
|
1058
|
+
pattern, keys = compile path
|
1149
1059
|
conditions, @conditions = @conditions, []
|
1150
|
-
remove_method method_name
|
1151
1060
|
|
1152
|
-
[ block.arity != 0 ?
|
1153
|
-
proc { unbound_method.bind(
|
1154
|
-
proc { unbound_method.bind(
|
1155
|
-
pattern, keys, conditions ]
|
1061
|
+
[ pattern, keys, conditions, block.arity != 0 ?
|
1062
|
+
proc { |a,p| unbound_method.bind(a).call(*p) } :
|
1063
|
+
proc { |a,p| unbound_method.bind(a).call } ]
|
1156
1064
|
end
|
1157
1065
|
|
1158
1066
|
def compile(path)
|
1159
1067
|
keys = []
|
1160
1068
|
if path.respond_to? :to_str
|
1161
1069
|
special_chars = %w{. + ( ) $}
|
1162
|
-
pattern =
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
1166
|
-
|
1167
|
-
|
1168
|
-
|
1169
|
-
|
1170
|
-
else
|
1171
|
-
keys << $2[1..-1]
|
1172
|
-
"([^/?#]+)"
|
1173
|
-
end
|
1070
|
+
pattern = path.to_str.gsub(/[^\?\%\\\/\:\*\w]/) { |c| encoded(c) }
|
1071
|
+
pattern.gsub! /((:\w+)|\*)/ do |match|
|
1072
|
+
if match == "*"
|
1073
|
+
keys << 'splat'
|
1074
|
+
"(.*?)"
|
1075
|
+
else
|
1076
|
+
keys << $2[1..-1]
|
1077
|
+
"([^/?#]+)"
|
1174
1078
|
end
|
1079
|
+
end
|
1175
1080
|
[/^#{pattern}$/, keys]
|
1176
1081
|
elsif path.respond_to?(:keys) && path.respond_to?(:match)
|
1177
1082
|
[path, path.keys]
|
@@ -1184,11 +1089,18 @@ module Sinatra
|
|
1184
1089
|
end
|
1185
1090
|
end
|
1186
1091
|
|
1092
|
+
def encoded(char)
|
1093
|
+
enc = URI.encode(char)
|
1094
|
+
enc = "(?:#{Regexp.escape enc}|#{URI.encode char, /./})" if enc == char
|
1095
|
+
enc = "(?:#{enc}|#{encoded('+')})" if char == " "
|
1096
|
+
enc
|
1097
|
+
end
|
1098
|
+
|
1187
1099
|
public
|
1188
1100
|
# Makes the methods defined in the block and in the Modules given
|
1189
1101
|
# in `extensions` available to the handlers and templates
|
1190
1102
|
def helpers(*extensions, &block)
|
1191
|
-
class_eval(&block)
|
1103
|
+
class_eval(&block) if block_given?
|
1192
1104
|
include(*extensions) if extensions.any?
|
1193
1105
|
end
|
1194
1106
|
|
@@ -1222,7 +1134,7 @@ module Sinatra
|
|
1222
1134
|
def quit!(server, handler_name)
|
1223
1135
|
# Use Thin's hard #stop! if available, otherwise just #stop.
|
1224
1136
|
server.respond_to?(:stop!) ? server.stop! : server.stop
|
1225
|
-
puts "\n== Sinatra has ended his set (crowd applauds)" unless handler_name =~/cgi/i
|
1137
|
+
STDERR.puts "\n== Sinatra has ended his set (crowd applauds)" unless handler_name =~/cgi/i
|
1226
1138
|
end
|
1227
1139
|
|
1228
1140
|
# Run the Sinatra app as a self-hosted server using
|
@@ -1231,14 +1143,14 @@ module Sinatra
|
|
1231
1143
|
set options
|
1232
1144
|
handler = detect_rack_handler
|
1233
1145
|
handler_name = handler.name.gsub(/.*::/, '')
|
1234
|
-
puts "== Sinatra/#{Sinatra::VERSION} has taken the stage " +
|
1146
|
+
STDERR.puts "== Sinatra/#{Sinatra::VERSION} has taken the stage " +
|
1235
1147
|
"on #{port} for #{environment} with backup from #{handler_name}" unless handler_name =~/cgi/i
|
1236
1148
|
handler.run self, :Host => bind, :Port => port do |server|
|
1237
1149
|
[:INT, :TERM].each { |sig| trap(sig) { quit!(server, handler_name) } }
|
1238
1150
|
set :running, true
|
1239
1151
|
end
|
1240
1152
|
rescue Errno::EADDRINUSE => e
|
1241
|
-
puts "== Someone is already performing on port #{port}!"
|
1153
|
+
STDERR.puts "== Someone is already performing on port #{port}!"
|
1242
1154
|
end
|
1243
1155
|
|
1244
1156
|
# The prototype instance used to process requests.
|
@@ -1253,19 +1165,14 @@ module Sinatra
|
|
1253
1165
|
# pipeline. The object is guaranteed to respond to #call but may not be
|
1254
1166
|
# an instance of the class new was called on.
|
1255
1167
|
def new(*args, &bk)
|
1256
|
-
build(*args, &bk).to_app
|
1168
|
+
build(Rack::Builder.new, *args, &bk).to_app
|
1257
1169
|
end
|
1258
1170
|
|
1259
1171
|
# Creates a Rack::Builder instance with all the middleware set up and
|
1260
1172
|
# an instance of this class as end point.
|
1261
|
-
def build(*args, &bk)
|
1262
|
-
builder
|
1263
|
-
builder
|
1264
|
-
builder.use ShowExceptions if show_exceptions?
|
1265
|
-
builder.use Rack::Head
|
1266
|
-
setup_logging builder
|
1267
|
-
setup_sessions builder
|
1268
|
-
middleware.each { |c,a,b| builder.use(c, *a, &b) }
|
1173
|
+
def build(builder, *args, &bk)
|
1174
|
+
setup_default_middleware builder
|
1175
|
+
setup_middleware builder
|
1269
1176
|
builder.run new!(*args, &bk)
|
1270
1177
|
builder
|
1271
1178
|
end
|
@@ -1275,6 +1182,18 @@ module Sinatra
|
|
1275
1182
|
end
|
1276
1183
|
|
1277
1184
|
private
|
1185
|
+
def setup_default_middleware(builder)
|
1186
|
+
builder.use ShowExceptions if show_exceptions?
|
1187
|
+
builder.use Rack::MethodOverride if method_override?
|
1188
|
+
builder.use Rack::Head
|
1189
|
+
setup_logging builder
|
1190
|
+
setup_sessions builder
|
1191
|
+
end
|
1192
|
+
|
1193
|
+
def setup_middleware(builder)
|
1194
|
+
middleware.each { |c,a,b| builder.use(c, *a, &b) }
|
1195
|
+
end
|
1196
|
+
|
1278
1197
|
def setup_logging(builder)
|
1279
1198
|
if logging?
|
1280
1199
|
builder.use Rack::CommonLogger
|
@@ -1300,7 +1219,7 @@ module Sinatra
|
|
1300
1219
|
servers = Array(server)
|
1301
1220
|
servers.each do |server_name|
|
1302
1221
|
begin
|
1303
|
-
return Rack::Handler.get(server_name
|
1222
|
+
return Rack::Handler.get(server_name)
|
1304
1223
|
rescue LoadError
|
1305
1224
|
rescue NameError
|
1306
1225
|
end
|
@@ -1310,6 +1229,7 @@ module Sinatra
|
|
1310
1229
|
|
1311
1230
|
def inherited(subclass)
|
1312
1231
|
subclass.reset!
|
1232
|
+
subclass.set :app_file, caller_files.first unless subclass.app_file?
|
1313
1233
|
super
|
1314
1234
|
end
|
1315
1235
|
|
@@ -1322,16 +1242,11 @@ module Sinatra
|
|
1322
1242
|
end
|
1323
1243
|
end
|
1324
1244
|
|
1325
|
-
def metadef(message, &block)
|
1326
|
-
(class << self; self; end).
|
1327
|
-
send :define_method, message, &block
|
1328
|
-
end
|
1329
|
-
|
1330
1245
|
public
|
1331
1246
|
CALLERS_TO_IGNORE = [ # :nodoc:
|
1332
1247
|
/\/sinatra(\/(base|main|showexceptions))?\.rb$/, # all sinatra code
|
1333
1248
|
/lib\/tilt.*\.rb$/, # all tilt code
|
1334
|
-
|
1249
|
+
/^\(.*\)$/, # generated code
|
1335
1250
|
/rubygems\/custom_require\.rb$/, # rubygems require hacks
|
1336
1251
|
/active_support/, # active_support require hacks
|
1337
1252
|
/bundler(\/runtime)?\.rb/, # bundler require hacks
|
@@ -1368,7 +1283,7 @@ module Sinatra
|
|
1368
1283
|
def self.force_encoding(data, encoding = default_encoding)
|
1369
1284
|
return if data == settings || data.is_a?(Tempfile)
|
1370
1285
|
if data.respond_to? :force_encoding
|
1371
|
-
data.force_encoding
|
1286
|
+
data.force_encoding(encoding).encode!
|
1372
1287
|
elsif data.respond_to? :each_value
|
1373
1288
|
data.each_value { |v| force_encoding(v, encoding) }
|
1374
1289
|
elsif data.respond_to? :each
|
@@ -1390,10 +1305,17 @@ module Sinatra
|
|
1390
1305
|
set :logging, false
|
1391
1306
|
set :method_override, false
|
1392
1307
|
set :default_encoding, "utf-8"
|
1393
|
-
set :add_charset, [
|
1308
|
+
set :add_charset, %w[javascript xml xhtml+xml json].map { |t| "application/#{t}" }
|
1309
|
+
settings.add_charset << /^text\//
|
1394
1310
|
|
1395
|
-
# explicitly generating
|
1396
|
-
|
1311
|
+
# explicitly generating a session secret eagerly to play nice with preforking
|
1312
|
+
begin
|
1313
|
+
require 'securerandom'
|
1314
|
+
set :session_secret, SecureRandom.hex(64)
|
1315
|
+
rescue LoadError, NotImplementedError
|
1316
|
+
# SecureRandom raises a NotImplementedError if no random device is available
|
1317
|
+
set :session_secret, "%064x" % Kernel.rand(2**256-1)
|
1318
|
+
end
|
1397
1319
|
|
1398
1320
|
class << self
|
1399
1321
|
alias_method :methodoverride?, :method_override?
|
@@ -1418,6 +1340,7 @@ module Sinatra
|
|
1418
1340
|
|
1419
1341
|
set :public, Proc.new { root && File.join(root, 'public') }
|
1420
1342
|
set :static, Proc.new { public && File.exist?(public) }
|
1343
|
+
set :static_cache_control, false
|
1421
1344
|
|
1422
1345
|
error ::Exception do
|
1423
1346
|
response.status = 500
|
@@ -1464,13 +1387,14 @@ module Sinatra
|
|
1464
1387
|
#
|
1465
1388
|
# The Application class should not be subclassed, unless you want to
|
1466
1389
|
# inherit all settings, routes, handlers, and error pages from the
|
1467
|
-
# top-level. Subclassing Sinatra::Base is
|
1390
|
+
# top-level. Subclassing Sinatra::Base is highly recommended for
|
1468
1391
|
# modular applications.
|
1469
1392
|
class Application < Base
|
1470
1393
|
set :logging, Proc.new { ! test? }
|
1471
1394
|
set :method_override, true
|
1472
1395
|
set :run, Proc.new { ! test? }
|
1473
1396
|
set :session_secret, Proc.new { super() unless development? }
|
1397
|
+
set :app_file, nil
|
1474
1398
|
|
1475
1399
|
def self.register(*extensions, &block) #:nodoc:
|
1476
1400
|
added_methods = extensions.map {|m| m.public_instance_methods }.flatten
|