actionpack 6.1.7.5 → 7.0.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +323 -399
  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 +27 -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