actionpack 4.1.7 → 4.2.11
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 +404 -451
- data/README.rdoc +7 -2
- data/lib/abstract_controller/base.rb +16 -6
- data/lib/abstract_controller/callbacks.rb +28 -51
- data/lib/abstract_controller/helpers.rb +11 -4
- data/lib/abstract_controller/railties/routes_helpers.rb +3 -3
- data/lib/abstract_controller/rendering.rb +7 -1
- data/lib/abstract_controller/url_for.rb +1 -1
- data/lib/action_controller/base.rb +3 -2
- data/lib/action_controller/caching/fragments.rb +7 -1
- data/lib/action_controller/caching.rb +1 -1
- data/lib/action_controller/log_subscriber.rb +26 -26
- data/lib/action_controller/metal/conditional_get.rb +37 -12
- data/lib/action_controller/metal/etag_with_template_digest.rb +50 -0
- data/lib/action_controller/metal/exceptions.rb +1 -1
- data/lib/action_controller/metal/force_ssl.rb +1 -1
- data/lib/action_controller/metal/head.rb +7 -3
- data/lib/action_controller/metal/http_authentication.rb +20 -10
- data/lib/action_controller/metal/instrumentation.rb +8 -5
- data/lib/action_controller/metal/live.rb +57 -6
- data/lib/action_controller/metal/mime_responds.rb +25 -246
- data/lib/action_controller/metal/params_wrapper.rb +5 -5
- data/lib/action_controller/metal/rack_delegation.rb +1 -1
- data/lib/action_controller/metal/redirecting.rb +14 -8
- data/lib/action_controller/metal/renderers.rb +29 -11
- data/lib/action_controller/metal/rendering.rb +2 -6
- data/lib/action_controller/metal/request_forgery_protection.rb +78 -7
- data/lib/action_controller/metal/streaming.rb +1 -1
- data/lib/action_controller/metal/strong_parameters.rb +129 -14
- data/lib/action_controller/metal/url_for.rb +11 -12
- data/lib/action_controller/metal.rb +12 -11
- data/lib/action_controller/model_naming.rb +1 -1
- data/lib/action_controller/railtie.rb +4 -0
- data/lib/action_controller/test_case.rb +119 -75
- data/lib/action_controller.rb +1 -1
- data/lib/action_dispatch/http/cache.rb +5 -4
- data/lib/action_dispatch/http/filter_parameters.rb +2 -2
- data/lib/action_dispatch/http/headers.rb +43 -9
- data/lib/action_dispatch/http/mime_negotiation.rb +10 -3
- data/lib/action_dispatch/http/mime_type.rb +18 -4
- data/lib/action_dispatch/http/parameter_filter.rb +1 -1
- data/lib/action_dispatch/http/parameters.rb +11 -26
- data/lib/action_dispatch/http/request.rb +37 -11
- data/lib/action_dispatch/http/response.rb +74 -23
- data/lib/action_dispatch/http/upload.rb +9 -8
- data/lib/action_dispatch/http/url.rb +89 -70
- data/lib/action_dispatch/journey/formatter.rb +34 -18
- data/lib/action_dispatch/journey/gtg/builder.rb +3 -3
- data/lib/action_dispatch/journey/gtg/simulator.rb +10 -7
- data/lib/action_dispatch/journey/gtg/transition_table.rb +20 -28
- data/lib/action_dispatch/journey/nfa/dot.rb +2 -2
- data/lib/action_dispatch/journey/nfa/simulator.rb +1 -1
- data/lib/action_dispatch/journey/nfa/transition_table.rb +5 -5
- data/lib/action_dispatch/journey/nodes/node.rb +4 -0
- data/lib/action_dispatch/journey/parser.rb +52 -60
- data/lib/action_dispatch/journey/parser.y +11 -10
- data/lib/action_dispatch/journey/path/pattern.rb +16 -19
- data/lib/action_dispatch/journey/route.rb +4 -19
- data/lib/action_dispatch/journey/router/strexp.rb +9 -6
- data/lib/action_dispatch/journey/router/utils.rb +1 -1
- data/lib/action_dispatch/journey/router.rb +53 -77
- data/lib/action_dispatch/journey/routes.rb +4 -0
- data/lib/action_dispatch/journey/scanner.rb +5 -5
- data/lib/action_dispatch/journey/visitors.rb +81 -92
- data/lib/action_dispatch/journey/visualizer/fsm.css +0 -4
- data/lib/action_dispatch/journey/visualizer/index.html.erb +2 -2
- data/lib/action_dispatch/middleware/callbacks.rb +1 -1
- data/lib/action_dispatch/middleware/cookies.rb +34 -34
- data/lib/action_dispatch/middleware/debug_exceptions.rb +15 -4
- data/lib/action_dispatch/middleware/exception_wrapper.rb +50 -18
- data/lib/action_dispatch/middleware/flash.rb +13 -7
- data/lib/action_dispatch/middleware/params_parser.rb +1 -1
- data/lib/action_dispatch/middleware/public_exceptions.rb +12 -3
- data/lib/action_dispatch/middleware/remote_ip.rb +40 -54
- data/lib/action_dispatch/middleware/request_id.rb +1 -1
- data/lib/action_dispatch/middleware/session/cookie_store.rb +1 -1
- data/lib/action_dispatch/middleware/show_exceptions.rb +1 -0
- data/lib/action_dispatch/middleware/ssl.rb +1 -1
- data/lib/action_dispatch/middleware/static.rb +75 -39
- data/lib/action_dispatch/middleware/templates/rescues/_source.erb +21 -19
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +37 -9
- data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +2 -8
- data/lib/action_dispatch/middleware/templates/rescues/{diagnostics.erb → diagnostics.html.erb} +0 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +6 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +4 -0
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +2 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -24
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +0 -1
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +120 -64
- data/lib/action_dispatch/railtie.rb +2 -0
- data/lib/action_dispatch/routing/endpoint.rb +10 -0
- data/lib/action_dispatch/routing/inspector.rb +5 -12
- data/lib/action_dispatch/routing/mapper.rb +414 -283
- data/lib/action_dispatch/routing/polymorphic_routes.rb +191 -79
- data/lib/action_dispatch/routing/redirection.rb +10 -12
- data/lib/action_dispatch/routing/route_set.rb +300 -173
- data/lib/action_dispatch/routing/routes_proxy.rb +5 -4
- data/lib/action_dispatch/routing/url_for.rb +17 -5
- data/lib/action_dispatch/testing/assertions/dom.rb +2 -26
- data/lib/action_dispatch/testing/assertions/response.rb +2 -7
- data/lib/action_dispatch/testing/assertions/routing.rb +22 -22
- data/lib/action_dispatch/testing/assertions/selector.rb +2 -429
- data/lib/action_dispatch/testing/assertions/tag.rb +2 -134
- data/lib/action_dispatch/testing/assertions.rb +11 -7
- data/lib/action_dispatch/testing/integration.rb +28 -20
- data/lib/action_dispatch/testing/test_request.rb +1 -1
- data/lib/action_dispatch/testing/test_response.rb +1 -5
- data/lib/action_pack/gem_version.rb +3 -3
- metadata +55 -13
- data/lib/action_controller/metal/responder.rb +0 -297
@@ -13,9 +13,9 @@ module ActionController
|
|
13
13
|
end
|
14
14
|
|
15
15
|
module ClassMethods
|
16
|
-
# Allows you to consider additional controller-wide information when generating an
|
16
|
+
# Allows you to consider additional controller-wide information when generating an ETag.
|
17
17
|
# For example, if you serve pages tailored depending on who's logged in at the moment, you
|
18
|
-
# may want to add the current user id to be part of the
|
18
|
+
# may want to add the current user id to be part of the ETag to prevent authorized displaying
|
19
19
|
# of cached pages.
|
20
20
|
#
|
21
21
|
# class InvoicesController < ApplicationController
|
@@ -32,7 +32,7 @@ module ActionController
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
-
# Sets the etag
|
35
|
+
# Sets the +etag+, +last_modified+, or both on the response and renders a
|
36
36
|
# <tt>304 Not Modified</tt> response if the request is already fresh.
|
37
37
|
#
|
38
38
|
# === Parameters:
|
@@ -41,6 +41,11 @@ module ActionController
|
|
41
41
|
# * <tt>:last_modified</tt>.
|
42
42
|
# * <tt>:public</tt> By default the Cache-Control header is private, set this to
|
43
43
|
# +true+ if you want your application to be cachable by other devices (proxy caches).
|
44
|
+
# * <tt>:template</tt> By default, the template digest for the current
|
45
|
+
# controller/action is included in ETags. If the action renders a
|
46
|
+
# different template, you can include its digest instead. If the action
|
47
|
+
# doesn't render a template at all, you can pass <tt>template: false</tt>
|
48
|
+
# to skip any attempt to check for a template digest.
|
44
49
|
#
|
45
50
|
# === Example:
|
46
51
|
#
|
@@ -49,11 +54,11 @@ module ActionController
|
|
49
54
|
# fresh_when(etag: @article, last_modified: @article.created_at, public: true)
|
50
55
|
# end
|
51
56
|
#
|
52
|
-
# This will render the show template if the request isn't sending a matching
|
57
|
+
# This will render the show template if the request isn't sending a matching ETag or
|
53
58
|
# If-Modified-Since header and just a <tt>304 Not Modified</tt> response if there's a match.
|
54
59
|
#
|
55
60
|
# You can also just pass a record where +last_modified+ will be set by calling
|
56
|
-
# +updated_at+ and the etag by passing the object itself.
|
61
|
+
# +updated_at+ and the +etag+ by passing the object itself.
|
57
62
|
#
|
58
63
|
# def show
|
59
64
|
# @article = Article.find(params[:id])
|
@@ -66,18 +71,24 @@ module ActionController
|
|
66
71
|
# @article = Article.find(params[:id])
|
67
72
|
# fresh_when(@article, public: true)
|
68
73
|
# end
|
74
|
+
#
|
75
|
+
# When rendering a different template than the default controller/action
|
76
|
+
# style, you can indicate which digest to include in the ETag:
|
77
|
+
#
|
78
|
+
# before_action { fresh_when @article, template: 'widgets/show' }
|
79
|
+
#
|
69
80
|
def fresh_when(record_or_options, additional_options = {})
|
70
81
|
if record_or_options.is_a? Hash
|
71
82
|
options = record_or_options
|
72
|
-
options.assert_valid_keys(:etag, :last_modified, :public)
|
83
|
+
options.assert_valid_keys(:etag, :last_modified, :public, :template)
|
73
84
|
else
|
74
85
|
record = record_or_options
|
75
86
|
options = { etag: record, last_modified: record.try(:updated_at) }.merge!(additional_options)
|
76
87
|
end
|
77
88
|
|
78
|
-
response.etag = combine_etags(options[:etag]
|
79
|
-
response.last_modified = options[:last_modified]
|
80
|
-
response.cache_control[:public] = true
|
89
|
+
response.etag = combine_etags(options) if options[:etag] || options[:template]
|
90
|
+
response.last_modified = options[:last_modified] if options[:last_modified]
|
91
|
+
response.cache_control[:public] = true if options[:public]
|
81
92
|
|
82
93
|
head :not_modified if request.fresh?(response)
|
83
94
|
end
|
@@ -93,6 +104,11 @@ module ActionController
|
|
93
104
|
# * <tt>:last_modified</tt>.
|
94
105
|
# * <tt>:public</tt> By default the Cache-Control header is private, set this to
|
95
106
|
# +true+ if you want your application to be cachable by other devices (proxy caches).
|
107
|
+
# * <tt>:template</tt> By default, the template digest for the current
|
108
|
+
# controller/action is included in ETags. If the action renders a
|
109
|
+
# different template, you can include its digest instead. If the action
|
110
|
+
# doesn't render a template at all, you can pass <tt>template: false</tt>
|
111
|
+
# to skip any attempt to check for a template digest.
|
96
112
|
#
|
97
113
|
# === Example:
|
98
114
|
#
|
@@ -108,7 +124,7 @@ module ActionController
|
|
108
124
|
# end
|
109
125
|
#
|
110
126
|
# You can also just pass a record where +last_modified+ will be set by calling
|
111
|
-
# updated_at and the etag by passing the object itself.
|
127
|
+
# +updated_at+ and the +etag+ by passing the object itself.
|
112
128
|
#
|
113
129
|
# def show
|
114
130
|
# @article = Article.find(params[:id])
|
@@ -133,6 +149,14 @@ module ActionController
|
|
133
149
|
# end
|
134
150
|
# end
|
135
151
|
# end
|
152
|
+
#
|
153
|
+
# When rendering a different template than the default controller/action
|
154
|
+
# style, you can indicate which digest to include in the ETag:
|
155
|
+
#
|
156
|
+
# def show
|
157
|
+
# super if stale? @article, template: 'widgets/show'
|
158
|
+
# end
|
159
|
+
#
|
136
160
|
def stale?(record_or_options, additional_options = {})
|
137
161
|
fresh_when(record_or_options, additional_options)
|
138
162
|
!request.fresh?(response)
|
@@ -168,8 +192,9 @@ module ActionController
|
|
168
192
|
end
|
169
193
|
|
170
194
|
private
|
171
|
-
def combine_etags(
|
172
|
-
|
195
|
+
def combine_etags(options)
|
196
|
+
etags = etaggers.map { |etagger| instance_exec(options, &etagger) }.compact
|
197
|
+
etags.unshift options[:etag]
|
173
198
|
end
|
174
199
|
end
|
175
200
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module ActionController
|
2
|
+
# When our views change, they should bubble up into HTTP cache freshness
|
3
|
+
# and bust browser caches. So the template digest for the current action
|
4
|
+
# is automatically included in the ETag.
|
5
|
+
#
|
6
|
+
# Enabled by default for apps that use Action View. Disable by setting
|
7
|
+
#
|
8
|
+
# config.action_controller.etag_with_template_digest = false
|
9
|
+
#
|
10
|
+
# Override the template to digest by passing +:template+ to +fresh_when+
|
11
|
+
# and +stale?+ calls. For example:
|
12
|
+
#
|
13
|
+
# # We're going to render widgets/show, not posts/show
|
14
|
+
# fresh_when @post, template: 'widgets/show'
|
15
|
+
#
|
16
|
+
# # We're not going to render a template, so omit it from the ETag.
|
17
|
+
# fresh_when @post, template: false
|
18
|
+
#
|
19
|
+
module EtagWithTemplateDigest
|
20
|
+
extend ActiveSupport::Concern
|
21
|
+
|
22
|
+
include ActionController::ConditionalGet
|
23
|
+
|
24
|
+
included do
|
25
|
+
class_attribute :etag_with_template_digest
|
26
|
+
self.etag_with_template_digest = true
|
27
|
+
|
28
|
+
ActiveSupport.on_load :action_view, yield: true do |action_view_base|
|
29
|
+
etag do |options|
|
30
|
+
determine_template_etag(options) if etag_with_template_digest
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def determine_template_etag(options)
|
37
|
+
if template = pick_template_for_etag(options)
|
38
|
+
lookup_and_digest_template(template)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def pick_template_for_etag(options)
|
43
|
+
options.fetch(:template) { "#{controller_name}/#{action_name}" }
|
44
|
+
end
|
45
|
+
|
46
|
+
def lookup_and_digest_template(template)
|
47
|
+
ActionView::Digestor.digest name: template, finder: lookup_context
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -85,7 +85,7 @@ module ActionController
|
|
85
85
|
if host_or_options.is_a?(Hash)
|
86
86
|
options.merge!(host_or_options)
|
87
87
|
elsif host_or_options
|
88
|
-
options
|
88
|
+
options[:host] = host_or_options
|
89
89
|
end
|
90
90
|
|
91
91
|
secure_url = ActionDispatch::Http::URL.url_for(options.slice(*URL_OPTIONS))
|
@@ -14,6 +14,8 @@ module ActionController
|
|
14
14
|
# return head(:method_not_allowed) unless request.post?
|
15
15
|
# return head(:bad_request) unless valid_request?
|
16
16
|
# render
|
17
|
+
#
|
18
|
+
# See Rack::Utils::SYMBOL_TO_STATUS_CODE for a full list of valid +status+ symbols.
|
17
19
|
def head(status, options = {})
|
18
20
|
options, status = status, nil if status.is_a?(Hash)
|
19
21
|
status ||= options.delete(:status) || :ok
|
@@ -27,15 +29,17 @@ module ActionController
|
|
27
29
|
self.status = status
|
28
30
|
self.location = url_for(location) if location
|
29
31
|
|
30
|
-
|
32
|
+
self.response_body = ""
|
33
|
+
|
34
|
+
if include_content?(self.response_code)
|
31
35
|
self.content_type = content_type || (Mime[formats.first] if formats)
|
32
36
|
self.response.charset = false if self.response
|
33
|
-
self.response_body = " "
|
34
37
|
else
|
35
38
|
headers.delete('Content-Type')
|
36
39
|
headers.delete('Content-Length')
|
37
|
-
self.response_body = ""
|
38
40
|
end
|
41
|
+
|
42
|
+
true
|
39
43
|
end
|
40
44
|
|
41
45
|
private
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'base64'
|
2
|
+
require 'active_support/security_utils'
|
2
3
|
|
3
4
|
module ActionController
|
4
5
|
# Makes it dead easy to do HTTP Basic, Digest and Token authentication.
|
@@ -53,10 +54,8 @@ module ActionController
|
|
53
54
|
# In your integration tests, you can do something like this:
|
54
55
|
#
|
55
56
|
# def test_access_granted_from_xml
|
56
|
-
#
|
57
|
-
#
|
58
|
-
# 'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials(users(:dhh).name, users(:dhh).password)
|
59
|
-
# )
|
57
|
+
# @request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(users(:dhh).name, users(:dhh).password)
|
58
|
+
# get "/notes/1.xml"
|
60
59
|
#
|
61
60
|
# assert_equal 200, status
|
62
61
|
# end
|
@@ -70,7 +69,11 @@ module ActionController
|
|
70
69
|
def http_basic_authenticate_with(options = {})
|
71
70
|
before_action(options.except(:name, :password, :realm)) do
|
72
71
|
authenticate_or_request_with_http_basic(options[:realm] || "Application") do |name, password|
|
73
|
-
|
72
|
+
# This comparison uses & so that it doesn't short circuit and
|
73
|
+
# uses `variable_size_secure_compare` so that length information
|
74
|
+
# isn't leaked.
|
75
|
+
ActiveSupport::SecurityUtils.variable_size_secure_compare(name, options[:name]) &
|
76
|
+
ActiveSupport::SecurityUtils.variable_size_secure_compare(password, options[:password])
|
74
77
|
end
|
75
78
|
end
|
76
79
|
end
|
@@ -397,6 +400,7 @@ module ActionController
|
|
397
400
|
#
|
398
401
|
# RewriteRule ^(.*)$ dispatch.fcgi [E=X-HTTP_AUTHORIZATION:%{HTTP:Authorization},QSA,L]
|
399
402
|
module Token
|
403
|
+
TOKEN_KEY = 'token='
|
400
404
|
TOKEN_REGEX = /^Token /
|
401
405
|
AUTHN_PAIR_DELIMITERS = /(?:,|;|\t+)/
|
402
406
|
extend self
|
@@ -462,16 +466,22 @@ module ActionController
|
|
462
466
|
raw_params.map { |param| param.split %r/=(.+)?/ }
|
463
467
|
end
|
464
468
|
|
465
|
-
# This removes the
|
469
|
+
# This removes the <tt>"</tt> characters wrapping the value.
|
466
470
|
def rewrite_param_values(array_params)
|
467
471
|
array_params.each { |param| (param[1] || "").gsub! %r/^"|"$/, '' }
|
468
472
|
end
|
469
473
|
|
470
474
|
# This method takes an authorization body and splits up the key-value
|
471
|
-
# pairs by the standardized
|
472
|
-
#
|
475
|
+
# pairs by the standardized <tt>:</tt>, <tt>;</tt>, or <tt>\t</tt>
|
476
|
+
# delimiters defined in +AUTHN_PAIR_DELIMITERS+.
|
473
477
|
def raw_params(auth)
|
474
|
-
auth.sub(TOKEN_REGEX, '').split(
|
478
|
+
_raw_params = auth.sub(TOKEN_REGEX, '').split(/\s*#{AUTHN_PAIR_DELIMITERS}\s*/)
|
479
|
+
|
480
|
+
if !(_raw_params.first =~ %r{\A#{TOKEN_KEY}})
|
481
|
+
_raw_params[0] = "#{TOKEN_KEY}#{_raw_params.first}"
|
482
|
+
end
|
483
|
+
|
484
|
+
_raw_params
|
475
485
|
end
|
476
486
|
|
477
487
|
# Encodes the given token and options into an Authorization header value.
|
@@ -481,7 +491,7 @@ module ActionController
|
|
481
491
|
#
|
482
492
|
# Returns String.
|
483
493
|
def encode_credentials(token, options = {})
|
484
|
-
values = ["
|
494
|
+
values = ["#{TOKEN_KEY}#{token.to_s.inspect}"] + options.map do |key, value|
|
485
495
|
"#{key}=#{value.to_s.inspect}"
|
486
496
|
end
|
487
497
|
"Token #{values * ", "}"
|
@@ -21,17 +21,20 @@ module ActionController
|
|
21
21
|
:action => self.action_name,
|
22
22
|
:params => request.filtered_parameters,
|
23
23
|
:format => request.format.try(:ref),
|
24
|
-
:method => request.
|
24
|
+
:method => request.request_method,
|
25
25
|
:path => (request.fullpath rescue "unknown")
|
26
26
|
}
|
27
27
|
|
28
28
|
ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload.dup)
|
29
29
|
|
30
30
|
ActiveSupport::Notifications.instrument("process_action.action_controller", raw_payload) do |payload|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
31
|
+
begin
|
32
|
+
result = super
|
33
|
+
payload[:status] = response.status
|
34
|
+
result
|
35
|
+
ensure
|
36
|
+
append_info_to_payload(payload)
|
37
|
+
end
|
35
38
|
end
|
36
39
|
end
|
37
40
|
|
@@ -102,16 +102,30 @@ module ActionController
|
|
102
102
|
end
|
103
103
|
end
|
104
104
|
|
105
|
-
|
105
|
+
message = json.gsub(/\n/, "\ndata: ")
|
106
|
+
@stream.write "data: #{message}\n\n"
|
106
107
|
end
|
107
108
|
end
|
108
109
|
|
110
|
+
class ClientDisconnected < RuntimeError
|
111
|
+
end
|
112
|
+
|
109
113
|
class Buffer < ActionDispatch::Response::Buffer #:nodoc:
|
110
114
|
include MonitorMixin
|
111
115
|
|
116
|
+
# Ignore that the client has disconnected.
|
117
|
+
#
|
118
|
+
# If this value is `true`, calling `write` after the client
|
119
|
+
# disconnects will result in the written content being silently
|
120
|
+
# discarded. If this value is `false` (the default), a
|
121
|
+
# ClientDisconnected exception will be raised.
|
122
|
+
attr_accessor :ignore_disconnect
|
123
|
+
|
112
124
|
def initialize(response)
|
113
125
|
@error_callback = lambda { true }
|
114
126
|
@cv = new_cond
|
127
|
+
@aborted = false
|
128
|
+
@ignore_disconnect = false
|
115
129
|
super(response, SizedQueue.new(10))
|
116
130
|
end
|
117
131
|
|
@@ -122,6 +136,17 @@ module ActionController
|
|
122
136
|
end
|
123
137
|
|
124
138
|
super
|
139
|
+
|
140
|
+
unless connected?
|
141
|
+
@buf.clear
|
142
|
+
|
143
|
+
unless @ignore_disconnect
|
144
|
+
# Raise ClientDisconnected, which is a RuntimeError (not an
|
145
|
+
# IOError), because that's more appropriate for something beyond
|
146
|
+
# the developer's control.
|
147
|
+
raise ClientDisconnected, "client disconnected"
|
148
|
+
end
|
149
|
+
end
|
125
150
|
end
|
126
151
|
|
127
152
|
def each
|
@@ -132,6 +157,10 @@ module ActionController
|
|
132
157
|
@response.sent!
|
133
158
|
end
|
134
159
|
|
160
|
+
# Write a 'close' event to the buffer; the producer/writing thread
|
161
|
+
# uses this to notify us that it's finished supplying content.
|
162
|
+
#
|
163
|
+
# See also #abort.
|
135
164
|
def close
|
136
165
|
synchronize do
|
137
166
|
super
|
@@ -140,6 +169,26 @@ module ActionController
|
|
140
169
|
end
|
141
170
|
end
|
142
171
|
|
172
|
+
# Inform the producer/writing thread that the client has
|
173
|
+
# disconnected; the reading thread is no longer interested in
|
174
|
+
# anything that's being written.
|
175
|
+
#
|
176
|
+
# See also #close.
|
177
|
+
def abort
|
178
|
+
synchronize do
|
179
|
+
@aborted = true
|
180
|
+
@buf.clear
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
# Is the client still connected and waiting for content?
|
185
|
+
#
|
186
|
+
# The result of calling `write` when this is `false` is determined
|
187
|
+
# by `ignore_disconnect`.
|
188
|
+
def connected?
|
189
|
+
!@aborted
|
190
|
+
end
|
191
|
+
|
143
192
|
def await_close
|
144
193
|
synchronize do
|
145
194
|
@cv.wait_until { @closed }
|
@@ -156,7 +205,7 @@ module ActionController
|
|
156
205
|
end
|
157
206
|
|
158
207
|
class Response < ActionDispatch::Response #:nodoc: all
|
159
|
-
class Header < DelegateClass(Hash)
|
208
|
+
class Header < DelegateClass(Hash) # :nodoc:
|
160
209
|
def initialize(response, header)
|
161
210
|
@response = response
|
162
211
|
super(header)
|
@@ -254,10 +303,12 @@ module ActionController
|
|
254
303
|
logger = ActionController::Base.logger
|
255
304
|
return unless logger
|
256
305
|
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
306
|
+
logger.fatal do
|
307
|
+
message = "\n#{exception.class} (#{exception.message}):\n"
|
308
|
+
message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
|
309
|
+
message << " " << exception.backtrace.join("\n ")
|
310
|
+
"#{message}\n\n"
|
311
|
+
end
|
261
312
|
end
|
262
313
|
|
263
314
|
def response_body=(body)
|
@@ -1,61 +1,28 @@
|
|
1
|
-
require 'active_support/core_ext/array/extract_options'
|
2
1
|
require 'abstract_controller/collector'
|
3
2
|
|
4
3
|
module ActionController #:nodoc:
|
5
4
|
module MimeResponds
|
6
5
|
extend ActiveSupport::Concern
|
7
6
|
|
8
|
-
|
9
|
-
class_attribute :responder, :mimes_for_respond_to
|
10
|
-
self.responder = ActionController::Responder
|
11
|
-
clear_respond_to
|
12
|
-
end
|
13
|
-
|
7
|
+
# :stopdoc:
|
14
8
|
module ClassMethods
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
# for <tt>:html</tt>, <tt>:xml</tt> and <tt>:json</tt>.
|
22
|
-
#
|
23
|
-
# To specify on per-action basis, use <tt>:only</tt> and
|
24
|
-
# <tt>:except</tt> with an array of actions or a single action:
|
25
|
-
#
|
26
|
-
# respond_to :html
|
27
|
-
# respond_to :xml, :json, except: [ :edit ]
|
28
|
-
#
|
29
|
-
# This specifies that all actions respond to <tt>:html</tt>
|
30
|
-
# and all actions except <tt>:edit</tt> respond to <tt>:xml</tt> and
|
31
|
-
# <tt>:json</tt>.
|
32
|
-
#
|
33
|
-
# respond_to :json, only: :create
|
34
|
-
#
|
35
|
-
# This specifies that the <tt>:create</tt> action and no other responds
|
36
|
-
# to <tt>:json</tt>.
|
37
|
-
def respond_to(*mimes)
|
38
|
-
options = mimes.extract_options!
|
39
|
-
|
40
|
-
only_actions = Array(options.delete(:only)).map(&:to_s)
|
41
|
-
except_actions = Array(options.delete(:except)).map(&:to_s)
|
42
|
-
|
43
|
-
new = mimes_for_respond_to.dup
|
44
|
-
mimes.each do |mime|
|
45
|
-
mime = mime.to_sym
|
46
|
-
new[mime] = {}
|
47
|
-
new[mime][:only] = only_actions unless only_actions.empty?
|
48
|
-
new[mime][:except] = except_actions unless except_actions.empty?
|
49
|
-
end
|
50
|
-
self.mimes_for_respond_to = new.freeze
|
9
|
+
def respond_to(*)
|
10
|
+
raise NoMethodError, "The controller-level `respond_to' feature has " \
|
11
|
+
"been extracted to the `responders` gem. Add it to your Gemfile to " \
|
12
|
+
"continue using this feature:\n" \
|
13
|
+
" gem 'responders', '~> 2.0'\n" \
|
14
|
+
"Consult the Rails upgrade guide for details."
|
51
15
|
end
|
16
|
+
end
|
52
17
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
18
|
+
def respond_with(*)
|
19
|
+
raise NoMethodError, "The `respond_with' feature has been extracted " \
|
20
|
+
"to the `responders` gem. Add it to your Gemfile to continue using " \
|
21
|
+
"this feature:\n" \
|
22
|
+
" gem 'responders', '~> 2.0'\n" \
|
23
|
+
"Consult the Rails upgrade guide for details."
|
58
24
|
end
|
25
|
+
# :startdoc:
|
59
26
|
|
60
27
|
# Without web-service support, an action which collects the data for displaying a list of people
|
61
28
|
# might look something like this:
|
@@ -169,18 +136,6 @@ module ActionController #:nodoc:
|
|
169
136
|
#
|
170
137
|
# render json: @people
|
171
138
|
#
|
172
|
-
# Since this is a common pattern, you can use the class method respond_to
|
173
|
-
# with the respond_with method to have the same results:
|
174
|
-
#
|
175
|
-
# class PeopleController < ApplicationController
|
176
|
-
# respond_to :html, :xml, :json
|
177
|
-
#
|
178
|
-
# def index
|
179
|
-
# @people = Person.all
|
180
|
-
# respond_with(@people)
|
181
|
-
# end
|
182
|
-
# end
|
183
|
-
#
|
184
139
|
# Formats can have different variants.
|
185
140
|
#
|
186
141
|
# The request variant is a specialization of the request format, like <tt>:tablet</tt>,
|
@@ -217,7 +172,7 @@ module ActionController #:nodoc:
|
|
217
172
|
# format.html.phone { redirect_to progress_path }
|
218
173
|
# format.html.none { render "trash" }
|
219
174
|
# end
|
220
|
-
#
|
175
|
+
#
|
221
176
|
# Variants also support common `any`/`all` block that formats have.
|
222
177
|
#
|
223
178
|
# It works for both inline:
|
@@ -248,194 +203,18 @@ module ActionController #:nodoc:
|
|
248
203
|
# format.html.phone # this gets rendered
|
249
204
|
# end
|
250
205
|
#
|
251
|
-
# Be sure to check the documentation of
|
252
|
-
#
|
253
|
-
def respond_to(*mimes
|
206
|
+
# Be sure to check the documentation of <tt>ActionController::MimeResponds.respond_to</tt>
|
207
|
+
# for more examples.
|
208
|
+
def respond_to(*mimes)
|
254
209
|
raise ArgumentError, "respond_to takes either types or a block, never both" if mimes.any? && block_given?
|
255
210
|
|
256
|
-
if collector = retrieve_collector_from_mimes(mimes, &block)
|
257
|
-
response = collector.response
|
258
|
-
response ? response.call : render({})
|
259
|
-
end
|
260
|
-
end
|
261
|
-
|
262
|
-
# For a given controller action, respond_with generates an appropriate
|
263
|
-
# response based on the mime-type requested by the client.
|
264
|
-
#
|
265
|
-
# If the method is called with just a resource, as in this example -
|
266
|
-
#
|
267
|
-
# class PeopleController < ApplicationController
|
268
|
-
# respond_to :html, :xml, :json
|
269
|
-
#
|
270
|
-
# def index
|
271
|
-
# @people = Person.all
|
272
|
-
# respond_with @people
|
273
|
-
# end
|
274
|
-
# end
|
275
|
-
#
|
276
|
-
# then the mime-type of the response is typically selected based on the
|
277
|
-
# request's Accept header and the set of available formats declared
|
278
|
-
# by previous calls to the controller's class method +respond_to+. Alternatively
|
279
|
-
# the mime-type can be selected by explicitly setting <tt>request.format</tt> in
|
280
|
-
# the controller.
|
281
|
-
#
|
282
|
-
# If an acceptable format is not identified, the application returns a
|
283
|
-
# '406 - not acceptable' status. Otherwise, the default response is to render
|
284
|
-
# a template named after the current action and the selected format,
|
285
|
-
# e.g. <tt>index.html.erb</tt>. If no template is available, the behavior
|
286
|
-
# depends on the selected format:
|
287
|
-
#
|
288
|
-
# * for an html response - if the request method is +get+, an exception
|
289
|
-
# is raised but for other requests such as +post+ the response
|
290
|
-
# depends on whether the resource has any validation errors (i.e.
|
291
|
-
# assuming that an attempt has been made to save the resource,
|
292
|
-
# e.g. by a +create+ action) -
|
293
|
-
# 1. If there are no errors, i.e. the resource
|
294
|
-
# was saved successfully, the response +redirect+'s to the resource
|
295
|
-
# i.e. its +show+ action.
|
296
|
-
# 2. If there are validation errors, the response
|
297
|
-
# renders a default action, which is <tt>:new</tt> for a
|
298
|
-
# +post+ request or <tt>:edit</tt> for +patch+ or +put+.
|
299
|
-
# Thus an example like this -
|
300
|
-
#
|
301
|
-
# respond_to :html, :xml
|
302
|
-
#
|
303
|
-
# def create
|
304
|
-
# @user = User.new(params[:user])
|
305
|
-
# flash[:notice] = 'User was successfully created.' if @user.save
|
306
|
-
# respond_with(@user)
|
307
|
-
# end
|
308
|
-
#
|
309
|
-
# is equivalent, in the absence of <tt>create.html.erb</tt>, to -
|
310
|
-
#
|
311
|
-
# def create
|
312
|
-
# @user = User.new(params[:user])
|
313
|
-
# respond_to do |format|
|
314
|
-
# if @user.save
|
315
|
-
# flash[:notice] = 'User was successfully created.'
|
316
|
-
# format.html { redirect_to(@user) }
|
317
|
-
# format.xml { render xml: @user }
|
318
|
-
# else
|
319
|
-
# format.html { render action: "new" }
|
320
|
-
# format.xml { render xml: @user }
|
321
|
-
# end
|
322
|
-
# end
|
323
|
-
# end
|
324
|
-
#
|
325
|
-
# * for a javascript request - if the template isn't found, an exception is
|
326
|
-
# raised.
|
327
|
-
# * for other requests - i.e. data formats such as xml, json, csv etc, if
|
328
|
-
# the resource passed to +respond_with+ responds to <code>to_<format></code>,
|
329
|
-
# the method attempts to render the resource in the requested format
|
330
|
-
# directly, e.g. for an xml request, the response is equivalent to calling
|
331
|
-
# <code>render xml: resource</code>.
|
332
|
-
#
|
333
|
-
# === Nested resources
|
334
|
-
#
|
335
|
-
# As outlined above, the +resources+ argument passed to +respond_with+
|
336
|
-
# can play two roles. It can be used to generate the redirect url
|
337
|
-
# for successful html requests (e.g. for +create+ actions when
|
338
|
-
# no template exists), while for formats other than html and javascript
|
339
|
-
# it is the object that gets rendered, by being converted directly to the
|
340
|
-
# required format (again assuming no template exists).
|
341
|
-
#
|
342
|
-
# For redirecting successful html requests, +respond_with+ also supports
|
343
|
-
# the use of nested resources, which are supplied in the same way as
|
344
|
-
# in <code>form_for</code> and <code>polymorphic_url</code>. For example -
|
345
|
-
#
|
346
|
-
# def create
|
347
|
-
# @project = Project.find(params[:project_id])
|
348
|
-
# @task = @project.comments.build(params[:task])
|
349
|
-
# flash[:notice] = 'Task was successfully created.' if @task.save
|
350
|
-
# respond_with(@project, @task)
|
351
|
-
# end
|
352
|
-
#
|
353
|
-
# This would cause +respond_with+ to redirect to <code>project_task_url</code>
|
354
|
-
# instead of <code>task_url</code>. For request formats other than html or
|
355
|
-
# javascript, if multiple resources are passed in this way, it is the last
|
356
|
-
# one specified that is rendered.
|
357
|
-
#
|
358
|
-
# === Customizing response behavior
|
359
|
-
#
|
360
|
-
# Like +respond_to+, +respond_with+ may also be called with a block that
|
361
|
-
# can be used to overwrite any of the default responses, e.g. -
|
362
|
-
#
|
363
|
-
# def create
|
364
|
-
# @user = User.new(params[:user])
|
365
|
-
# flash[:notice] = "User was successfully created." if @user.save
|
366
|
-
#
|
367
|
-
# respond_with(@user) do |format|
|
368
|
-
# format.html { render }
|
369
|
-
# end
|
370
|
-
# end
|
371
|
-
#
|
372
|
-
# The argument passed to the block is an ActionController::MimeResponds::Collector
|
373
|
-
# object which stores the responses for the formats defined within the
|
374
|
-
# block. Note that formats with responses defined explicitly in this way
|
375
|
-
# do not have to first be declared using the class method +respond_to+.
|
376
|
-
#
|
377
|
-
# Also, a hash passed to +respond_with+ immediately after the specified
|
378
|
-
# resource(s) is interpreted as a set of options relevant to all
|
379
|
-
# formats. Any option accepted by +render+ can be used, e.g.
|
380
|
-
# respond_with @people, status: 200
|
381
|
-
# However, note that these options are ignored after an unsuccessful attempt
|
382
|
-
# to save a resource, e.g. when automatically rendering <tt>:new</tt>
|
383
|
-
# after a post request.
|
384
|
-
#
|
385
|
-
# Two additional options are relevant specifically to +respond_with+ -
|
386
|
-
# 1. <tt>:location</tt> - overwrites the default redirect location used after
|
387
|
-
# a successful html +post+ request.
|
388
|
-
# 2. <tt>:action</tt> - overwrites the default render action used after an
|
389
|
-
# unsuccessful html +post+ request.
|
390
|
-
def respond_with(*resources, &block)
|
391
|
-
if self.class.mimes_for_respond_to.empty?
|
392
|
-
raise "In order to use respond_with, first you need to declare the " \
|
393
|
-
"formats your controller responds to in the class level."
|
394
|
-
end
|
395
|
-
|
396
|
-
if collector = retrieve_collector_from_mimes(&block)
|
397
|
-
options = resources.size == 1 ? {} : resources.extract_options!
|
398
|
-
options = options.clone
|
399
|
-
options[:default_response] = collector.response
|
400
|
-
(options.delete(:responder) || self.class.responder).call(self, resources, options)
|
401
|
-
end
|
402
|
-
end
|
403
|
-
|
404
|
-
protected
|
405
|
-
|
406
|
-
# Collect mimes declared in the class method respond_to valid for the
|
407
|
-
# current action.
|
408
|
-
def collect_mimes_from_class_level #:nodoc:
|
409
|
-
action = action_name.to_s
|
410
|
-
|
411
|
-
self.class.mimes_for_respond_to.keys.select do |mime|
|
412
|
-
config = self.class.mimes_for_respond_to[mime]
|
413
|
-
|
414
|
-
if config[:except]
|
415
|
-
!config[:except].include?(action)
|
416
|
-
elsif config[:only]
|
417
|
-
config[:only].include?(action)
|
418
|
-
else
|
419
|
-
true
|
420
|
-
end
|
421
|
-
end
|
422
|
-
end
|
423
|
-
|
424
|
-
# Returns a Collector object containing the appropriate mime-type response
|
425
|
-
# for the current request, based on the available responses defined by a block.
|
426
|
-
# In typical usage this is the block passed to +respond_with+ or +respond_to+.
|
427
|
-
#
|
428
|
-
# Sends :not_acceptable to the client and returns nil if no suitable format
|
429
|
-
# is available.
|
430
|
-
def retrieve_collector_from_mimes(mimes=nil, &block) #:nodoc:
|
431
|
-
mimes ||= collect_mimes_from_class_level
|
432
211
|
collector = Collector.new(mimes, request.variant)
|
433
|
-
|
434
|
-
format = collector.negotiate_format(request)
|
212
|
+
yield collector if block_given?
|
435
213
|
|
436
|
-
if format
|
214
|
+
if format = collector.negotiate_format(request)
|
437
215
|
_process_format(format)
|
438
|
-
collector
|
216
|
+
response = collector.response
|
217
|
+
response ? response.call : render({})
|
439
218
|
else
|
440
219
|
raise ActionController::UnknownFormat
|
441
220
|
end
|
@@ -444,8 +223,8 @@ module ActionController #:nodoc:
|
|
444
223
|
# A container for responses available from the current controller for
|
445
224
|
# requests for different mime-types sent to a particular action.
|
446
225
|
#
|
447
|
-
# The public controller methods +
|
448
|
-
#
|
226
|
+
# The public controller methods +respond_to+ may be called with a block
|
227
|
+
# that is used to define responses to different mime-types, e.g.
|
449
228
|
# for +respond_to+ :
|
450
229
|
#
|
451
230
|
# respond_to do |format|
|