actionpack 6.1.7.5 → 7.0.8

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.

Files changed (128) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +319 -401
  3. data/MIT-LICENSE +1 -0
  4. data/README.rdoc +4 -5
  5. data/lib/abstract_controller/asset_paths.rb +1 -1
  6. data/lib/abstract_controller/base.rb +13 -26
  7. data/lib/abstract_controller/caching/fragments.rb +2 -2
  8. data/lib/abstract_controller/caching.rb +1 -1
  9. data/lib/abstract_controller/callbacks.rb +21 -7
  10. data/lib/abstract_controller/collector.rb +2 -2
  11. data/lib/abstract_controller/error.rb +1 -1
  12. data/lib/abstract_controller/helpers.rb +17 -12
  13. data/lib/abstract_controller/logger.rb +1 -1
  14. data/lib/abstract_controller/railties/routes_helpers.rb +2 -0
  15. data/lib/abstract_controller/rendering.rb +9 -11
  16. data/lib/abstract_controller/translation.rb +5 -4
  17. data/lib/abstract_controller/url_for.rb +4 -6
  18. data/lib/action_controller/api.rb +7 -7
  19. data/lib/action_controller/base.rb +5 -4
  20. data/lib/action_controller/form_builder.rb +2 -2
  21. data/lib/action_controller/log_subscriber.rb +4 -3
  22. data/lib/action_controller/metal/basic_implicit_render.rb +3 -1
  23. data/lib/action_controller/metal/conditional_get.rb +137 -102
  24. data/lib/action_controller/metal/content_security_policy.rb +36 -2
  25. data/lib/action_controller/metal/cookies.rb +1 -1
  26. data/lib/action_controller/metal/data_streaming.rb +23 -31
  27. data/lib/action_controller/metal/etag_with_flash.rb +1 -1
  28. data/lib/action_controller/metal/exceptions.rb +19 -30
  29. data/lib/action_controller/metal/flash.rb +6 -2
  30. data/lib/action_controller/metal/head.rb +1 -1
  31. data/lib/action_controller/metal/helpers.rb +2 -2
  32. data/lib/action_controller/metal/http_authentication.rb +66 -39
  33. data/lib/action_controller/metal/instrumentation.rb +57 -52
  34. data/lib/action_controller/metal/live.rb +43 -2
  35. data/lib/action_controller/metal/mime_responds.rb +3 -3
  36. data/lib/action_controller/metal/params_wrapper.rb +20 -11
  37. data/lib/action_controller/metal/permissions_policy.rb +19 -28
  38. data/lib/action_controller/metal/redirecting.rb +95 -22
  39. data/lib/action_controller/metal/renderers.rb +12 -13
  40. data/lib/action_controller/metal/rendering.rb +121 -9
  41. data/lib/action_controller/metal/request_forgery_protection.rb +83 -32
  42. data/lib/action_controller/metal/rescue.rb +5 -4
  43. data/lib/action_controller/metal/streaming.rb +7 -9
  44. data/lib/action_controller/metal/strong_parameters.rb +138 -115
  45. data/lib/action_controller/metal/testing.rb +9 -2
  46. data/lib/action_controller/metal/url_for.rb +3 -5
  47. data/lib/action_controller/metal.rb +10 -13
  48. data/lib/action_controller/railtie.rb +50 -6
  49. data/lib/action_controller/renderer.rb +1 -20
  50. data/lib/action_controller/test_case.rb +28 -7
  51. data/lib/action_controller.rb +2 -5
  52. data/lib/action_dispatch/http/cache.rb +20 -13
  53. data/lib/action_dispatch/http/content_security_policy.rb +113 -36
  54. data/lib/action_dispatch/http/filter_parameters.rb +4 -19
  55. data/lib/action_dispatch/http/headers.rb +1 -1
  56. data/lib/action_dispatch/http/mime_negotiation.rb +15 -5
  57. data/lib/action_dispatch/http/mime_type.rb +9 -11
  58. data/lib/action_dispatch/http/parameters.rb +5 -5
  59. data/lib/action_dispatch/http/permissions_policy.rb +17 -1
  60. data/lib/action_dispatch/http/request.rb +27 -37
  61. data/lib/action_dispatch/http/response.rb +3 -20
  62. data/lib/action_dispatch/http/upload.rb +13 -2
  63. data/lib/action_dispatch/http/url.rb +11 -19
  64. data/lib/action_dispatch/journey/gtg/builder.rb +11 -12
  65. data/lib/action_dispatch/journey/gtg/simulator.rb +10 -4
  66. data/lib/action_dispatch/journey/gtg/transition_table.rb +77 -21
  67. data/lib/action_dispatch/journey/nodes/node.rb +70 -5
  68. data/lib/action_dispatch/journey/path/pattern.rb +22 -13
  69. data/lib/action_dispatch/journey/route.rb +6 -13
  70. data/lib/action_dispatch/journey/router/utils.rb +2 -2
  71. data/lib/action_dispatch/journey/router.rb +1 -1
  72. data/lib/action_dispatch/journey/routes.rb +3 -3
  73. data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
  74. data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
  75. data/lib/action_dispatch/middleware/actionable_exceptions.rb +0 -1
  76. data/lib/action_dispatch/middleware/cookies.rb +20 -13
  77. data/lib/action_dispatch/middleware/debug_exceptions.rb +6 -4
  78. data/lib/action_dispatch/middleware/debug_locks.rb +3 -3
  79. data/lib/action_dispatch/middleware/exception_wrapper.rb +4 -0
  80. data/lib/action_dispatch/middleware/executor.rb +3 -0
  81. data/lib/action_dispatch/middleware/flash.rb +17 -18
  82. data/lib/action_dispatch/middleware/host_authorization.rb +13 -17
  83. data/lib/action_dispatch/middleware/remote_ip.rb +20 -8
  84. data/lib/action_dispatch/middleware/request_id.rb +3 -3
  85. data/lib/action_dispatch/middleware/server_timing.rb +76 -0
  86. data/lib/action_dispatch/middleware/session/abstract_store.rb +1 -1
  87. data/lib/action_dispatch/middleware/session/cookie_store.rb +9 -9
  88. data/lib/action_dispatch/middleware/show_exceptions.rb +17 -16
  89. data/lib/action_dispatch/middleware/stack.rb +27 -9
  90. data/lib/action_dispatch/middleware/static.rb +5 -9
  91. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +1 -1
  92. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -11
  93. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +2 -2
  94. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +10 -5
  95. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +7 -3
  96. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +4 -4
  97. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +3 -3
  98. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +28 -18
  99. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +3 -3
  100. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +3 -3
  101. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +3 -3
  102. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +3 -3
  103. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +3 -3
  104. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +22 -22
  105. data/lib/action_dispatch/railtie.rb +8 -2
  106. data/lib/action_dispatch/request/session.rb +43 -13
  107. data/lib/action_dispatch/routing/inspector.rb +1 -1
  108. data/lib/action_dispatch/routing/mapper.rb +82 -83
  109. data/lib/action_dispatch/routing/redirection.rb +5 -2
  110. data/lib/action_dispatch/routing/route_set.rb +17 -7
  111. data/lib/action_dispatch/routing/routes_proxy.rb +1 -1
  112. data/lib/action_dispatch/routing/url_for.rb +24 -25
  113. data/lib/action_dispatch/routing.rb +5 -6
  114. data/lib/action_dispatch/system_test_case.rb +5 -5
  115. data/lib/action_dispatch/system_testing/browser.rb +3 -13
  116. data/lib/action_dispatch/system_testing/driver.rb +34 -10
  117. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +11 -7
  118. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +0 -8
  119. data/lib/action_dispatch/testing/assertions/response.rb +1 -1
  120. data/lib/action_dispatch/testing/assertions/routing.rb +3 -2
  121. data/lib/action_dispatch/testing/assertions.rb +2 -5
  122. data/lib/action_dispatch/testing/integration.rb +6 -8
  123. data/lib/action_dispatch/testing/test_process.rb +3 -29
  124. data/lib/action_dispatch/testing/test_response.rb +20 -2
  125. data/lib/action_dispatch.rb +1 -0
  126. data/lib/action_pack/gem_version.rb +5 -5
  127. data/lib/action_pack/version.rb +1 -1
  128. metadata +16 -15
@@ -9,11 +9,14 @@ module ActionController
9
9
  # Wraps the parameters hash into a nested hash. This will allow clients to
10
10
  # submit requests without having to specify any root elements.
11
11
  #
12
- # This functionality is enabled in +config/initializers/wrap_parameters.rb+
13
- # and can be customized.
12
+ # This functionality is enabled by default for JSON, and can be customized by
13
+ # setting the format array:
14
14
  #
15
- # You could also turn it on per controller by setting the format array to
16
- # a non-empty array:
15
+ # class ApplicationController < ActionController::Base
16
+ # wrap_parameters format: [:json, :xml]
17
+ # end
18
+ #
19
+ # You could also turn it on per controller:
17
20
  #
18
21
  # class UsersController < ApplicationController
19
22
  # wrap_parameters format: [:json, :xml, :url_encoded_form, :multipart_form]
@@ -68,6 +71,12 @@ module ActionController
68
71
  # will try to check if <tt>Admin::User</tt> or +User+ model exists, and use it to
69
72
  # determine the wrapper key respectively. If both models don't exist,
70
73
  # it will then fallback to use +user+ as the key.
74
+ #
75
+ # To disable this functionality for a controller:
76
+ #
77
+ # class UsersController < ApplicationController
78
+ # wrap_parameters false
79
+ # end
71
80
  module ParamsWrapper
72
81
  extend ActiveSupport::Concern
73
82
 
@@ -242,14 +251,14 @@ module ActionController
242
251
  end
243
252
  end
244
253
 
245
- # Performs parameters wrapping upon the request. Called automatically
246
- # by the metal call stack.
247
- def process_action(*)
248
- _perform_parameter_wrapping if _wrapper_enabled?
249
- super
250
- end
251
-
252
254
  private
255
+ # Performs parameters wrapping upon the request. Called automatically
256
+ # by the metal call stack.
257
+ def process_action(*)
258
+ _perform_parameter_wrapping if _wrapper_enabled?
259
+ super
260
+ end
261
+
253
262
  # Returns the wrapper key which will be used to store wrapped parameters.
254
263
  def _wrapper_key
255
264
  _wrapper_options.name
@@ -1,37 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module ActionController #:nodoc:
4
- # HTTP Permissions Policy is a web standard for defining a mechanism to
5
- # allow and deny the use of browser permissions in its own context, and
6
- # in content within any <iframe> elements in the document.
7
- #
8
- # Full details of HTTP Permissions Policy specification and guidelines can
9
- # be found at MDN:
10
- #
11
- # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Feature-Policy
12
- #
13
- # Examples of usage:
14
- #
15
- # # Global policy
16
- # Rails.application.config.permissions_policy do |f|
17
- # f.camera :none
18
- # f.gyroscope :none
19
- # f.microphone :none
20
- # f.usb :none
21
- # f.fullscreen :self
22
- # f.payment :self, "https://secure.example.com"
23
- # end
24
- #
25
- # # Controller level policy
26
- # class PagesController < ApplicationController
27
- # permissions_policy do |p|
28
- # p.geolocation "https://example.com"
29
- # end
30
- # end
3
+ module ActionController # :nodoc:
31
4
  module PermissionsPolicy
32
5
  extend ActiveSupport::Concern
33
6
 
34
7
  module ClassMethods
8
+ # Overrides parts of the globally configured +Feature-Policy+
9
+ # header:
10
+ #
11
+ # class PagesController < ApplicationController
12
+ # permissions_policy do |policy|
13
+ # policy.geolocation "https://example.com"
14
+ # end
15
+ # end
16
+ #
17
+ # Options can be passed similar to +before_action+. For example, pass
18
+ # <tt>only: :index</tt> to override the header on the index action only:
19
+ #
20
+ # class PagesController < ApplicationController
21
+ # permissions_policy(only: :index) do |policy|
22
+ # policy.camera :self
23
+ # end
24
+ # end
25
+ #
35
26
  def permissions_policy(**options, &block)
36
27
  before_action(options) do
37
28
  if block_given?
@@ -4,13 +4,17 @@ module ActionController
4
4
  module Redirecting
5
5
  extend ActiveSupport::Concern
6
6
 
7
+ ILLEGAL_HEADER_VALUE_REGEX = /[\x00-\x08\x0A-\x1F]/.freeze
8
+
7
9
  include AbstractController::Logger
8
10
  include ActionController::UrlFor
9
11
 
10
- ILLEGAL_HEADER_VALUE_REGEX = /[\x00-\x08\x0A-\x1F]/.freeze
11
-
12
12
  class UnsafeRedirectError < StandardError; end
13
13
 
14
+ included do
15
+ mattr_accessor :raise_on_open_redirects, default: false
16
+ end
17
+
14
18
  # Redirects the browser to the target specified in +options+. This parameter can be any one of:
15
19
  #
16
20
  # * <tt>Hash</tt> - The URL will be generated by calling url_for with the +options+.
@@ -58,20 +62,46 @@ module ActionController
58
62
  #
59
63
  # Statements after +redirect_to+ in our controller get executed, so +redirect_to+ doesn't stop the execution of the function.
60
64
  # To terminate the execution of the function immediately after the +redirect_to+, use return.
65
+ #
61
66
  # redirect_to post_url(@post) and return
67
+ #
68
+ # === Open Redirect protection
69
+ #
70
+ # By default, Rails protects against redirecting to external hosts for your app's safety, so called open redirects.
71
+ # Note: this was a new default in Rails 7.0, after upgrading opt-in by uncommenting the line with +raise_on_open_redirects+ in <tt>config/initializers/new_framework_defaults_7_0.rb</tt>
72
+ #
73
+ # Here #redirect_to automatically validates the potentially-unsafe URL:
74
+ #
75
+ # redirect_to params[:redirect_url]
76
+ #
77
+ # Raises UnsafeRedirectError in the case of an unsafe redirect.
78
+ #
79
+ # To allow any external redirects pass <tt>allow_other_host: true</tt>, though using a user-provided param in that case is unsafe.
80
+ #
81
+ # redirect_to "https://rubyonrails.org", allow_other_host: true
82
+ #
83
+ # See #url_from for more information on what an internal and safe URL is, or how to fall back to an alternate redirect URL in the unsafe case.
62
84
  def redirect_to(options = {}, response_options = {})
63
85
  raise ActionControllerError.new("Cannot redirect to nil!") unless options
64
86
  raise AbstractController::DoubleRenderError if response_body
65
87
 
66
- self.status = _extract_redirect_to_status(options, response_options)
88
+ allow_other_host = response_options.delete(:allow_other_host) { _allow_other_host }
89
+
90
+ self.status = _extract_redirect_to_status(options, response_options)
67
91
 
68
92
  redirect_to_location = _compute_redirect_to_location(request, options)
69
93
  _ensure_url_is_http_header_safe(redirect_to_location)
70
94
 
71
- self.location = redirect_to_location
95
+ self.location = _enforce_open_redirect_protection(redirect_to_location, allow_other_host: allow_other_host)
72
96
  self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(response.location)}\">redirected</a>.</body></html>"
73
97
  end
74
98
 
99
+ # Soft deprecated alias for #redirect_back_or_to where the +fallback_location+ location is supplied as a keyword argument instead
100
+ # of the first positional argument.
101
+ def redirect_back(fallback_location:, allow_other_host: _allow_other_host, **args)
102
+ redirect_back_or_to fallback_location, allow_other_host: allow_other_host, **args
103
+ end
104
+
75
105
  # Redirects the browser to the page that issued the request (the referrer)
76
106
  # if possible, otherwise redirects to the provided default fallback
77
107
  # location.
@@ -81,35 +111,37 @@ module ActionController
81
111
  # subject to browser security settings and user preferences. If the request
82
112
  # is missing this header, the <tt>fallback_location</tt> will be used.
83
113
  #
84
- # redirect_back fallback_location: { action: "show", id: 5 }
85
- # redirect_back fallback_location: @post
86
- # redirect_back fallback_location: "http://www.rubyonrails.org"
87
- # redirect_back fallback_location: "/images/screenshot.jpg"
88
- # redirect_back fallback_location: posts_url
89
- # redirect_back fallback_location: proc { edit_post_url(@post) }
90
- # redirect_back fallback_location: '/', allow_other_host: false
114
+ # redirect_back_or_to({ action: "show", id: 5 })
115
+ # redirect_back_or_to @post
116
+ # redirect_back_or_to "http://www.rubyonrails.org"
117
+ # redirect_back_or_to "/images/screenshot.jpg"
118
+ # redirect_back_or_to posts_url
119
+ # redirect_back_or_to proc { edit_post_url(@post) }
120
+ # redirect_back_or_to '/', allow_other_host: false
91
121
  #
92
122
  # ==== Options
93
- # * <tt>:fallback_location</tt> - The default fallback location that will be used on missing +Referer+ header.
94
123
  # * <tt>:allow_other_host</tt> - Allow or disallow redirection to the host that is different to the current host, defaults to true.
95
124
  #
96
125
  # All other options that can be passed to #redirect_to are accepted as
97
- # options and the behavior is identical.
98
- def redirect_back(fallback_location:, allow_other_host: true, **args)
99
- referer = request.headers["Referer"]
100
- redirect_to_referer = referer && (allow_other_host || _url_host_allowed?(referer))
101
- redirect_to redirect_to_referer ? referer : fallback_location, **args
126
+ # options, and the behavior is identical.
127
+ def redirect_back_or_to(fallback_location, allow_other_host: _allow_other_host, **options)
128
+ if request.referer && (allow_other_host || _url_host_allowed?(request.referer))
129
+ redirect_to request.referer, allow_other_host: allow_other_host, **options
130
+ else
131
+ # The method level `allow_other_host` doesn't apply in the fallback case, omit and let the `redirect_to` handling take over.
132
+ redirect_to fallback_location, **options
133
+ end
102
134
  end
103
135
 
104
- def _compute_redirect_to_location(request, options) #:nodoc:
136
+ def _compute_redirect_to_location(request, options) # :nodoc:
105
137
  case options
106
138
  # The scheme name consist of a letter followed by any combination of
107
139
  # letters, digits, and the plus ("+"), period ("."), or hyphen ("-")
108
140
  # characters; and is terminated by a colon (":").
109
141
  # See https://tools.ietf.org/html/rfc3986#section-3.1
110
142
  # The protocol relative scheme starts with a double slash "//".
111
- when /\A([a-z][a-z\d\-+\.]*:|\/\/).*/i
112
- options
143
+ when /\A([a-z][a-z\d\-+.]*:|\/\/).*/i
144
+ options.to_str
113
145
  when String
114
146
  request.protocol + request.host_with_port + options
115
147
  when Proc
@@ -121,7 +153,35 @@ module ActionController
121
153
  module_function :_compute_redirect_to_location
122
154
  public :_compute_redirect_to_location
123
155
 
156
+ # Verifies the passed +location+ is an internal URL that's safe to redirect to and returns it, or nil if not.
157
+ # Useful to wrap a params provided redirect URL and fallback to an alternate URL to redirect to:
158
+ #
159
+ # redirect_to url_from(params[:redirect_url]) || root_url
160
+ #
161
+ # The +location+ is considered internal, and safe, if it's on the same host as <tt>request.host</tt>:
162
+ #
163
+ # # If request.host is example.com:
164
+ # url_from("https://example.com/profile") # => "https://example.com/profile"
165
+ # url_from("http://example.com/profile") # => "http://example.com/profile"
166
+ # url_from("http://evil.com/profile") # => nil
167
+ #
168
+ # Subdomains are considered part of the host:
169
+ #
170
+ # # If request.host is on https://example.com or https://app.example.com, you'd get:
171
+ # url_from("https://dev.example.com/profile") # => nil
172
+ #
173
+ # NOTE: there's a similarity with {url_for}[rdoc-ref:ActionDispatch::Routing::UrlFor#url_for], which generates an internal URL from various options from within the app, e.g. <tt>url_for(@post)</tt>.
174
+ # However, #url_from is meant to take an external parameter to verify as in <tt>url_from(params[:redirect_url])</tt>.
175
+ def url_from(location)
176
+ location = location.presence
177
+ location if location && _url_host_allowed?(location)
178
+ end
179
+
124
180
  private
181
+ def _allow_other_host
182
+ !raise_on_open_redirects
183
+ end
184
+
125
185
  def _extract_redirect_to_status(options, response_options)
126
186
  if options.is_a?(Hash) && options.key?(:status)
127
187
  Rack::Utils.status_code(options.delete(:status))
@@ -132,8 +192,21 @@ module ActionController
132
192
  end
133
193
  end
134
194
 
195
+ def _enforce_open_redirect_protection(location, allow_other_host:)
196
+ if allow_other_host || _url_host_allowed?(location)
197
+ location
198
+ else
199
+ raise UnsafeRedirectError, "Unsafe redirect to #{location.truncate(100).inspect}, pass allow_other_host: true to redirect anyway."
200
+ end
201
+ end
202
+
135
203
  def _url_host_allowed?(url)
136
- URI(url.to_s).host == request.host
204
+ host = URI(url.to_s).host
205
+
206
+ return true if host == request.host
207
+ return false unless host.nil?
208
+ return false unless url.to_s.start_with?("/")
209
+ !url.to_s.start_with?("//")
137
210
  rescue ArgumentError, URI::Error
138
211
  false
139
212
  end
@@ -142,7 +215,7 @@ module ActionController
142
215
  # Attempt to comply with the set of valid token characters
143
216
  # defined for an HTTP header value in
144
217
  # https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.6
145
- if url.match(ILLEGAL_HEADER_VALUE_REGEX)
218
+ if url.match?(ILLEGAL_HEADER_VALUE_REGEX)
146
219
  msg = "The redirect URL #{url} contains one or more illegal HTTP header field character. " \
147
220
  "Set of legal characters defined in https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.6"
148
221
  raise UnsafeRedirectError, msg
@@ -3,12 +3,12 @@
3
3
  require "set"
4
4
 
5
5
  module ActionController
6
- # See <tt>Renderers.add</tt>
6
+ # See Renderers.add
7
7
  def self.add_renderer(key, &block)
8
8
  Renderers.add(key, &block)
9
9
  end
10
10
 
11
- # See <tt>Renderers.remove</tt>
11
+ # See Renderers.remove
12
12
  def self.remove_renderer(key)
13
13
  Renderers.remove(key)
14
14
  end
@@ -31,8 +31,7 @@ module ActionController
31
31
  class_attribute :_renderers, default: Set.new.freeze
32
32
  end
33
33
 
34
- # Used in <tt>ActionController::Base</tt>
35
- # and <tt>ActionController::API</tt> to include all
34
+ # Used in ActionController::Base and ActionController::API to include all
36
35
  # renderers by default.
37
36
  module All
38
37
  extend ActiveSupport::Concern
@@ -45,7 +44,7 @@ module ActionController
45
44
 
46
45
  # Adds a new renderer to call within controller actions.
47
46
  # A renderer is invoked by passing its name as an option to
48
- # <tt>AbstractController::Rendering#render</tt>. To create a renderer
47
+ # AbstractController::Rendering#render. To create a renderer
49
48
  # pass it a name and a block. The block takes two arguments, the first
50
49
  # is the value paired with its key and the second is the remaining
51
50
  # hash of options passed to +render+.
@@ -96,18 +95,18 @@ module ActionController
96
95
  # Adds, by name, a renderer or renderers to the +_renderers+ available
97
96
  # to call within controller actions.
98
97
  #
99
- # It is useful when rendering from an <tt>ActionController::Metal</tt> controller or
98
+ # It is useful when rendering from an ActionController::Metal controller or
100
99
  # otherwise to add an available renderer proc to a specific controller.
101
100
  #
102
- # Both <tt>ActionController::Base</tt> and <tt>ActionController::API</tt>
103
- # include <tt>ActionController::Renderers::All</tt>, making all renderers
101
+ # Both ActionController::Base and ActionController::API
102
+ # include ActionController::Renderers::All, making all renderers
104
103
  # available in the controller. See <tt>Renderers::RENDERERS</tt> and <tt>Renderers.add</tt>.
105
104
  #
106
- # Since <tt>ActionController::Metal</tt> controllers cannot render, the controller
107
- # must include <tt>AbstractController::Rendering</tt>, <tt>ActionController::Rendering</tt>,
108
- # and <tt>ActionController::Renderers</tt>, and have at least one renderer.
105
+ # Since ActionController::Metal controllers cannot render, the controller
106
+ # must include AbstractController::Rendering, ActionController::Rendering,
107
+ # and ActionController::Renderers, and have at least one renderer.
109
108
  #
110
- # Rather than including <tt>ActionController::Renderers::All</tt> and including all renderers,
109
+ # Rather than including ActionController::Renderers::All and including all renderers,
111
110
  # you may specify which renderers to include by passing the renderer name or names to
112
111
  # +use_renderers+. For example, a controller that includes only the <tt>:json</tt> renderer
113
112
  # (+_render_with_renderer_json+) might look like:
@@ -133,7 +132,7 @@ module ActionController
133
132
  alias use_renderer use_renderers
134
133
  end
135
134
 
136
- # Called by +render+ in <tt>AbstractController::Rendering</tt>
135
+ # Called by +render+ in AbstractController::Rendering
137
136
  # which sets the return value as the +response_body+.
138
137
  #
139
138
  # If no renderer is found, +super+ returns control to
@@ -24,19 +24,125 @@ module ActionController
24
24
  end
25
25
  end
26
26
 
27
- # Before processing, set the request formats in current controller formats.
28
- def process_action(*) #:nodoc:
29
- self.formats = request.formats.map(&:ref).compact
30
- super
31
- end
32
-
27
+ # Renders a template and assigns the result to +self.response_body+.
28
+ #
29
+ # If no rendering mode option is specified, the template will be derived
30
+ # from the first argument.
31
+ #
32
+ # render "posts/show"
33
+ # # => renders app/views/posts/show.html.erb
34
+ #
35
+ # # In a PostsController action...
36
+ # render :show
37
+ # # => renders app/views/posts/show.html.erb
38
+ #
39
+ # If the first argument responds to +render_in+, the template will be
40
+ # rendered by calling +render_in+ with the current view context.
41
+ #
42
+ # ==== \Rendering Mode
43
+ #
44
+ # [+:partial+]
45
+ # See ActionView::PartialRenderer for details.
46
+ #
47
+ # render partial: "posts/form", locals: { post: Post.new }
48
+ # # => renders app/views/posts/_form.html.erb
49
+ #
50
+ # [+:file+]
51
+ # Renders the contents of a file. This option should <b>not</b> be used
52
+ # with unsanitized user input.
53
+ #
54
+ # render file: "/path/to/some/file"
55
+ # # => renders /path/to/some/file
56
+ #
57
+ # [+:inline+]
58
+ # Renders an ERB template string.
59
+ #
60
+ # @name = "World"
61
+ # render inline: "<h1>Hello, <%= @name %>!</h1>"
62
+ # # => renders "<h1>Hello, World!</h1>"
63
+ #
64
+ # [+:body+]
65
+ # Renders the provided text, and sets the content type as +text/plain+.
66
+ #
67
+ # render body: "Hello, World!"
68
+ # # => renders "Hello, World!"
69
+ #
70
+ # [+:plain+]
71
+ # Renders the provided text, and sets the content type as +text/plain+.
72
+ #
73
+ # render plain: "Hello, World!"
74
+ # # => renders "Hello, World!"
75
+ #
76
+ # [+:html+]
77
+ # Renders the provided HTML string, and sets the content type as +text/html+.
78
+ # If the string is not +html_safe?+, performs HTML escaping on the string
79
+ # before rendering.
80
+ #
81
+ # render html: "<h1>Hello, World!</h1>".html_safe
82
+ # # => renders "<h1>Hello, World!</h1>"
83
+ #
84
+ # render html: "<h1>Hello, World!</h1>"
85
+ # # => renders "&lt;h1&gt;Hello, World!&lt;/h1&gt;"
86
+ #
87
+ # [+:json+]
88
+ # Renders the provided object as JSON, and sets the content type as
89
+ # +application/json+. If the object is not a string, it will be converted
90
+ # to JSON by calling +to_json+.
91
+ #
92
+ # render json: { hello: "world" }
93
+ # # => renders "{\"hello\":\"world\"}"
94
+ #
95
+ # By default, when a rendering mode is specified, no layout template is
96
+ # rendered.
97
+ #
98
+ # ==== Options
99
+ #
100
+ # [+:assigns+]
101
+ # Hash of instance variable assignments for the template.
102
+ #
103
+ # render inline: "<h1>Hello, <%= @name %>!</h1>", assigns: { name: "World" }
104
+ # # => renders "<h1>Hello, World!</h1>"
105
+ #
106
+ # [+:locals+]
107
+ # Hash of local variable assignments for the template.
108
+ #
109
+ # render inline: "<h1>Hello, <%= name %>!</h1>", locals: { name: "World" }
110
+ # # => renders "<h1>Hello, World!</h1>"
111
+ #
112
+ # [+:layout+]
113
+ # The layout template to render. Can also be +false+ or +true+ to disable
114
+ # or (re)enable the default layout template.
115
+ #
116
+ # render "posts/show", layout: "holiday"
117
+ # # => renders app/views/posts/show.html.erb with the app/views/layouts/holiday.html.erb layout
118
+ #
119
+ # render "posts/show", layout: false
120
+ # # => renders app/views/posts/show.html.erb with no layout
121
+ #
122
+ # render inline: "<h1>Hello, World!</h1>", layout: true
123
+ # # => renders "<h1>Hello, World!</h1>" with the default layout
124
+ #
125
+ # [+:status+]
126
+ # The HTTP status code to send with the response. Can be specified as a
127
+ # number or as the status name in Symbol form. Defaults to 200.
128
+ #
129
+ # render "posts/new", status: 422
130
+ # # => renders app/views/posts/new.html.erb with HTTP status code 422
131
+ #
132
+ # render "posts/new", status: :unprocessable_entity
133
+ # # => renders app/views/posts/new.html.erb with HTTP status code 422
134
+ #
135
+ #--
33
136
  # Check for double render errors and set the content_type after rendering.
34
- def render(*args) #:nodoc:
137
+ def render(*args)
35
138
  raise ::AbstractController::DoubleRenderError if response_body
36
139
  super
37
140
  end
38
141
 
39
- # Overwrite render_to_string because body can now be set to a Rack body.
142
+ # Similar to #render, but only returns the rendered template as a string,
143
+ # instead of setting +self.response_body+.
144
+ #--
145
+ # Override render_to_string because body can now be set to a Rack body.
40
146
  def render_to_string(*)
41
147
  result = super
42
148
  if result.respond_to?(:each)
@@ -48,11 +154,17 @@ module ActionController
48
154
  end
49
155
  end
50
156
 
51
- def render_to_body(options = {})
157
+ def render_to_body(options = {}) # :nodoc:
52
158
  super || _render_in_priorities(options) || " "
53
159
  end
54
160
 
55
161
  private
162
+ # Before processing, set the request formats in current controller formats.
163
+ def process_action(*) # :nodoc:
164
+ self.formats = request.formats.filter_map(&:ref)
165
+ super
166
+ end
167
+
56
168
  def _process_variant(options)
57
169
  if defined?(request) && !request.nil? && request.variant.present?
58
170
  options[:variant] = request.variant