actionpack 4.2.11.3 → 5.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/CHANGELOG.md +379 -462
- data/MIT-LICENSE +1 -1
- data/README.rdoc +2 -3
- data/lib/abstract_controller.rb +0 -2
- data/lib/abstract_controller/base.rb +17 -32
- data/lib/abstract_controller/callbacks.rb +52 -19
- data/lib/abstract_controller/collector.rb +4 -9
- data/lib/abstract_controller/helpers.rb +2 -2
- data/lib/abstract_controller/railties/routes_helpers.rb +2 -2
- data/lib/abstract_controller/rendering.rb +27 -22
- data/lib/abstract_controller/translation.rb +8 -7
- data/lib/action_controller.rb +4 -3
- data/lib/action_controller/api.rb +146 -0
- data/lib/action_controller/base.rb +6 -10
- data/lib/action_controller/caching.rb +1 -3
- data/lib/action_controller/caching/fragments.rb +48 -3
- data/lib/action_controller/form_builder.rb +48 -0
- data/lib/action_controller/log_subscriber.rb +1 -10
- data/lib/action_controller/metal.rb +89 -62
- data/lib/action_controller/metal/basic_implicit_render.rb +11 -0
- data/lib/action_controller/metal/conditional_get.rb +65 -24
- data/lib/action_controller/metal/cookies.rb +0 -2
- data/lib/action_controller/metal/data_streaming.rb +2 -22
- data/lib/action_controller/metal/etag_with_template_digest.rb +1 -1
- data/lib/action_controller/metal/exceptions.rb +11 -6
- data/lib/action_controller/metal/force_ssl.rb +6 -6
- data/lib/action_controller/metal/head.rb +14 -7
- data/lib/action_controller/metal/helpers.rb +9 -5
- data/lib/action_controller/metal/http_authentication.rb +37 -38
- data/lib/action_controller/metal/implicit_render.rb +23 -6
- data/lib/action_controller/metal/instrumentation.rb +0 -1
- data/lib/action_controller/metal/live.rb +17 -55
- data/lib/action_controller/metal/mime_responds.rb +17 -37
- data/lib/action_controller/metal/params_wrapper.rb +8 -8
- data/lib/action_controller/metal/redirecting.rb +32 -9
- data/lib/action_controller/metal/renderers.rb +10 -8
- data/lib/action_controller/metal/rendering.rb +38 -6
- data/lib/action_controller/metal/request_forgery_protection.rb +67 -35
- data/lib/action_controller/metal/rescue.rb +2 -4
- data/lib/action_controller/metal/streaming.rb +4 -4
- data/lib/action_controller/metal/strong_parameters.rb +231 -78
- data/lib/action_controller/metal/testing.rb +1 -12
- data/lib/action_controller/metal/url_for.rb +12 -5
- data/lib/action_controller/renderer.rb +111 -0
- data/lib/action_controller/template_assertions.rb +9 -0
- data/lib/action_controller/test_case.rb +267 -363
- data/lib/action_dispatch.rb +2 -1
- data/lib/action_dispatch/http/cache.rb +23 -26
- data/lib/action_dispatch/http/filter_parameters.rb +6 -8
- data/lib/action_dispatch/http/filter_redirect.rb +7 -8
- data/lib/action_dispatch/http/headers.rb +28 -11
- data/lib/action_dispatch/http/mime_negotiation.rb +40 -26
- data/lib/action_dispatch/http/mime_type.rb +92 -61
- data/lib/action_dispatch/http/mime_types.rb +1 -4
- data/lib/action_dispatch/http/parameter_filter.rb +18 -8
- data/lib/action_dispatch/http/parameters.rb +45 -41
- data/lib/action_dispatch/http/request.rb +146 -82
- data/lib/action_dispatch/http/response.rb +180 -99
- data/lib/action_dispatch/http/url.rb +117 -8
- data/lib/action_dispatch/journey/formatter.rb +34 -28
- data/lib/action_dispatch/journey/gtg/transition_table.rb +1 -1
- data/lib/action_dispatch/journey/nfa/dot.rb +0 -2
- data/lib/action_dispatch/journey/nfa/transition_table.rb +1 -46
- data/lib/action_dispatch/journey/nodes/node.rb +14 -4
- data/lib/action_dispatch/journey/parser_extras.rb +4 -0
- data/lib/action_dispatch/journey/path/pattern.rb +37 -41
- data/lib/action_dispatch/journey/route.rb +71 -17
- data/lib/action_dispatch/journey/router.rb +5 -6
- data/lib/action_dispatch/journey/router/utils.rb +5 -5
- data/lib/action_dispatch/journey/routes.rb +14 -15
- data/lib/action_dispatch/journey/visitors.rb +86 -43
- data/lib/action_dispatch/middleware/cookies.rb +184 -135
- data/lib/action_dispatch/middleware/debug_exceptions.rb +115 -45
- data/lib/action_dispatch/middleware/exception_wrapper.rb +21 -20
- data/lib/action_dispatch/middleware/flash.rb +61 -45
- data/lib/action_dispatch/middleware/load_interlock.rb +21 -0
- data/lib/action_dispatch/middleware/params_parser.rb +30 -46
- data/lib/action_dispatch/middleware/public_exceptions.rb +2 -2
- data/lib/action_dispatch/middleware/reloader.rb +2 -4
- data/lib/action_dispatch/middleware/remote_ip.rb +29 -19
- data/lib/action_dispatch/middleware/request_id.rb +11 -6
- data/lib/action_dispatch/middleware/session/abstract_store.rb +23 -11
- data/lib/action_dispatch/middleware/session/cache_store.rb +9 -6
- data/lib/action_dispatch/middleware/session/cookie_store.rb +29 -23
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +4 -0
- data/lib/action_dispatch/middleware/show_exceptions.rb +11 -9
- data/lib/action_dispatch/middleware/ssl.rb +93 -36
- data/lib/action_dispatch/middleware/stack.rb +43 -48
- data/lib/action_dispatch/middleware/static.rb +52 -40
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +2 -14
- data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +0 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +59 -63
- data/lib/action_dispatch/railtie.rb +0 -2
- data/lib/action_dispatch/request/session.rb +66 -34
- data/lib/action_dispatch/request/utils.rb +51 -19
- data/lib/action_dispatch/routing.rb +3 -8
- data/lib/action_dispatch/routing/inspector.rb +6 -30
- data/lib/action_dispatch/routing/mapper.rb +447 -322
- data/lib/action_dispatch/routing/polymorphic_routes.rb +8 -14
- data/lib/action_dispatch/routing/redirection.rb +3 -3
- data/lib/action_dispatch/routing/route_set.rb +124 -227
- data/lib/action_dispatch/routing/url_for.rb +27 -10
- data/lib/action_dispatch/testing/assertions.rb +1 -1
- data/lib/action_dispatch/testing/assertions/response.rb +27 -9
- data/lib/action_dispatch/testing/assertions/routing.rb +9 -9
- data/lib/action_dispatch/testing/integration.rb +237 -76
- data/lib/action_dispatch/testing/test_process.rb +5 -5
- data/lib/action_dispatch/testing/test_request.rb +12 -21
- data/lib/action_dispatch/testing/test_response.rb +1 -4
- data/lib/action_pack.rb +1 -1
- data/lib/action_pack/gem_version.rb +4 -4
- metadata +26 -25
- data/lib/action_controller/metal/hide_actions.rb +0 -40
- data/lib/action_controller/metal/rack_delegation.rb +0 -32
- data/lib/action_controller/middleware.rb +0 -39
- data/lib/action_controller/model_naming.rb +0 -12
- data/lib/action_dispatch/journey/router/strexp.rb +0 -27
- data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
- data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
- data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
data/lib/action_dispatch.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#--
|
2
|
-
# Copyright (c) 2004-
|
2
|
+
# Copyright (c) 2004-2015 David Heinemeier Hansson
|
3
3
|
#
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
5
|
# a copy of this software and associated documentation files (the
|
@@ -52,6 +52,7 @@ module ActionDispatch
|
|
52
52
|
autoload :DebugExceptions
|
53
53
|
autoload :ExceptionWrapper
|
54
54
|
autoload :Flash
|
55
|
+
autoload :LoadInterlock
|
55
56
|
autoload :ParamsParser
|
56
57
|
autoload :PublicExceptions
|
57
58
|
autoload :Reloader
|
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
module ActionDispatch
|
3
2
|
module Http
|
4
3
|
module Cache
|
@@ -8,13 +7,13 @@ module ActionDispatch
|
|
8
7
|
HTTP_IF_NONE_MATCH = 'HTTP_IF_NONE_MATCH'.freeze
|
9
8
|
|
10
9
|
def if_modified_since
|
11
|
-
if since =
|
10
|
+
if since = get_header(HTTP_IF_MODIFIED_SINCE)
|
12
11
|
Time.rfc2822(since) rescue nil
|
13
12
|
end
|
14
13
|
end
|
15
14
|
|
16
15
|
def if_none_match
|
17
|
-
|
16
|
+
get_header HTTP_IF_NONE_MATCH
|
18
17
|
end
|
19
18
|
|
20
19
|
def if_none_match_etags
|
@@ -51,52 +50,51 @@ module ActionDispatch
|
|
51
50
|
end
|
52
51
|
|
53
52
|
module Response
|
54
|
-
attr_reader :cache_control
|
55
|
-
alias :etag? :etag
|
53
|
+
attr_reader :cache_control
|
56
54
|
|
57
55
|
def last_modified
|
58
|
-
if last =
|
56
|
+
if last = get_header(LAST_MODIFIED)
|
59
57
|
Time.httpdate(last)
|
60
58
|
end
|
61
59
|
end
|
62
60
|
|
63
61
|
def last_modified?
|
64
|
-
|
62
|
+
has_header? LAST_MODIFIED
|
65
63
|
end
|
66
64
|
|
67
65
|
def last_modified=(utc_time)
|
68
|
-
|
66
|
+
set_header LAST_MODIFIED, utc_time.httpdate
|
69
67
|
end
|
70
68
|
|
71
69
|
def date
|
72
|
-
if date_header =
|
70
|
+
if date_header = get_header(DATE)
|
73
71
|
Time.httpdate(date_header)
|
74
72
|
end
|
75
73
|
end
|
76
74
|
|
77
75
|
def date?
|
78
|
-
|
76
|
+
has_header? DATE
|
79
77
|
end
|
80
78
|
|
81
79
|
def date=(utc_time)
|
82
|
-
|
80
|
+
set_header DATE, utc_time.httpdate
|
83
81
|
end
|
84
82
|
|
85
83
|
def etag=(etag)
|
86
84
|
key = ActiveSupport::Cache.expand_cache_key(etag)
|
87
|
-
|
85
|
+
super %("#{Digest::MD5.hexdigest(key)}")
|
88
86
|
end
|
89
87
|
|
88
|
+
def etag?; etag; end
|
89
|
+
|
90
90
|
private
|
91
91
|
|
92
92
|
DATE = 'Date'.freeze
|
93
93
|
LAST_MODIFIED = "Last-Modified".freeze
|
94
|
-
ETAG = "ETag".freeze
|
95
|
-
CACHE_CONTROL = "Cache-Control".freeze
|
96
94
|
SPECIAL_KEYS = Set.new(%w[extras no-cache max-age public must-revalidate])
|
97
95
|
|
98
96
|
def cache_control_segments
|
99
|
-
if cache_control =
|
97
|
+
if cache_control = _cache_control
|
100
98
|
cache_control.delete(' ').split(',')
|
101
99
|
else
|
102
100
|
[]
|
@@ -123,12 +121,11 @@ module ActionDispatch
|
|
123
121
|
|
124
122
|
def prepare_cache_control!
|
125
123
|
@cache_control = cache_control_headers
|
126
|
-
@etag = self[ETAG]
|
127
124
|
end
|
128
125
|
|
129
126
|
def handle_conditional_get!
|
130
127
|
if etag? || last_modified? || !@cache_control.empty?
|
131
|
-
set_conditional_cache_control!
|
128
|
+
set_conditional_cache_control!(@cache_control)
|
132
129
|
end
|
133
130
|
end
|
134
131
|
|
@@ -138,24 +135,24 @@ module ActionDispatch
|
|
138
135
|
PRIVATE = "private".freeze
|
139
136
|
MUST_REVALIDATE = "must-revalidate".freeze
|
140
137
|
|
141
|
-
def set_conditional_cache_control!
|
138
|
+
def set_conditional_cache_control!(cache_control)
|
142
139
|
control = {}
|
143
140
|
cc_headers = cache_control_headers
|
144
141
|
if extras = cc_headers.delete(:extras)
|
145
|
-
|
146
|
-
|
147
|
-
|
142
|
+
cache_control[:extras] ||= []
|
143
|
+
cache_control[:extras] += extras
|
144
|
+
cache_control[:extras].uniq!
|
148
145
|
end
|
149
146
|
|
150
147
|
control.merge! cc_headers
|
151
|
-
control.merge!
|
148
|
+
control.merge! cache_control
|
152
149
|
|
153
150
|
if control.empty?
|
154
|
-
|
151
|
+
self._cache_control = DEFAULT_CACHE_CONTROL
|
155
152
|
elsif control[:no_cache]
|
156
|
-
|
153
|
+
self._cache_control = NO_CACHE
|
157
154
|
if control[:extras]
|
158
|
-
|
155
|
+
self._cache_control = _cache_control + ", #{control[:extras].join(', ')}"
|
159
156
|
end
|
160
157
|
else
|
161
158
|
extras = control[:extras]
|
@@ -167,7 +164,7 @@ module ActionDispatch
|
|
167
164
|
options << MUST_REVALIDATE if control[:must_revalidate]
|
168
165
|
options.concat(extras) if extras
|
169
166
|
|
170
|
-
|
167
|
+
self._cache_control = options.join(", ")
|
171
168
|
end
|
172
169
|
end
|
173
170
|
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'active_support/core_ext/hash/keys'
|
2
|
-
require 'active_support/core_ext/object/duplicable'
|
3
1
|
require 'action_dispatch/http/parameter_filter'
|
4
2
|
|
5
3
|
module ActionDispatch
|
@@ -16,7 +14,7 @@ module ActionDispatch
|
|
16
14
|
# env["action_dispatch.parameter_filter"] = [:foo, "bar"]
|
17
15
|
# => replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
|
18
16
|
#
|
19
|
-
# env["action_dispatch.parameter_filter"] =
|
17
|
+
# env["action_dispatch.parameter_filter"] = -> (k, v) do
|
20
18
|
# v.reverse! if k =~ /secret/i
|
21
19
|
# end
|
22
20
|
# => reverses the value to all keys matching /secret/i
|
@@ -25,19 +23,19 @@ module ActionDispatch
|
|
25
23
|
NULL_PARAM_FILTER = ParameterFilter.new # :nodoc:
|
26
24
|
NULL_ENV_FILTER = ParameterFilter.new ENV_MATCH # :nodoc:
|
27
25
|
|
28
|
-
def initialize
|
26
|
+
def initialize
|
29
27
|
super
|
30
28
|
@filtered_parameters = nil
|
31
29
|
@filtered_env = nil
|
32
30
|
@filtered_path = nil
|
33
31
|
end
|
34
32
|
|
35
|
-
#
|
33
|
+
# Returns a hash of parameters with all sensitive data replaced.
|
36
34
|
def filtered_parameters
|
37
35
|
@filtered_parameters ||= parameter_filter.filter(parameters)
|
38
36
|
end
|
39
37
|
|
40
|
-
#
|
38
|
+
# Returns a hash of request.env with all sensitive data replaced.
|
41
39
|
def filtered_env
|
42
40
|
@filtered_env ||= env_filter.filter(@env)
|
43
41
|
end
|
@@ -50,13 +48,13 @@ module ActionDispatch
|
|
50
48
|
protected
|
51
49
|
|
52
50
|
def parameter_filter
|
53
|
-
parameter_filter_for
|
51
|
+
parameter_filter_for fetch_header("action_dispatch.parameter_filter") {
|
54
52
|
return NULL_PARAM_FILTER
|
55
53
|
}
|
56
54
|
end
|
57
55
|
|
58
56
|
def env_filter
|
59
|
-
user_key =
|
57
|
+
user_key = fetch_header("action_dispatch.parameter_filter") {
|
60
58
|
return NULL_ENV_FILTER
|
61
59
|
}
|
62
60
|
parameter_filter_for(Array(user_key) + ENV_MATCH)
|
@@ -4,9 +4,8 @@ module ActionDispatch
|
|
4
4
|
|
5
5
|
FILTERED = '[FILTERED]'.freeze # :nodoc:
|
6
6
|
|
7
|
-
def filtered_location
|
8
|
-
|
9
|
-
if !filters.empty? && location_filter_match?(filters)
|
7
|
+
def filtered_location # :nodoc:
|
8
|
+
if location_filter_match?
|
10
9
|
FILTERED
|
11
10
|
else
|
12
11
|
location
|
@@ -15,20 +14,20 @@ module ActionDispatch
|
|
15
14
|
|
16
15
|
private
|
17
16
|
|
18
|
-
def
|
17
|
+
def location_filters
|
19
18
|
if request
|
20
|
-
request.
|
19
|
+
request.get_header('action_dispatch.redirect_filter') || []
|
21
20
|
else
|
22
21
|
[]
|
23
22
|
end
|
24
23
|
end
|
25
24
|
|
26
|
-
def location_filter_match?
|
27
|
-
|
25
|
+
def location_filter_match?
|
26
|
+
location_filters.any? do |filter|
|
28
27
|
if String === filter
|
29
28
|
location.include?(filter)
|
30
29
|
elsif Regexp === filter
|
31
|
-
location
|
30
|
+
location =~ filter
|
32
31
|
end
|
33
32
|
end
|
34
33
|
end
|
@@ -30,27 +30,37 @@ module ActionDispatch
|
|
30
30
|
HTTP_HEADER = /\A[A-Za-z0-9-]+\z/
|
31
31
|
|
32
32
|
include Enumerable
|
33
|
-
attr_reader :env
|
34
33
|
|
35
|
-
def
|
36
|
-
|
34
|
+
def self.from_hash(hash)
|
35
|
+
new ActionDispatch::Request.new hash
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize(request) # :nodoc:
|
39
|
+
@req = request
|
37
40
|
end
|
38
41
|
|
39
42
|
# Returns the value for the given key mapped to @env.
|
40
43
|
def [](key)
|
41
|
-
@
|
44
|
+
@req.get_header env_name(key)
|
42
45
|
end
|
43
46
|
|
44
47
|
# Sets the given value for the key mapped to @env.
|
45
48
|
def []=(key, value)
|
46
|
-
@
|
49
|
+
@req.set_header env_name(key), value
|
50
|
+
end
|
51
|
+
|
52
|
+
# Add a value to a multivalued header like Vary or Accept-Encoding.
|
53
|
+
def add(key, value)
|
54
|
+
@req.add_header env_name(key), value
|
47
55
|
end
|
48
56
|
|
49
57
|
def key?(key)
|
50
|
-
@
|
58
|
+
@req.has_header? env_name(key)
|
51
59
|
end
|
52
60
|
alias :include? :key?
|
53
61
|
|
62
|
+
DEFAULT = Object.new # :nodoc:
|
63
|
+
|
54
64
|
# Returns the value for the given key mapped to @env.
|
55
65
|
#
|
56
66
|
# If the key is not found and an optional code block is not provided,
|
@@ -58,18 +68,22 @@ module ActionDispatch
|
|
58
68
|
#
|
59
69
|
# If the code block is provided, then it will be run and
|
60
70
|
# its result returned.
|
61
|
-
def fetch(key,
|
62
|
-
@
|
71
|
+
def fetch(key, default = DEFAULT)
|
72
|
+
@req.fetch_header(env_name(key)) do
|
73
|
+
return default unless default == DEFAULT
|
74
|
+
return yield if block_given?
|
75
|
+
raise NameError, key
|
76
|
+
end
|
63
77
|
end
|
64
78
|
|
65
79
|
def each(&block)
|
66
|
-
@
|
80
|
+
@req.each_header(&block)
|
67
81
|
end
|
68
82
|
|
69
83
|
# Returns a new Http::Headers instance containing the contents of
|
70
84
|
# <tt>headers_or_env</tt> and the original instance.
|
71
85
|
def merge(headers_or_env)
|
72
|
-
headers =
|
86
|
+
headers = @req.dup.headers
|
73
87
|
headers.merge!(headers_or_env)
|
74
88
|
headers
|
75
89
|
end
|
@@ -79,11 +93,14 @@ module ActionDispatch
|
|
79
93
|
# <tt>headers_or_env</tt>.
|
80
94
|
def merge!(headers_or_env)
|
81
95
|
headers_or_env.each do |key, value|
|
82
|
-
|
96
|
+
@req.set_header env_name(key), value
|
83
97
|
end
|
84
98
|
end
|
85
99
|
|
100
|
+
def env; @req.env.dup; end
|
101
|
+
|
86
102
|
private
|
103
|
+
|
87
104
|
# Converts a HTTP header name to an environment variable name if it is
|
88
105
|
# not contained within the headers hash.
|
89
106
|
def env_name(key)
|
@@ -10,19 +10,18 @@ module ActionDispatch
|
|
10
10
|
self.ignore_accept_header = false
|
11
11
|
end
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
# The MIME type of the HTTP request, such as Mime::XML.
|
13
|
+
# The MIME type of the HTTP request, such as Mime[:xml].
|
16
14
|
#
|
17
15
|
# For backward compatibility, the post \format is extracted from the
|
18
16
|
# X-Post-Data-Format HTTP header if present.
|
19
17
|
def content_mime_type
|
20
|
-
|
21
|
-
if
|
18
|
+
fetch_header("action_dispatch.request.content_type") do |k|
|
19
|
+
v = if get_header('CONTENT_TYPE') =~ /^([^,\;]*)/
|
22
20
|
Mime::Type.lookup($1.strip.downcase)
|
23
21
|
else
|
24
22
|
nil
|
25
23
|
end
|
24
|
+
set_header k, v
|
26
25
|
end
|
27
26
|
end
|
28
27
|
|
@@ -30,31 +29,36 @@ module ActionDispatch
|
|
30
29
|
content_mime_type && content_mime_type.to_s
|
31
30
|
end
|
32
31
|
|
32
|
+
def has_content_type?
|
33
|
+
has_header? 'CONTENT_TYPE'
|
34
|
+
end
|
35
|
+
|
33
36
|
# Returns the accepted MIME type for the request.
|
34
37
|
def accepts
|
35
|
-
|
36
|
-
header =
|
38
|
+
fetch_header("action_dispatch.request.accepts") do |k|
|
39
|
+
header = get_header('HTTP_ACCEPT').to_s.strip
|
37
40
|
|
38
|
-
if header.empty?
|
41
|
+
v = if header.empty?
|
39
42
|
[content_mime_type]
|
40
43
|
else
|
41
44
|
Mime::Type.parse(header)
|
42
45
|
end
|
46
|
+
set_header k, v
|
43
47
|
end
|
44
48
|
end
|
45
49
|
|
46
50
|
# Returns the MIME type for the \format used in the request.
|
47
51
|
#
|
48
|
-
# GET /posts/5.xml | request.format => Mime
|
49
|
-
# GET /posts/5.xhtml | request.format => Mime
|
50
|
-
# GET /posts/5 | request.format => Mime
|
52
|
+
# GET /posts/5.xml | request.format => Mime[:xml]
|
53
|
+
# GET /posts/5.xhtml | request.format => Mime[:html]
|
54
|
+
# GET /posts/5 | request.format => Mime[:html] or Mime[:js], or request.accepts.first
|
51
55
|
#
|
52
56
|
def format(view_path = [])
|
53
57
|
formats.first || Mime::NullType.instance
|
54
58
|
end
|
55
59
|
|
56
60
|
def formats
|
57
|
-
|
61
|
+
fetch_header("action_dispatch.request.formats") do |k|
|
58
62
|
params_readable = begin
|
59
63
|
parameters[:format]
|
60
64
|
rescue ActionController::BadRequest
|
@@ -63,34 +67,37 @@ module ActionDispatch
|
|
63
67
|
|
64
68
|
v = if params_readable
|
65
69
|
Array(Mime[parameters[:format]])
|
70
|
+
elsif format = format_from_path_extension
|
71
|
+
Array(Mime[format])
|
66
72
|
elsif use_accept_header && valid_accept_header
|
67
73
|
accepts
|
68
74
|
elsif xhr?
|
69
|
-
[Mime
|
75
|
+
[Mime[:js]]
|
70
76
|
else
|
71
|
-
[Mime
|
72
|
-
end
|
73
|
-
|
74
|
-
v.select do |format|
|
75
|
-
format.symbol || format.ref == "*/*"
|
77
|
+
[Mime[:html]]
|
76
78
|
end
|
79
|
+
set_header k, v
|
77
80
|
end
|
78
81
|
end
|
79
82
|
|
80
83
|
# Sets the \variant for template.
|
81
84
|
def variant=(variant)
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
@variant = variant
|
85
|
+
variant = Array(variant)
|
86
|
+
|
87
|
+
if variant.all? { |v| v.is_a?(Symbol) }
|
88
|
+
@variant = ActiveSupport::ArrayInquirer.new(variant)
|
86
89
|
else
|
87
|
-
raise ArgumentError, "request.variant must be set to a Symbol or an Array of Symbols
|
90
|
+
raise ArgumentError, "request.variant must be set to a Symbol or an Array of Symbols. " \
|
88
91
|
"For security reasons, never directly set the variant to a user-provided value, " \
|
89
92
|
"like params[:variant].to_sym. Check user-provided value against a whitelist first, " \
|
90
93
|
"then set the variant: request.variant = :tablet if params[:variant] == 'tablet'"
|
91
94
|
end
|
92
95
|
end
|
93
96
|
|
97
|
+
def variant
|
98
|
+
@variant ||= ActiveSupport::ArrayInquirer.new
|
99
|
+
end
|
100
|
+
|
94
101
|
# Sets the \format by string extension, which can be used to force custom formats
|
95
102
|
# that are not controlled by the extension.
|
96
103
|
#
|
@@ -104,7 +111,7 @@ module ActionDispatch
|
|
104
111
|
# end
|
105
112
|
def format=(extension)
|
106
113
|
parameters[:format] = extension.to_s
|
107
|
-
|
114
|
+
set_header "action_dispatch.request.formats", [Mime::Type.lookup_by_extension(parameters[:format])]
|
108
115
|
end
|
109
116
|
|
110
117
|
# Sets the \formats by string extensions. This differs from #format= by allowing you
|
@@ -123,9 +130,9 @@ module ActionDispatch
|
|
123
130
|
# end
|
124
131
|
def formats=(extensions)
|
125
132
|
parameters[:format] = extensions.first.to_s
|
126
|
-
|
133
|
+
set_header "action_dispatch.request.formats", extensions.collect { |extension|
|
127
134
|
Mime::Type.lookup_by_extension(extension)
|
128
|
-
|
135
|
+
}
|
129
136
|
end
|
130
137
|
|
131
138
|
# Receives an array of mimes and return the first user sent mime that
|
@@ -155,6 +162,13 @@ module ActionDispatch
|
|
155
162
|
def use_accept_header
|
156
163
|
!self.class.ignore_accept_header
|
157
164
|
end
|
165
|
+
|
166
|
+
def format_from_path_extension
|
167
|
+
path = @env['action_dispatch.original_path'] || @env['PATH_INFO']
|
168
|
+
if match = path && path.match(/\.(\w+)\z/)
|
169
|
+
match.captures.first
|
170
|
+
end
|
171
|
+
end
|
158
172
|
end
|
159
173
|
end
|
160
174
|
end
|