actionpack 4.2.8 → 5.2.4.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 (166) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +285 -444
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +6 -7
  5. data/lib/abstract_controller.rb +12 -5
  6. data/lib/abstract_controller/asset_paths.rb +2 -0
  7. data/lib/abstract_controller/base.rb +45 -49
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/{action_controller → abstract_controller}/caching/fragments.rb +78 -15
  10. data/lib/abstract_controller/callbacks.rb +47 -31
  11. data/lib/abstract_controller/collector.rb +8 -11
  12. data/lib/abstract_controller/error.rb +6 -0
  13. data/lib/abstract_controller/helpers.rb +25 -25
  14. data/lib/abstract_controller/logger.rb +2 -0
  15. data/lib/abstract_controller/railties/routes_helpers.rb +4 -2
  16. data/lib/abstract_controller/rendering.rb +42 -41
  17. data/lib/abstract_controller/translation.rb +10 -7
  18. data/lib/abstract_controller/url_for.rb +2 -0
  19. data/lib/action_controller.rb +29 -21
  20. data/lib/action_controller/api.rb +149 -0
  21. data/lib/action_controller/api/api_rendering.rb +16 -0
  22. data/lib/action_controller/base.rb +27 -19
  23. data/lib/action_controller/caching.rb +14 -57
  24. data/lib/action_controller/form_builder.rb +50 -0
  25. data/lib/action_controller/log_subscriber.rb +10 -15
  26. data/lib/action_controller/metal.rb +98 -83
  27. data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
  28. data/lib/action_controller/metal/conditional_get.rb +118 -44
  29. data/lib/action_controller/metal/content_security_policy.rb +52 -0
  30. data/lib/action_controller/metal/cookies.rb +3 -3
  31. data/lib/action_controller/metal/data_streaming.rb +27 -46
  32. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  33. data/lib/action_controller/metal/etag_with_template_digest.rb +20 -13
  34. data/lib/action_controller/metal/exceptions.rb +8 -14
  35. data/lib/action_controller/metal/flash.rb +4 -3
  36. data/lib/action_controller/metal/force_ssl.rb +23 -21
  37. data/lib/action_controller/metal/head.rb +21 -19
  38. data/lib/action_controller/metal/helpers.rb +24 -14
  39. data/lib/action_controller/metal/http_authentication.rb +64 -57
  40. data/lib/action_controller/metal/implicit_render.rb +62 -8
  41. data/lib/action_controller/metal/instrumentation.rb +19 -21
  42. data/lib/action_controller/metal/live.rb +90 -106
  43. data/lib/action_controller/metal/mime_responds.rb +33 -46
  44. data/lib/action_controller/metal/parameter_encoding.rb +51 -0
  45. data/lib/action_controller/metal/params_wrapper.rb +61 -53
  46. data/lib/action_controller/metal/redirecting.rb +49 -28
  47. data/lib/action_controller/metal/renderers.rb +87 -44
  48. data/lib/action_controller/metal/rendering.rb +72 -50
  49. data/lib/action_controller/metal/request_forgery_protection.rb +203 -92
  50. data/lib/action_controller/metal/rescue.rb +9 -16
  51. data/lib/action_controller/metal/streaming.rb +12 -10
  52. data/lib/action_controller/metal/strong_parameters.rb +582 -165
  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/railtie.rb +28 -10
  56. data/lib/action_controller/railties/helpers.rb +2 -0
  57. data/lib/action_controller/renderer.rb +117 -0
  58. data/lib/action_controller/template_assertions.rb +11 -0
  59. data/lib/action_controller/test_case.rb +280 -411
  60. data/lib/action_dispatch.rb +27 -19
  61. data/lib/action_dispatch/http/cache.rb +93 -47
  62. data/lib/action_dispatch/http/content_security_policy.rb +272 -0
  63. data/lib/action_dispatch/http/filter_parameters.rb +26 -20
  64. data/lib/action_dispatch/http/filter_redirect.rb +10 -11
  65. data/lib/action_dispatch/http/headers.rb +55 -22
  66. data/lib/action_dispatch/http/mime_negotiation.rb +60 -41
  67. data/lib/action_dispatch/http/mime_type.rb +134 -121
  68. data/lib/action_dispatch/http/mime_types.rb +20 -6
  69. data/lib/action_dispatch/http/parameter_filter.rb +25 -11
  70. data/lib/action_dispatch/http/parameters.rb +98 -39
  71. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  72. data/lib/action_dispatch/http/request.rb +200 -118
  73. data/lib/action_dispatch/http/response.rb +225 -110
  74. data/lib/action_dispatch/http/upload.rb +12 -6
  75. data/lib/action_dispatch/http/url.rb +110 -28
  76. data/lib/action_dispatch/journey.rb +7 -5
  77. data/lib/action_dispatch/journey/formatter.rb +55 -32
  78. data/lib/action_dispatch/journey/gtg/builder.rb +7 -5
  79. data/lib/action_dispatch/journey/gtg/simulator.rb +3 -9
  80. data/lib/action_dispatch/journey/gtg/transition_table.rb +17 -16
  81. data/lib/action_dispatch/journey/nfa/builder.rb +5 -3
  82. data/lib/action_dispatch/journey/nfa/dot.rb +13 -13
  83. data/lib/action_dispatch/journey/nfa/simulator.rb +3 -1
  84. data/lib/action_dispatch/journey/nfa/transition_table.rb +5 -48
  85. data/lib/action_dispatch/journey/nodes/node.rb +18 -6
  86. data/lib/action_dispatch/journey/parser.rb +23 -22
  87. data/lib/action_dispatch/journey/parser.y +3 -2
  88. data/lib/action_dispatch/journey/parser_extras.rb +12 -4
  89. data/lib/action_dispatch/journey/path/pattern.rb +50 -44
  90. data/lib/action_dispatch/journey/route.rb +106 -28
  91. data/lib/action_dispatch/journey/router.rb +35 -23
  92. data/lib/action_dispatch/journey/router/utils.rb +20 -11
  93. data/lib/action_dispatch/journey/routes.rb +18 -16
  94. data/lib/action_dispatch/journey/scanner.rb +18 -15
  95. data/lib/action_dispatch/journey/visitors.rb +99 -52
  96. data/lib/action_dispatch/middleware/callbacks.rb +1 -2
  97. data/lib/action_dispatch/middleware/cookies.rb +304 -193
  98. data/lib/action_dispatch/middleware/debug_exceptions.rb +152 -57
  99. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  100. data/lib/action_dispatch/middleware/exception_wrapper.rb +68 -69
  101. data/lib/action_dispatch/middleware/executor.rb +21 -0
  102. data/lib/action_dispatch/middleware/flash.rb +78 -54
  103. data/lib/action_dispatch/middleware/public_exceptions.rb +27 -25
  104. data/lib/action_dispatch/middleware/reloader.rb +5 -91
  105. data/lib/action_dispatch/middleware/remote_ip.rb +41 -31
  106. data/lib/action_dispatch/middleware/request_id.rb +17 -9
  107. data/lib/action_dispatch/middleware/session/abstract_store.rb +41 -25
  108. data/lib/action_dispatch/middleware/session/cache_store.rb +24 -14
  109. data/lib/action_dispatch/middleware/session/cookie_store.rb +72 -67
  110. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -2
  111. data/lib/action_dispatch/middleware/show_exceptions.rb +26 -22
  112. data/lib/action_dispatch/middleware/ssl.rb +114 -36
  113. data/lib/action_dispatch/middleware/stack.rb +31 -44
  114. data/lib/action_dispatch/middleware/static.rb +57 -50
  115. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +2 -14
  116. data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +0 -0
  117. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +21 -0
  119. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +13 -0
  120. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +1 -0
  121. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -1
  122. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
  123. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
  124. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +64 -64
  125. data/lib/action_dispatch/railtie.rb +19 -11
  126. data/lib/action_dispatch/request/session.rb +106 -59
  127. data/lib/action_dispatch/request/utils.rb +67 -24
  128. data/lib/action_dispatch/routing.rb +17 -18
  129. data/lib/action_dispatch/routing/endpoint.rb +9 -2
  130. data/lib/action_dispatch/routing/inspector.rb +58 -67
  131. data/lib/action_dispatch/routing/mapper.rb +734 -447
  132. data/lib/action_dispatch/routing/polymorphic_routes.rb +161 -139
  133. data/lib/action_dispatch/routing/redirection.rb +36 -26
  134. data/lib/action_dispatch/routing/route_set.rb +321 -291
  135. data/lib/action_dispatch/routing/routes_proxy.rb +32 -5
  136. data/lib/action_dispatch/routing/url_for.rb +65 -25
  137. data/lib/action_dispatch/system_test_case.rb +147 -0
  138. data/lib/action_dispatch/system_testing/browser.rb +49 -0
  139. data/lib/action_dispatch/system_testing/driver.rb +59 -0
  140. data/lib/action_dispatch/system_testing/server.rb +31 -0
  141. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +96 -0
  142. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +31 -0
  143. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
  144. data/lib/action_dispatch/testing/assertion_response.rb +47 -0
  145. data/lib/action_dispatch/testing/assertions.rb +6 -4
  146. data/lib/action_dispatch/testing/assertions/response.rb +45 -20
  147. data/lib/action_dispatch/testing/assertions/routing.rb +30 -26
  148. data/lib/action_dispatch/testing/integration.rb +347 -209
  149. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  150. data/lib/action_dispatch/testing/test_process.rb +28 -22
  151. data/lib/action_dispatch/testing/test_request.rb +27 -34
  152. data/lib/action_dispatch/testing/test_response.rb +35 -7
  153. data/lib/action_pack.rb +4 -2
  154. data/lib/action_pack/gem_version.rb +5 -3
  155. data/lib/action_pack/version.rb +3 -1
  156. metadata +56 -39
  157. data/lib/action_controller/metal/hide_actions.rb +0 -40
  158. data/lib/action_controller/metal/rack_delegation.rb +0 -32
  159. data/lib/action_controller/middleware.rb +0 -39
  160. data/lib/action_controller/model_naming.rb +0 -12
  161. data/lib/action_dispatch/journey/backwards.rb +0 -5
  162. data/lib/action_dispatch/journey/router/strexp.rb +0 -27
  163. data/lib/action_dispatch/middleware/params_parser.rb +0 -60
  164. data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
  165. data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
  166. data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
@@ -1,4 +1,4 @@
1
- Copyright (c) 2004-2014 David Heinemeier Hansson
1
+ Copyright (c) 2004-2018 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
@@ -28,31 +28,30 @@ can be used outside of Rails.
28
28
 
29
29
  The latest version of Action Pack can be installed with RubyGems:
30
30
 
31
- % [sudo] gem install actionpack
31
+ $ gem install actionpack
32
32
 
33
- Source code can be downloaded as part of the Rails project on GitHub
33
+ Source code can be downloaded as part of the Rails project on GitHub:
34
34
 
35
- * https://github.com/rails/rails/tree/4-2-stable/actionpack
35
+ * https://github.com/rails/rails/tree/5-2-stable/actionpack
36
36
 
37
37
 
38
38
  == License
39
39
 
40
40
  Action Pack is released under the MIT license:
41
41
 
42
- * http://www.opensource.org/licenses/MIT
42
+ * https://opensource.org/licenses/MIT
43
43
 
44
44
 
45
45
  == Support
46
46
 
47
- API documentation is at
47
+ API documentation is at:
48
48
 
49
49
  * http://api.rubyonrails.org
50
50
 
51
- Bug reports can be filed for the Ruby on Rails project here:
51
+ Bug reports for the Ruby on Rails project can be filed here:
52
52
 
53
53
  * https://github.com/rails/rails/issues
54
54
 
55
55
  Feature requests should be discussed on the rails-core mailing list here:
56
56
 
57
57
  * https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core
58
-
@@ -1,13 +1,15 @@
1
- require 'action_pack'
2
- require 'active_support/rails'
3
- require 'active_support/core_ext/module/attr_internal'
4
- require 'active_support/core_ext/module/anonymous'
5
- require 'active_support/i18n'
1
+ # frozen_string_literal: true
2
+
3
+ require "action_pack"
4
+ require "active_support/rails"
5
+ require "active_support/i18n"
6
6
 
7
7
  module AbstractController
8
8
  extend ActiveSupport::Autoload
9
9
 
10
+ autoload :ActionNotFound, "abstract_controller/base"
10
11
  autoload :Base
12
+ autoload :Caching
11
13
  autoload :Callbacks
12
14
  autoload :Collector
13
15
  autoload :DoubleRenderError, "abstract_controller/rendering"
@@ -17,4 +19,9 @@ module AbstractController
17
19
  autoload :Translation
18
20
  autoload :AssetPaths
19
21
  autoload :UrlFor
22
+
23
+ def self.eager_load!
24
+ super
25
+ AbstractController::Caching.eager_load!
26
+ end
20
27
  end
@@ -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,36 @@
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
13
12
  end
14
13
 
15
- # <tt>AbstractController::Base</tt> is a low-level API. Nobody should be
14
+ # AbstractController::Base is a low-level API. Nobody should be
16
15
  # using it directly, and subclasses (like ActionController::Base) are
17
16
  # expected to provide their own +render+ method, since rendering means
18
17
  # different things depending on the context.
19
18
  class Base
19
+ ##
20
+ # Returns the body of the HTTP response sent by the controller.
20
21
  attr_internal :response_body
22
+
23
+ ##
24
+ # Returns the name of the action this controller is processing.
21
25
  attr_internal :action_name
26
+
27
+ ##
28
+ # Returns the formats that can be processed by the controller.
22
29
  attr_internal :formats
23
30
 
24
31
  include ActiveSupport::Configurable
25
32
  extend ActiveSupport::DescendantsTracker
26
33
 
27
- undef_method :not_implemented
28
34
  class << self
29
35
  attr_reader :abstract
30
36
  alias_method :abstract?, :abstract
@@ -49,7 +55,7 @@ module AbstractController
49
55
  # instance methods on that abstract class. Public instance methods of
50
56
  # a controller would normally be considered action methods, so methods
51
57
  # declared on abstract classes are being removed.
52
- # (ActionController::Metal and ActionController::Base are defined as abstract)
58
+ # (<tt>ActionController::Metal</tt> and ActionController::Base are defined as abstract)
53
59
  def internal_methods
54
60
  controller = self
55
61
 
@@ -57,21 +63,11 @@ module AbstractController
57
63
  controller.public_instance_methods(true)
58
64
  end
59
65
 
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
66
  # A list of method names that should be considered actions. This
71
67
  # includes all public instance methods on a controller, less
72
- # any internal methods (see #internal_methods), adding back in
68
+ # any internal methods (see internal_methods), adding back in
73
69
  # any methods that are internal, but still exist on the class
74
- # itself. Finally, #hidden_actions are removed.
70
+ # itself.
75
71
  #
76
72
  # ==== Returns
77
73
  # * <tt>Set</tt> - A set of all methods that should be considered actions.
@@ -82,30 +78,31 @@ module AbstractController
82
78
  # Except for public instance methods of Base and its ancestors
83
79
  internal_methods +
84
80
  # 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
81
+ public_instance_methods(false)).uniq.map(&:to_s)
88
82
 
89
- # Clear out AS callback method pollution
90
- Set.new(methods.reject { |method| method =~ /_one_time_conditions/ })
83
+ methods.to_set
91
84
  end
92
85
  end
93
86
 
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
87
+ # action_methods are cached and there is sometimes a need to refresh
88
+ # them. ::clear_action_methods! allows you to do that, so next time
89
+ # you run action_methods, they will be recalculated.
97
90
  def clear_action_methods!
98
91
  @action_methods = nil
99
92
  end
100
93
 
101
94
  # 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.
95
+ #
96
+ # class MyApp::MyPostsController < AbstractController::Base
97
+ #
98
+ # end
99
+ #
100
+ # MyApp::MyPostsController.controller_path # => "my_app/my_posts"
104
101
  #
105
102
  # ==== Returns
106
103
  # * <tt>String</tt>
107
104
  def controller_path
108
- @controller_path ||= name.sub(/Controller$/, '').underscore unless anonymous?
105
+ @controller_path ||= name.sub(/Controller$/, "".freeze).underscore unless anonymous?
109
106
  end
110
107
 
111
108
  # Refresh the cached action_methods when a new action_method is added.
@@ -137,12 +134,12 @@ module AbstractController
137
134
  process_action(action_name, *args)
138
135
  end
139
136
 
140
- # Delegates to the class' #controller_path
137
+ # Delegates to the class' ::controller_path
141
138
  def controller_path
142
139
  self.class.controller_path
143
140
  end
144
141
 
145
- # Delegates to the class' #action_methods
142
+ # Delegates to the class' ::action_methods
146
143
  def action_methods
147
144
  self.class.action_methods
148
145
  end
@@ -157,11 +154,15 @@ module AbstractController
157
154
  #
158
155
  # ==== Parameters
159
156
  # * <tt>action_name</tt> - The name of an action to be tested
160
- #
161
- # ==== Returns
162
- # * <tt>TrueClass</tt>, <tt>FalseClass</tt>
163
157
  def available_action?(action_name)
164
- _find_action_name(action_name).present?
158
+ _find_action_name(action_name)
159
+ end
160
+
161
+ # Tests if a response body is set. Used to determine if the
162
+ # +process_action+ callback needs to be terminated in
163
+ # +AbstractController::Callbacks+.
164
+ def performed?
165
+ response_body
165
166
  end
166
167
 
167
168
  # Returns true if the given controller is capable of rendering
@@ -179,11 +180,6 @@ module AbstractController
179
180
  #
180
181
  # ==== Parameters
181
182
  # * <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
183
  def action_method?(name)
188
184
  self.class.action_methods.include?(name)
189
185
  end
@@ -225,7 +221,7 @@ module AbstractController
225
221
  # ==== Returns
226
222
  # * <tt>string</tt> - The name of the method that handles the action
227
223
  # * false - No valid method name could be found.
228
- # Raise AbstractController::ActionNotFound.
224
+ # Raise +AbstractController::ActionNotFound+.
229
225
  def _find_action_name(action_name)
230
226
  _valid_action_name?(action_name) && method_for_action(action_name)
231
227
  end
@@ -241,11 +237,11 @@ module AbstractController
241
237
  # with a template matching the action name is considered to exist.
242
238
  #
243
239
  # If you override this method to handle additional cases, you may
244
- # also provide a method (like _handle_method_missing) to handle
240
+ # also provide a method (like +_handle_method_missing+) to handle
245
241
  # the case.
246
242
  #
247
- # If none of these conditions are true, and method_for_action
248
- # returns nil, an AbstractController::ActionNotFound exception will be raised.
243
+ # If none of these conditions are true, and +method_for_action+
244
+ # returns +nil+, an +AbstractController::ActionNotFound+ exception will be raised.
249
245
  #
250
246
  # ==== Parameters
251
247
  # * <tt>action_name</tt> - An action name to find a method name for
@@ -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,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,79 @@ 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 :fragment_cache_key
32
+ helper_method :combined_fragment_cache_key
33
+ end
34
+ end
35
+
36
+ module ClassMethods
37
+ # Allows you to specify controller-wide key prefixes for
38
+ # cache fragments. Pass either a constant +value+, or a block
39
+ # which computes a value each time a cache key is generated.
40
+ #
41
+ # For example, you may want to prefix all fragment cache keys
42
+ # with a global version identifier, so you can easily
43
+ # invalidate all caches.
44
+ #
45
+ # class ApplicationController
46
+ # fragment_cache_key "v1"
47
+ # end
48
+ #
49
+ # When it's time to invalidate all fragments, simply change
50
+ # the string constant. Or, progressively roll out the cache
51
+ # invalidation using a computed value:
52
+ #
53
+ # class ApplicationController
54
+ # fragment_cache_key do
55
+ # @account.id.odd? ? "v1" : "v2"
56
+ # end
57
+ # end
58
+ def fragment_cache_key(value = nil, &key)
59
+ self.fragment_cache_keys += [key || -> { value }]
60
+ end
61
+ end
62
+
17
63
  # Given a key (as described in +expire_fragment+), returns
18
64
  # 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.
65
+ # cached fragment. All keys begin with <tt>views/</tt>,
66
+ # followed by any controller-wide key prefix values, ending
67
+ # with the specified +key+ value. The key is expanded using
68
+ # ActiveSupport::Cache.expand_cache_key.
21
69
  def fragment_cache_key(key)
22
- ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key).split("://").last : key, :views)
70
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
71
+ Calling fragment_cache_key directly is deprecated and will be removed in Rails 6.0.
72
+ All fragment accessors now use the combined_fragment_cache_key method that retains the key as an array,
73
+ such that the caching stores can interrogate the parts for cache versions used in
74
+ recyclable cache keys.
75
+ MSG
76
+
77
+ head = self.class.fragment_cache_keys.map { |k| instance_exec(&k) }
78
+ tail = key.is_a?(Hash) ? url_for(key).split("://").last : key
79
+ ActiveSupport::Cache.expand_cache_key([*head, *tail], :views)
80
+ end
81
+
82
+ # Given a key (as described in +expire_fragment+), returns
83
+ # a key array suitable for use in reading, writing, or expiring a
84
+ # cached fragment. All keys begin with <tt>:views</tt>,
85
+ # followed by ENV["RAILS_CACHE_ID"] or ENV["RAILS_APP_VERSION"] if set,
86
+ # followed by any controller-wide key prefix values, ending
87
+ # with the specified +key+ value.
88
+ def combined_fragment_cache_key(key)
89
+ head = self.class.fragment_cache_keys.map { |k| instance_exec(&k) }
90
+ tail = key.is_a?(Hash) ? url_for(key).split("://").last : key
91
+ [ :views, (ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"]), *head, *tail ].compact
23
92
  end
24
93
 
25
94
  # Writes +content+ to the location signified by
@@ -27,7 +96,7 @@ module ActionController
27
96
  def write_fragment(key, content, options = nil)
28
97
  return content unless cache_configured?
29
98
 
30
- key = fragment_cache_key(key)
99
+ key = combined_fragment_cache_key(key)
31
100
  instrument_fragment_cache :write_fragment, key do
32
101
  content = content.to_str
33
102
  cache_store.write(key, content, options)
@@ -40,7 +109,7 @@ module ActionController
40
109
  def read_fragment(key, options = nil)
41
110
  return unless cache_configured?
42
111
 
43
- key = fragment_cache_key(key)
112
+ key = combined_fragment_cache_key(key)
44
113
  instrument_fragment_cache :read_fragment, key do
45
114
  result = cache_store.read(key, options)
46
115
  result.respond_to?(:html_safe) ? result.html_safe : result
@@ -51,7 +120,7 @@ module ActionController
51
120
  # +key+ exists (see +expire_fragment+ for acceptable formats).
52
121
  def fragment_exist?(key, options = nil)
53
122
  return unless cache_configured?
54
- key = fragment_cache_key(key)
123
+ key = combined_fragment_cache_key(key)
55
124
 
56
125
  instrument_fragment_cache :exist_fragment?, key do
57
126
  cache_store.exist?(key, options)
@@ -78,7 +147,7 @@ module ActionController
78
147
  # method (or <tt>delete_matched</tt>, for Regexp keys).
79
148
  def expire_fragment(key, options = nil)
80
149
  return unless cache_configured?
81
- key = fragment_cache_key(key) unless key.is_a?(Regexp)
150
+ key = combined_fragment_cache_key(key) unless key.is_a?(Regexp)
82
151
 
83
152
  instrument_fragment_cache :expire_fragment, key do
84
153
  if key.is_a?(Regexp)
@@ -90,13 +159,7 @@ module ActionController
90
159
  end
91
160
 
92
161
  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 }
162
+ ActiveSupport::Notifications.instrument("#{name}.#{instrument_name}", instrument_payload(key)) { yield }
100
163
  end
101
164
  end
102
165
  end