sinatra 1.2.1 → 1.2.2
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 +28 -0
- data/README.de.rdoc +467 -395
- data/README.es.rdoc +56 -9
- data/README.pt-pt.rdoc +646 -0
- data/README.rdoc +40 -0
- data/Rakefile +14 -4
- data/lib/sinatra/base.rb +63 -25
- data/lib/sinatra/showexceptions.rb +1 -1
- data/sinatra.gemspec +3 -2
- data/test/encoding_test.rb +2 -2
- data/test/helpers_test.rb +62 -0
- data/test/routing_test.rb +90 -3
- metadata +198 -197
data/README.rdoc
CHANGED
@@ -1584,6 +1584,45 @@ application (Rails/Ramaze/Camping/...):
|
|
1584
1584
|
get('/') { "Hello #{session['user_name']}." }
|
1585
1585
|
end
|
1586
1586
|
|
1587
|
+
=== Dynamic Application Creation
|
1588
|
+
|
1589
|
+
Sometimes you want to create new applications at runtime without having to
|
1590
|
+
assign them to a constant, you can do this with `Sinatra.new`:
|
1591
|
+
|
1592
|
+
require 'sinatra/base'
|
1593
|
+
my_app = Sinatra.new { get('/') { "hi" } }
|
1594
|
+
my_app.run!
|
1595
|
+
|
1596
|
+
It takes the application to inherit from as optional argument:
|
1597
|
+
|
1598
|
+
require 'sinatra/base'
|
1599
|
+
|
1600
|
+
controller = Sinatra.new do
|
1601
|
+
enable :logging
|
1602
|
+
helpers MyHelpers
|
1603
|
+
end
|
1604
|
+
|
1605
|
+
map('/a') do
|
1606
|
+
run Sinatra.new(controller) { get('/') { 'a' } }
|
1607
|
+
end
|
1608
|
+
|
1609
|
+
map('/b') do
|
1610
|
+
run Sinatra.new(controller) { get('/') { 'b' } }
|
1611
|
+
end
|
1612
|
+
|
1613
|
+
This is especially useful for testing Sinatra extensions or using Sinatra in
|
1614
|
+
your own library.
|
1615
|
+
|
1616
|
+
This also makes using Sinatra as middleware extremely easy:
|
1617
|
+
|
1618
|
+
require 'sinatra/base'
|
1619
|
+
|
1620
|
+
use Sinatra do
|
1621
|
+
get('/') { ... }
|
1622
|
+
end
|
1623
|
+
|
1624
|
+
run RailsProject::Application
|
1625
|
+
|
1587
1626
|
== Scopes and Binding
|
1588
1627
|
|
1589
1628
|
The scope you are currently in determines what methods and variables are
|
@@ -1616,6 +1655,7 @@ You have the application scope binding inside:
|
|
1616
1655
|
* Methods defined by extensions
|
1617
1656
|
* The block passed to +helpers+
|
1618
1657
|
* Procs/blocks used as value for +set+
|
1658
|
+
* The block passed to <tt>Sinatra.new</tt>
|
1619
1659
|
|
1620
1660
|
You can reach the scope object (the class) like this:
|
1621
1661
|
|
data/Rakefile
CHANGED
@@ -15,6 +15,15 @@ def source_version
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
+
def prev_feature
|
19
|
+
source_version.gsub(/^(\d\.)(\d+)\..*$/) { $1 + ($2.to_i - 1).to_s }
|
20
|
+
end
|
21
|
+
|
22
|
+
def prev_version
|
23
|
+
return prev_feature + '.0' if source_version.end_with? '.0'
|
24
|
+
source_version.gsub(/\d+$/) { |s| s.to_i - 1 }
|
25
|
+
end
|
26
|
+
|
18
27
|
# SPECS ===============================================================
|
19
28
|
|
20
29
|
task :test do
|
@@ -69,13 +78,14 @@ end
|
|
69
78
|
# Thanks in announcement ===============================================
|
70
79
|
|
71
80
|
team = ["Ryan Tomayko", "Blake Mizerany", "Simon Rozet", "Konstantin Haase"]
|
72
|
-
desc "list of contributors
|
81
|
+
desc "list of contributors"
|
73
82
|
task :thanks, [:release,:backports] do |t, a|
|
74
|
-
a.with_defaults :release => "
|
83
|
+
a.with_defaults :release => "#{prev_version}..HEAD",
|
84
|
+
:backports => "#{prev_feature}.0..#{prev_feature}.x"
|
75
85
|
included = `git log --format=format:"%aN\t%s" #{a.release}`.lines.to_a
|
76
86
|
excluded = `git log --format=format:"%aN\t%s" #{a.backports}`.lines.to_a
|
77
|
-
commits
|
78
|
-
authors
|
87
|
+
commits = (included - excluded).group_by { |c| c[/^[^\t]+/] }
|
88
|
+
authors = commits.keys.sort_by { |n| - commits[n].size } - team
|
79
89
|
puts authors[0..-2].join(', ') << " and " << authors.last,
|
80
90
|
"(based on commits included in #{a.release}, but not in #{a.backports})"
|
81
91
|
end
|
data/lib/sinatra/base.rb
CHANGED
@@ -7,14 +7,30 @@ require 'sinatra/showexceptions'
|
|
7
7
|
require 'tilt'
|
8
8
|
|
9
9
|
module Sinatra
|
10
|
-
VERSION = '1.2.
|
10
|
+
VERSION = '1.2.2'
|
11
11
|
|
12
12
|
# The request object. See Rack::Request for more info:
|
13
13
|
# http://rack.rubyforge.org/doc/classes/Rack/Request.html
|
14
14
|
class Request < Rack::Request
|
15
|
+
def self.new(env)
|
16
|
+
env['sinatra.request'] ||= super
|
17
|
+
end
|
18
|
+
|
15
19
|
# Returns an array of acceptable media types for the response
|
16
20
|
def accept
|
17
|
-
@env['
|
21
|
+
@env['sinatra.accept'] ||= begin
|
22
|
+
entries = @env['HTTP_ACCEPT'].to_s.split(',')
|
23
|
+
entries.map { |e| accept_entry(e) }.sort_by(&:last).map(&:first)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def preferred_type(*types)
|
28
|
+
return accept.first if types.empty?
|
29
|
+
types.flatten!
|
30
|
+
accept.detect do |pattern|
|
31
|
+
type = types.detect { |t| File.fnmatch(pattern, t) }
|
32
|
+
return type if type
|
33
|
+
end
|
18
34
|
end
|
19
35
|
|
20
36
|
if Rack.release <= "1.2"
|
@@ -34,16 +50,22 @@ module Sinatra
|
|
34
50
|
end
|
35
51
|
|
36
52
|
def route
|
37
|
-
@route ||=
|
38
|
-
path = Rack::Utils.unescape(path_info)
|
39
|
-
path.empty? ? "/" : path
|
40
|
-
end
|
53
|
+
@route ||= Rack::Utils.unescape(path_info)
|
41
54
|
end
|
42
55
|
|
43
56
|
def path_info=(value)
|
44
57
|
@route = nil
|
45
58
|
super
|
46
59
|
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def accept_entry(entry)
|
64
|
+
type, *options = entry.gsub(/\s/, '').split(';')
|
65
|
+
quality = 0 # we sort smalles first
|
66
|
+
options.delete_if { |e| quality = 1 - e[2..-1].to_f if e.start_with? 'q=' }
|
67
|
+
[type, [quality, type.count('*'), 1 - options.size]]
|
68
|
+
end
|
47
69
|
end
|
48
70
|
|
49
71
|
# The response object. See Rack::Response and Rack::ResponseHelpers for
|
@@ -99,14 +121,14 @@ module Sinatra
|
|
99
121
|
|
100
122
|
# According to RFC 2616 section 14.30, "the field value consists of a
|
101
123
|
# single absolute URI"
|
102
|
-
response['Location'] =
|
124
|
+
response['Location'] = uri(uri, settings.absolute_redirects?, settings.prefixed_redirects?)
|
103
125
|
halt(*args)
|
104
126
|
end
|
105
127
|
|
106
128
|
# Generates the absolute URI for a given path in the app.
|
107
129
|
# Takes Rack routers and reverse proxies into account.
|
108
130
|
def uri(addr = nil, absolute = true, add_script_name = true)
|
109
|
-
return addr if addr =~
|
131
|
+
return addr if addr =~ /\A[A-z][A-z0-9\+\.\-]*:/
|
110
132
|
uri = [host = ""]
|
111
133
|
if absolute
|
112
134
|
host << 'http'
|
@@ -156,7 +178,8 @@ module Sinatra
|
|
156
178
|
|
157
179
|
# Set the Content-Type of the response body given a media type or file
|
158
180
|
# extension.
|
159
|
-
def content_type(type, params={})
|
181
|
+
def content_type(type = nil, params={})
|
182
|
+
return response['Content-Type'] unless type
|
160
183
|
default = params.delete :default
|
161
184
|
mime_type = mime_type(type) || default
|
162
185
|
fail "Unknown media type: %p" % type if mime_type.nil?
|
@@ -164,7 +187,11 @@ module Sinatra
|
|
164
187
|
unless params.include? :charset or settings.add_charset.all? { |p| not p === mime_type }
|
165
188
|
params[:charset] = params.delete('charset') || settings.default_encoding
|
166
189
|
end
|
167
|
-
|
190
|
+
params.delete :charset if mime_type.include? 'charset'
|
191
|
+
unless params.empty?
|
192
|
+
mime_type << (mime_type.include?(';') ? ', ' : ';')
|
193
|
+
mime_type << params.map { |kv| kv.join('=') }.join(', ')
|
194
|
+
end
|
168
195
|
response['Content-Type'] = mime_type
|
169
196
|
end
|
170
197
|
|
@@ -557,7 +584,7 @@ module Sinatra
|
|
557
584
|
|
558
585
|
case
|
559
586
|
when data.is_a?(Symbol)
|
560
|
-
body, path, line =
|
587
|
+
body, path, line = settings.templates[data]
|
561
588
|
if body
|
562
589
|
body = body.call if body.respond_to?(:call)
|
563
590
|
template.new(path, line.to_i, options) { body }
|
@@ -575,7 +602,7 @@ module Sinatra
|
|
575
602
|
end
|
576
603
|
when data.is_a?(Proc) || data.is_a?(String)
|
577
604
|
body = data.is_a?(String) ? Proc.new { data } : data
|
578
|
-
path, line =
|
605
|
+
path, line = settings.caller_locations.first
|
579
606
|
template.new(path, line.to_i, options, &body)
|
580
607
|
else
|
581
608
|
raise ArgumentError
|
@@ -680,13 +707,13 @@ module Sinatra
|
|
680
707
|
|
681
708
|
private
|
682
709
|
# Run filters defined on the class and all superclasses.
|
683
|
-
def filter!(type, base =
|
710
|
+
def filter!(type, base = settings)
|
684
711
|
filter! type, base.superclass if base.superclass.respond_to?(:filters)
|
685
712
|
base.filters[type].each { |block| instance_eval(&block) }
|
686
713
|
end
|
687
714
|
|
688
715
|
# Run routes defined on the class and all superclasses.
|
689
|
-
def route!(base=
|
716
|
+
def route!(base = settings, pass_block=nil)
|
690
717
|
if routes = base.routes[@request.request_method]
|
691
718
|
routes.each do |pattern, keys, conditions, block|
|
692
719
|
pass_block = process_route(pattern, keys, conditions) do
|
@@ -716,7 +743,9 @@ module Sinatra
|
|
716
743
|
# Returns pass block.
|
717
744
|
def process_route(pattern, keys, conditions)
|
718
745
|
@original_params ||= @params
|
719
|
-
|
746
|
+
route = @request.route
|
747
|
+
route = '/' if route.empty? and not settings.empty_path_info?
|
748
|
+
if match = pattern.match(route)
|
720
749
|
values = match.captures.to_a
|
721
750
|
params =
|
722
751
|
if keys.any?
|
@@ -861,7 +890,7 @@ module Sinatra
|
|
861
890
|
# Find an custom error block for the key(s) specified.
|
862
891
|
def error_block!(*keys)
|
863
892
|
keys.each do |key|
|
864
|
-
base =
|
893
|
+
base = settings
|
865
894
|
while base.respond_to?(:errors)
|
866
895
|
if block = base.errors[key]
|
867
896
|
# found a handler, eval and return result
|
@@ -1013,6 +1042,14 @@ module Sinatra
|
|
1013
1042
|
Rack::Mime::MIME_TYPES[type] = value
|
1014
1043
|
end
|
1015
1044
|
|
1045
|
+
# provides all mime types matching type, including deprecated types:
|
1046
|
+
# mime_types :html # => ['text/html']
|
1047
|
+
# mime_types :js # => ['application/javascript', 'text/javascript']
|
1048
|
+
def mime_types(type)
|
1049
|
+
type = mime_type type
|
1050
|
+
type =~ /^application\/(xml|javascript)$/ ? [type, "text/#$1"] : [type]
|
1051
|
+
end
|
1052
|
+
|
1016
1053
|
# Define a before filter; runs before all requests within the same
|
1017
1054
|
# context as route handlers and may access/modify the request and
|
1018
1055
|
# response.
|
@@ -1065,12 +1102,11 @@ module Sinatra
|
|
1065
1102
|
|
1066
1103
|
# Condition for matching mimetypes. Accepts file extensions.
|
1067
1104
|
def provides(*types)
|
1068
|
-
types.map! { |t|
|
1069
|
-
|
1105
|
+
types.map! { |t| mime_types(t) }
|
1106
|
+
types.flatten!
|
1070
1107
|
condition do
|
1071
|
-
|
1072
|
-
|
1073
|
-
content_type matching_types.first
|
1108
|
+
if type = request.preferred_type(types)
|
1109
|
+
content_type(type)
|
1074
1110
|
true
|
1075
1111
|
else
|
1076
1112
|
false
|
@@ -1099,6 +1135,7 @@ module Sinatra
|
|
1099
1135
|
def route(verb, path, options={}, &block)
|
1100
1136
|
# Because of self.options.host
|
1101
1137
|
host_name(options.delete(:host)) if options.key?(:host)
|
1138
|
+
enable :empty_path_info if path == "" and empty_path_info.nil?
|
1102
1139
|
|
1103
1140
|
block, pattern, keys, conditions = compile! verb, path, block, options
|
1104
1141
|
invoke_hook(:route_added, verb, path, block)
|
@@ -1232,10 +1269,10 @@ module Sinatra
|
|
1232
1269
|
# an instance of this class as end point.
|
1233
1270
|
def build(*args, &bk)
|
1234
1271
|
builder = Rack::Builder.new
|
1272
|
+
builder.use Rack::MethodOverride if method_override?
|
1273
|
+
builder.use ShowExceptions if show_exceptions?
|
1274
|
+
builder.use Rack::CommonLogger if logging?
|
1235
1275
|
setup_sessions builder
|
1236
|
-
builder.use Rack::CommonLogger if logging?
|
1237
|
-
builder.use Rack::MethodOverride if method_override?
|
1238
|
-
builder.use ShowExceptions if show_exceptions?
|
1239
1276
|
middleware.each { |c,a,b| builder.use(c, *a, &b) }
|
1240
1277
|
builder.run new!(*args, &bk)
|
1241
1278
|
builder
|
@@ -1363,6 +1400,7 @@ module Sinatra
|
|
1363
1400
|
|
1364
1401
|
set :absolute_redirects, true
|
1365
1402
|
set :prefixed_redirects, false
|
1403
|
+
set :empty_path_info, nil
|
1366
1404
|
|
1367
1405
|
set :app_file, nil
|
1368
1406
|
set :root, Proc.new { app_file && File.expand_path(File.dirname(app_file)) }
|
@@ -1401,7 +1439,7 @@ module Sinatra
|
|
1401
1439
|
</head>
|
1402
1440
|
<body>
|
1403
1441
|
<h2>Sinatra doesn't know this ditty.</h2>
|
1404
|
-
<img src='/__sinatra__/404.png'>
|
1442
|
+
<img src='#{uri "/__sinatra__/404.png"}'>
|
1405
1443
|
<div id="c">
|
1406
1444
|
Try this:
|
1407
1445
|
<pre>#{request.request_method.downcase} '#{request.path_info}' do\n "Hello World"\nend</pre>
|
@@ -174,7 +174,7 @@ TEMPLATE = <<-HTML # :nodoc:
|
|
174
174
|
<body>
|
175
175
|
<div id="wrap">
|
176
176
|
<div id="header">
|
177
|
-
<img src="
|
177
|
+
<img src="<%= env['SCRIPT_NAME'] %>/__sinatra__/500.png" alt="application error" height="161" width="313" />
|
178
178
|
<div id="summary">
|
179
179
|
<h1><strong><%=h exception.class %></strong> at <strong><%=h path %>
|
180
180
|
</strong></h1>
|
data/sinatra.gemspec
CHANGED
@@ -3,8 +3,8 @@ Gem::Specification.new do |s|
|
|
3
3
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
4
4
|
|
5
5
|
s.name = 'sinatra'
|
6
|
-
s.version = '1.2.
|
7
|
-
s.date = '2011-
|
6
|
+
s.version = '1.2.2'
|
7
|
+
s.date = '2011-04-08'
|
8
8
|
|
9
9
|
s.description = "Classy web-development dressed in a DSL"
|
10
10
|
s.summary = "Classy web-development dressed in a DSL"
|
@@ -24,6 +24,7 @@ Gem::Specification.new do |s|
|
|
24
24
|
README.hu.rdoc
|
25
25
|
README.jp.rdoc
|
26
26
|
README.pt-br.rdoc
|
27
|
+
README.pt-pt.rdoc
|
27
28
|
README.rdoc
|
28
29
|
README.ru.rdoc
|
29
30
|
README.zh.rdoc
|
data/test/encoding_test.rb
CHANGED
@@ -10,11 +10,11 @@ class BaseTest < Test::Unit::TestCase
|
|
10
10
|
|
11
11
|
it 'allows unicode strings in ascii templates per default (1.9)' do
|
12
12
|
next unless defined? Encoding
|
13
|
-
@base.new
|
13
|
+
@base.new!.erb(File.read(@base.views + "/ascii.erb").encode("ASCII"), {}, :value => "åkej")
|
14
14
|
end
|
15
15
|
|
16
16
|
it 'allows ascii strings in unicode templates per default (1.9)' do
|
17
17
|
next unless defined? Encoding
|
18
|
-
@base.new
|
18
|
+
@base.new!.erb(:utf8, {}, :value => "Some Lyrics".encode("ASCII"))
|
19
19
|
end
|
20
20
|
end
|
data/test/helpers_test.rb
CHANGED
@@ -123,6 +123,34 @@ class HelpersTest < Test::Unit::TestCase
|
|
123
123
|
response = request.get('/', 'HTTP_X_FORWARDED_HOST' => 'example.com', 'SERVER_PORT' => '8080')
|
124
124
|
assert_equal 'http://example.com/foo', response['Location']
|
125
125
|
end
|
126
|
+
|
127
|
+
it 'accepts absolute URIs' do
|
128
|
+
mock_app do
|
129
|
+
get '/' do
|
130
|
+
redirect 'http://google.com'
|
131
|
+
fail 'redirect should halt'
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
get '/'
|
136
|
+
assert_equal 302, status
|
137
|
+
assert_equal '', body
|
138
|
+
assert_equal 'http://google.com', response['Location']
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'accepts absolute URIs with a different schema' do
|
142
|
+
mock_app do
|
143
|
+
get '/' do
|
144
|
+
redirect 'mailto:jsmith@example.com'
|
145
|
+
fail 'redirect should halt'
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
get '/'
|
150
|
+
assert_equal 302, status
|
151
|
+
assert_equal '', body
|
152
|
+
assert_equal 'mailto:jsmith@example.com', response['Location']
|
153
|
+
end
|
126
154
|
end
|
127
155
|
|
128
156
|
describe 'error' do
|
@@ -401,6 +429,28 @@ class HelpersTest < Test::Unit::TestCase
|
|
401
429
|
get '/'
|
402
430
|
assert tests_ran
|
403
431
|
end
|
432
|
+
|
433
|
+
it 'handles already present params' do
|
434
|
+
mock_app do
|
435
|
+
get '/' do
|
436
|
+
content_type 'foo/bar;level=1', :charset => 'utf-8'
|
437
|
+
'ok'
|
438
|
+
end
|
439
|
+
end
|
440
|
+
get '/'
|
441
|
+
assert_equal 'foo/bar;level=1, charset=utf-8', response['Content-Type']
|
442
|
+
end
|
443
|
+
|
444
|
+
it 'does not add charset if present' do
|
445
|
+
mock_app do
|
446
|
+
get '/' do
|
447
|
+
content_type 'text/plain;charset=utf-16'
|
448
|
+
'ok'
|
449
|
+
end
|
450
|
+
end
|
451
|
+
get '/'
|
452
|
+
assert_equal 'text/plain;charset=utf-16', response['Content-Type']
|
453
|
+
end
|
404
454
|
end
|
405
455
|
|
406
456
|
describe 'send_file' do
|
@@ -795,6 +845,18 @@ class HelpersTest < Test::Unit::TestCase
|
|
795
845
|
assert_equal 'http://example.org/foo/bar', body
|
796
846
|
end
|
797
847
|
|
848
|
+
it 'handles absolute URIs' do
|
849
|
+
mock_app { get('/') { uri 'http://google.com' }}
|
850
|
+
get '/'
|
851
|
+
assert_equal 'http://google.com', body
|
852
|
+
end
|
853
|
+
|
854
|
+
it 'handles different protocols' do
|
855
|
+
mock_app { get('/') { uri 'mailto:jsmith@example.com' }}
|
856
|
+
get '/'
|
857
|
+
assert_equal 'mailto:jsmith@example.com', body
|
858
|
+
end
|
859
|
+
|
798
860
|
it 'is aliased to #url' do
|
799
861
|
mock_app { get('/') { url }}
|
800
862
|
get '/'
|
data/test/routing_test.rb
CHANGED
@@ -93,12 +93,28 @@ class RoutingTest < Test::Unit::TestCase
|
|
93
93
|
assert_equal "<h1>Not Found</h1>", response.body
|
94
94
|
end
|
95
95
|
|
96
|
-
it 'matches empty PATH_INFO to "/"' do
|
97
|
-
mock_app
|
96
|
+
it 'matches empty PATH_INFO to "/" if no route is defined for ""' do
|
97
|
+
mock_app do
|
98
98
|
get '/' do
|
99
99
|
'worked'
|
100
100
|
end
|
101
|
-
|
101
|
+
end
|
102
|
+
|
103
|
+
get '/', {}, "PATH_INFO" => ""
|
104
|
+
assert ok?
|
105
|
+
assert_equal 'worked', body
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'matches empty PATH_INFO to "" if a route is defined for ""' do
|
109
|
+
mock_app do
|
110
|
+
get '/' do
|
111
|
+
'did not work'
|
112
|
+
end
|
113
|
+
|
114
|
+
get '' do
|
115
|
+
'worked'
|
116
|
+
end
|
117
|
+
end
|
102
118
|
|
103
119
|
get '/', {}, "PATH_INFO" => ""
|
104
120
|
assert ok?
|
@@ -724,6 +740,77 @@ class RoutingTest < Test::Unit::TestCase
|
|
724
740
|
assert_equal 'default', body
|
725
741
|
end
|
726
742
|
|
743
|
+
it 'respects user agent prefferences for the content type' do
|
744
|
+
mock_app { get('/', :provides => [:png, :html]) { content_type }}
|
745
|
+
get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.5,text/html;q=0.8' }
|
746
|
+
assert_body 'text/html;charset=utf-8'
|
747
|
+
get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.8,text/html;q=0.5' }
|
748
|
+
assert_body 'image/png'
|
749
|
+
end
|
750
|
+
|
751
|
+
it 'accepts generic types' do
|
752
|
+
mock_app do
|
753
|
+
get('/', :provides => :xml) { content_type }
|
754
|
+
get('/') { 'no match' }
|
755
|
+
end
|
756
|
+
get '/'
|
757
|
+
assert_body 'no match'
|
758
|
+
get '/', {}, { 'HTTP_ACCEPT' => 'foo/*' }
|
759
|
+
assert_body 'no match'
|
760
|
+
get '/', {}, { 'HTTP_ACCEPT' => 'application/*' }
|
761
|
+
assert_body 'application/xml;charset=utf-8'
|
762
|
+
get '/', {}, { 'HTTP_ACCEPT' => '*/*' }
|
763
|
+
assert_body 'application/xml;charset=utf-8'
|
764
|
+
end
|
765
|
+
|
766
|
+
it 'prefers concrete over partly generic types' do
|
767
|
+
mock_app { get('/', :provides => [:png, :html]) { content_type }}
|
768
|
+
get '/', {}, { 'HTTP_ACCEPT' => 'image/*, text/html' }
|
769
|
+
assert_body 'text/html;charset=utf-8'
|
770
|
+
get '/', {}, { 'HTTP_ACCEPT' => 'image/png, text/*' }
|
771
|
+
assert_body 'image/png'
|
772
|
+
end
|
773
|
+
|
774
|
+
it 'prefers concrete over fully generic types' do
|
775
|
+
mock_app { get('/', :provides => [:png, :html]) { content_type }}
|
776
|
+
get '/', {}, { 'HTTP_ACCEPT' => '*/*, text/html' }
|
777
|
+
assert_body 'text/html;charset=utf-8'
|
778
|
+
get '/', {}, { 'HTTP_ACCEPT' => 'image/png, */*' }
|
779
|
+
assert_body 'image/png'
|
780
|
+
end
|
781
|
+
|
782
|
+
it 'prefers partly generic over fully generic types' do
|
783
|
+
mock_app { get('/', :provides => [:png, :html]) { content_type }}
|
784
|
+
get '/', {}, { 'HTTP_ACCEPT' => '*/*, text/*' }
|
785
|
+
assert_body 'text/html;charset=utf-8'
|
786
|
+
get '/', {}, { 'HTTP_ACCEPT' => 'image/*, */*' }
|
787
|
+
assert_body 'image/png'
|
788
|
+
end
|
789
|
+
|
790
|
+
it 'respects quality with generic types' do
|
791
|
+
mock_app { get('/', :provides => [:png, :html]) { content_type }}
|
792
|
+
get '/', {}, { 'HTTP_ACCEPT' => 'image/*;q=1, text/html;q=0' }
|
793
|
+
assert_body 'image/png'
|
794
|
+
get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.5, text/*;q=0.7' }
|
795
|
+
assert_body 'text/html;charset=utf-8'
|
796
|
+
end
|
797
|
+
|
798
|
+
it 'accepts both text/javascript and application/javascript for js' do
|
799
|
+
mock_app { get('/', :provides => :js) { content_type }}
|
800
|
+
get '/', {}, { 'HTTP_ACCEPT' => 'application/javascript' }
|
801
|
+
assert_body 'application/javascript;charset=utf-8'
|
802
|
+
get '/', {}, { 'HTTP_ACCEPT' => 'text/javascript' }
|
803
|
+
assert_body 'text/javascript;charset=utf-8'
|
804
|
+
end
|
805
|
+
|
806
|
+
it 'accepts both text/xml and application/xml for xml' do
|
807
|
+
mock_app { get('/', :provides => :xml) { content_type }}
|
808
|
+
get '/', {}, { 'HTTP_ACCEPT' => 'application/xml' }
|
809
|
+
assert_body 'application/xml;charset=utf-8'
|
810
|
+
get '/', {}, { 'HTTP_ACCEPT' => 'text/xml' }
|
811
|
+
assert_body 'text/xml;charset=utf-8'
|
812
|
+
end
|
813
|
+
|
727
814
|
it 'passes a single url param as block parameters when one param is specified' do
|
728
815
|
mock_app {
|
729
816
|
get '/:foo' do |foo|
|