actionpack 5.2.3

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 (170) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +429 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.rdoc +57 -0
  5. data/lib/abstract_controller.rb +27 -0
  6. data/lib/abstract_controller/asset_paths.rb +12 -0
  7. data/lib/abstract_controller/base.rb +265 -0
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/abstract_controller/caching/fragments.rb +166 -0
  10. data/lib/abstract_controller/callbacks.rb +212 -0
  11. data/lib/abstract_controller/collector.rb +43 -0
  12. data/lib/abstract_controller/error.rb +6 -0
  13. data/lib/abstract_controller/helpers.rb +194 -0
  14. data/lib/abstract_controller/logger.rb +14 -0
  15. data/lib/abstract_controller/railties/routes_helpers.rb +20 -0
  16. data/lib/abstract_controller/rendering.rb +127 -0
  17. data/lib/abstract_controller/translation.rb +31 -0
  18. data/lib/abstract_controller/url_for.rb +35 -0
  19. data/lib/action_controller.rb +66 -0
  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 +276 -0
  23. data/lib/action_controller/caching.rb +46 -0
  24. data/lib/action_controller/form_builder.rb +50 -0
  25. data/lib/action_controller/log_subscriber.rb +78 -0
  26. data/lib/action_controller/metal.rb +256 -0
  27. data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
  28. data/lib/action_controller/metal/conditional_get.rb +274 -0
  29. data/lib/action_controller/metal/content_security_policy.rb +52 -0
  30. data/lib/action_controller/metal/cookies.rb +16 -0
  31. data/lib/action_controller/metal/data_streaming.rb +152 -0
  32. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  33. data/lib/action_controller/metal/etag_with_template_digest.rb +57 -0
  34. data/lib/action_controller/metal/exceptions.rb +53 -0
  35. data/lib/action_controller/metal/flash.rb +61 -0
  36. data/lib/action_controller/metal/force_ssl.rb +99 -0
  37. data/lib/action_controller/metal/head.rb +60 -0
  38. data/lib/action_controller/metal/helpers.rb +123 -0
  39. data/lib/action_controller/metal/http_authentication.rb +519 -0
  40. data/lib/action_controller/metal/implicit_render.rb +73 -0
  41. data/lib/action_controller/metal/instrumentation.rb +107 -0
  42. data/lib/action_controller/metal/live.rb +312 -0
  43. data/lib/action_controller/metal/mime_responds.rb +313 -0
  44. data/lib/action_controller/metal/parameter_encoding.rb +51 -0
  45. data/lib/action_controller/metal/params_wrapper.rb +293 -0
  46. data/lib/action_controller/metal/redirecting.rb +133 -0
  47. data/lib/action_controller/metal/renderers.rb +181 -0
  48. data/lib/action_controller/metal/rendering.rb +122 -0
  49. data/lib/action_controller/metal/request_forgery_protection.rb +445 -0
  50. data/lib/action_controller/metal/rescue.rb +28 -0
  51. data/lib/action_controller/metal/streaming.rb +223 -0
  52. data/lib/action_controller/metal/strong_parameters.rb +1086 -0
  53. data/lib/action_controller/metal/testing.rb +16 -0
  54. data/lib/action_controller/metal/url_for.rb +58 -0
  55. data/lib/action_controller/railtie.rb +89 -0
  56. data/lib/action_controller/railties/helpers.rb +24 -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 +629 -0
  60. data/lib/action_dispatch.rb +112 -0
  61. data/lib/action_dispatch/http/cache.rb +222 -0
  62. data/lib/action_dispatch/http/content_security_policy.rb +272 -0
  63. data/lib/action_dispatch/http/filter_parameters.rb +84 -0
  64. data/lib/action_dispatch/http/filter_redirect.rb +37 -0
  65. data/lib/action_dispatch/http/headers.rb +132 -0
  66. data/lib/action_dispatch/http/mime_negotiation.rb +175 -0
  67. data/lib/action_dispatch/http/mime_type.rb +342 -0
  68. data/lib/action_dispatch/http/mime_types.rb +50 -0
  69. data/lib/action_dispatch/http/parameter_filter.rb +86 -0
  70. data/lib/action_dispatch/http/parameters.rb +126 -0
  71. data/lib/action_dispatch/http/rack_cache.rb +63 -0
  72. data/lib/action_dispatch/http/request.rb +430 -0
  73. data/lib/action_dispatch/http/response.rb +519 -0
  74. data/lib/action_dispatch/http/upload.rb +84 -0
  75. data/lib/action_dispatch/http/url.rb +350 -0
  76. data/lib/action_dispatch/journey.rb +7 -0
  77. data/lib/action_dispatch/journey/formatter.rb +189 -0
  78. data/lib/action_dispatch/journey/gtg/builder.rb +164 -0
  79. data/lib/action_dispatch/journey/gtg/simulator.rb +41 -0
  80. data/lib/action_dispatch/journey/gtg/transition_table.rb +158 -0
  81. data/lib/action_dispatch/journey/nfa/builder.rb +78 -0
  82. data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
  83. data/lib/action_dispatch/journey/nfa/simulator.rb +49 -0
  84. data/lib/action_dispatch/journey/nfa/transition_table.rb +120 -0
  85. data/lib/action_dispatch/journey/nodes/node.rb +140 -0
  86. data/lib/action_dispatch/journey/parser.rb +199 -0
  87. data/lib/action_dispatch/journey/parser.y +50 -0
  88. data/lib/action_dispatch/journey/parser_extras.rb +31 -0
  89. data/lib/action_dispatch/journey/path/pattern.rb +198 -0
  90. data/lib/action_dispatch/journey/route.rb +203 -0
  91. data/lib/action_dispatch/journey/router.rb +156 -0
  92. data/lib/action_dispatch/journey/router/utils.rb +102 -0
  93. data/lib/action_dispatch/journey/routes.rb +82 -0
  94. data/lib/action_dispatch/journey/scanner.rb +64 -0
  95. data/lib/action_dispatch/journey/visitors.rb +268 -0
  96. data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
  97. data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
  98. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  99. data/lib/action_dispatch/middleware/callbacks.rb +36 -0
  100. data/lib/action_dispatch/middleware/cookies.rb +685 -0
  101. data/lib/action_dispatch/middleware/debug_exceptions.rb +205 -0
  102. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  103. data/lib/action_dispatch/middleware/exception_wrapper.rb +147 -0
  104. data/lib/action_dispatch/middleware/executor.rb +21 -0
  105. data/lib/action_dispatch/middleware/flash.rb +300 -0
  106. data/lib/action_dispatch/middleware/public_exceptions.rb +57 -0
  107. data/lib/action_dispatch/middleware/reloader.rb +12 -0
  108. data/lib/action_dispatch/middleware/remote_ip.rb +183 -0
  109. data/lib/action_dispatch/middleware/request_id.rb +43 -0
  110. data/lib/action_dispatch/middleware/session/abstract_store.rb +92 -0
  111. data/lib/action_dispatch/middleware/session/cache_store.rb +54 -0
  112. data/lib/action_dispatch/middleware/session/cookie_store.rb +118 -0
  113. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +28 -0
  114. data/lib/action_dispatch/middleware/show_exceptions.rb +62 -0
  115. data/lib/action_dispatch/middleware/ssl.rb +150 -0
  116. data/lib/action_dispatch/middleware/stack.rb +116 -0
  117. data/lib/action_dispatch/middleware/static.rb +130 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +22 -0
  119. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  120. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +27 -0
  121. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  122. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +52 -0
  123. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
  124. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +16 -0
  125. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  126. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +21 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +13 -0
  128. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +161 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
  132. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
  136. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  137. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
  138. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +200 -0
  139. data/lib/action_dispatch/railtie.rb +55 -0
  140. data/lib/action_dispatch/request/session.rb +234 -0
  141. data/lib/action_dispatch/request/utils.rb +78 -0
  142. data/lib/action_dispatch/routing.rb +260 -0
  143. data/lib/action_dispatch/routing/endpoint.rb +17 -0
  144. data/lib/action_dispatch/routing/inspector.rb +225 -0
  145. data/lib/action_dispatch/routing/mapper.rb +2267 -0
  146. data/lib/action_dispatch/routing/polymorphic_routes.rb +352 -0
  147. data/lib/action_dispatch/routing/redirection.rb +201 -0
  148. data/lib/action_dispatch/routing/route_set.rb +890 -0
  149. data/lib/action_dispatch/routing/routes_proxy.rb +69 -0
  150. data/lib/action_dispatch/routing/url_for.rb +236 -0
  151. data/lib/action_dispatch/system_test_case.rb +147 -0
  152. data/lib/action_dispatch/system_testing/browser.rb +49 -0
  153. data/lib/action_dispatch/system_testing/driver.rb +59 -0
  154. data/lib/action_dispatch/system_testing/server.rb +31 -0
  155. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +96 -0
  156. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +31 -0
  157. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
  158. data/lib/action_dispatch/testing/assertion_response.rb +47 -0
  159. data/lib/action_dispatch/testing/assertions.rb +24 -0
  160. data/lib/action_dispatch/testing/assertions/response.rb +107 -0
  161. data/lib/action_dispatch/testing/assertions/routing.rb +222 -0
  162. data/lib/action_dispatch/testing/integration.rb +652 -0
  163. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  164. data/lib/action_dispatch/testing/test_process.rb +50 -0
  165. data/lib/action_dispatch/testing/test_request.rb +71 -0
  166. data/lib/action_dispatch/testing/test_response.rb +53 -0
  167. data/lib/action_pack.rb +26 -0
  168. data/lib/action_pack/gem_version.rb +17 -0
  169. data/lib/action_pack/version.rb +10 -0
  170. metadata +318 -0
@@ -0,0 +1,57 @@
1
+ = Action Pack -- From request to response
2
+
3
+ Action Pack is a framework for handling and responding to web requests. It
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.
8
+
9
+ It consists of several modules:
10
+
11
+ * Action Dispatch, which parses information about the web request, handles
12
+ routing as defined by the user, and does advanced processing related to HTTP
13
+ such as MIME-type negotiation, decoding parameters in POST, PATCH, or PUT bodies,
14
+ handling HTTP caching logic, cookies and sessions.
15
+
16
+ * Action Controller, which provides a base controller class that can be
17
+ subclassed to implement filters and actions to handle requests. The result
18
+ of an action is typically content generated from views.
19
+
20
+ With the Ruby on Rails framework, users only directly interface with the
21
+ Action Controller module. Necessary Action Dispatch functionality is activated
22
+ by default and Action View rendering is implicitly triggered by Action
23
+ Controller. However, these modules are designed to function on their own and
24
+ can be used outside of Rails.
25
+
26
+
27
+ == Download and installation
28
+
29
+ The latest version of Action Pack can be installed with RubyGems:
30
+
31
+ $ gem install actionpack
32
+
33
+ Source code can be downloaded as part of the Rails project on GitHub:
34
+
35
+ * https://github.com/rails/rails/tree/5-2-stable/actionpack
36
+
37
+
38
+ == License
39
+
40
+ Action Pack is released under the MIT license:
41
+
42
+ * https://opensource.org/licenses/MIT
43
+
44
+
45
+ == Support
46
+
47
+ API documentation is at:
48
+
49
+ * http://api.rubyonrails.org
50
+
51
+ Bug reports for the Ruby on Rails project can be filed here:
52
+
53
+ * https://github.com/rails/rails/issues
54
+
55
+ Feature requests should be discussed on the rails-core mailing list here:
56
+
57
+ * https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "action_pack"
4
+ require "active_support/rails"
5
+ require "active_support/i18n"
6
+
7
+ module AbstractController
8
+ extend ActiveSupport::Autoload
9
+
10
+ autoload :ActionNotFound, "abstract_controller/base"
11
+ autoload :Base
12
+ autoload :Caching
13
+ autoload :Callbacks
14
+ autoload :Collector
15
+ autoload :DoubleRenderError, "abstract_controller/rendering"
16
+ autoload :Helpers
17
+ autoload :Logger
18
+ autoload :Rendering
19
+ autoload :Translation
20
+ autoload :AssetPaths
21
+ autoload :UrlFor
22
+
23
+ def self.eager_load!
24
+ super
25
+ AbstractController::Caching.eager_load!
26
+ end
27
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AbstractController
4
+ module AssetPaths #:nodoc:
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ config_accessor :asset_host, :assets_dir, :javascripts_dir,
9
+ :stylesheets_dir, :default_asset_host_protocol, :relative_url_root
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,265 @@
1
+ # frozen_string_literal: true
2
+
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"
8
+
9
+ module AbstractController
10
+ # Raised when a non-existing controller action is triggered.
11
+ class ActionNotFound < StandardError
12
+ end
13
+
14
+ # AbstractController::Base is a low-level API. Nobody should be
15
+ # using it directly, and subclasses (like ActionController::Base) are
16
+ # expected to provide their own +render+ method, since rendering means
17
+ # different things depending on the context.
18
+ class Base
19
+ ##
20
+ # Returns the body of the HTTP response sent by the controller.
21
+ attr_internal :response_body
22
+
23
+ ##
24
+ # Returns the name of the action this controller is processing.
25
+ attr_internal :action_name
26
+
27
+ ##
28
+ # Returns the formats that can be processed by the controller.
29
+ attr_internal :formats
30
+
31
+ include ActiveSupport::Configurable
32
+ extend ActiveSupport::DescendantsTracker
33
+
34
+ class << self
35
+ attr_reader :abstract
36
+ alias_method :abstract?, :abstract
37
+
38
+ # Define a controller as abstract. See internal_methods for more
39
+ # details.
40
+ def abstract!
41
+ @abstract = true
42
+ end
43
+
44
+ def inherited(klass) # :nodoc:
45
+ # Define the abstract ivar on subclasses so that we don't get
46
+ # uninitialized ivar warnings
47
+ unless klass.instance_variable_defined?(:@abstract)
48
+ klass.instance_variable_set(:@abstract, false)
49
+ end
50
+ super
51
+ end
52
+
53
+ # A list of all internal methods for a controller. This finds the first
54
+ # abstract superclass of a controller, and gets a list of all public
55
+ # instance methods on that abstract class. Public instance methods of
56
+ # a controller would normally be considered action methods, so methods
57
+ # declared on abstract classes are being removed.
58
+ # (<tt>ActionController::Metal</tt> and ActionController::Base are defined as abstract)
59
+ def internal_methods
60
+ controller = self
61
+
62
+ controller = controller.superclass until controller.abstract?
63
+ controller.public_instance_methods(true)
64
+ end
65
+
66
+ # A list of method names that should be considered actions. This
67
+ # includes all public instance methods on a controller, less
68
+ # any internal methods (see internal_methods), adding back in
69
+ # any methods that are internal, but still exist on the class
70
+ # itself.
71
+ #
72
+ # ==== Returns
73
+ # * <tt>Set</tt> - A set of all methods that should be considered actions.
74
+ def action_methods
75
+ @action_methods ||= begin
76
+ # All public instance methods of this class, including ancestors
77
+ methods = (public_instance_methods(true) -
78
+ # Except for public instance methods of Base and its ancestors
79
+ internal_methods +
80
+ # Be sure to include shadowed public instance methods of this class
81
+ public_instance_methods(false)).uniq.map(&:to_s)
82
+
83
+ methods.to_set
84
+ end
85
+ end
86
+
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.
90
+ def clear_action_methods!
91
+ @action_methods = nil
92
+ end
93
+
94
+ # Returns the full controller name, underscored, without the ending Controller.
95
+ #
96
+ # class MyApp::MyPostsController < AbstractController::Base
97
+ #
98
+ # end
99
+ #
100
+ # MyApp::MyPostsController.controller_path # => "my_app/my_posts"
101
+ #
102
+ # ==== Returns
103
+ # * <tt>String</tt>
104
+ def controller_path
105
+ @controller_path ||= name.sub(/Controller$/, "".freeze).underscore unless anonymous?
106
+ end
107
+
108
+ # Refresh the cached action_methods when a new action_method is added.
109
+ def method_added(name)
110
+ super
111
+ clear_action_methods!
112
+ end
113
+ end
114
+
115
+ abstract!
116
+
117
+ # Calls the action going through the entire action dispatch stack.
118
+ #
119
+ # The actual method that is called is determined by calling
120
+ # #method_for_action. If no method can handle the action, then an
121
+ # AbstractController::ActionNotFound error is raised.
122
+ #
123
+ # ==== Returns
124
+ # * <tt>self</tt>
125
+ def process(action, *args)
126
+ @_action_name = action.to_s
127
+
128
+ unless action_name = _find_action_name(@_action_name)
129
+ raise ActionNotFound, "The action '#{action}' could not be found for #{self.class.name}"
130
+ end
131
+
132
+ @_response_body = nil
133
+
134
+ process_action(action_name, *args)
135
+ end
136
+
137
+ # Delegates to the class' ::controller_path
138
+ def controller_path
139
+ self.class.controller_path
140
+ end
141
+
142
+ # Delegates to the class' ::action_methods
143
+ def action_methods
144
+ self.class.action_methods
145
+ end
146
+
147
+ # Returns true if a method for the action is available and
148
+ # can be dispatched, false otherwise.
149
+ #
150
+ # Notice that <tt>action_methods.include?("foo")</tt> may return
151
+ # false and <tt>available_action?("foo")</tt> returns true because
152
+ # this method considers actions that are also available
153
+ # through other means, for example, implicit render ones.
154
+ #
155
+ # ==== Parameters
156
+ # * <tt>action_name</tt> - The name of an action to be tested
157
+ def available_action?(action_name)
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
166
+ end
167
+
168
+ # Returns true if the given controller is capable of rendering
169
+ # a path. A subclass of +AbstractController::Base+
170
+ # may return false. An Email controller for example does not
171
+ # support paths, only full URLs.
172
+ def self.supports_path?
173
+ true
174
+ end
175
+
176
+ private
177
+
178
+ # Returns true if the name can be considered an action because
179
+ # it has a method defined in the controller.
180
+ #
181
+ # ==== Parameters
182
+ # * <tt>name</tt> - The name of an action to be tested
183
+ def action_method?(name)
184
+ self.class.action_methods.include?(name)
185
+ end
186
+
187
+ # Call the action. Override this in a subclass to modify the
188
+ # behavior around processing an action. This, and not #process,
189
+ # is the intended way to override action dispatching.
190
+ #
191
+ # Notice that the first argument is the method to be dispatched
192
+ # which is *not* necessarily the same as the action name.
193
+ def process_action(method_name, *args)
194
+ send_action(method_name, *args)
195
+ end
196
+
197
+ # Actually call the method associated with the action. Override
198
+ # this method if you wish to change how action methods are called,
199
+ # not to add additional behavior around it. For example, you would
200
+ # override #send_action if you want to inject arguments into the
201
+ # method.
202
+ alias send_action send
203
+
204
+ # If the action name was not found, but a method called "action_missing"
205
+ # was found, #method_for_action will return "_handle_action_missing".
206
+ # This method calls #action_missing with the current action name.
207
+ def _handle_action_missing(*args)
208
+ action_missing(@_action_name, *args)
209
+ end
210
+
211
+ # Takes an action name and returns the name of the method that will
212
+ # handle the action.
213
+ #
214
+ # It checks if the action name is valid and returns false otherwise.
215
+ #
216
+ # See method_for_action for more information.
217
+ #
218
+ # ==== Parameters
219
+ # * <tt>action_name</tt> - An action name to find a method name for
220
+ #
221
+ # ==== Returns
222
+ # * <tt>string</tt> - The name of the method that handles the action
223
+ # * false - No valid method name could be found.
224
+ # Raise +AbstractController::ActionNotFound+.
225
+ def _find_action_name(action_name)
226
+ _valid_action_name?(action_name) && method_for_action(action_name)
227
+ end
228
+
229
+ # Takes an action name and returns the name of the method that will
230
+ # handle the action. In normal cases, this method returns the same
231
+ # name as it receives. By default, if #method_for_action receives
232
+ # a name that is not an action, it will look for an #action_missing
233
+ # method and return "_handle_action_missing" if one is found.
234
+ #
235
+ # Subclasses may override this method to add additional conditions
236
+ # that should be considered an action. For instance, an HTTP controller
237
+ # with a template matching the action name is considered to exist.
238
+ #
239
+ # If you override this method to handle additional cases, you may
240
+ # also provide a method (like +_handle_method_missing+) to handle
241
+ # the case.
242
+ #
243
+ # If none of these conditions are true, and +method_for_action+
244
+ # returns +nil+, an +AbstractController::ActionNotFound+ exception will be raised.
245
+ #
246
+ # ==== Parameters
247
+ # * <tt>action_name</tt> - An action name to find a method name for
248
+ #
249
+ # ==== Returns
250
+ # * <tt>string</tt> - The name of the method that handles the action
251
+ # * <tt>nil</tt> - No method name could be found.
252
+ def method_for_action(action_name)
253
+ if action_method?(action_name)
254
+ action_name
255
+ elsif respond_to?(:action_missing, true)
256
+ "_handle_action_missing"
257
+ end
258
+ end
259
+
260
+ # Checks if the action name is valid and returns false otherwise.
261
+ def _valid_action_name?(action_name)
262
+ !action_name.to_s.include? File::SEPARATOR
263
+ end
264
+ end
265
+ 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
@@ -0,0 +1,166 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AbstractController
4
+ module Caching
5
+ # Fragment caching is used for caching various blocks within
6
+ # views without caching the entire action as a whole. This is
7
+ # useful when certain elements of an action change frequently or
8
+ # depend on complicated state while other parts rarely change or
9
+ # can be shared amongst multiple parties. The caching is done using
10
+ # the +cache+ helper available in the Action View. See
11
+ # ActionView::Helpers::CacheHelper for more information.
12
+ #
13
+ # While it's strongly recommended that you use key-based cache
14
+ # expiration (see links in CacheHelper for more information),
15
+ # it is also possible to manually expire caches. For example:
16
+ #
17
+ # expire_fragment('name_of_cache')
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
+
63
+ # Given a key (as described in +expire_fragment+), returns
64
+ # a key suitable for use in reading, writing, or expiring a
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.
69
+ def fragment_cache_key(key)
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
92
+ end
93
+
94
+ # Writes +content+ to the location signified by
95
+ # +key+ (see +expire_fragment+ for acceptable formats).
96
+ def write_fragment(key, content, options = nil)
97
+ return content unless cache_configured?
98
+
99
+ key = combined_fragment_cache_key(key)
100
+ instrument_fragment_cache :write_fragment, key do
101
+ content = content.to_str
102
+ cache_store.write(key, content, options)
103
+ end
104
+ content
105
+ end
106
+
107
+ # Reads a cached fragment from the location signified by +key+
108
+ # (see +expire_fragment+ for acceptable formats).
109
+ def read_fragment(key, options = nil)
110
+ return unless cache_configured?
111
+
112
+ key = combined_fragment_cache_key(key)
113
+ instrument_fragment_cache :read_fragment, key do
114
+ result = cache_store.read(key, options)
115
+ result.respond_to?(:html_safe) ? result.html_safe : result
116
+ end
117
+ end
118
+
119
+ # Check if a cached fragment from the location signified by
120
+ # +key+ exists (see +expire_fragment+ for acceptable formats).
121
+ def fragment_exist?(key, options = nil)
122
+ return unless cache_configured?
123
+ key = combined_fragment_cache_key(key)
124
+
125
+ instrument_fragment_cache :exist_fragment?, key do
126
+ cache_store.exist?(key, options)
127
+ end
128
+ end
129
+
130
+ # Removes fragments from the cache.
131
+ #
132
+ # +key+ can take one of three forms:
133
+ #
134
+ # * String - This would normally take the form of a path, like
135
+ # <tt>pages/45/notes</tt>.
136
+ # * Hash - Treated as an implicit call to +url_for+, like
137
+ # <tt>{ controller: 'pages', action: 'notes', id: 45}</tt>
138
+ # * Regexp - Will remove any fragment that matches, so
139
+ # <tt>%r{pages/\d*/notes}</tt> might remove all notes. Make sure you
140
+ # don't use anchors in the regex (<tt>^</tt> or <tt>$</tt>) because
141
+ # the actual filename matched looks like
142
+ # <tt>./cache/filename/path.cache</tt>. Note: Regexp expiration is
143
+ # only supported on caches that can iterate over all keys (unlike
144
+ # memcached).
145
+ #
146
+ # +options+ is passed through to the cache store's +delete+
147
+ # method (or <tt>delete_matched</tt>, for Regexp keys).
148
+ def expire_fragment(key, options = nil)
149
+ return unless cache_configured?
150
+ key = combined_fragment_cache_key(key) unless key.is_a?(Regexp)
151
+
152
+ instrument_fragment_cache :expire_fragment, key do
153
+ if key.is_a?(Regexp)
154
+ cache_store.delete_matched(key, options)
155
+ else
156
+ cache_store.delete(key, options)
157
+ end
158
+ end
159
+ end
160
+
161
+ def instrument_fragment_cache(name, key) # :nodoc:
162
+ ActiveSupport::Notifications.instrument("#{name}.#{instrument_name}", instrument_payload(key)) { yield }
163
+ end
164
+ end
165
+ end
166
+ end