actionpack 4.2.11.1 → 6.1.3.2

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 (187) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +291 -489
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +9 -9
  5. data/lib/abstract_controller/asset_paths.rb +2 -0
  6. data/lib/abstract_controller/base.rb +81 -51
  7. data/lib/{action_controller → abstract_controller}/caching/fragments.rb +64 -17
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/abstract_controller/callbacks.rb +61 -33
  10. data/lib/abstract_controller/collector.rb +9 -13
  11. data/lib/abstract_controller/error.rb +6 -0
  12. data/lib/abstract_controller/helpers.rb +115 -99
  13. data/lib/abstract_controller/logger.rb +2 -0
  14. data/lib/abstract_controller/railties/routes_helpers.rb +21 -3
  15. data/lib/abstract_controller/rendering.rb +48 -47
  16. data/lib/abstract_controller/translation.rb +17 -8
  17. data/lib/abstract_controller/url_for.rb +2 -0
  18. data/lib/abstract_controller.rb +13 -5
  19. data/lib/action_controller/api/api_rendering.rb +16 -0
  20. data/lib/action_controller/api.rb +150 -0
  21. data/lib/action_controller/base.rb +29 -24
  22. data/lib/action_controller/caching.rb +12 -57
  23. data/lib/action_controller/form_builder.rb +50 -0
  24. data/lib/action_controller/log_subscriber.rb +17 -19
  25. data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
  26. data/lib/action_controller/metal/conditional_get.rb +134 -46
  27. data/lib/action_controller/metal/content_security_policy.rb +51 -0
  28. data/lib/action_controller/metal/cookies.rb +6 -4
  29. data/lib/action_controller/metal/data_streaming.rb +30 -50
  30. data/lib/action_controller/metal/default_headers.rb +17 -0
  31. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  32. data/lib/action_controller/metal/etag_with_template_digest.rb +21 -16
  33. data/lib/action_controller/metal/exceptions.rb +63 -15
  34. data/lib/action_controller/metal/flash.rb +9 -8
  35. data/lib/action_controller/metal/head.rb +26 -21
  36. data/lib/action_controller/metal/helpers.rb +37 -18
  37. data/lib/action_controller/metal/http_authentication.rb +81 -73
  38. data/lib/action_controller/metal/implicit_render.rb +53 -9
  39. data/lib/action_controller/metal/instrumentation.rb +32 -35
  40. data/lib/action_controller/metal/live.rb +102 -120
  41. data/lib/action_controller/metal/logging.rb +20 -0
  42. data/lib/action_controller/metal/mime_responds.rb +49 -47
  43. data/lib/action_controller/metal/parameter_encoding.rb +82 -0
  44. data/lib/action_controller/metal/params_wrapper.rb +83 -66
  45. data/lib/action_controller/metal/permissions_policy.rb +46 -0
  46. data/lib/action_controller/metal/redirecting.rb +53 -32
  47. data/lib/action_controller/metal/renderers.rb +87 -44
  48. data/lib/action_controller/metal/rendering.rb +77 -50
  49. data/lib/action_controller/metal/request_forgery_protection.rb +267 -103
  50. data/lib/action_controller/metal/rescue.rb +10 -17
  51. data/lib/action_controller/metal/streaming.rb +12 -11
  52. data/lib/action_controller/metal/strong_parameters.rb +714 -186
  53. data/lib/action_controller/metal/testing.rb +2 -17
  54. data/lib/action_controller/metal/url_for.rb +19 -10
  55. data/lib/action_controller/metal.rb +104 -87
  56. data/lib/action_controller/railtie.rb +28 -10
  57. data/lib/action_controller/railties/helpers.rb +3 -1
  58. data/lib/action_controller/renderer.rb +141 -0
  59. data/lib/action_controller/template_assertions.rb +11 -0
  60. data/lib/action_controller/test_case.rb +296 -422
  61. data/lib/action_controller.rb +34 -23
  62. data/lib/action_dispatch/http/cache.rb +107 -56
  63. data/lib/action_dispatch/http/content_disposition.rb +45 -0
  64. data/lib/action_dispatch/http/content_security_policy.rb +286 -0
  65. data/lib/action_dispatch/http/filter_parameters.rb +32 -25
  66. data/lib/action_dispatch/http/filter_redirect.rb +10 -12
  67. data/lib/action_dispatch/http/headers.rb +55 -22
  68. data/lib/action_dispatch/http/mime_negotiation.rb +79 -51
  69. data/lib/action_dispatch/http/mime_type.rb +153 -121
  70. data/lib/action_dispatch/http/mime_types.rb +20 -6
  71. data/lib/action_dispatch/http/parameters.rb +90 -40
  72. data/lib/action_dispatch/http/permissions_policy.rb +173 -0
  73. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  74. data/lib/action_dispatch/http/request.rb +226 -121
  75. data/lib/action_dispatch/http/response.rb +248 -113
  76. data/lib/action_dispatch/http/upload.rb +21 -7
  77. data/lib/action_dispatch/http/url.rb +182 -100
  78. data/lib/action_dispatch/journey/formatter.rb +90 -43
  79. data/lib/action_dispatch/journey/gtg/builder.rb +28 -41
  80. data/lib/action_dispatch/journey/gtg/simulator.rb +11 -16
  81. data/lib/action_dispatch/journey/gtg/transition_table.rb +23 -21
  82. data/lib/action_dispatch/journey/nfa/dot.rb +3 -14
  83. data/lib/action_dispatch/journey/nodes/node.rb +29 -15
  84. data/lib/action_dispatch/journey/parser.rb +17 -16
  85. data/lib/action_dispatch/journey/parser.y +4 -3
  86. data/lib/action_dispatch/journey/parser_extras.rb +12 -4
  87. data/lib/action_dispatch/journey/path/pattern.rb +58 -54
  88. data/lib/action_dispatch/journey/route.rb +100 -32
  89. data/lib/action_dispatch/journey/router/utils.rb +29 -18
  90. data/lib/action_dispatch/journey/router.rb +55 -51
  91. data/lib/action_dispatch/journey/routes.rb +17 -17
  92. data/lib/action_dispatch/journey/scanner.rb +26 -17
  93. data/lib/action_dispatch/journey/visitors.rb +98 -54
  94. data/lib/action_dispatch/journey.rb +5 -5
  95. data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
  96. data/lib/action_dispatch/middleware/callbacks.rb +3 -6
  97. data/lib/action_dispatch/middleware/cookies.rb +347 -217
  98. data/lib/action_dispatch/middleware/debug_exceptions.rb +135 -63
  99. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  100. data/lib/action_dispatch/middleware/debug_view.rb +66 -0
  101. data/lib/action_dispatch/middleware/exception_wrapper.rb +115 -71
  102. data/lib/action_dispatch/middleware/executor.rb +21 -0
  103. data/lib/action_dispatch/middleware/flash.rb +78 -54
  104. data/lib/action_dispatch/middleware/host_authorization.rb +130 -0
  105. data/lib/action_dispatch/middleware/public_exceptions.rb +32 -27
  106. data/lib/action_dispatch/middleware/reloader.rb +5 -91
  107. data/lib/action_dispatch/middleware/remote_ip.rb +53 -45
  108. data/lib/action_dispatch/middleware/request_id.rb +17 -10
  109. data/lib/action_dispatch/middleware/session/abstract_store.rb +41 -26
  110. data/lib/action_dispatch/middleware/session/cache_store.rb +24 -14
  111. data/lib/action_dispatch/middleware/session/cookie_store.rb +74 -75
  112. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -2
  113. data/lib/action_dispatch/middleware/show_exceptions.rb +28 -23
  114. data/lib/action_dispatch/middleware/ssl.rb +118 -35
  115. data/lib/action_dispatch/middleware/stack.rb +82 -41
  116. data/lib/action_dispatch/middleware/static.rb +156 -89
  117. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  119. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
  120. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -14
  121. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
  122. data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +4 -2
  123. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  124. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +45 -35
  125. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
  126. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +23 -4
  128. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
  129. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +15 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +105 -8
  132. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +2 -2
  135. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -1
  136. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +3 -3
  137. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
  138. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
  139. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
  140. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +87 -64
  141. data/lib/action_dispatch/railtie.rb +27 -13
  142. data/lib/action_dispatch/request/session.rb +109 -61
  143. data/lib/action_dispatch/request/utils.rb +90 -23
  144. data/lib/action_dispatch/routing/endpoint.rb +9 -2
  145. data/lib/action_dispatch/routing/inspector.rb +141 -102
  146. data/lib/action_dispatch/routing/mapper.rb +811 -473
  147. data/lib/action_dispatch/routing/polymorphic_routes.rb +167 -143
  148. data/lib/action_dispatch/routing/redirection.rb +37 -27
  149. data/lib/action_dispatch/routing/route_set.rb +363 -331
  150. data/lib/action_dispatch/routing/routes_proxy.rb +32 -5
  151. data/lib/action_dispatch/routing/url_for.rb +66 -26
  152. data/lib/action_dispatch/routing.rb +36 -36
  153. data/lib/action_dispatch/system_test_case.rb +190 -0
  154. data/lib/action_dispatch/system_testing/browser.rb +86 -0
  155. data/lib/action_dispatch/system_testing/driver.rb +67 -0
  156. data/lib/action_dispatch/system_testing/server.rb +31 -0
  157. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +138 -0
  158. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +29 -0
  159. data/lib/action_dispatch/testing/assertion_response.rb +46 -0
  160. data/lib/action_dispatch/testing/assertions/response.rb +44 -22
  161. data/lib/action_dispatch/testing/assertions/routing.rb +47 -31
  162. data/lib/action_dispatch/testing/assertions.rb +6 -4
  163. data/lib/action_dispatch/testing/integration.rb +391 -220
  164. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  165. data/lib/action_dispatch/testing/test_process.rb +53 -22
  166. data/lib/action_dispatch/testing/test_request.rb +27 -34
  167. data/lib/action_dispatch/testing/test_response.rb +11 -11
  168. data/lib/action_dispatch.rb +35 -21
  169. data/lib/action_pack/gem_version.rb +6 -4
  170. data/lib/action_pack/version.rb +3 -1
  171. data/lib/action_pack.rb +4 -2
  172. metadata +78 -48
  173. data/lib/action_controller/metal/force_ssl.rb +0 -97
  174. data/lib/action_controller/metal/hide_actions.rb +0 -40
  175. data/lib/action_controller/metal/rack_delegation.rb +0 -32
  176. data/lib/action_controller/middleware.rb +0 -39
  177. data/lib/action_controller/model_naming.rb +0 -12
  178. data/lib/action_dispatch/http/parameter_filter.rb +0 -72
  179. data/lib/action_dispatch/journey/backwards.rb +0 -5
  180. data/lib/action_dispatch/journey/nfa/builder.rb +0 -76
  181. data/lib/action_dispatch/journey/nfa/simulator.rb +0 -47
  182. data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -163
  183. data/lib/action_dispatch/journey/router/strexp.rb +0 -27
  184. data/lib/action_dispatch/middleware/params_parser.rb +0 -60
  185. data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
  186. data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
  187. data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2004-2014 David Heinemeier Hansson
1
+ Copyright (c) 2004-2020 David Heinemeier Hansson
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.rdoc CHANGED
@@ -23,36 +23,36 @@ by default and Action View rendering is implicitly triggered by Action
23
23
  Controller. However, these modules are designed to function on their own and
24
24
  can be used outside of Rails.
25
25
 
26
+ You can read more about Action Pack in the {Action Controller Overview}[https://guides.rubyonrails.org/action_controller_overview.html] guide.
26
27
 
27
28
  == Download and installation
28
29
 
29
30
  The latest version of Action Pack can be installed with RubyGems:
30
31
 
31
- % [sudo] gem install actionpack
32
+ $ gem install actionpack
32
33
 
33
- Source code can be downloaded as part of the Rails project on GitHub
34
+ Source code can be downloaded as part of the Rails project on GitHub:
34
35
 
35
- * https://github.com/rails/rails/tree/4-2-stable/actionpack
36
+ * https://github.com/rails/rails/tree/main/actionpack
36
37
 
37
38
 
38
39
  == License
39
40
 
40
41
  Action Pack is released under the MIT license:
41
42
 
42
- * http://www.opensource.org/licenses/MIT
43
+ * https://opensource.org/licenses/MIT
43
44
 
44
45
 
45
46
  == Support
46
47
 
47
- API documentation is at
48
+ API documentation is at:
48
49
 
49
- * http://api.rubyonrails.org
50
+ * https://api.rubyonrails.org
50
51
 
51
- Bug reports can be filed for the Ruby on Rails project here:
52
+ Bug reports for the Ruby on Rails project can be filed here:
52
53
 
53
54
  * https://github.com/rails/rails/issues
54
55
 
55
56
  Feature requests should be discussed on the rails-core mailing list here:
56
57
 
57
- * https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core
58
-
58
+ * https://discuss.rubyonrails.org/c/rubyonrails-core
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module AbstractController
2
4
  module AssetPaths #:nodoc:
3
5
  extend ActiveSupport::Concern
@@ -1,30 +1,65 @@
1
- require 'erubis'
2
- require 'set'
3
- require 'active_support/configurable'
4
- require 'active_support/descendants_tracker'
5
- require 'active_support/core_ext/module/anonymous'
1
+ # frozen_string_literal: true
6
2
 
7
- module AbstractController
8
- class Error < StandardError #:nodoc:
9
- end
3
+ require "abstract_controller/error"
4
+ require "active_support/configurable"
5
+ require "active_support/descendants_tracker"
6
+ require "active_support/core_ext/module/anonymous"
7
+ require "active_support/core_ext/module/attr_internal"
10
8
 
9
+ module AbstractController
11
10
  # Raised when a non-existing controller action is triggered.
12
11
  class ActionNotFound < StandardError
12
+ attr_reader :controller, :action
13
+ def initialize(message = nil, controller = nil, action = nil)
14
+ @controller = controller
15
+ @action = action
16
+ super(message)
17
+ end
18
+
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
27
+
28
+ maybe_these.sort_by { |n|
29
+ DidYouMean::Jaro.distance(@error.action.to_s, n)
30
+ }.reverse.first(4)
31
+ else
32
+ []
33
+ end
34
+ end
35
+ 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
13
41
  end
14
42
 
15
- # <tt>AbstractController::Base</tt> is a low-level API. Nobody should be
43
+ # AbstractController::Base is a low-level API. Nobody should be
16
44
  # using it directly, and subclasses (like ActionController::Base) are
17
45
  # expected to provide their own +render+ method, since rendering means
18
46
  # different things depending on the context.
19
47
  class Base
48
+ ##
49
+ # Returns the body of the HTTP response sent by the controller.
20
50
  attr_internal :response_body
51
+
52
+ ##
53
+ # Returns the name of the action this controller is processing.
21
54
  attr_internal :action_name
55
+
56
+ ##
57
+ # Returns the formats that can be processed by the controller.
22
58
  attr_internal :formats
23
59
 
24
60
  include ActiveSupport::Configurable
25
61
  extend ActiveSupport::DescendantsTracker
26
62
 
27
- undef_method :not_implemented
28
63
  class << self
29
64
  attr_reader :abstract
30
65
  alias_method :abstract?, :abstract
@@ -49,7 +84,7 @@ module AbstractController
49
84
  # instance methods on that abstract class. Public instance methods of
50
85
  # a controller would normally be considered action methods, so methods
51
86
  # declared on abstract classes are being removed.
52
- # (ActionController::Metal and ActionController::Base are defined as abstract)
87
+ # (<tt>ActionController::Metal</tt> and ActionController::Base are defined as abstract)
53
88
  def internal_methods
54
89
  controller = self
55
90
 
@@ -57,21 +92,11 @@ module AbstractController
57
92
  controller.public_instance_methods(true)
58
93
  end
59
94
 
60
- # The list of hidden actions. Defaults to an empty array.
61
- # This can be modified by other modules or subclasses
62
- # to specify particular actions as hidden.
63
- #
64
- # ==== Returns
65
- # * <tt>Array</tt> - An array of method names that should not be considered actions.
66
- def hidden_actions
67
- []
68
- end
69
-
70
95
  # A list of method names that should be considered actions. This
71
96
  # includes all public instance methods on a controller, less
72
- # any internal methods (see #internal_methods), adding back in
97
+ # any internal methods (see internal_methods), adding back in
73
98
  # any methods that are internal, but still exist on the class
74
- # itself. Finally, #hidden_actions are removed.
99
+ # itself.
75
100
  #
76
101
  # ==== Returns
77
102
  # * <tt>Set</tt> - A set of all methods that should be considered actions.
@@ -82,30 +107,33 @@ module AbstractController
82
107
  # Except for public instance methods of Base and its ancestors
83
108
  internal_methods +
84
109
  # Be sure to include shadowed public instance methods of this class
85
- public_instance_methods(false)).uniq.map { |x| x.to_s } -
86
- # And always exclude explicitly hidden actions
87
- hidden_actions.to_a
110
+ public_instance_methods(false))
88
111
 
89
- # Clear out AS callback method pollution
90
- Set.new(methods.reject { |method| method =~ /_one_time_conditions/ })
112
+ methods.map!(&:to_s)
113
+
114
+ methods.to_set
91
115
  end
92
116
  end
93
117
 
94
- # action_methods are cached and there is sometimes need to refresh
95
- # them. clear_action_methods! allows you to do that, so next time
96
- # you run action_methods, they will be recalculated
118
+ # action_methods are cached and there is sometimes a need to refresh
119
+ # them. ::clear_action_methods! allows you to do that, so next time
120
+ # you run action_methods, they will be recalculated.
97
121
  def clear_action_methods!
98
122
  @action_methods = nil
99
123
  end
100
124
 
101
125
  # Returns the full controller name, underscored, without the ending Controller.
102
- # For instance, MyApp::MyPostsController would return "my_app/my_posts" for
103
- # controller_path.
126
+ #
127
+ # class MyApp::MyPostsController < AbstractController::Base
128
+ #
129
+ # end
130
+ #
131
+ # MyApp::MyPostsController.controller_path # => "my_app/my_posts"
104
132
  #
105
133
  # ==== Returns
106
134
  # * <tt>String</tt>
107
135
  def controller_path
108
- @controller_path ||= name.sub(/Controller$/, '').underscore unless anonymous?
136
+ @controller_path ||= name.delete_suffix("Controller").underscore unless anonymous?
109
137
  end
110
138
 
111
139
  # Refresh the cached action_methods when a new action_method is added.
@@ -129,7 +157,7 @@ module AbstractController
129
157
  @_action_name = action.to_s
130
158
 
131
159
  unless action_name = _find_action_name(@_action_name)
132
- raise ActionNotFound, "The action '#{action}' could not be found for #{self.class.name}"
160
+ raise ActionNotFound.new("The action '#{action}' could not be found for #{self.class.name}", self, action)
133
161
  end
134
162
 
135
163
  @_response_body = nil
@@ -137,12 +165,12 @@ module AbstractController
137
165
  process_action(action_name, *args)
138
166
  end
139
167
 
140
- # Delegates to the class' #controller_path
168
+ # Delegates to the class' ::controller_path
141
169
  def controller_path
142
170
  self.class.controller_path
143
171
  end
144
172
 
145
- # Delegates to the class' #action_methods
173
+ # Delegates to the class' ::action_methods
146
174
  def action_methods
147
175
  self.class.action_methods
148
176
  end
@@ -157,11 +185,15 @@ module AbstractController
157
185
  #
158
186
  # ==== Parameters
159
187
  # * <tt>action_name</tt> - The name of an action to be tested
160
- #
161
- # ==== Returns
162
- # * <tt>TrueClass</tt>, <tt>FalseClass</tt>
163
188
  def available_action?(action_name)
164
- _find_action_name(action_name).present?
189
+ _find_action_name(action_name)
190
+ end
191
+
192
+ # Tests if a response body is set. Used to determine if the
193
+ # +process_action+ callback needs to be terminated in
194
+ # +AbstractController::Callbacks+.
195
+ def performed?
196
+ response_body
165
197
  end
166
198
 
167
199
  # Returns true if the given controller is capable of rendering
@@ -172,18 +204,16 @@ module AbstractController
172
204
  true
173
205
  end
174
206
 
175
- private
207
+ def inspect # :nodoc:
208
+ "#<#{self.class.name}:#{'%#016x' % (object_id << 1)}>"
209
+ end
176
210
 
211
+ private
177
212
  # Returns true if the name can be considered an action because
178
213
  # it has a method defined in the controller.
179
214
  #
180
215
  # ==== Parameters
181
216
  # * <tt>name</tt> - The name of an action to be tested
182
- #
183
- # ==== Returns
184
- # * <tt>TrueClass</tt>, <tt>FalseClass</tt>
185
- #
186
- # :api: private
187
217
  def action_method?(name)
188
218
  self.class.action_methods.include?(name)
189
219
  end
@@ -225,7 +255,7 @@ module AbstractController
225
255
  # ==== Returns
226
256
  # * <tt>string</tt> - The name of the method that handles the action
227
257
  # * false - No valid method name could be found.
228
- # Raise AbstractController::ActionNotFound.
258
+ # Raise +AbstractController::ActionNotFound+.
229
259
  def _find_action_name(action_name)
230
260
  _valid_action_name?(action_name) && method_for_action(action_name)
231
261
  end
@@ -241,11 +271,11 @@ module AbstractController
241
271
  # with a template matching the action name is considered to exist.
242
272
  #
243
273
  # If you override this method to handle additional cases, you may
244
- # also provide a method (like _handle_method_missing) to handle
274
+ # also provide a method (like +_handle_method_missing+) to handle
245
275
  # the case.
246
276
  #
247
- # If none of these conditions are true, and method_for_action
248
- # returns nil, an AbstractController::ActionNotFound exception will be raised.
277
+ # If none of these conditions are true, and +method_for_action+
278
+ # returns +nil+, an +AbstractController::ActionNotFound+ exception will be raised.
249
279
  #
250
280
  # ==== Parameters
251
281
  # * <tt>action_name</tt> - An action name to find a method name for
@@ -1,4 +1,6 @@
1
- module ActionController
1
+ # frozen_string_literal: true
2
+
3
+ module AbstractController
2
4
  module Caching
3
5
  # Fragment caching is used for caching various blocks within
4
6
  # views without caching the entire action as a whole. This is
@@ -14,12 +16,63 @@ module ActionController
14
16
  #
15
17
  # expire_fragment('name_of_cache')
16
18
  module Fragments
19
+ extend ActiveSupport::Concern
20
+
21
+ included do
22
+ if respond_to?(:class_attribute)
23
+ class_attribute :fragment_cache_keys
24
+ else
25
+ mattr_writer :fragment_cache_keys
26
+ end
27
+
28
+ self.fragment_cache_keys = []
29
+
30
+ if respond_to?(:helper_method)
31
+ helper_method :combined_fragment_cache_key
32
+ end
33
+ end
34
+
35
+ module ClassMethods
36
+ # Allows you to specify controller-wide key prefixes for
37
+ # cache fragments. Pass either a constant +value+, or a block
38
+ # which computes a value each time a cache key is generated.
39
+ #
40
+ # For example, you may want to prefix all fragment cache keys
41
+ # with a global version identifier, so you can easily
42
+ # invalidate all caches.
43
+ #
44
+ # class ApplicationController
45
+ # fragment_cache_key "v1"
46
+ # end
47
+ #
48
+ # When it's time to invalidate all fragments, simply change
49
+ # the string constant. Or, progressively roll out the cache
50
+ # invalidation using a computed value:
51
+ #
52
+ # class ApplicationController
53
+ # fragment_cache_key do
54
+ # @account.id.odd? ? "v1" : "v2"
55
+ # end
56
+ # end
57
+ def fragment_cache_key(value = nil, &key)
58
+ self.fragment_cache_keys += [key || -> { value }]
59
+ end
60
+ end
61
+
17
62
  # Given a key (as described in +expire_fragment+), returns
18
- # a key suitable for use in reading, writing, or expiring a
19
- # cached fragment. All keys are prefixed with <tt>views/</tt> and uses
20
- # ActiveSupport::Cache.expand_cache_key for the expansion.
21
- def fragment_cache_key(key)
22
- ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key).split("://").last : key, :views)
63
+ # a key array suitable for use in reading, writing, or expiring a
64
+ # cached fragment. All keys begin with <tt>:views</tt>,
65
+ # followed by <tt>ENV["RAILS_CACHE_ID"]</tt> or <tt>ENV["RAILS_APP_VERSION"]</tt> if set,
66
+ # followed by any controller-wide key prefix values, ending
67
+ # with the specified +key+ value.
68
+ def combined_fragment_cache_key(key)
69
+ head = self.class.fragment_cache_keys.map { |k| instance_exec(&k) }
70
+ tail = key.is_a?(Hash) ? url_for(key).split("://").last : key
71
+
72
+ cache_key = [:views, ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"], head, tail]
73
+ cache_key.flatten!(1)
74
+ cache_key.compact!
75
+ cache_key
23
76
  end
24
77
 
25
78
  # Writes +content+ to the location signified by
@@ -27,7 +80,7 @@ module ActionController
27
80
  def write_fragment(key, content, options = nil)
28
81
  return content unless cache_configured?
29
82
 
30
- key = fragment_cache_key(key)
83
+ key = combined_fragment_cache_key(key)
31
84
  instrument_fragment_cache :write_fragment, key do
32
85
  content = content.to_str
33
86
  cache_store.write(key, content, options)
@@ -40,7 +93,7 @@ module ActionController
40
93
  def read_fragment(key, options = nil)
41
94
  return unless cache_configured?
42
95
 
43
- key = fragment_cache_key(key)
96
+ key = combined_fragment_cache_key(key)
44
97
  instrument_fragment_cache :read_fragment, key do
45
98
  result = cache_store.read(key, options)
46
99
  result.respond_to?(:html_safe) ? result.html_safe : result
@@ -51,7 +104,7 @@ module ActionController
51
104
  # +key+ exists (see +expire_fragment+ for acceptable formats).
52
105
  def fragment_exist?(key, options = nil)
53
106
  return unless cache_configured?
54
- key = fragment_cache_key(key)
107
+ key = combined_fragment_cache_key(key)
55
108
 
56
109
  instrument_fragment_cache :exist_fragment?, key do
57
110
  cache_store.exist?(key, options)
@@ -78,7 +131,7 @@ module ActionController
78
131
  # method (or <tt>delete_matched</tt>, for Regexp keys).
79
132
  def expire_fragment(key, options = nil)
80
133
  return unless cache_configured?
81
- key = fragment_cache_key(key) unless key.is_a?(Regexp)
134
+ key = combined_fragment_cache_key(key) unless key.is_a?(Regexp)
82
135
 
83
136
  instrument_fragment_cache :expire_fragment, key do
84
137
  if key.is_a?(Regexp)
@@ -90,13 +143,7 @@ module ActionController
90
143
  end
91
144
 
92
145
  def instrument_fragment_cache(name, key) # :nodoc:
93
- payload = {
94
- controller: controller_name,
95
- action: action_name,
96
- key: key
97
- }
98
-
99
- ActiveSupport::Notifications.instrument("#{name}.action_controller", payload) { yield }
146
+ ActiveSupport::Notifications.instrument("#{name}.#{instrument_name}", instrument_payload(key)) { yield }
100
147
  end
101
148
  end
102
149
  end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AbstractController
4
+ module Caching
5
+ extend ActiveSupport::Concern
6
+ extend ActiveSupport::Autoload
7
+
8
+ eager_autoload do
9
+ autoload :Fragments
10
+ end
11
+
12
+ module ConfigMethods
13
+ def cache_store
14
+ config.cache_store
15
+ end
16
+
17
+ def cache_store=(store)
18
+ config.cache_store = ActiveSupport::Cache.lookup_store(*store)
19
+ end
20
+
21
+ private
22
+ def cache_configured?
23
+ perform_caching && cache_store
24
+ end
25
+ end
26
+
27
+ include ConfigMethods
28
+ include AbstractController::Caching::Fragments
29
+
30
+ included do
31
+ extend ConfigMethods
32
+
33
+ config_accessor :default_static_extension
34
+ self.default_static_extension ||= ".html"
35
+
36
+ config_accessor :perform_caching
37
+ self.perform_caching = true if perform_caching.nil?
38
+
39
+ config_accessor :enable_fragment_cache_logging
40
+ self.enable_fragment_cache_logging = false
41
+
42
+ class_attribute :_view_cache_dependencies, default: []
43
+ helper_method :view_cache_dependencies if respond_to?(:helper_method)
44
+ end
45
+
46
+ module ClassMethods
47
+ def view_cache_dependency(&dependency)
48
+ self._view_cache_dependencies += [dependency]
49
+ end
50
+ end
51
+
52
+ def view_cache_dependencies
53
+ self.class._view_cache_dependencies.map { |dep| instance_exec(&dep) }.compact
54
+ end
55
+
56
+ private
57
+ # Convenience accessor.
58
+ def cache(key, options = {}, &block) # :doc:
59
+ if cache_configured?
60
+ cache_store.fetch(ActiveSupport::Cache.expand_cache_key(key, :controller), options, &block)
61
+ else
62
+ yield
63
+ end
64
+ end
65
+ end
66
+ end
@@ -1,4 +1,26 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module AbstractController
4
+ # = Abstract Controller Callbacks
5
+ #
6
+ # Abstract Controller provides hooks during the life cycle of a controller action.
7
+ # Callbacks allow you to trigger logic during this cycle. Available callbacks are:
8
+ #
9
+ # * <tt>after_action</tt>
10
+ # * <tt>append_after_action</tt>
11
+ # * <tt>append_around_action</tt>
12
+ # * <tt>append_before_action</tt>
13
+ # * <tt>around_action</tt>
14
+ # * <tt>before_action</tt>
15
+ # * <tt>prepend_after_action</tt>
16
+ # * <tt>prepend_around_action</tt>
17
+ # * <tt>prepend_before_action</tt>
18
+ # * <tt>skip_after_action</tt>
19
+ # * <tt>skip_around_action</tt>
20
+ # * <tt>skip_before_action</tt>
21
+ #
22
+ # NOTE: Calling the same callback multiple times will overwrite previous callback definitions.
23
+ #
2
24
  module Callbacks
3
25
  extend ActiveSupport::Concern
4
26
 
@@ -9,53 +31,51 @@ module AbstractController
9
31
 
10
32
  included do
11
33
  define_callbacks :process_action,
12
- terminator: ->(controller,_) { controller.response_body },
34
+ terminator: ->(controller, result_lambda) { result_lambda.call; controller.performed? },
13
35
  skip_after_callbacks_if_terminated: true
14
36
  end
15
37
 
16
- # Override AbstractController::Base's process_action to run the
17
- # process_action callbacks around the normal behavior.
18
- def process_action(*args)
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(*)
19
41
  run_callbacks(:process_action) do
20
42
  super
21
43
  end
22
44
  end
23
45
 
24
46
  module ClassMethods
25
- # If :only or :except are used, convert the options into the
26
- # :unless and :if options of ActiveSupport::Callbacks.
27
- # The basic idea is that :only => :index gets converted to
28
- # :if => proc {|c| c.action_name == "index" }.
47
+ # If +:only+ or +:except+ are used, convert the options into the
48
+ # +:if+ and +:unless+ options of ActiveSupport::Callbacks.
49
+ #
50
+ # The basic idea is that <tt>:only => :index</tt> gets converted to
51
+ # <tt>:if => proc {|c| c.action_name == "index" }</tt>.
52
+ #
53
+ # Note that <tt>:only</tt> has priority over <tt>:if</tt> in case they
54
+ # are used together.
55
+ #
56
+ # only: :index, if: -> { true } # the :if option will be ignored.
57
+ #
58
+ # Note that <tt>:if</tt> has priority over <tt>:except</tt> in case they
59
+ # are used together.
60
+ #
61
+ # except: :index, if: -> { true } # the :except option will be ignored.
29
62
  #
30
63
  # ==== Options
31
- # * <tt>only</tt> - The callback should be run only for this action
32
- # * <tt>except</tt> - The callback should be run for all actions except this action
64
+ # * <tt>only</tt> - The callback should be run only for this action.
65
+ # * <tt>except</tt> - The callback should be run for all actions except this action.
33
66
  def _normalize_callback_options(options)
34
67
  _normalize_callback_option(options, :only, :if)
35
68
  _normalize_callback_option(options, :except, :unless)
36
69
  end
37
70
 
38
71
  def _normalize_callback_option(options, from, to) # :nodoc:
39
- if from = options[from]
40
- from = Array(from).map {|o| "action_name == '#{o}'"}.join(" || ")
72
+ if from = options.delete(from)
73
+ _from = Array(from).map(&:to_s).to_set
74
+ from = proc { |c| _from.include? c.action_name }
41
75
  options[to] = Array(options[to]).unshift(from)
42
76
  end
43
77
  end
44
78
 
45
- # Skip before, after, and around action callbacks matching any of the names.
46
- #
47
- # ==== Parameters
48
- # * <tt>names</tt> - A list of valid names that could be used for
49
- # callbacks. Note that skipping uses Ruby equality, so it's
50
- # impossible to skip a callback defined using an anonymous proc
51
- # using #skip_action_callback
52
- def skip_action_callback(*names)
53
- skip_before_action(*names)
54
- skip_after_action(*names)
55
- skip_around_action(*names)
56
- end
57
- alias_method :skip_filter, :skip_action_callback
58
-
59
79
  # Take callback names and an optional callback proc, normalize them,
60
80
  # then call the block with each callback. This allows us to abstract
61
81
  # the normalization across several methods that use it.
@@ -66,8 +86,8 @@ module AbstractController
66
86
  # * <tt>block</tt> - A proc that should be added to the callbacks.
67
87
  #
68
88
  # ==== Block Parameters
69
- # * <tt>name</tt> - The callback to be added
70
- # * <tt>options</tt> - A hash of options to be used when adding the callback
89
+ # * <tt>name</tt> - The callback to be added.
90
+ # * <tt>options</tt> - A hash of options to be used when adding the callback.
71
91
  def _insert_callbacks(callbacks, block = nil)
72
92
  options = callbacks.extract_options!
73
93
  _normalize_callback_options(options)
@@ -83,6 +103,10 @@ module AbstractController
83
103
  # :call-seq: before_action(names, block)
84
104
  #
85
105
  # Append a callback before actions. See _insert_callbacks for parameter details.
106
+ #
107
+ # If the callback renders or redirects, the action will not run. If there
108
+ # are additional callbacks scheduled to run after that callback, they are
109
+ # also cancelled.
86
110
 
87
111
  ##
88
112
  # :method: prepend_before_action
@@ -90,6 +114,10 @@ module AbstractController
90
114
  # :call-seq: prepend_before_action(names, block)
91
115
  #
92
116
  # Prepend a callback before actions. See _insert_callbacks for parameter details.
117
+ #
118
+ # If the callback renders or redirects, the action will not run. If there
119
+ # are additional callbacks scheduled to run after that callback, they are
120
+ # also cancelled.
93
121
 
94
122
  ##
95
123
  # :method: skip_before_action
@@ -104,6 +132,10 @@ module AbstractController
104
132
  # :call-seq: append_before_action(names, block)
105
133
  #
106
134
  # Append a callback before actions. See _insert_callbacks for parameter details.
135
+ #
136
+ # If the callback renders or redirects, the action will not run. If there
137
+ # are additional callbacks scheduled to run after that callback, they are
138
+ # also cancelled.
107
139
 
108
140
  ##
109
141
  # :method: after_action
@@ -169,14 +201,12 @@ module AbstractController
169
201
  set_callback(:process_action, callback, name, options)
170
202
  end
171
203
  end
172
- alias_method :"#{callback}_filter", :"#{callback}_action"
173
204
 
174
205
  define_method "prepend_#{callback}_action" do |*names, &blk|
175
206
  _insert_callbacks(names, blk) do |name, options|
176
- set_callback(:process_action, callback, name, options.merge(:prepend => true))
207
+ set_callback(:process_action, callback, name, options.merge(prepend: true))
177
208
  end
178
209
  end
179
- alias_method :"prepend_#{callback}_filter", :"prepend_#{callback}_action"
180
210
 
181
211
  # Skip a before, after or around callback. See _insert_callbacks
182
212
  # for details on the allowed parameters.
@@ -185,11 +215,9 @@ module AbstractController
185
215
  skip_callback(:process_action, callback, name, options)
186
216
  end
187
217
  end
188
- alias_method :"skip_#{callback}_filter", :"skip_#{callback}_action"
189
218
 
190
219
  # *_action is the same as append_*_action
191
220
  alias_method :"append_#{callback}_action", :"#{callback}_action"
192
- alias_method :"append_#{callback}_filter", :"#{callback}_action"
193
221
  end
194
222
  end
195
223
  end