sinatra 1.2.9 → 1.3.0.a
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/CHANGES +12 -118
- data/Gemfile +23 -37
- data/LICENSE +1 -1
- data/README.de.rdoc +11 -15
- data/README.es.rdoc +50 -76
- data/README.fr.rdoc +7 -7
- data/README.hu.rdoc +1 -1
- data/README.jp.rdoc +2 -8
- data/README.pt-br.rdoc +1 -1
- data/README.pt-pt.rdoc +1 -1
- data/README.rdoc +64 -139
- data/README.ru.rdoc +618 -68
- data/README.zh.rdoc +7 -7
- data/Rakefile +6 -29
- data/lib/sinatra.rb +3 -0
- data/lib/sinatra/base.rb +115 -179
- data/lib/sinatra/main.rb +1 -1
- data/lib/sinatra/rack.rb +44 -0
- data/lib/sinatra/showexceptions.rb +3 -3
- data/sinatra.gemspec +5 -4
- data/test/coffee_test.rb +11 -15
- data/test/delegator_test.rb +3 -43
- data/test/helper.rb +0 -4
- data/test/helpers_test.rb +44 -37
- data/test/nokogiri_test.rb +6 -5
- data/test/result_test.rb +4 -4
- data/test/routing_test.rb +8 -132
- data/test/server_test.rb +2 -3
- data/test/settings_test.rb +0 -26
- data/test/slim_test.rb +25 -15
- data/test/static_test.rb +1 -0
- metadata +17 -67
- data/test/rack_test.rb +0 -45
data/README.zh.rdoc
CHANGED
@@ -843,8 +843,8 @@ Session被用来在请求之间保持状态。如果被激活,每一个用户
|
|
843
843
|
。简单的使用 +call+ 可以做到这一点:
|
844
844
|
|
845
845
|
get '/foo' do
|
846
|
-
status, headers, body = call env.merge("PATH_INFO" => '/bar')
|
847
|
-
[status,
|
846
|
+
status, headers, body = call request.env.merge("PATH_INFO" => '/bar')
|
847
|
+
[status, body.upcase]
|
848
848
|
end
|
849
849
|
|
850
850
|
get '/bar' do
|
@@ -884,7 +884,7 @@ Session被用来在请求之间保持状态。如果被激活,每一个用户
|
|
884
884
|
get '/foo' do
|
885
885
|
status 418
|
886
886
|
headers \
|
887
|
-
"Allow" => "BREW, POST, GET, PROPFIND, WHEN"
|
887
|
+
"Allow" => "BREW, POST, GET, PROPFIND, WHEN"
|
888
888
|
"Refresh" => "Refresh: 20; http://www.ietf.org/rfc/rfc2324.txt"
|
889
889
|
body "I'm a tea pot!"
|
890
890
|
end
|
@@ -948,7 +948,7 @@ Sinatra并不理解。使用 +mime_type+ 通过文件扩展名来注册它们:
|
|
948
948
|
|
949
949
|
或者使用session:
|
950
950
|
|
951
|
-
enable :
|
951
|
+
enable :session
|
952
952
|
|
953
953
|
get '/foo' do
|
954
954
|
session[:secret] = 'foo'
|
@@ -1314,7 +1314,7 @@ Sinatra会自动处理range请求。
|
|
1314
1314
|
自定义错误:
|
1315
1315
|
|
1316
1316
|
error MyCustomError do
|
1317
|
-
'So what happened was...' + env['sinatra.error'].message
|
1317
|
+
'So what happened was...' + request.env['sinatra.error'].message
|
1318
1318
|
end
|
1319
1319
|
|
1320
1320
|
那么,当这个发生的时候:
|
@@ -1500,7 +1500,7 @@ Sinatra::Base子类可用的方法实际上就是通过顶层 DSL
|
|
1500
1500
|
或者使用一个 <tt>config.ru</tt>,允许你使用任何Rack处理器:
|
1501
1501
|
|
1502
1502
|
# config.ru
|
1503
|
-
require '
|
1503
|
+
require 'my_app'
|
1504
1504
|
run MyApp
|
1505
1505
|
|
1506
1506
|
运行:
|
@@ -1520,7 +1520,7 @@ Sinatra::Base子类可用的方法实际上就是通过顶层 DSL
|
|
1520
1520
|
|
1521
1521
|
加入相应的 <tt>config.ru</tt>:
|
1522
1522
|
|
1523
|
-
require '
|
1523
|
+
require 'app'
|
1524
1524
|
run Sinatra::Application
|
1525
1525
|
|
1526
1526
|
=== 什么时候用 config.ru?
|
data/Rakefile
CHANGED
@@ -3,12 +3,6 @@ require 'rake/testtask'
|
|
3
3
|
require 'fileutils'
|
4
4
|
require 'date'
|
5
5
|
|
6
|
-
# CI Reporter is only needed for the CI
|
7
|
-
begin
|
8
|
-
require 'ci/reporter/rake/test_unit'
|
9
|
-
rescue LoadError
|
10
|
-
end
|
11
|
-
|
12
6
|
task :default => :test
|
13
7
|
task :spec => :test
|
14
8
|
|
@@ -43,16 +37,6 @@ Rake::TestTask.new(:test) do |t|
|
|
43
37
|
t.ruby_opts << '-I.'
|
44
38
|
end
|
45
39
|
|
46
|
-
Rake::TestTask.new(:"test:core") do |t|
|
47
|
-
core_tests = %w[base delegator encoding extensions filter
|
48
|
-
helpers mapped_error middleware radius rdoc
|
49
|
-
readme request response result route_added_hook
|
50
|
-
routing server settings sinatra static templates]
|
51
|
-
t.test_files = core_tests.map {|n| "test/#{n}_test.rb"}
|
52
|
-
t.ruby_opts = ["-rubygems"] if defined? Gem
|
53
|
-
t.ruby_opts << "-I."
|
54
|
-
end
|
55
|
-
|
56
40
|
# Rcov ================================================================
|
57
41
|
|
58
42
|
namespace :test do
|
@@ -83,7 +67,7 @@ task :add_template, [:name] do |t, args|
|
|
83
67
|
puts "Liquid not found in #{file}"
|
84
68
|
else
|
85
69
|
puts "Adding section to #{file}"
|
86
|
-
template = template.gsub(/Liquid/, args.name.capitalize).gsub(/liquid/, args.name.downcase)
|
70
|
+
template = template.gsub(/Liquid/, args.name.capitalize).gsub(/liquid/, args.name.downcase)
|
87
71
|
code.gsub! /^(\s*===.*CoffeeScript)/, "\n" << template << "\n\\1"
|
88
72
|
File.open(file, "w") { |f| f << code }
|
89
73
|
end
|
@@ -106,22 +90,18 @@ task :thanks, [:release,:backports] do |t, a|
|
|
106
90
|
"(based on commits included in #{a.release}, but not in #{a.backports})"
|
107
91
|
end
|
108
92
|
|
109
|
-
|
110
|
-
|
111
|
-
a.with_defaults :format => "%s (%d)", :sep => ", ", :commit_range => '--all'
|
93
|
+
task :authors, [:format, :sep] do |t, a|
|
94
|
+
a.with_defaults :format => "%s (%d)", :sep => ', '
|
112
95
|
authors = Hash.new { |h,k| h[k] = 0 }
|
113
96
|
blake = "Blake Mizerany"
|
114
|
-
overall = 0
|
115
97
|
mapping = {
|
116
98
|
"blake.mizerany@gmail.com" => blake, "bmizerany" => blake,
|
117
99
|
"a_user@mac.com" => blake, "ichverstehe" => "Harry Vangberg",
|
118
100
|
"Wu Jiang (nouse)" => "Wu Jiang" }
|
119
|
-
`git shortlog -s
|
101
|
+
`git shortlog -s`.lines.map do |line|
|
120
102
|
num, name = line.split("\t", 2).map(&:strip)
|
121
103
|
authors[mapping[name] || name] += num.to_i
|
122
|
-
overall += num.to_i
|
123
104
|
end
|
124
|
-
puts "#{overall} commits by #{authors.count} authors:"
|
125
105
|
puts authors.sort_by { |n,c| -c }.map { |e| a.format % e }.join(a.sep)
|
126
106
|
end
|
127
107
|
|
@@ -167,8 +147,9 @@ if defined?(Gem)
|
|
167
147
|
# read spec file and split out manifest section
|
168
148
|
spec = File.read(f.name)
|
169
149
|
head, manifest, tail = spec.split(" # = MANIFEST =\n")
|
170
|
-
# replace version
|
150
|
+
# replace version and date
|
171
151
|
head.sub!(/\.version = '.*'/, ".version = '#{source_version}'")
|
152
|
+
head.sub!(/\.date = '.*'/, ".date = '#{Date.today.to_s}'")
|
172
153
|
# determine file list from git ls-files
|
173
154
|
files = `git ls-files`.
|
174
155
|
split("\n").
|
@@ -185,10 +166,6 @@ if defined?(Gem)
|
|
185
166
|
end
|
186
167
|
|
187
168
|
task 'release' => ['test', package('.gem')] do
|
188
|
-
if File.read("CHANGES") =~ /= \d\.\d\.\d . not yet released$/i
|
189
|
-
fail 'please update changes first'
|
190
|
-
end
|
191
|
-
|
192
169
|
sh <<-SH
|
193
170
|
gem install #{package('.gem')} --local &&
|
194
171
|
gem push #{package('.gem')} &&
|
data/lib/sinatra.rb
CHANGED
data/lib/sinatra/base.rb
CHANGED
@@ -1,70 +1,38 @@
|
|
1
1
|
require 'thread'
|
2
2
|
require 'time'
|
3
3
|
require 'uri'
|
4
|
-
require 'rack'
|
5
|
-
require 'rack/builder'
|
4
|
+
require 'sinatra/rack'
|
6
5
|
require 'sinatra/showexceptions'
|
7
6
|
require 'tilt'
|
8
|
-
require 'backports'
|
9
|
-
|
10
|
-
warn "WARNING: Sinatra 1.2.x has reached its EOL. Please upgrade."
|
11
7
|
|
12
8
|
module Sinatra
|
13
|
-
VERSION = '1.
|
9
|
+
VERSION = '1.3.0.a'
|
14
10
|
|
15
11
|
# The request object. See Rack::Request for more info:
|
16
12
|
# http://rack.rubyforge.org/doc/classes/Rack/Request.html
|
17
13
|
class Request < Rack::Request
|
18
14
|
# Returns an array of acceptable media types for the response
|
19
15
|
def accept
|
20
|
-
@env['
|
21
|
-
entries = @env['HTTP_ACCEPT'].to_s.split(',')
|
22
|
-
entries.map { |e| accept_entry(e) }.sort_by { |e| e.last }.map { |e| e.first }
|
23
|
-
end
|
16
|
+
@env['HTTP_ACCEPT'].to_s.split(',').map { |a| a.split(';')[0].strip }
|
24
17
|
end
|
25
18
|
|
26
|
-
|
27
|
-
return accept.first if types.empty?
|
28
|
-
types.flatten!
|
29
|
-
accept.detect do |pattern|
|
30
|
-
type = types.detect { |t| File.fnmatch(pattern, t) }
|
31
|
-
return type if type
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
if Rack.release <= "1.2"
|
36
|
-
# Whether or not the web server (or a reverse proxy in front of it) is
|
37
|
-
# using SSL to communicate with the client.
|
38
|
-
def secure?
|
39
|
-
@env['HTTPS'] == 'on' or
|
40
|
-
@env['HTTP_X_FORWARDED_PROTO'] == 'https' or
|
41
|
-
@env['rack.url_scheme'] == 'https'
|
42
|
-
end
|
43
|
-
else
|
44
|
-
alias secure? ssl?
|
45
|
-
end
|
19
|
+
alias secure? ssl?
|
46
20
|
|
47
21
|
def forwarded?
|
48
22
|
@env.include? "HTTP_X_FORWARDED_HOST"
|
49
23
|
end
|
50
24
|
|
51
25
|
def route
|
52
|
-
@route ||=
|
26
|
+
@route ||= begin
|
27
|
+
path = Rack::Utils.unescape(path_info)
|
28
|
+
path.empty? ? "/" : path
|
29
|
+
end
|
53
30
|
end
|
54
31
|
|
55
32
|
def path_info=(value)
|
56
33
|
@route = nil
|
57
34
|
super
|
58
35
|
end
|
59
|
-
|
60
|
-
private
|
61
|
-
|
62
|
-
def accept_entry(entry)
|
63
|
-
type, *options = entry.gsub(/\s/, '').split(';')
|
64
|
-
quality = 0 # we sort smalles first
|
65
|
-
options.delete_if { |e| quality = 1 - e[2..-1].to_f if e =~ /^q=/ }
|
66
|
-
[type, [quality, type.count('*'), 1 - options.size]]
|
67
|
-
end
|
68
36
|
end
|
69
37
|
|
70
38
|
# The response object. See Rack::Response and Rack::ResponseHelpers for
|
@@ -72,16 +40,16 @@ module Sinatra
|
|
72
40
|
# http://rack.rubyforge.org/doc/classes/Rack/Response.html
|
73
41
|
# http://rack.rubyforge.org/doc/classes/Rack/Response/Helpers.html
|
74
42
|
class Response < Rack::Response
|
75
|
-
def finish
|
43
|
+
def finish
|
44
|
+
@body = block if block_given?
|
76
45
|
if [204, 304].include?(status.to_i)
|
77
46
|
header.delete "Content-Type"
|
78
|
-
header.delete "Content-Length"
|
79
47
|
[status.to_i, header.to_hash, []]
|
80
48
|
else
|
81
|
-
body =
|
82
|
-
body = [body] if
|
83
|
-
if
|
84
|
-
header["Content-Length"] = body.
|
49
|
+
body = @body || []
|
50
|
+
body = [body] if body.respond_to? :to_str
|
51
|
+
if body.respond_to?(:to_ary)
|
52
|
+
header["Content-Length"] = body.to_ary.
|
85
53
|
inject(0) { |len, part| len + Rack::Utils.bytesize(part) }.to_s
|
86
54
|
end
|
87
55
|
[status.to_i, header.to_hash, body]
|
@@ -130,7 +98,9 @@ module Sinatra
|
|
130
98
|
return addr if addr =~ /\A[A-z][A-z0-9\+\.\-]*:/
|
131
99
|
uri = [host = ""]
|
132
100
|
if absolute
|
133
|
-
host <<
|
101
|
+
host << 'http'
|
102
|
+
host << 's' if request.secure?
|
103
|
+
host << "://"
|
134
104
|
if request.forwarded? or request.port != (request.secure? ? 443 : 80)
|
135
105
|
host << request.host_with_port
|
136
106
|
else
|
@@ -168,6 +138,11 @@ module Sinatra
|
|
168
138
|
request.session
|
169
139
|
end
|
170
140
|
|
141
|
+
# Access shared logger object.
|
142
|
+
def logger
|
143
|
+
request.logger
|
144
|
+
end
|
145
|
+
|
171
146
|
# Look up a media type by file extension in Rack's mime registry.
|
172
147
|
def mime_type(type)
|
173
148
|
Base.mime_type(type)
|
@@ -175,8 +150,7 @@ module Sinatra
|
|
175
150
|
|
176
151
|
# Set the Content-Type of the response body given a media type or file
|
177
152
|
# extension.
|
178
|
-
def content_type(type
|
179
|
-
return response['Content-Type'] unless type
|
153
|
+
def content_type(type, params={})
|
180
154
|
default = params.delete :default
|
181
155
|
mime_type = mime_type(type) || default
|
182
156
|
fail "Unknown media type: %p" % type if mime_type.nil?
|
@@ -184,11 +158,7 @@ module Sinatra
|
|
184
158
|
unless params.include? :charset or settings.add_charset.all? { |p| not p === mime_type }
|
185
159
|
params[:charset] = params.delete('charset') || settings.default_encoding
|
186
160
|
end
|
187
|
-
params.
|
188
|
-
unless params.empty?
|
189
|
-
mime_type << (mime_type.include?(';') ? ', ' : ';')
|
190
|
-
mime_type << params.map { |kv| kv.join('=') }.join(', ')
|
191
|
-
end
|
161
|
+
mime_type << ";#{params.map { |kv| kv.join('=') }.join(', ')}" unless params.empty?
|
192
162
|
response['Content-Type'] = mime_type
|
193
163
|
end
|
194
164
|
|
@@ -241,11 +211,6 @@ module Sinatra
|
|
241
211
|
|
242
212
|
attr_accessor :range # a Range or nil
|
243
213
|
|
244
|
-
def initialize(*args)
|
245
|
-
super(*args)
|
246
|
-
@range = nil
|
247
|
-
end
|
248
|
-
|
249
214
|
# Checks for byte-ranges in the request and sets self.range appropriately.
|
250
215
|
# Returns false if the ranges are unsatisfiable and the request should return 416.
|
251
216
|
def parse_ranges(env, size)
|
@@ -287,10 +252,6 @@ module Sinatra
|
|
287
252
|
ranges
|
288
253
|
end
|
289
254
|
|
290
|
-
def close
|
291
|
-
super unless closed?
|
292
|
-
end
|
293
|
-
|
294
255
|
CHUNK_SIZE = 8192
|
295
256
|
|
296
257
|
def each
|
@@ -351,8 +312,8 @@ module Sinatra
|
|
351
312
|
def expires(amount, *values)
|
352
313
|
values << {} unless values.last.kind_of?(Hash)
|
353
314
|
|
354
|
-
if amount
|
355
|
-
time = Time.now + amount
|
315
|
+
if Integer === amount
|
316
|
+
time = Time.now + amount
|
356
317
|
max_age = amount
|
357
318
|
else
|
358
319
|
time = time_for amount
|
@@ -377,7 +338,7 @@ module Sinatra
|
|
377
338
|
time = time_for time
|
378
339
|
response['Last-Modified'] = time.httpdate
|
379
340
|
# compare based on seconds since epoch
|
380
|
-
halt 304 if Time.httpdate(env['HTTP_IF_MODIFIED_SINCE']).to_i >= time.to_i
|
341
|
+
halt 304 if Time.httpdate(request.env['HTTP_IF_MODIFIED_SINCE']).to_i >= time.to_i
|
381
342
|
rescue ArgumentError
|
382
343
|
end
|
383
344
|
|
@@ -416,7 +377,7 @@ module Sinatra
|
|
416
377
|
def time_for(value)
|
417
378
|
if value.respond_to? :to_time
|
418
379
|
value.to_time
|
419
|
-
elsif value
|
380
|
+
elsif Time === value
|
420
381
|
value
|
421
382
|
elsif value.respond_to? :new_offset
|
422
383
|
# DateTime#to_time does the same on 1.9
|
@@ -426,13 +387,13 @@ module Sinatra
|
|
426
387
|
elsif value.respond_to? :mday
|
427
388
|
# Date#to_time does the same on 1.9
|
428
389
|
Time.local(value.year, value.mon, value.mday)
|
429
|
-
elsif value
|
390
|
+
elsif Numeric === value
|
430
391
|
Time.at value
|
431
392
|
else
|
432
393
|
Time.parse value.to_s
|
433
394
|
end
|
434
395
|
rescue ArgumentError => boom
|
435
|
-
raise boom
|
396
|
+
raise boom.to_s
|
436
397
|
rescue Exception
|
437
398
|
raise ArgumentError, "unable to convert #{value.inspect} to a Time object"
|
438
399
|
end
|
@@ -461,11 +422,6 @@ module Sinatra
|
|
461
422
|
attr_accessor :content_type
|
462
423
|
end
|
463
424
|
|
464
|
-
def initialize
|
465
|
-
super
|
466
|
-
@default_layout = :layout
|
467
|
-
end
|
468
|
-
|
469
425
|
def erb(template, options={}, locals={})
|
470
426
|
render :erb, template, options, locals
|
471
427
|
end
|
@@ -539,9 +495,8 @@ module Sinatra
|
|
539
495
|
# Calls the given block for every possible template file in views,
|
540
496
|
# named name.ext, where ext is registered on engine.
|
541
497
|
def find_template(views, name, engine)
|
542
|
-
|
543
|
-
|
544
|
-
next unless ext != @preferred_extension and Array(engines).include? engine
|
498
|
+
Tilt.mappings.each do |ext, klass|
|
499
|
+
next unless klass == engine
|
545
500
|
yield ::File.join(views, "#{name}.#{ext}")
|
546
501
|
end
|
547
502
|
end
|
@@ -563,6 +518,7 @@ module Sinatra
|
|
563
518
|
# extract generic options
|
564
519
|
locals = options.delete(:locals) || locals || {}
|
565
520
|
views = options.delete(:views) || settings.views || "./views"
|
521
|
+
@default_layout = :layout if @default_layout.nil?
|
566
522
|
layout = options.delete(:layout)
|
567
523
|
eat_errors = layout.nil?
|
568
524
|
layout = @default_layout if layout.nil? or layout == true
|
@@ -571,14 +527,11 @@ module Sinatra
|
|
571
527
|
scope = options.delete(:scope) || self
|
572
528
|
|
573
529
|
# compile and render template
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
ensure
|
580
|
-
@default_layout = layout_was
|
581
|
-
end
|
530
|
+
layout_was = @default_layout
|
531
|
+
@default_layout = false
|
532
|
+
template = compile_template(engine, data, options, views)
|
533
|
+
output = template.render(scope, locals, &block)
|
534
|
+
@default_layout = layout_was
|
582
535
|
|
583
536
|
# render layout
|
584
537
|
if layout
|
@@ -598,13 +551,12 @@ module Sinatra
|
|
598
551
|
|
599
552
|
case
|
600
553
|
when data.is_a?(Symbol)
|
601
|
-
body, path, line =
|
554
|
+
body, path, line = self.class.templates[data]
|
602
555
|
if body
|
603
556
|
body = body.call if body.respond_to?(:call)
|
604
557
|
template.new(path, line.to_i, options) { body }
|
605
558
|
else
|
606
559
|
found = false
|
607
|
-
@preferred_extension = engine.to_s
|
608
560
|
find_template(views, data, template) do |file|
|
609
561
|
path ||= file # keep the initial path rather than the last one
|
610
562
|
if found = File.exists?(file)
|
@@ -617,7 +569,7 @@ module Sinatra
|
|
617
569
|
end
|
618
570
|
when data.is_a?(Proc) || data.is_a?(String)
|
619
571
|
body = data.is_a?(String) ? Proc.new { data } : data
|
620
|
-
path, line =
|
572
|
+
path, line = self.class.caller_locations.first
|
621
573
|
template.new(path, line.to_i, options, &body)
|
622
574
|
else
|
623
575
|
raise ArgumentError
|
@@ -636,7 +588,6 @@ module Sinatra
|
|
636
588
|
attr_reader :template_cache
|
637
589
|
|
638
590
|
def initialize(app=nil)
|
639
|
-
super()
|
640
591
|
@app = app
|
641
592
|
@template_cache = Tilt::Cache.new
|
642
593
|
yield self if block_given?
|
@@ -662,14 +613,24 @@ module Sinatra
|
|
662
613
|
invoke { dispatch! }
|
663
614
|
invoke { error_block!(response.status) }
|
664
615
|
unless @response['Content-Type']
|
665
|
-
if
|
616
|
+
if body.respond_to?(:to_ary) and body.first.respond_to? :content_type
|
666
617
|
content_type body.first.content_type
|
667
618
|
else
|
668
619
|
content_type :html
|
669
620
|
end
|
670
621
|
end
|
671
622
|
|
672
|
-
@response.finish
|
623
|
+
status, header, body = @response.finish
|
624
|
+
|
625
|
+
# Never produce a body on HEAD requests. Do retain the Content-Length
|
626
|
+
# unless it's "0", in which case we assume it was calculated erroneously
|
627
|
+
# for a manual HEAD response and remove it entirely.
|
628
|
+
if @env['REQUEST_METHOD'] == 'HEAD'
|
629
|
+
body = []
|
630
|
+
header.delete('Content-Length') if header['Content-Length'] == '0'
|
631
|
+
end
|
632
|
+
|
633
|
+
[status, header, body]
|
673
634
|
end
|
674
635
|
|
675
636
|
# Access settings defined with Base.set.
|
@@ -704,7 +665,7 @@ module Sinatra
|
|
704
665
|
# Forward the request to the downstream app -- middleware only.
|
705
666
|
def forward
|
706
667
|
fail "downstream app not set" unless @app.respond_to? :call
|
707
|
-
status, headers, body = @app.call
|
668
|
+
status, headers, body = @app.call(@request.env)
|
708
669
|
@response.status = status
|
709
670
|
@response.body = body
|
710
671
|
@response.headers.merge! headers
|
@@ -713,13 +674,13 @@ module Sinatra
|
|
713
674
|
|
714
675
|
private
|
715
676
|
# Run filters defined on the class and all superclasses.
|
716
|
-
def filter!(type, base =
|
677
|
+
def filter!(type, base = self.class)
|
717
678
|
filter! type, base.superclass if base.superclass.respond_to?(:filters)
|
718
679
|
base.filters[type].each { |block| instance_eval(&block) }
|
719
680
|
end
|
720
681
|
|
721
682
|
# Run routes defined on the class and all superclasses.
|
722
|
-
def route!(base
|
683
|
+
def route!(base=self.class, pass_block=nil)
|
723
684
|
if routes = base.routes[@request.request_method]
|
724
685
|
routes.each do |pattern, keys, conditions, block|
|
725
686
|
pass_block = process_route(pattern, keys, conditions) do
|
@@ -749,9 +710,7 @@ module Sinatra
|
|
749
710
|
# Returns pass block.
|
750
711
|
def process_route(pattern, keys, conditions)
|
751
712
|
@original_params ||= @params
|
752
|
-
|
753
|
-
route = '/' if route.empty? and not settings.empty_path_info?
|
754
|
-
if match = pattern.match(route)
|
713
|
+
if match = pattern.match(@request.route)
|
755
714
|
values = match.captures.to_a
|
756
715
|
params =
|
757
716
|
if keys.any?
|
@@ -800,8 +759,7 @@ module Sinatra
|
|
800
759
|
public_dir = File.expand_path(public_dir)
|
801
760
|
|
802
761
|
path = File.expand_path(public_dir + unescape(request.path_info))
|
803
|
-
return
|
804
|
-
return unless File.file?(path)
|
762
|
+
return unless path.start_with?(public_dir) and File.file?(path)
|
805
763
|
|
806
764
|
env['sinatra.static_file'] = path
|
807
765
|
send_file path, :disposition => nil
|
@@ -822,14 +780,15 @@ module Sinatra
|
|
822
780
|
end
|
823
781
|
|
824
782
|
# Run the block with 'throw :halt' support and apply result to the response.
|
825
|
-
def invoke
|
826
|
-
res = catch(:halt) {
|
783
|
+
def invoke(&block)
|
784
|
+
res = catch(:halt) { instance_eval(&block) }
|
827
785
|
return if res.nil?
|
828
786
|
|
829
787
|
case
|
830
788
|
when res.respond_to?(:to_str)
|
831
789
|
@response.body = [res]
|
832
|
-
when
|
790
|
+
when res.respond_to?(:to_ary)
|
791
|
+
res = res.to_ary
|
833
792
|
if Fixnum === res.first
|
834
793
|
if res.length == 3
|
835
794
|
@response.status, headers, body = res
|
@@ -895,7 +854,7 @@ module Sinatra
|
|
895
854
|
# Find an custom error block for the key(s) specified.
|
896
855
|
def error_block!(*keys)
|
897
856
|
keys.each do |key|
|
898
|
-
base =
|
857
|
+
base = self.class
|
899
858
|
while base.respond_to?(:errors)
|
900
859
|
if block = base.errors[key]
|
901
860
|
# found a handler, eval and return result
|
@@ -956,15 +915,14 @@ module Sinatra
|
|
956
915
|
|
957
916
|
# Sets an option to the given value. If the value is a proc,
|
958
917
|
# the proc will be called every time the option is accessed.
|
959
|
-
def set(option, value
|
960
|
-
raise ArgumentError if block
|
918
|
+
def set(option, value=self, &block)
|
919
|
+
raise ArgumentError if block && value != self
|
961
920
|
value = block if block
|
962
921
|
if value.kind_of?(Proc)
|
963
922
|
metadef(option, &value)
|
964
923
|
metadef("#{option}?") { !!__send__(option) }
|
965
924
|
metadef("#{option}=") { |val| metadef(option, &Proc.new{val}) }
|
966
|
-
elsif
|
967
|
-
raise ArgumentError unless option.respond_to?(:each)
|
925
|
+
elsif value == self && option.respond_to?(:each)
|
968
926
|
option.each { |k,v| set(k, v) }
|
969
927
|
elsif respond_to?("#{option}=")
|
970
928
|
__send__ "#{option}=", value
|
@@ -1048,14 +1006,6 @@ module Sinatra
|
|
1048
1006
|
Rack::Mime::MIME_TYPES[type] = value
|
1049
1007
|
end
|
1050
1008
|
|
1051
|
-
# provides all mime types matching type, including deprecated types:
|
1052
|
-
# mime_types :html # => ['text/html']
|
1053
|
-
# mime_types :js # => ['application/javascript', 'text/javascript']
|
1054
|
-
def mime_types(type)
|
1055
|
-
type = mime_type type
|
1056
|
-
type =~ /^application\/(xml|javascript)$/ ? [type, "text/#$1"] : [type]
|
1057
|
-
end
|
1058
|
-
|
1059
1009
|
# Define a before filter; runs before all requests within the same
|
1060
1010
|
# context as route handlers and may access/modify the request and
|
1061
1011
|
# response.
|
@@ -1086,16 +1036,7 @@ module Sinatra
|
|
1086
1036
|
@conditions << block
|
1087
1037
|
end
|
1088
1038
|
|
1089
|
-
|
1090
|
-
set :public_folder, value
|
1091
|
-
end
|
1092
|
-
|
1093
|
-
def public(*)
|
1094
|
-
super
|
1095
|
-
public_folder
|
1096
|
-
end
|
1097
|
-
|
1098
|
-
private
|
1039
|
+
private
|
1099
1040
|
# Condition for matching host name. Parameter might be String or Regexp.
|
1100
1041
|
def host_name(pattern)
|
1101
1042
|
condition { pattern === request.host }
|
@@ -1117,11 +1058,12 @@ module Sinatra
|
|
1117
1058
|
|
1118
1059
|
# Condition for matching mimetypes. Accepts file extensions.
|
1119
1060
|
def provides(*types)
|
1120
|
-
types.map! { |t|
|
1121
|
-
|
1061
|
+
types.map! { |t| mime_type(t) }
|
1062
|
+
|
1122
1063
|
condition do
|
1123
|
-
|
1124
|
-
|
1064
|
+
matching_types = (request.accept & types)
|
1065
|
+
unless matching_types.empty?
|
1066
|
+
content_type matching_types.first
|
1125
1067
|
true
|
1126
1068
|
else
|
1127
1069
|
false
|
@@ -1145,12 +1087,12 @@ module Sinatra
|
|
1145
1087
|
def delete(path, opts={}, &bk) route 'DELETE', path, opts, &bk end
|
1146
1088
|
def head(path, opts={}, &bk) route 'HEAD', path, opts, &bk end
|
1147
1089
|
def options(path, opts={}, &bk) route 'OPTIONS', path, opts, &bk end
|
1090
|
+
def patch(path, opts={}, &bk) route 'PATCH', path, opts, &bk end
|
1148
1091
|
|
1149
1092
|
private
|
1150
1093
|
def route(verb, path, options={}, &block)
|
1151
1094
|
# Because of self.options.host
|
1152
1095
|
host_name(options.delete(:host)) if options.key?(:host)
|
1153
|
-
enable :empty_path_info if path == "" and empty_path_info.nil?
|
1154
1096
|
|
1155
1097
|
block, pattern, keys, conditions = compile! verb, path, block, options
|
1156
1098
|
invoke_hook(:route_added, verb, path, block)
|
@@ -1212,7 +1154,7 @@ module Sinatra
|
|
1212
1154
|
# Makes the methods defined in the block and in the Modules given
|
1213
1155
|
# in `extensions` available to the handlers and templates
|
1214
1156
|
def helpers(*extensions, &block)
|
1215
|
-
class_eval(&block)
|
1157
|
+
class_eval(&block) if block_given?
|
1216
1158
|
include(*extensions) if extensions.any?
|
1217
1159
|
end
|
1218
1160
|
|
@@ -1246,7 +1188,7 @@ module Sinatra
|
|
1246
1188
|
def quit!(server, handler_name)
|
1247
1189
|
# Use Thin's hard #stop! if available, otherwise just #stop.
|
1248
1190
|
server.respond_to?(:stop!) ? server.stop! : server.stop
|
1249
|
-
|
1191
|
+
puts "\n== Sinatra has ended his set (crowd applauds)" unless handler_name =~/cgi/i
|
1250
1192
|
end
|
1251
1193
|
|
1252
1194
|
# Run the Sinatra app as a self-hosted server using
|
@@ -1255,14 +1197,14 @@ module Sinatra
|
|
1255
1197
|
set options
|
1256
1198
|
handler = detect_rack_handler
|
1257
1199
|
handler_name = handler.name.gsub(/.*::/, '')
|
1258
|
-
|
1200
|
+
puts "== Sinatra/#{Sinatra::VERSION} has taken the stage " +
|
1259
1201
|
"on #{port} for #{environment} with backup from #{handler_name}" unless handler_name =~/cgi/i
|
1260
1202
|
handler.run self, :Host => bind, :Port => port do |server|
|
1261
1203
|
[:INT, :TERM].each { |sig| trap(sig) { quit!(server, handler_name) } }
|
1262
1204
|
set :running, true
|
1263
1205
|
end
|
1264
1206
|
rescue Errno::EADDRINUSE => e
|
1265
|
-
|
1207
|
+
puts "== Someone is already performing on port #{port}!"
|
1266
1208
|
end
|
1267
1209
|
|
1268
1210
|
# The prototype instance used to process requests.
|
@@ -1284,11 +1226,10 @@ module Sinatra
|
|
1284
1226
|
# an instance of this class as end point.
|
1285
1227
|
def build(*args, &bk)
|
1286
1228
|
builder = Rack::Builder.new
|
1229
|
+
setup_logging builder
|
1230
|
+
setup_sessions builder
|
1287
1231
|
builder.use Rack::MethodOverride if method_override?
|
1288
1232
|
builder.use ShowExceptions if show_exceptions?
|
1289
|
-
builder.use Rack::CommonLogger if logging?
|
1290
|
-
builder.use Rack::Head
|
1291
|
-
setup_sessions builder
|
1292
1233
|
middleware.each { |c,a,b| builder.use(c, *a, &b) }
|
1293
1234
|
builder.run new!(*args, &bk)
|
1294
1235
|
builder
|
@@ -1299,16 +1240,31 @@ module Sinatra
|
|
1299
1240
|
end
|
1300
1241
|
|
1301
1242
|
private
|
1243
|
+
def setup_logging(builder)
|
1244
|
+
if logging?
|
1245
|
+
builder.use Rack::CommonLogger
|
1246
|
+
if logging.respond_to? :to_int
|
1247
|
+
builder.use Rack::Logger, logging
|
1248
|
+
else
|
1249
|
+
builder.use Rack::Logger
|
1250
|
+
end
|
1251
|
+
else
|
1252
|
+
builder.use Rack::NullLogger
|
1253
|
+
end
|
1254
|
+
end
|
1255
|
+
|
1302
1256
|
def setup_sessions(builder)
|
1303
1257
|
return unless sessions?
|
1304
|
-
|
1258
|
+
options = { :secret => session_secret }
|
1259
|
+
options.merge! sessions.to_hash if sessions.respond_to? :to_hash
|
1260
|
+
builder.use Rack::Session::Cookie, options
|
1305
1261
|
end
|
1306
1262
|
|
1307
1263
|
def detect_rack_handler
|
1308
1264
|
servers = Array(server)
|
1309
1265
|
servers.each do |server_name|
|
1310
1266
|
begin
|
1311
|
-
return Rack::Handler.get(server_name)
|
1267
|
+
return Rack::Handler.get(server_name.downcase)
|
1312
1268
|
rescue LoadError
|
1313
1269
|
rescue NameError
|
1314
1270
|
end
|
@@ -1339,18 +1295,15 @@ module Sinatra
|
|
1339
1295
|
CALLERS_TO_IGNORE = [ # :nodoc:
|
1340
1296
|
/\/sinatra(\/(base|main|showexceptions))?\.rb$/, # all sinatra code
|
1341
1297
|
/lib\/tilt.*\.rb$/, # all tilt code
|
1342
|
-
|
1298
|
+
/\(.*\)/, # generated code
|
1343
1299
|
/rubygems\/custom_require\.rb$/, # rubygems require hacks
|
1344
1300
|
/active_support/, # active_support require hacks
|
1345
1301
|
/bundler(\/runtime)?\.rb/, # bundler require hacks
|
1346
1302
|
/<internal:/ # internal in ruby >= 1.9.2
|
1347
1303
|
]
|
1348
1304
|
|
1349
|
-
#
|
1350
|
-
if defined?(RUBY_IGNORE_CALLERS)
|
1351
|
-
warn "RUBY_IGNORE_CALLERS is deprecated and will no longer be supported by Sinatra 2.0"
|
1352
|
-
CALLERS_TO_IGNORE.concat(RUBY_IGNORE_CALLERS)
|
1353
|
-
end
|
1305
|
+
# add rubinius (and hopefully other VM impls) ignore patterns ...
|
1306
|
+
CALLERS_TO_IGNORE.concat(RUBY_IGNORE_CALLERS) if defined?(RUBY_IGNORE_CALLERS)
|
1354
1307
|
|
1355
1308
|
# Like Kernel#caller but excluding certain magic entries and without
|
1356
1309
|
# line / method information; the resulting array contains filenames only.
|
@@ -1379,7 +1332,7 @@ module Sinatra
|
|
1379
1332
|
def self.force_encoding(data, encoding = default_encoding)
|
1380
1333
|
return if data == settings || data.is_a?(Tempfile)
|
1381
1334
|
if data.respond_to? :force_encoding
|
1382
|
-
data.force_encoding
|
1335
|
+
data.force_encoding encoding
|
1383
1336
|
elsif data.respond_to? :each_value
|
1384
1337
|
data.each_value { |v| force_encoding(v, encoding) }
|
1385
1338
|
elsif data.respond_to? :each
|
@@ -1389,7 +1342,7 @@ module Sinatra
|
|
1389
1342
|
end
|
1390
1343
|
else
|
1391
1344
|
def self.force_encoding(data, *) data end
|
1392
|
-
|
1345
|
+
end
|
1393
1346
|
|
1394
1347
|
reset!
|
1395
1348
|
|
@@ -1401,8 +1354,7 @@ module Sinatra
|
|
1401
1354
|
set :logging, false
|
1402
1355
|
set :method_override, false
|
1403
1356
|
set :default_encoding, "utf-8"
|
1404
|
-
set :add_charset,
|
1405
|
-
settings.add_charset << /^text\//
|
1357
|
+
set :add_charset, [/^text\//, 'application/javascript', 'application/xml', 'application/xhtml+xml']
|
1406
1358
|
|
1407
1359
|
# explicitly generating this eagerly to play nice with preforking
|
1408
1360
|
set :session_secret, '%x' % rand(2**255)
|
@@ -1420,15 +1372,14 @@ module Sinatra
|
|
1420
1372
|
|
1421
1373
|
set :absolute_redirects, true
|
1422
1374
|
set :prefixed_redirects, false
|
1423
|
-
set :empty_path_info, nil
|
1424
1375
|
|
1425
1376
|
set :app_file, nil
|
1426
1377
|
set :root, Proc.new { app_file && File.expand_path(File.dirname(app_file)) }
|
1427
1378
|
set :views, Proc.new { root && File.join(root, 'views') }
|
1428
|
-
set :reload_templates, Proc.new { development?
|
1379
|
+
set :reload_templates, Proc.new { development? }
|
1429
1380
|
set :lock, false
|
1430
1381
|
|
1431
|
-
set :
|
1382
|
+
set :public, Proc.new { root && File.join(root, 'public') }
|
1432
1383
|
set :static, Proc.new { public && File.exist?(public) }
|
1433
1384
|
|
1434
1385
|
error ::Exception do
|
@@ -1458,8 +1409,8 @@ module Sinatra
|
|
1458
1409
|
</style>
|
1459
1410
|
</head>
|
1460
1411
|
<body>
|
1461
|
-
<h2>Sinatra doesn
|
1462
|
-
<img src='
|
1412
|
+
<h2>Sinatra doesn't know this ditty.</h2>
|
1413
|
+
<img src='/__sinatra__/404.png'>
|
1463
1414
|
<div id="c">
|
1464
1415
|
Try this:
|
1465
1416
|
<pre>#{request.request_method.downcase} '#{request.path_info}' do\n "Hello World"\nend</pre>
|
@@ -1476,13 +1427,12 @@ module Sinatra
|
|
1476
1427
|
#
|
1477
1428
|
# The Application class should not be subclassed, unless you want to
|
1478
1429
|
# inherit all settings, routes, handlers, and error pages from the
|
1479
|
-
# top-level. Subclassing Sinatra::Base is
|
1430
|
+
# top-level. Subclassing Sinatra::Base is heavily recommended for
|
1480
1431
|
# modular applications.
|
1481
1432
|
class Application < Base
|
1482
1433
|
set :logging, Proc.new { ! test? }
|
1483
1434
|
set :method_override, true
|
1484
1435
|
set :run, Proc.new { ! test? }
|
1485
|
-
set :session_secret, Proc.new { super() unless development? }
|
1486
1436
|
|
1487
1437
|
def self.register(*extensions, &block) #:nodoc:
|
1488
1438
|
added_methods = extensions.map {|m| m.public_instance_methods }.flatten
|
@@ -1495,32 +1445,18 @@ module Sinatra
|
|
1495
1445
|
# methods to be delegated to the Sinatra::Application class. Used primarily
|
1496
1446
|
# at the top-level.
|
1497
1447
|
module Delegator #:nodoc:
|
1498
|
-
TEMPLATE = <<-RUBY
|
1499
|
-
def %1$s(*args, &b)
|
1500
|
-
return super if respond_to? :%1$s
|
1501
|
-
::Sinatra::Delegator.target.send("%2$s", *args, &b)
|
1502
|
-
end
|
1503
|
-
RUBY
|
1504
|
-
|
1505
1448
|
def self.delegate(*methods)
|
1506
1449
|
methods.each do |method_name|
|
1507
|
-
|
1508
|
-
|
1509
|
-
|
1510
|
-
|
1511
|
-
|
1512
|
-
|
1513
|
-
rescue SyntaxError
|
1514
|
-
code = TEMPLATE % [:_delegate, method_name]
|
1515
|
-
eval code, binding, '(__DELEGATE__)', 1
|
1516
|
-
alias_method method_name, :_delegate
|
1517
|
-
undef_method :_delegate
|
1518
|
-
end
|
1519
|
-
private method_name
|
1450
|
+
eval <<-RUBY, binding, '(__DELEGATE__)', 1
|
1451
|
+
def #{method_name}(*args, &b)
|
1452
|
+
::Sinatra::Delegator.target.send(#{method_name.inspect}, *args, &b)
|
1453
|
+
end
|
1454
|
+
private #{method_name.inspect}
|
1455
|
+
RUBY
|
1520
1456
|
end
|
1521
1457
|
end
|
1522
1458
|
|
1523
|
-
delegate :get, :put, :post, :delete, :head, :options, :template, :layout,
|
1459
|
+
delegate :get, :patch, :put, :post, :delete, :head, :options, :template, :layout,
|
1524
1460
|
:before, :after, :error, :not_found, :configure, :set, :mime_type,
|
1525
1461
|
:enable, :disable, :use, :development?, :test?, :production?,
|
1526
1462
|
:helpers, :settings
|
@@ -1542,11 +1478,11 @@ module Sinatra
|
|
1542
1478
|
|
1543
1479
|
# Extend the top-level DSL with the modules provided.
|
1544
1480
|
def self.register(*extensions, &block)
|
1545
|
-
|
1481
|
+
Application.register(*extensions, &block)
|
1546
1482
|
end
|
1547
1483
|
|
1548
1484
|
# Include the helper modules provided in Sinatra's request context.
|
1549
1485
|
def self.helpers(*extensions, &block)
|
1550
|
-
|
1486
|
+
Application.helpers(*extensions, &block)
|
1551
1487
|
end
|
1552
1488
|
end
|