actionpack 6.1.7 → 7.0.4.1

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 (121) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +269 -406
  3. data/MIT-LICENSE +1 -0
  4. data/README.rdoc +2 -3
  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 +4 -3
  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/translation.rb +3 -2
  16. data/lib/abstract_controller/url_for.rb +4 -6
  17. data/lib/action_controller/api.rb +6 -6
  18. data/lib/action_controller/base.rb +5 -4
  19. data/lib/action_controller/form_builder.rb +2 -2
  20. data/lib/action_controller/log_subscriber.rb +4 -3
  21. data/lib/action_controller/metal/conditional_get.rb +39 -2
  22. data/lib/action_controller/metal/content_security_policy.rb +36 -2
  23. data/lib/action_controller/metal/cookies.rb +1 -1
  24. data/lib/action_controller/metal/data_streaming.rb +5 -13
  25. data/lib/action_controller/metal/exceptions.rb +19 -30
  26. data/lib/action_controller/metal/flash.rb +6 -2
  27. data/lib/action_controller/metal/helpers.rb +2 -2
  28. data/lib/action_controller/metal/http_authentication.rb +66 -39
  29. data/lib/action_controller/metal/instrumentation.rb +57 -52
  30. data/lib/action_controller/metal/live.rb +43 -2
  31. data/lib/action_controller/metal/mime_responds.rb +3 -3
  32. data/lib/action_controller/metal/params_wrapper.rb +20 -11
  33. data/lib/action_controller/metal/permissions_policy.rb +19 -28
  34. data/lib/action_controller/metal/redirecting.rb +93 -18
  35. data/lib/action_controller/metal/renderers.rb +10 -11
  36. data/lib/action_controller/metal/rendering.rb +8 -8
  37. data/lib/action_controller/metal/request_forgery_protection.rb +78 -29
  38. data/lib/action_controller/metal/rescue.rb +1 -1
  39. data/lib/action_controller/metal/streaming.rb +6 -8
  40. data/lib/action_controller/metal/strong_parameters.rb +100 -54
  41. data/lib/action_controller/metal/testing.rb +9 -2
  42. data/lib/action_controller/metal/url_for.rb +3 -3
  43. data/lib/action_controller/metal.rb +10 -13
  44. data/lib/action_controller/railtie.rb +49 -6
  45. data/lib/action_controller/renderer.rb +1 -1
  46. data/lib/action_controller/test_case.rb +28 -7
  47. data/lib/action_controller.rb +2 -5
  48. data/lib/action_dispatch/http/cache.rb +14 -7
  49. data/lib/action_dispatch/http/content_security_policy.rb +108 -35
  50. data/lib/action_dispatch/http/filter_parameters.rb +5 -0
  51. data/lib/action_dispatch/http/mime_negotiation.rb +15 -5
  52. data/lib/action_dispatch/http/mime_type.rb +9 -11
  53. data/lib/action_dispatch/http/parameters.rb +5 -5
  54. data/lib/action_dispatch/http/permissions_policy.rb +17 -1
  55. data/lib/action_dispatch/http/request.rb +12 -21
  56. data/lib/action_dispatch/http/response.rb +3 -16
  57. data/lib/action_dispatch/http/url.rb +11 -19
  58. data/lib/action_dispatch/journey/gtg/builder.rb +11 -12
  59. data/lib/action_dispatch/journey/gtg/simulator.rb +10 -4
  60. data/lib/action_dispatch/journey/gtg/transition_table.rb +77 -21
  61. data/lib/action_dispatch/journey/nodes/node.rb +70 -5
  62. data/lib/action_dispatch/journey/path/pattern.rb +22 -13
  63. data/lib/action_dispatch/journey/route.rb +6 -13
  64. data/lib/action_dispatch/journey/router/utils.rb +2 -2
  65. data/lib/action_dispatch/journey/router.rb +1 -1
  66. data/lib/action_dispatch/journey/routes.rb +3 -3
  67. data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
  68. data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
  69. data/lib/action_dispatch/middleware/actionable_exceptions.rb +0 -1
  70. data/lib/action_dispatch/middleware/cookies.rb +42 -27
  71. data/lib/action_dispatch/middleware/debug_exceptions.rb +6 -4
  72. data/lib/action_dispatch/middleware/debug_locks.rb +3 -3
  73. data/lib/action_dispatch/middleware/exception_wrapper.rb +4 -0
  74. data/lib/action_dispatch/middleware/executor.rb +3 -0
  75. data/lib/action_dispatch/middleware/flash.rb +17 -18
  76. data/lib/action_dispatch/middleware/host_authorization.rb +1 -12
  77. data/lib/action_dispatch/middleware/remote_ip.rb +16 -4
  78. data/lib/action_dispatch/middleware/request_id.rb +1 -1
  79. data/lib/action_dispatch/middleware/server_timing.rb +76 -0
  80. data/lib/action_dispatch/middleware/session/abstract_store.rb +1 -1
  81. data/lib/action_dispatch/middleware/session/cookie_store.rb +9 -9
  82. data/lib/action_dispatch/middleware/show_exceptions.rb +7 -9
  83. data/lib/action_dispatch/middleware/stack.rb +27 -9
  84. data/lib/action_dispatch/middleware/static.rb +2 -6
  85. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +1 -1
  86. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -11
  87. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +2 -2
  88. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +3 -2
  89. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +2 -0
  90. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +4 -4
  91. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +3 -3
  92. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +28 -18
  93. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +3 -3
  94. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +3 -3
  95. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +3 -3
  96. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +3 -3
  97. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +3 -3
  98. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +5 -14
  99. data/lib/action_dispatch/railtie.rb +8 -2
  100. data/lib/action_dispatch/request/session.rb +43 -13
  101. data/lib/action_dispatch/routing/inspector.rb +1 -1
  102. data/lib/action_dispatch/routing/mapper.rb +59 -83
  103. data/lib/action_dispatch/routing/redirection.rb +5 -2
  104. data/lib/action_dispatch/routing/route_set.rb +17 -7
  105. data/lib/action_dispatch/routing/routes_proxy.rb +1 -1
  106. data/lib/action_dispatch/routing/url_for.rb +4 -5
  107. data/lib/action_dispatch/routing.rb +5 -6
  108. data/lib/action_dispatch/system_test_case.rb +5 -5
  109. data/lib/action_dispatch/system_testing/browser.rb +2 -12
  110. data/lib/action_dispatch/system_testing/driver.rb +35 -11
  111. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +11 -7
  112. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +0 -8
  113. data/lib/action_dispatch/testing/assertions/routing.rb +3 -2
  114. data/lib/action_dispatch/testing/assertions.rb +2 -5
  115. data/lib/action_dispatch/testing/integration.rb +6 -8
  116. data/lib/action_dispatch/testing/test_process.rb +3 -29
  117. data/lib/action_dispatch/testing/test_response.rb +20 -2
  118. data/lib/action_dispatch.rb +1 -0
  119. data/lib/action_pack/gem_version.rb +5 -5
  120. data/lib/action_pack/version.rb +1 -1
  121. metadata +16 -15
data/MIT-LICENSE CHANGED
@@ -18,3 +18,4 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
18
  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
19
  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
20
  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
data/README.rdoc CHANGED
@@ -2,9 +2,8 @@
2
2
 
3
3
  Action Pack is a framework for handling and responding to web requests. It
4
4
  provides mechanisms for *routing* (mapping request URLs to actions), defining
5
- *controllers* that implement actions, and generating responses by rendering
6
- *views*, which are templates of various formats. In short, Action Pack
7
- provides the view and controller layers in the MVC paradigm.
5
+ *controllers* that implement actions, and generating responses. In short, Action Pack
6
+ provides the controller layer in the MVC paradigm.
8
7
 
9
8
  It consists of several modules:
10
9
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AbstractController
4
- module AssetPaths #:nodoc:
4
+ module AssetPaths # :nodoc:
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  included do
@@ -9,35 +9,21 @@ require "active_support/core_ext/module/attr_internal"
9
9
  module AbstractController
10
10
  # Raised when a non-existing controller action is triggered.
11
11
  class ActionNotFound < StandardError
12
- attr_reader :controller, :action
13
- def initialize(message = nil, controller = nil, action = nil)
12
+ attr_reader :controller, :action # :nodoc:
13
+
14
+ def initialize(message = nil, controller = nil, action = nil) # :nodoc:
14
15
  @controller = controller
15
16
  @action = action
16
17
  super(message)
17
18
  end
18
19
 
19
- class Correction
20
- def initialize(error)
21
- @error = error
22
- end
23
-
24
- def corrections
25
- if @error.action
26
- maybe_these = @error.controller.class.action_methods
20
+ if defined?(DidYouMean::Correctable) && defined?(DidYouMean::SpellChecker)
21
+ include DidYouMean::Correctable # :nodoc:
27
22
 
28
- maybe_these.sort_by { |n|
29
- DidYouMean::Jaro.distance(@error.action.to_s, n)
30
- }.reverse.first(4)
31
- else
32
- []
33
- end
23
+ def corrections # :nodoc:
24
+ @corrections ||= DidYouMean::SpellChecker.new(dictionary: controller.class.action_methods).correct(action)
34
25
  end
35
26
  end
36
-
37
- # We may not have DYM, and DYM might not let us register error handlers
38
- if defined?(DidYouMean) && DidYouMean.respond_to?(:correct_error)
39
- DidYouMean.correct_error(self, Correction)
40
- end
41
27
  end
42
28
 
43
29
  # AbstractController::Base is a low-level API. Nobody should be
@@ -164,13 +150,14 @@ module AbstractController
164
150
 
165
151
  process_action(action_name, *args)
166
152
  end
153
+ ruby2_keywords(:process)
167
154
 
168
- # Delegates to the class' ::controller_path
155
+ # Delegates to the class's ::controller_path.
169
156
  def controller_path
170
157
  self.class.controller_path
171
158
  end
172
159
 
173
- # Delegates to the class' ::action_methods
160
+ # Delegates to the class's ::action_methods.
174
161
  def action_methods
175
162
  self.class.action_methods
176
163
  end
@@ -191,7 +178,7 @@ module AbstractController
191
178
 
192
179
  # Tests if a response body is set. Used to determine if the
193
180
  # +process_action+ callback needs to be terminated in
194
- # +AbstractController::Callbacks+.
181
+ # AbstractController::Callbacks.
195
182
  def performed?
196
183
  response_body
197
184
  end
@@ -224,8 +211,8 @@ module AbstractController
224
211
  #
225
212
  # Notice that the first argument is the method to be dispatched
226
213
  # which is *not* necessarily the same as the action name.
227
- def process_action(method_name, *args)
228
- send_action(method_name, *args)
214
+ def process_action(...)
215
+ send_action(...)
229
216
  end
230
217
 
231
218
  # Actually call the method associated with the action. Override
@@ -142,8 +142,8 @@ module AbstractController
142
142
  end
143
143
  end
144
144
 
145
- def instrument_fragment_cache(name, key) # :nodoc:
146
- ActiveSupport::Notifications.instrument("#{name}.#{instrument_name}", instrument_payload(key)) { yield }
145
+ def instrument_fragment_cache(name, key, &block) # :nodoc:
146
+ ActiveSupport::Notifications.instrument("#{name}.#{instrument_name}", instrument_payload(key), &block)
147
147
  end
148
148
  end
149
149
  end
@@ -50,7 +50,7 @@ module AbstractController
50
50
  end
51
51
 
52
52
  def view_cache_dependencies
53
- self.class._view_cache_dependencies.map { |dep| instance_exec(&dep) }.compact
53
+ self.class._view_cache_dependencies.filter_map { |dep| instance_exec(&dep) }
54
54
  end
55
55
 
56
56
  private
@@ -35,12 +35,18 @@ module AbstractController
35
35
  skip_after_callbacks_if_terminated: true
36
36
  end
37
37
 
38
- # Override <tt>AbstractController::Base#process_action</tt> to run the
39
- # <tt>process_action</tt> callbacks around the normal behavior.
40
- def process_action(*)
41
- run_callbacks(:process_action) do
42
- super
38
+ class ActionFilter # :nodoc:
39
+ def initialize(actions)
40
+ @actions = Array(actions).map(&:to_s).to_set
43
41
  end
42
+
43
+ def match?(controller)
44
+ @actions.include?(controller.action_name)
45
+ end
46
+
47
+ alias after match?
48
+ alias before match?
49
+ alias around match?
44
50
  end
45
51
 
46
52
  module ClassMethods
@@ -70,8 +76,7 @@ module AbstractController
70
76
 
71
77
  def _normalize_callback_option(options, from, to) # :nodoc:
72
78
  if from = options.delete(from)
73
- _from = Array(from).map(&:to_s).to_set
74
- from = proc { |c| _from.include? c.action_name }
79
+ from = ActionFilter.new(from)
75
80
  options[to] = Array(options[to]).unshift(from)
76
81
  end
77
82
  end
@@ -220,5 +225,14 @@ module AbstractController
220
225
  alias_method :"append_#{callback}_action", :"#{callback}_action"
221
226
  end
222
227
  end
228
+
229
+ private
230
+ # Override <tt>AbstractController::Base#process_action</tt> to run the
231
+ # <tt>process_action</tt> callbacks around the normal behavior.
232
+ def process_action(...)
233
+ run_callbacks(:process_action) do
234
+ super
235
+ end
236
+ end
223
237
  end
224
238
  end
@@ -10,7 +10,7 @@ module AbstractController
10
10
  def #{sym}(*args, &block)
11
11
  custom(Mime[:#{sym}], *args, &block)
12
12
  end
13
- ruby2_keywords(:#{sym}) if respond_to?(:ruby2_keywords, true)
13
+ ruby2_keywords(:#{sym})
14
14
  RUBY
15
15
  end
16
16
 
@@ -39,6 +39,6 @@ module AbstractController
39
39
  super
40
40
  end
41
41
  end
42
- ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
42
+ ruby2_keywords(:method_missing)
43
43
  end
44
44
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AbstractController
4
- class Error < StandardError #:nodoc:
4
+ class Error < StandardError # :nodoc:
5
5
  end
6
6
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/dependencies"
4
+ require "active_support/core_ext/name_error"
4
5
 
5
6
  module AbstractController
6
7
  module Helpers
@@ -83,11 +84,11 @@ module AbstractController
83
84
  file, line = location.path, location.lineno
84
85
 
85
86
  methods.each do |method|
86
- _helpers_for_modification.class_eval <<-ruby_eval, file, line
87
+ _helpers_for_modification.class_eval <<~ruby_eval, file, line
87
88
  def #{method}(*args, &block) # def current_user(*args, &block)
88
89
  controller.send(:'#{method}', *args, &block) # controller.send(:'current_user', *args, &block)
89
90
  end # end
90
- ruby2_keywords(:'#{method}') if respond_to?(:ruby2_keywords, true)
91
+ ruby2_keywords(:'#{method}')
91
92
  ruby_eval
92
93
  end
93
94
  end
@@ -109,7 +110,7 @@ module AbstractController
109
110
  # The last two assume that <tt>"foo".camelize</tt> returns "Foo".
110
111
  #
111
112
  # When strings or symbols are passed, the method finds the actual module
112
- # object using +String#constantize+. Therefore, if the module has not been
113
+ # object using String#constantize. Therefore, if the module has not been
113
114
  # yet loaded, it has to be autoloadable, which is normally the case.
114
115
  #
115
116
  # Namespaces are supported. The following calls include +Foo::BarHelper+:
@@ -3,7 +3,7 @@
3
3
  require "active_support/benchmarkable"
4
4
 
5
5
  module AbstractController
6
- module Logger #:nodoc:
6
+ module Logger # :nodoc:
7
7
  extend ActiveSupport::Concern
8
8
 
9
9
  included do
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/module/introspection"
4
+
3
5
  module AbstractController
4
6
  module Railties
5
7
  module RoutesHelpers
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/symbol/starts_ends_with"
3
+ require "active_support/html_safe_translation"
4
4
 
5
5
  module AbstractController
6
6
  module Translation
@@ -24,7 +24,8 @@ module AbstractController
24
24
  end
25
25
 
26
26
  i18n_raise = options.fetch(:raise, self.raise_on_missing_translations)
27
- I18n.translate(key, **options, raise: i18n_raise)
27
+
28
+ ActiveSupport::HtmlSafeTranslation.translate(key, **options, raise: i18n_raise)
28
29
  end
29
30
  alias :t :translate
30
31
 
@@ -22,12 +22,10 @@ module AbstractController
22
22
  end
23
23
 
24
24
  def action_methods
25
- @action_methods ||= begin
26
- if _routes
27
- super - _routes.named_routes.helper_names
28
- else
29
- super
30
- end
25
+ @action_methods ||= if _routes
26
+ super - _routes.named_routes.helper_names
27
+ else
28
+ super
31
29
  end
32
30
  end
33
31
  end
@@ -5,7 +5,7 @@ require "action_controller"
5
5
  require "action_controller/log_subscriber"
6
6
 
7
7
  module ActionController
8
- # API Controller is a lightweight version of <tt>ActionController::Base</tt>,
8
+ # API Controller is a lightweight version of ActionController::Base,
9
9
  # created for applications that don't require all functionalities that a complete
10
10
  # \Rails controller provides, allowing you to create controllers with just the
11
11
  # features that you need for API only applications.
@@ -32,12 +32,12 @@ module ActionController
32
32
  # end
33
33
  #
34
34
  # Request, response, and parameters objects all work the exact same way as
35
- # <tt>ActionController::Base</tt>.
35
+ # ActionController::Base.
36
36
  #
37
37
  # == Renders
38
38
  #
39
39
  # The default API Controller stack includes all renderers, which means you
40
- # can use <tt>render :json</tt> and brothers freely in your controllers. Keep
40
+ # can use <tt>render :json</tt> and siblings freely in your controllers. Keep
41
41
  # in mind that templates are not going to be rendered, so you need to ensure
42
42
  # your controller is calling either <tt>render</tt> or <tt>redirect_to</tt> in
43
43
  # all actions, otherwise it will return 204 No Content.
@@ -51,7 +51,7 @@ module ActionController
51
51
  #
52
52
  # Redirects are used to move from one action to another. You can use the
53
53
  # <tt>redirect_to</tt> method in your controllers in the same way as in
54
- # <tt>ActionController::Base</tt>. For example:
54
+ # ActionController::Base. For example:
55
55
  #
56
56
  # def create
57
57
  # redirect_to root_url and return if not_authorized?
@@ -61,7 +61,7 @@ module ActionController
61
61
  # == Adding New Behavior
62
62
  #
63
63
  # In some scenarios you may want to add back some functionality provided by
64
- # <tt>ActionController::Base</tt> that is not present by default in
64
+ # ActionController::Base that is not present by default in
65
65
  # <tt>ActionController::API</tt>, for instance <tt>MimeResponds</tt>. This
66
66
  # module gives you the <tt>respond_to</tt> method. Adding it is quite simple,
67
67
  # you just need to include the module in a specific controller or in
@@ -83,7 +83,7 @@ module ActionController
83
83
  # end
84
84
  # end
85
85
  #
86
- # Make sure to check the modules included in <tt>ActionController::Base</tt>
86
+ # Make sure to check the modules included in ActionController::Base
87
87
  # if you want to use any other functionality that is not provided
88
88
  # by <tt>ActionController::API</tt> out of the box.
89
89
  class API < Metal
@@ -87,10 +87,11 @@ module ActionController
87
87
  #
88
88
  # or you can remove the entire session with +reset_session+.
89
89
  #
90
- # Sessions are stored by default in a browser cookie that's cryptographically signed, but unencrypted.
91
- # This prevents the user from tampering with the session but also allows them to see its contents.
92
- #
93
- # Do not put secret information in cookie-based sessions!
90
+ # By default, sessions are stored in an encrypted browser cookie (see
91
+ # ActionDispatch::Session::CookieStore). Thus the user will not be able to
92
+ # read or edit the session data. However, the user can keep a copy of the
93
+ # cookie even after it has expired, so you should avoid storing sensitive
94
+ # information in cookie-based sessions.
94
95
  #
95
96
  # == Responses
96
97
  #
@@ -3,7 +3,7 @@
3
3
  module ActionController
4
4
  # Override the default form builder for all views rendered by this
5
5
  # controller and any of its descendants. Accepts a subclass of
6
- # +ActionView::Helpers::FormBuilder+.
6
+ # ActionView::Helpers::FormBuilder.
7
7
  #
8
8
  # For example, given a form builder:
9
9
  #
@@ -36,7 +36,7 @@ module ActionController
36
36
  # in the views rendered by this controller and its subclasses.
37
37
  #
38
38
  # ==== Parameters
39
- # * <tt>builder</tt> - Default form builder, an instance of +ActionView::Helpers::FormBuilder+
39
+ # * <tt>builder</tt> - Default form builder, an instance of ActionView::Helpers::FormBuilder
40
40
  def default_form_builder(builder)
41
41
  self._default_form_builder = builder
42
42
  end
@@ -56,12 +56,13 @@ module ActionController
56
56
  def unpermitted_parameters(event)
57
57
  debug do
58
58
  unpermitted_keys = event.payload[:keys]
59
- color("Unpermitted parameter#{'s' if unpermitted_keys.size > 1}: #{unpermitted_keys.map { |e| ":#{e}" }.join(", ")}", RED)
59
+ display_unpermitted_keys = unpermitted_keys.map { |e| ":#{e}" }.join(", ")
60
+ context = event.payload[:context].map { |k, v| "#{k}: #{v}" }.join(", ")
61
+ color("Unpermitted parameter#{'s' if unpermitted_keys.size > 1}: #{display_unpermitted_keys}. Context: { #{context} }", RED)
60
62
  end
61
63
  end
62
64
 
63
- %w(write_fragment read_fragment exist_fragment?
64
- expire_fragment expire_page write_page).each do |method|
65
+ %w(write_fragment read_fragment exist_fragment? expire_fragment).each do |method|
65
66
  class_eval <<-METHOD, __FILE__, __LINE__ + 1
66
67
  def #{method}(event)
67
68
  return unless logger.info? && ActionController::Base.enable_fragment_cache_logging
@@ -57,6 +57,8 @@ module ActionController
57
57
  # 304 Not Modified response if last_modified <= If-Modified-Since.
58
58
  # * <tt>:public</tt> By default the Cache-Control header is private, set this to
59
59
  # +true+ if you want your application to be cacheable by other devices (proxy caches).
60
+ # * <tt>:cache_control</tt> When given will overwrite an existing Cache-Control header.
61
+ # See https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.
60
62
  # * <tt>:template</tt> By default, the template digest for the current
61
63
  # controller/action is included in ETags. If the action renders a
62
64
  # different template, you can include its digest instead. If the action
@@ -98,12 +100,22 @@ module ActionController
98
100
  # fresh_when(@article, public: true)
99
101
  # end
100
102
  #
103
+ # When overwriting Cache-Control header:
104
+ #
105
+ # def show
106
+ # @article = Article.find(params[:id])
107
+ # fresh_when(@article, public: true, cache_control: { no_cache: true })
108
+ # end
109
+ #
110
+ # This will set in the response Cache-Control = public, no-cache.
111
+ #
101
112
  # When rendering a different template than the default controller/action
102
113
  # style, you can indicate which digest to include in the ETag:
103
114
  #
104
115
  # before_action { fresh_when @article, template: 'widgets/show' }
105
116
  #
106
- def fresh_when(object = nil, etag: nil, weak_etag: nil, strong_etag: nil, last_modified: nil, public: false, template: nil)
117
+ def fresh_when(object = nil, etag: nil, weak_etag: nil, strong_etag: nil, last_modified: nil, public: false, cache_control: {}, template: nil)
118
+ response.cache_control.delete(:no_store)
107
119
  weak_etag ||= etag || object unless strong_etag
108
120
  last_modified ||= object.try(:updated_at) || object.try(:maximum, :updated_at)
109
121
 
@@ -117,6 +129,7 @@ module ActionController
117
129
 
118
130
  response.last_modified = last_modified if last_modified
119
131
  response.cache_control[:public] = true if public
132
+ response.cache_control.merge!(cache_control)
120
133
 
121
134
  head :not_modified if request.fresh?(response)
122
135
  end
@@ -147,6 +160,8 @@ module ActionController
147
160
  # 304 Not Modified response if last_modified <= If-Modified-Since.
148
161
  # * <tt>:public</tt> By default the Cache-Control header is private, set this to
149
162
  # +true+ if you want your application to be cacheable by other devices (proxy caches).
163
+ # * <tt>:cache_control</tt> When given will overwrite an existing Cache-Control header.
164
+ # See https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.
150
165
  # * <tt>:template</tt> By default, the template digest for the current
151
166
  # controller/action is included in ETags. If the action renders a
152
167
  # different template, you can include its digest instead. If the action
@@ -209,6 +224,21 @@ module ActionController
209
224
  # end
210
225
  # end
211
226
  #
227
+ # When overwriting Cache-Control header:
228
+ #
229
+ # def show
230
+ # @article = Article.find(params[:id])
231
+ #
232
+ # if stale?(@article, public: true, cache_control: { no_cache: true })
233
+ # @statistics = @articles.really_expensive_call
234
+ # respond_to do |format|
235
+ # # all the supported formats
236
+ # end
237
+ # end
238
+ # end
239
+ #
240
+ # This will set in the response Cache-Control = public, no-cache.
241
+ #
212
242
  # When rendering a different template than the default controller/action
213
243
  # style, you can indicate which digest to include in the ETag:
214
244
  #
@@ -238,12 +268,13 @@ module ActionController
238
268
  # expires_in 3.hours, public: true, stale_while_revalidate: 60.seconds, stale_if_error: 5.minutes
239
269
  #
240
270
  # HTTP Cache-Control Extensions other values: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
241
- # Any additional key-value pairs are concatenated onto the `Cache-Control` header in the response:
271
+ # Any additional key-value pairs are concatenated onto the Cache-Control header in the response:
242
272
  #
243
273
  # expires_in 3.hours, public: true, "s-maxage": 3.hours, "no-transform": true
244
274
  #
245
275
  # The method will also ensure an HTTP Date header for client compatibility.
246
276
  def expires_in(seconds, options = {})
277
+ response.cache_control.delete(:no_store)
247
278
  response.cache_control.merge!(
248
279
  max_age: seconds,
249
280
  public: options.delete(:public),
@@ -280,6 +311,12 @@ module ActionController
280
311
  public: public)
281
312
  end
282
313
 
314
+ # Sets an HTTP 1.1 Cache-Control header of <tt>no-store</tt>. This means the
315
+ # resource may not be stored in any cache.
316
+ def no_store
317
+ response.cache_control.replace(no_store: true)
318
+ end
319
+
283
320
  private
284
321
  def combine_etags(validator, options)
285
322
  [validator, *etaggers.map { |etagger| instance_exec(options, &etagger) }].compact
@@ -1,8 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module ActionController #:nodoc:
3
+ module ActionController # :nodoc:
4
4
  module ContentSecurityPolicy
5
- # TODO: Documentation
6
5
  extend ActiveSupport::Concern
7
6
 
8
7
  include AbstractController::Helpers
@@ -14,6 +13,29 @@ module ActionController #:nodoc:
14
13
  end
15
14
 
16
15
  module ClassMethods
16
+ # Overrides parts of the globally configured Content-Security-Policy
17
+ # header:
18
+ #
19
+ # class PostsController < ApplicationController
20
+ # content_security_policy do |policy|
21
+ # policy.base_uri "https://www.example.com"
22
+ # end
23
+ # end
24
+ #
25
+ # Options can be passed similar to +before_action+. For example, pass
26
+ # <tt>only: :index</tt> to override the header on the index action only:
27
+ #
28
+ # class PostsController < ApplicationController
29
+ # content_security_policy(only: :index) do |policy|
30
+ # policy.default_src :self, :https
31
+ # end
32
+ # end
33
+ #
34
+ # Pass +false+ to remove the Content-Security-Policy header:
35
+ #
36
+ # class PostsController < ApplicationController
37
+ # content_security_policy false, only: :index
38
+ # end
17
39
  def content_security_policy(enabled = true, **options, &block)
18
40
  before_action(options) do
19
41
  if block_given?
@@ -28,6 +50,18 @@ module ActionController #:nodoc:
28
50
  end
29
51
  end
30
52
 
53
+ # Overrides the globally configured Content-Security-Policy-Report-Only
54
+ # header:
55
+ #
56
+ # class PostsController < ApplicationController
57
+ # content_security_policy_report_only only: :index
58
+ # end
59
+ #
60
+ # Pass +false+ to remove the Content-Security-Policy-Report-Only header:
61
+ #
62
+ # class PostsController < ApplicationController
63
+ # content_security_policy_report_only false, only: :index
64
+ # end
31
65
  def content_security_policy_report_only(report_only = true, **options)
32
66
  before_action(options) do
33
67
  request.content_security_policy_report_only = report_only
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module ActionController #:nodoc:
3
+ module ActionController # :nodoc:
4
4
  module Cookies
5
5
  extend ActiveSupport::Concern
6
6
 
@@ -3,7 +3,7 @@
3
3
  require "action_controller/metal/exceptions"
4
4
  require "action_dispatch/http/content_disposition"
5
5
 
6
- module ActionController #:nodoc:
6
+ module ActionController # :nodoc:
7
7
  # Methods for sending arbitrary data and for streaming files to the browser,
8
8
  # instead of rendering.
9
9
  module DataStreaming
@@ -11,8 +11,8 @@ module ActionController #:nodoc:
11
11
 
12
12
  include ActionController::Rendering
13
13
 
14
- DEFAULT_SEND_FILE_TYPE = "application/octet-stream" #:nodoc:
15
- DEFAULT_SEND_FILE_DISPOSITION = "attachment" #:nodoc:
14
+ DEFAULT_SEND_FILE_TYPE = "application/octet-stream" # :nodoc:
15
+ DEFAULT_SEND_FILE_DISPOSITION = "attachment" # :nodoc:
16
16
 
17
17
  private
18
18
  # Sends the file. This uses a server-appropriate method (such as X-Sendfile)
@@ -66,7 +66,7 @@ module ActionController #:nodoc:
66
66
  # https://www.mnot.net/cache_docs/ for an overview of web caching and
67
67
  # https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
68
68
  # for the Cache-Control header spec.
69
- def send_file(path, options = {}) #:doc:
69
+ def send_file(path, options = {}) # :doc:
70
70
  raise MissingFile, "Cannot read file #{path}" unless File.file?(path) && File.readable?(path)
71
71
 
72
72
  options[:filename] ||= File.basename(path) unless options[:url_based_filename]
@@ -106,7 +106,7 @@ module ActionController #:nodoc:
106
106
  # send_data image.data, type: image.content_type, disposition: 'inline'
107
107
  #
108
108
  # See +send_file+ for more information on HTTP Content-* headers and caching.
109
- def send_data(data, options = {}) #:doc:
109
+ def send_data(data, options = {}) # :doc:
110
110
  send_file_headers! options
111
111
  render options.slice(:status, :content_type).merge(body: data)
112
112
  end
@@ -138,14 +138,6 @@ module ActionController #:nodoc:
138
138
  end
139
139
 
140
140
  headers["Content-Transfer-Encoding"] = "binary"
141
-
142
- # Fix a problem with IE 6.0 on opening downloaded files:
143
- # If Cache-Control: no-cache is set (which Rails does by default),
144
- # IE removes the file it just downloaded from its cache immediately
145
- # after it displays the "open/save" dialog, which means that if you
146
- # hit "open" the file isn't there anymore when the application that
147
- # is called for handling the download is run, so let's workaround that
148
- response.cache_control[:public] ||= false
149
141
  end
150
142
  end
151
143
  end