actionpack 3.2.22.5 → 5.2.4

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 (271) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +279 -603
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +13 -297
  5. data/lib/abstract_controller/asset_paths.rb +4 -2
  6. data/lib/abstract_controller/base.rb +82 -52
  7. data/lib/abstract_controller/caching/fragments.rb +166 -0
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/abstract_controller/callbacks.rb +117 -103
  10. data/lib/abstract_controller/collector.rb +18 -7
  11. data/lib/abstract_controller/error.rb +6 -0
  12. data/lib/abstract_controller/helpers.rb +65 -38
  13. data/lib/abstract_controller/logger.rb +3 -2
  14. data/lib/abstract_controller/railties/routes_helpers.rb +5 -3
  15. data/lib/abstract_controller/rendering.rb +77 -129
  16. data/lib/abstract_controller/translation.rb +21 -3
  17. data/lib/abstract_controller/url_for.rb +9 -7
  18. data/lib/abstract_controller.rb +12 -13
  19. data/lib/action_controller/api/api_rendering.rb +16 -0
  20. data/lib/action_controller/api.rb +149 -0
  21. data/lib/action_controller/base.rb +81 -40
  22. data/lib/action_controller/caching.rb +22 -62
  23. data/lib/action_controller/form_builder.rb +50 -0
  24. data/lib/action_controller/log_subscriber.rb +30 -18
  25. data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
  26. data/lib/action_controller/metal/conditional_get.rb +190 -47
  27. data/lib/action_controller/metal/content_security_policy.rb +52 -0
  28. data/lib/action_controller/metal/cookies.rb +3 -3
  29. data/lib/action_controller/metal/data_streaming.rb +40 -65
  30. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  31. data/lib/action_controller/metal/etag_with_template_digest.rb +57 -0
  32. data/lib/action_controller/metal/exceptions.rb +19 -12
  33. data/lib/action_controller/metal/flash.rb +42 -9
  34. data/lib/action_controller/metal/force_ssl.rb +79 -19
  35. data/lib/action_controller/metal/head.rb +35 -10
  36. data/lib/action_controller/metal/helpers.rb +31 -21
  37. data/lib/action_controller/metal/http_authentication.rb +182 -134
  38. data/lib/action_controller/metal/implicit_render.rb +62 -8
  39. data/lib/action_controller/metal/instrumentation.rb +28 -26
  40. data/lib/action_controller/metal/live.rb +312 -0
  41. data/lib/action_controller/metal/mime_responds.rb +159 -163
  42. data/lib/action_controller/metal/parameter_encoding.rb +51 -0
  43. data/lib/action_controller/metal/params_wrapper.rb +146 -93
  44. data/lib/action_controller/metal/redirecting.rb +80 -56
  45. data/lib/action_controller/metal/renderers.rb +119 -47
  46. data/lib/action_controller/metal/rendering.rb +89 -32
  47. data/lib/action_controller/metal/request_forgery_protection.rb +373 -41
  48. data/lib/action_controller/metal/rescue.rb +9 -16
  49. data/lib/action_controller/metal/streaming.rb +39 -45
  50. data/lib/action_controller/metal/strong_parameters.rb +1086 -0
  51. data/lib/action_controller/metal/testing.rb +8 -29
  52. data/lib/action_controller/metal/url_for.rb +43 -32
  53. data/lib/action_controller/metal.rb +112 -106
  54. data/lib/action_controller/railtie.rb +56 -18
  55. data/lib/action_controller/railties/helpers.rb +24 -0
  56. data/lib/action_controller/renderer.rb +117 -0
  57. data/lib/action_controller/template_assertions.rb +11 -0
  58. data/lib/action_controller/test_case.rb +402 -347
  59. data/lib/action_controller.rb +31 -30
  60. data/lib/action_dispatch/http/cache.rb +133 -34
  61. data/lib/action_dispatch/http/content_security_policy.rb +272 -0
  62. data/lib/action_dispatch/http/filter_parameters.rb +40 -24
  63. data/lib/action_dispatch/http/filter_redirect.rb +37 -0
  64. data/lib/action_dispatch/http/headers.rb +117 -16
  65. data/lib/action_dispatch/http/mime_negotiation.rb +98 -33
  66. data/lib/action_dispatch/http/mime_type.rb +198 -146
  67. data/lib/action_dispatch/http/mime_types.rb +22 -7
  68. data/lib/action_dispatch/http/parameter_filter.rb +61 -49
  69. data/lib/action_dispatch/http/parameters.rb +94 -51
  70. data/lib/action_dispatch/http/rack_cache.rb +4 -3
  71. data/lib/action_dispatch/http/request.rb +262 -117
  72. data/lib/action_dispatch/http/response.rb +400 -86
  73. data/lib/action_dispatch/http/upload.rb +66 -29
  74. data/lib/action_dispatch/http/url.rb +232 -60
  75. data/lib/action_dispatch/journey/formatter.rb +189 -0
  76. data/lib/action_dispatch/journey/gtg/builder.rb +164 -0
  77. data/lib/action_dispatch/journey/gtg/simulator.rb +41 -0
  78. data/lib/action_dispatch/journey/gtg/transition_table.rb +158 -0
  79. data/lib/action_dispatch/journey/nfa/builder.rb +78 -0
  80. data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
  81. data/lib/action_dispatch/journey/nfa/simulator.rb +49 -0
  82. data/lib/action_dispatch/journey/nfa/transition_table.rb +120 -0
  83. data/lib/action_dispatch/journey/nodes/node.rb +140 -0
  84. data/lib/action_dispatch/journey/parser.rb +199 -0
  85. data/lib/action_dispatch/journey/parser.y +50 -0
  86. data/lib/action_dispatch/journey/parser_extras.rb +31 -0
  87. data/lib/action_dispatch/journey/path/pattern.rb +199 -0
  88. data/lib/action_dispatch/journey/route.rb +203 -0
  89. data/lib/action_dispatch/journey/router/utils.rb +102 -0
  90. data/lib/action_dispatch/journey/router.rb +156 -0
  91. data/lib/action_dispatch/journey/routes.rb +82 -0
  92. data/lib/action_dispatch/journey/scanner.rb +64 -0
  93. data/lib/action_dispatch/journey/visitors.rb +268 -0
  94. data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
  95. data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
  96. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  97. data/lib/action_dispatch/journey.rb +7 -0
  98. data/lib/action_dispatch/middleware/callbacks.rb +17 -13
  99. data/lib/action_dispatch/middleware/cookies.rb +494 -162
  100. data/lib/action_dispatch/middleware/debug_exceptions.rb +176 -53
  101. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  102. data/lib/action_dispatch/middleware/exception_wrapper.rb +103 -38
  103. data/lib/action_dispatch/middleware/executor.rb +21 -0
  104. data/lib/action_dispatch/middleware/flash.rb +128 -91
  105. data/lib/action_dispatch/middleware/public_exceptions.rb +43 -16
  106. data/lib/action_dispatch/middleware/reloader.rb +6 -83
  107. data/lib/action_dispatch/middleware/remote_ip.rb +151 -49
  108. data/lib/action_dispatch/middleware/request_id.rb +19 -15
  109. data/lib/action_dispatch/middleware/session/abstract_store.rb +38 -34
  110. data/lib/action_dispatch/middleware/session/cache_store.rb +14 -9
  111. data/lib/action_dispatch/middleware/session/cookie_store.rb +94 -44
  112. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +15 -4
  113. data/lib/action_dispatch/middleware/show_exceptions.rb +36 -61
  114. data/lib/action_dispatch/middleware/ssl.rb +150 -0
  115. data/lib/action_dispatch/middleware/stack.rb +33 -41
  116. data/lib/action_dispatch/middleware/static.rb +92 -48
  117. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +22 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  119. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +27 -0
  120. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  121. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +52 -0
  122. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
  123. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +16 -0
  124. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  125. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +21 -0
  126. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +13 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +134 -5
  128. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  132. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  136. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
  137. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +200 -0
  138. data/lib/action_dispatch/railtie.rb +29 -8
  139. data/lib/action_dispatch/request/session.rb +234 -0
  140. data/lib/action_dispatch/request/utils.rb +78 -0
  141. data/lib/action_dispatch/routing/endpoint.rb +17 -0
  142. data/lib/action_dispatch/routing/inspector.rb +225 -0
  143. data/lib/action_dispatch/routing/mapper.rb +1329 -582
  144. data/lib/action_dispatch/routing/polymorphic_routes.rb +237 -94
  145. data/lib/action_dispatch/routing/redirection.rb +120 -50
  146. data/lib/action_dispatch/routing/route_set.rb +545 -322
  147. data/lib/action_dispatch/routing/routes_proxy.rb +37 -7
  148. data/lib/action_dispatch/routing/url_for.rb +103 -34
  149. data/lib/action_dispatch/routing.rb +66 -99
  150. data/lib/action_dispatch/system_test_case.rb +147 -0
  151. data/lib/action_dispatch/system_testing/browser.rb +49 -0
  152. data/lib/action_dispatch/system_testing/driver.rb +59 -0
  153. data/lib/action_dispatch/system_testing/server.rb +31 -0
  154. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +96 -0
  155. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +31 -0
  156. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
  157. data/lib/action_dispatch/testing/assertion_response.rb +47 -0
  158. data/lib/action_dispatch/testing/assertions/response.rb +53 -42
  159. data/lib/action_dispatch/testing/assertions/routing.rb +79 -74
  160. data/lib/action_dispatch/testing/assertions.rb +15 -9
  161. data/lib/action_dispatch/testing/integration.rb +361 -207
  162. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  163. data/lib/action_dispatch/testing/test_process.rb +28 -19
  164. data/lib/action_dispatch/testing/test_request.rb +30 -33
  165. data/lib/action_dispatch/testing/test_response.rb +35 -11
  166. data/lib/action_dispatch.rb +42 -32
  167. data/lib/action_pack/gem_version.rb +17 -0
  168. data/lib/action_pack/version.rb +7 -7
  169. data/lib/action_pack.rb +4 -2
  170. metadata +116 -175
  171. data/lib/abstract_controller/layouts.rb +0 -423
  172. data/lib/abstract_controller/view_paths.rb +0 -96
  173. data/lib/action_controller/caching/actions.rb +0 -185
  174. data/lib/action_controller/caching/fragments.rb +0 -127
  175. data/lib/action_controller/caching/pages.rb +0 -187
  176. data/lib/action_controller/caching/sweeping.rb +0 -97
  177. data/lib/action_controller/deprecated/integration_test.rb +0 -2
  178. data/lib/action_controller/deprecated/performance_test.rb +0 -1
  179. data/lib/action_controller/deprecated.rb +0 -3
  180. data/lib/action_controller/metal/compatibility.rb +0 -65
  181. data/lib/action_controller/metal/hide_actions.rb +0 -41
  182. data/lib/action_controller/metal/rack_delegation.rb +0 -26
  183. data/lib/action_controller/metal/responder.rb +0 -286
  184. data/lib/action_controller/metal/session_management.rb +0 -14
  185. data/lib/action_controller/middleware.rb +0 -39
  186. data/lib/action_controller/railties/paths.rb +0 -25
  187. data/lib/action_controller/record_identifier.rb +0 -85
  188. data/lib/action_controller/vendor/html-scanner/html/document.rb +0 -68
  189. data/lib/action_controller/vendor/html-scanner/html/node.rb +0 -532
  190. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +0 -177
  191. data/lib/action_controller/vendor/html-scanner/html/selector.rb +0 -830
  192. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +0 -107
  193. data/lib/action_controller/vendor/html-scanner/html/version.rb +0 -11
  194. data/lib/action_controller/vendor/html-scanner.rb +0 -20
  195. data/lib/action_dispatch/middleware/best_standards_support.rb +0 -30
  196. data/lib/action_dispatch/middleware/body_proxy.rb +0 -30
  197. data/lib/action_dispatch/middleware/head.rb +0 -18
  198. data/lib/action_dispatch/middleware/params_parser.rb +0 -75
  199. data/lib/action_dispatch/middleware/rescue.rb +0 -26
  200. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +0 -31
  201. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +0 -26
  202. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +0 -10
  203. data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +0 -2
  204. data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +0 -15
  205. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +0 -17
  206. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +0 -2
  207. data/lib/action_dispatch/testing/assertions/dom.rb +0 -37
  208. data/lib/action_dispatch/testing/assertions/selector.rb +0 -435
  209. data/lib/action_dispatch/testing/assertions/tag.rb +0 -138
  210. data/lib/action_dispatch/testing/performance_test.rb +0 -10
  211. data/lib/action_view/asset_paths.rb +0 -142
  212. data/lib/action_view/base.rb +0 -220
  213. data/lib/action_view/buffers.rb +0 -43
  214. data/lib/action_view/context.rb +0 -36
  215. data/lib/action_view/flows.rb +0 -79
  216. data/lib/action_view/helpers/active_model_helper.rb +0 -50
  217. data/lib/action_view/helpers/asset_paths.rb +0 -7
  218. data/lib/action_view/helpers/asset_tag_helper.rb +0 -457
  219. data/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +0 -146
  220. data/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +0 -93
  221. data/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +0 -193
  222. data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +0 -148
  223. data/lib/action_view/helpers/atom_feed_helper.rb +0 -200
  224. data/lib/action_view/helpers/cache_helper.rb +0 -64
  225. data/lib/action_view/helpers/capture_helper.rb +0 -203
  226. data/lib/action_view/helpers/controller_helper.rb +0 -25
  227. data/lib/action_view/helpers/csrf_helper.rb +0 -32
  228. data/lib/action_view/helpers/date_helper.rb +0 -1062
  229. data/lib/action_view/helpers/debug_helper.rb +0 -40
  230. data/lib/action_view/helpers/form_helper.rb +0 -1486
  231. data/lib/action_view/helpers/form_options_helper.rb +0 -658
  232. data/lib/action_view/helpers/form_tag_helper.rb +0 -685
  233. data/lib/action_view/helpers/javascript_helper.rb +0 -110
  234. data/lib/action_view/helpers/number_helper.rb +0 -622
  235. data/lib/action_view/helpers/output_safety_helper.rb +0 -38
  236. data/lib/action_view/helpers/record_tag_helper.rb +0 -111
  237. data/lib/action_view/helpers/rendering_helper.rb +0 -92
  238. data/lib/action_view/helpers/sanitize_helper.rb +0 -259
  239. data/lib/action_view/helpers/tag_helper.rb +0 -167
  240. data/lib/action_view/helpers/text_helper.rb +0 -426
  241. data/lib/action_view/helpers/translation_helper.rb +0 -91
  242. data/lib/action_view/helpers/url_helper.rb +0 -693
  243. data/lib/action_view/helpers.rb +0 -60
  244. data/lib/action_view/locale/en.yml +0 -160
  245. data/lib/action_view/log_subscriber.rb +0 -28
  246. data/lib/action_view/lookup_context.rb +0 -258
  247. data/lib/action_view/path_set.rb +0 -101
  248. data/lib/action_view/railtie.rb +0 -55
  249. data/lib/action_view/renderer/abstract_renderer.rb +0 -41
  250. data/lib/action_view/renderer/partial_renderer.rb +0 -415
  251. data/lib/action_view/renderer/renderer.rb +0 -61
  252. data/lib/action_view/renderer/streaming_template_renderer.rb +0 -106
  253. data/lib/action_view/renderer/template_renderer.rb +0 -95
  254. data/lib/action_view/template/error.rb +0 -128
  255. data/lib/action_view/template/handlers/builder.rb +0 -26
  256. data/lib/action_view/template/handlers/erb.rb +0 -125
  257. data/lib/action_view/template/handlers.rb +0 -50
  258. data/lib/action_view/template/resolver.rb +0 -298
  259. data/lib/action_view/template/text.rb +0 -30
  260. data/lib/action_view/template.rb +0 -337
  261. data/lib/action_view/test_case.rb +0 -246
  262. data/lib/action_view/testing/resolvers.rb +0 -49
  263. data/lib/action_view.rb +0 -84
  264. data/lib/sprockets/assets.rake +0 -99
  265. data/lib/sprockets/bootstrap.rb +0 -37
  266. data/lib/sprockets/compressors.rb +0 -83
  267. data/lib/sprockets/helpers/isolated_helper.rb +0 -13
  268. data/lib/sprockets/helpers/rails_helper.rb +0 -182
  269. data/lib/sprockets/helpers.rb +0 -6
  270. data/lib/sprockets/railtie.rb +0 -62
  271. data/lib/sprockets/static_compiler.rb +0 -56
@@ -1,73 +1,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/hash/keys"
4
+
1
5
  module ActionController
2
6
  module ConditionalGet
3
7
  extend ActiveSupport::Concern
4
8
 
5
- include RackDelegation
6
9
  include Head
7
10
 
8
- # Sets the etag, last_modified, or both on the response and renders a
11
+ included do
12
+ class_attribute :etaggers, default: []
13
+ end
14
+
15
+ module ClassMethods
16
+ # Allows you to consider additional controller-wide information when generating an ETag.
17
+ # For example, if you serve pages tailored depending on who's logged in at the moment, you
18
+ # may want to add the current user id to be part of the ETag to prevent unauthorized displaying
19
+ # of cached pages.
20
+ #
21
+ # class InvoicesController < ApplicationController
22
+ # etag { current_user.try :id }
23
+ #
24
+ # def show
25
+ # # Etag will differ even for the same invoice when it's viewed by a different current_user
26
+ # @invoice = Invoice.find(params[:id])
27
+ # fresh_when(@invoice)
28
+ # end
29
+ # end
30
+ def etag(&etagger)
31
+ self.etaggers += [etagger]
32
+ end
33
+ end
34
+
35
+ # Sets the +etag+, +last_modified+, or both on the response and renders a
9
36
  # <tt>304 Not Modified</tt> response if the request is already fresh.
10
37
  #
11
- # Parameters:
12
- # * <tt>:etag</tt>
13
- # * <tt>:last_modified</tt>
14
- # * <tt>:public</tt> By default the Cache-Control header is private, set this to true if you want your application to be cachable by other devices (proxy caches).
38
+ # === Parameters:
39
+ #
40
+ # * <tt>:etag</tt> Sets a "weak" ETag validator on the response. See the
41
+ # +:weak_etag+ option.
42
+ # * <tt>:weak_etag</tt> Sets a "weak" ETag validator on the response.
43
+ # Requests that set If-None-Match header may return a 304 Not Modified
44
+ # response if it matches the ETag exactly. A weak ETag indicates semantic
45
+ # equivalence, not byte-for-byte equality, so they're good for caching
46
+ # HTML pages in browser caches. They can't be used for responses that
47
+ # must be byte-identical, like serving Range requests within a PDF file.
48
+ # * <tt>:strong_etag</tt> Sets a "strong" ETag validator on the response.
49
+ # Requests that set If-None-Match header may return a 304 Not Modified
50
+ # response if it matches the ETag exactly. A strong ETag implies exact
51
+ # equality: the response must match byte for byte. This is necessary for
52
+ # doing Range requests within a large video or PDF file, for example, or
53
+ # for compatibility with some CDNs that don't support weak ETags.
54
+ # * <tt>:last_modified</tt> Sets a "weak" last-update validator on the
55
+ # response. Subsequent requests that set If-Modified-Since may return a
56
+ # 304 Not Modified response if last_modified <= If-Modified-Since.
57
+ # * <tt>:public</tt> By default the Cache-Control header is private, set this to
58
+ # +true+ if you want your application to be cacheable by other devices (proxy caches).
59
+ # * <tt>:template</tt> By default, the template digest for the current
60
+ # controller/action is included in ETags. If the action renders a
61
+ # different template, you can include its digest instead. If the action
62
+ # doesn't render a template at all, you can pass <tt>template: false</tt>
63
+ # to skip any attempt to check for a template digest.
15
64
  #
16
- # Example:
65
+ # === Example:
17
66
  #
18
67
  # def show
19
68
  # @article = Article.find(params[:id])
20
- # fresh_when(:etag => @article, :last_modified => @article.created_at, :public => true)
69
+ # fresh_when(etag: @article, last_modified: @article.updated_at, public: true)
21
70
  # end
22
71
  #
23
- # This will render the show template if the request isn't sending a matching etag or
72
+ # This will render the show template if the request isn't sending a matching ETag or
24
73
  # If-Modified-Since header and just a <tt>304 Not Modified</tt> response if there's a match.
25
74
  #
26
- # You can also just pass a record where last_modified will be set by calling updated_at and the etag by passing the object itself. Example:
75
+ # You can also just pass a record. In this case +last_modified+ will be set
76
+ # by calling +updated_at+ and +etag+ by passing the object itself.
27
77
  #
28
78
  # def show
29
79
  # @article = Article.find(params[:id])
30
80
  # fresh_when(@article)
31
81
  # end
32
82
  #
33
- # When passing a record, you can still set whether the public header:
83
+ # You can also pass an object that responds to +maximum+, such as a
84
+ # collection of active records. In this case +last_modified+ will be set by
85
+ # calling <tt>maximum(:updated_at)</tt> on the collection (the timestamp of the
86
+ # most recently updated record) and the +etag+ by passing the object itself.
87
+ #
88
+ # def index
89
+ # @articles = Article.all
90
+ # fresh_when(@articles)
91
+ # end
92
+ #
93
+ # When passing a record or a collection, you can still set the public header:
34
94
  #
35
95
  # def show
36
96
  # @article = Article.find(params[:id])
37
- # fresh_when(@article, :public => true)
97
+ # fresh_when(@article, public: true)
38
98
  # end
39
- def fresh_when(record_or_options, additional_options = {})
40
- if record_or_options.is_a? Hash
41
- options = record_or_options
42
- options.assert_valid_keys(:etag, :last_modified, :public)
43
- else
44
- record = record_or_options
45
- options = { :etag => record, :last_modified => record.try(:updated_at) }.merge(additional_options)
99
+ #
100
+ # When rendering a different template than the default controller/action
101
+ # style, you can indicate which digest to include in the ETag:
102
+ #
103
+ # before_action { fresh_when @article, template: 'widgets/show' }
104
+ #
105
+ def fresh_when(object = nil, etag: nil, weak_etag: nil, strong_etag: nil, last_modified: nil, public: false, template: nil)
106
+ weak_etag ||= etag || object unless strong_etag
107
+ last_modified ||= object.try(:updated_at) || object.try(:maximum, :updated_at)
108
+
109
+ if strong_etag
110
+ response.strong_etag = combine_etags strong_etag,
111
+ last_modified: last_modified, public: public, template: template
112
+ elsif weak_etag || template
113
+ response.weak_etag = combine_etags weak_etag,
114
+ last_modified: last_modified, public: public, template: template
46
115
  end
47
116
 
48
- response.etag = options[:etag] if options[:etag]
49
- response.last_modified = options[:last_modified] if options[:last_modified]
50
- response.cache_control[:public] = true if options[:public]
117
+ response.last_modified = last_modified if last_modified
118
+ response.cache_control[:public] = true if public
51
119
 
52
120
  head :not_modified if request.fresh?(response)
53
121
  end
54
122
 
55
- # Sets the etag and/or last_modified on the response and checks it against
123
+ # Sets the +etag+ and/or +last_modified+ on the response and checks it against
56
124
  # the client request. If the request doesn't match the options provided, the
57
125
  # request is considered stale and should be generated from scratch. Otherwise,
58
126
  # it's fresh and we don't need to generate anything and a reply of <tt>304 Not Modified</tt> is sent.
59
127
  #
60
- # Parameters:
61
- # * <tt>:etag</tt>
62
- # * <tt>:last_modified</tt>
63
- # * <tt>:public</tt> By default the Cache-Control header is private, set this to true if you want your application to be cachable by other devices (proxy caches).
128
+ # === Parameters:
64
129
  #
65
- # Example:
130
+ # * <tt>:etag</tt> Sets a "weak" ETag validator on the response. See the
131
+ # +:weak_etag+ option.
132
+ # * <tt>:weak_etag</tt> Sets a "weak" ETag validator on the response.
133
+ # Requests that set If-None-Match header may return a 304 Not Modified
134
+ # response if it matches the ETag exactly. A weak ETag indicates semantic
135
+ # equivalence, not byte-for-byte equality, so they're good for caching
136
+ # HTML pages in browser caches. They can't be used for responses that
137
+ # must be byte-identical, like serving Range requests within a PDF file.
138
+ # * <tt>:strong_etag</tt> Sets a "strong" ETag validator on the response.
139
+ # Requests that set If-None-Match header may return a 304 Not Modified
140
+ # response if it matches the ETag exactly. A strong ETag implies exact
141
+ # equality: the response must match byte for byte. This is necessary for
142
+ # doing Range requests within a large video or PDF file, for example, or
143
+ # for compatibility with some CDNs that don't support weak ETags.
144
+ # * <tt>:last_modified</tt> Sets a "weak" last-update validator on the
145
+ # response. Subsequent requests that set If-Modified-Since may return a
146
+ # 304 Not Modified response if last_modified <= If-Modified-Since.
147
+ # * <tt>:public</tt> By default the Cache-Control header is private, set this to
148
+ # +true+ if you want your application to be cacheable by other devices (proxy caches).
149
+ # * <tt>:template</tt> By default, the template digest for the current
150
+ # controller/action is included in ETags. If the action renders a
151
+ # different template, you can include its digest instead. If the action
152
+ # doesn't render a template at all, you can pass <tt>template: false</tt>
153
+ # to skip any attempt to check for a template digest.
154
+ #
155
+ # === Example:
66
156
  #
67
157
  # def show
68
158
  # @article = Article.find(params[:id])
69
159
  #
70
- # if stale?(:etag => @article, :last_modified => @article.created_at)
160
+ # if stale?(etag: @article, last_modified: @article.updated_at)
71
161
  # @statistics = @article.really_expensive_call
72
162
  # respond_to do |format|
73
163
  # # all the supported formats
@@ -75,7 +165,8 @@ module ActionController
75
165
  # end
76
166
  # end
77
167
  #
78
- # You can also just pass a record where last_modified will be set by calling updated_at and the etag by passing the object itself. Example:
168
+ # You can also just pass a record. In this case +last_modified+ will be set
169
+ # by calling +updated_at+ and +etag+ by passing the object itself.
79
170
  #
80
171
  # def show
81
172
  # @article = Article.find(params[:id])
@@ -88,44 +179,96 @@ module ActionController
88
179
  # end
89
180
  # end
90
181
  #
91
- # When passing a record, you can still set whether the public header:
182
+ # You can also pass an object that responds to +maximum+, such as a
183
+ # collection of active records. In this case +last_modified+ will be set by
184
+ # calling +maximum(:updated_at)+ on the collection (the timestamp of the
185
+ # most recently updated record) and the +etag+ by passing the object itself.
186
+ #
187
+ # def index
188
+ # @articles = Article.all
189
+ #
190
+ # if stale?(@articles)
191
+ # @statistics = @articles.really_expensive_call
192
+ # respond_to do |format|
193
+ # # all the supported formats
194
+ # end
195
+ # end
196
+ # end
197
+ #
198
+ # When passing a record or a collection, you can still set the public header:
92
199
  #
93
200
  # def show
94
201
  # @article = Article.find(params[:id])
95
202
  #
96
- # if stale?(@article, :public => true)
203
+ # if stale?(@article, public: true)
97
204
  # @statistics = @article.really_expensive_call
98
205
  # respond_to do |format|
99
206
  # # all the supported formats
100
207
  # end
101
208
  # end
102
209
  # end
103
- def stale?(record_or_options, additional_options = {})
104
- fresh_when(record_or_options, additional_options)
210
+ #
211
+ # When rendering a different template than the default controller/action
212
+ # style, you can indicate which digest to include in the ETag:
213
+ #
214
+ # def show
215
+ # super if stale? @article, template: 'widgets/show'
216
+ # end
217
+ #
218
+ def stale?(object = nil, **freshness_kwargs)
219
+ fresh_when(object, **freshness_kwargs)
105
220
  !request.fresh?(response)
106
221
  end
107
222
 
108
- # Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a <tt>private</tt> instruction, so that
109
- # intermediate caches must not cache the response.
223
+ # Sets an HTTP 1.1 Cache-Control header. Defaults to issuing a +private+
224
+ # instruction, so that intermediate caches must not cache the response.
110
225
  #
111
- # Examples:
112
226
  # expires_in 20.minutes
113
- # expires_in 3.hours, :public => true
114
- # expires_in 3.hours, 'max-stale' => 5.hours, :public => true
227
+ # expires_in 3.hours, public: true
228
+ # expires_in 3.hours, public: true, must_revalidate: true
115
229
  #
116
230
  # This method will overwrite an existing Cache-Control header.
117
- # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.
118
- def expires_in(seconds, options = {}) #:doc:
119
- response.cache_control.merge!(:max_age => seconds, :public => options.delete(:public))
231
+ # See https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.
232
+ #
233
+ # The method will also ensure an HTTP Date header for client compatibility.
234
+ def expires_in(seconds, options = {})
235
+ response.cache_control.merge!(
236
+ max_age: seconds,
237
+ public: options.delete(:public),
238
+ must_revalidate: options.delete(:must_revalidate)
239
+ )
120
240
  options.delete(:private)
121
241
 
122
- response.cache_control[:extras] = options.map {|k,v| "#{k}=#{v}"}
242
+ response.cache_control[:extras] = options.map { |k, v| "#{k}=#{v}" }
243
+ response.date = Time.now unless response.date?
123
244
  end
124
245
 
125
- # Sets a HTTP 1.1 Cache-Control header of <tt>no-cache</tt> so no caching should occur by the browser or
126
- # intermediate caches (like caching proxy servers).
127
- def expires_now #:doc:
128
- response.cache_control.replace(:no_cache => true)
246
+ # Sets an HTTP 1.1 Cache-Control header of <tt>no-cache</tt>. This means the
247
+ # resource will be marked as stale, so clients must always revalidate.
248
+ # Intermediate/browser caches may still store the asset.
249
+ def expires_now
250
+ response.cache_control.replace(no_cache: true)
129
251
  end
252
+
253
+ # Cache or yield the block. The cache is supposed to never expire.
254
+ #
255
+ # You can use this method when you have an HTTP response that never changes,
256
+ # and the browser and proxies should cache it indefinitely.
257
+ #
258
+ # * +public+: By default, HTTP responses are private, cached only on the
259
+ # user's web browser. To allow proxies to cache the response, set +true+ to
260
+ # indicate that they can serve the cached response to all users.
261
+ def http_cache_forever(public: false)
262
+ expires_in 100.years, public: public
263
+
264
+ yield if stale?(etag: request.fullpath,
265
+ last_modified: Time.new(2011, 1, 1).utc,
266
+ public: public)
267
+ end
268
+
269
+ private
270
+ def combine_etags(validator, options)
271
+ [validator, *etaggers.map { |etagger| instance_exec(options, &etagger) }].compact
272
+ end
130
273
  end
131
274
  end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionController #:nodoc:
4
+ module ContentSecurityPolicy
5
+ # TODO: Documentation
6
+ extend ActiveSupport::Concern
7
+
8
+ include AbstractController::Helpers
9
+ include AbstractController::Callbacks
10
+
11
+ included do
12
+ helper_method :content_security_policy?
13
+ helper_method :content_security_policy_nonce
14
+ end
15
+
16
+ module ClassMethods
17
+ def content_security_policy(enabled = true, **options, &block)
18
+ before_action(options) do
19
+ if block_given?
20
+ policy = current_content_security_policy
21
+ yield policy
22
+ request.content_security_policy = policy
23
+ end
24
+
25
+ unless enabled
26
+ request.content_security_policy = nil
27
+ end
28
+ end
29
+ end
30
+
31
+ def content_security_policy_report_only(report_only = true, **options)
32
+ before_action(options) do
33
+ request.content_security_policy_report_only = report_only
34
+ end
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def content_security_policy?
41
+ request.content_security_policy
42
+ end
43
+
44
+ def content_security_policy_nonce
45
+ request.content_security_policy_nonce
46
+ end
47
+
48
+ def current_content_security_policy
49
+ request.content_security_policy.try(:clone) || ActionDispatch::ContentSecurityPolicy.new
50
+ end
51
+ end
52
+ end
@@ -1,11 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActionController #:nodoc:
2
4
  module Cookies
3
5
  extend ActiveSupport::Concern
4
6
 
5
- include RackDelegation
6
-
7
7
  included do
8
- helper_method :cookies
8
+ helper_method :cookies if defined?(helper_method)
9
9
  end
10
10
 
11
11
  private
@@ -1,5 +1,6 @@
1
- require 'active_support/core_ext/file/path'
2
- require 'action_controller/metal/exceptions'
1
+ # frozen_string_literal: true
2
+
3
+ require "action_controller/metal/exceptions"
3
4
 
4
5
  module ActionController #:nodoc:
5
6
  # Methods for sending arbitrary data and for streaming files to the browser,
@@ -9,15 +10,13 @@ module ActionController #:nodoc:
9
10
 
10
11
  include ActionController::Rendering
11
12
 
12
- DEFAULT_SEND_FILE_OPTIONS = {
13
- :type => 'application/octet-stream'.freeze,
14
- :disposition => 'attachment'.freeze,
15
- }.freeze
13
+ DEFAULT_SEND_FILE_TYPE = "application/octet-stream".freeze #:nodoc:
14
+ DEFAULT_SEND_FILE_DISPOSITION = "attachment".freeze #:nodoc:
16
15
 
17
- protected
16
+ private
18
17
  # Sends the file. This uses a server-appropriate method (such as X-Sendfile)
19
18
  # via the Rack::Sendfile middleware. The header to use is set via
20
- # config.action_dispatch.x_sendfile_header.
19
+ # +config.action_dispatch.x_sendfile_header+.
21
20
  # Your server can also configure this for you by setting the X-Sendfile-Type header.
22
21
  #
23
22
  # Be careful to sanitize the path parameter if it is coming from a web
@@ -28,14 +27,13 @@ module ActionController #:nodoc:
28
27
  # * <tt>:filename</tt> - suggests a filename for the browser to use.
29
28
  # Defaults to <tt>File.basename(path)</tt>.
30
29
  # * <tt>:type</tt> - specifies an HTTP content type.
31
- # You can specify either a string or a symbol for a registered type register with
32
- # <tt>Mime::Type.register</tt>, for example :json
33
- # If omitted, type will be guessed from the file extension specified in <tt>:filename</tt>.
34
- # If no content type is registered for the extension, default type 'application/octet-stream' will be used.
30
+ # You can specify either a string or a symbol for a registered type with <tt>Mime::Type.register</tt>, for example :json.
31
+ # If omitted, the type will be inferred from the file extension specified in <tt>:filename</tt>.
32
+ # If no content type is registered for the extension, the default type 'application/octet-stream' will be used.
35
33
  # * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
36
34
  # Valid values are 'inline' and 'attachment' (default).
37
35
  # * <tt>:status</tt> - specifies the status code to send with the response. Defaults to 200.
38
- # * <tt>:url_based_filename</tt> - set to +true+ if you want the browser guess the filename from
36
+ # * <tt>:url_based_filename</tt> - set to +true+ if you want the browser to guess the filename from
39
37
  # the URL, which is necessary for i18n filenames on certain browsers
40
38
  # (setting <tt>:filename</tt> overrides this option).
41
39
  #
@@ -50,66 +48,46 @@ module ActionController #:nodoc:
50
48
  #
51
49
  # Show a JPEG in the browser:
52
50
  #
53
- # send_file '/path/to.jpeg', :type => 'image/jpeg', :disposition => 'inline'
51
+ # send_file '/path/to.jpeg', type: 'image/jpeg', disposition: 'inline'
54
52
  #
55
53
  # Show a 404 page in the browser:
56
54
  #
57
- # send_file '/path/to/404.html', :type => 'text/html; charset=utf-8', :status => 404
55
+ # send_file '/path/to/404.html', type: 'text/html; charset=utf-8', status: 404
58
56
  #
59
57
  # Read about the other Content-* HTTP headers if you'd like to
60
58
  # provide the user with more information (such as Content-Description) in
61
- # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11.
59
+ # https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11.
62
60
  #
63
61
  # Also be aware that the document may be cached by proxies and browsers.
64
62
  # The Pragma and Cache-Control headers declare how the file may be cached
65
63
  # by intermediaries. They default to require clients to validate with
66
64
  # the server before releasing cached responses. See
67
- # http://www.mnot.net/cache_docs/ for an overview of web caching and
68
- # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
65
+ # https://www.mnot.net/cache_docs/ for an overview of web caching and
66
+ # https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
69
67
  # for the Cache-Control header spec.
70
68
  def send_file(path, options = {}) #:doc:
71
- raise MissingFile, "Cannot read file #{path}" unless File.file?(path) and File.readable?(path)
69
+ raise MissingFile, "Cannot read file #{path}" unless File.file?(path) && File.readable?(path)
72
70
 
73
71
  options[:filename] ||= File.basename(path) unless options[:url_based_filename]
74
72
  send_file_headers! options
75
73
 
76
74
  self.status = options[:status] || 200
77
75
  self.content_type = options[:content_type] if options.key?(:content_type)
78
- self.response_body = FileBody.new(path)
79
- end
80
-
81
- # Avoid having to pass an open file handle as the response body.
82
- # Rack::Sendfile will usually intercept the response and uses
83
- # the path directly, so there is no reason to open the file.
84
- class FileBody #:nodoc:
85
- attr_reader :to_path
86
-
87
- def initialize(path)
88
- @to_path = path
89
- end
90
-
91
- # Stream the file's contents if Rack::Sendfile isn't present.
92
- def each
93
- File.open(to_path, 'rb') do |file|
94
- while chunk = file.read(16384)
95
- yield chunk
96
- end
97
- end
98
- end
76
+ response.send_file path
99
77
  end
100
78
 
101
79
  # Sends the given binary data to the browser. This method is similar to
102
- # <tt>render :text => data</tt>, but also allows you to specify whether
80
+ # <tt>render plain: data</tt>, but also allows you to specify whether
103
81
  # the browser should display the response as a file attachment (i.e. in a
104
82
  # download dialog) or as inline data. You may also set the content type,
105
- # the apparent file name, and other things.
83
+ # the file name, and other things.
106
84
  #
107
85
  # Options:
108
86
  # * <tt>:filename</tt> - suggests a filename for the browser to use.
109
- # * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify
110
- # either a string or a symbol for a registered type register with <tt>Mime::Type.register</tt>, for example :json
111
- # If omitted, type will be guessed from the file extension specified in <tt>:filename</tt>.
112
- # If no content type is registered for the extension, default type 'application/octet-stream' will be used.
87
+ # * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'.
88
+ # You can specify either a string or a symbol for a registered type with <tt>Mime::Type.register</tt>, for example :json.
89
+ # If omitted, type will be inferred from the file extension specified in <tt>:filename</tt>.
90
+ # If no content type is registered for the extension, the default type 'application/octet-stream' will be used.
113
91
  # * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
114
92
  # Valid values are 'inline' and 'attachment' (default).
115
93
  # * <tt>:status</tt> - specifies the status code to send with the response. Defaults to 200.
@@ -120,31 +98,26 @@ module ActionController #:nodoc:
120
98
  #
121
99
  # Download a dynamically-generated tarball:
122
100
  #
123
- # send_data generate_tgz('dir'), :filename => 'dir.tgz'
101
+ # send_data generate_tgz('dir'), filename: 'dir.tgz'
124
102
  #
125
103
  # Display an image Active Record in the browser:
126
104
  #
127
- # send_data image.data, :type => image.content_type, :disposition => 'inline'
105
+ # send_data image.data, type: image.content_type, disposition: 'inline'
128
106
  #
129
107
  # See +send_file+ for more information on HTTP Content-* headers and caching.
130
108
  def send_data(data, options = {}) #:doc:
131
- send_file_headers! options.dup
132
- render options.slice(:status, :content_type).merge(:text => data)
109
+ send_file_headers! options
110
+ render options.slice(:status, :content_type).merge(body: data)
133
111
  end
134
112
 
135
- private
136
113
  def send_file_headers!(options)
137
114
  type_provided = options.has_key?(:type)
138
115
 
139
- options.update(DEFAULT_SEND_FILE_OPTIONS.merge(options))
140
- [:type, :disposition].each do |arg|
141
- raise ArgumentError, ":#{arg} option required" if options[arg].nil?
142
- end
143
-
144
- disposition = options[:disposition].to_s
145
- disposition += %(; filename="#{options[:filename]}") if options[:filename]
116
+ content_type = options.fetch(:type, DEFAULT_SEND_FILE_TYPE)
117
+ self.content_type = content_type
118
+ response.sending_file = true
146
119
 
147
- content_type = options[:type]
120
+ raise ArgumentError, ":type option required" if content_type.nil?
148
121
 
149
122
  if content_type.is_a?(Symbol)
150
123
  extension = Mime[content_type]
@@ -153,17 +126,19 @@ module ActionController #:nodoc:
153
126
  else
154
127
  if !type_provided && options[:filename]
155
128
  # If type wasn't provided, try guessing from file extension.
156
- content_type = Mime::Type.lookup_by_extension(File.extname(options[:filename]).downcase.tr('.','')) || content_type
129
+ content_type = Mime::Type.lookup_by_extension(File.extname(options[:filename]).downcase.delete(".")) || content_type
157
130
  end
158
131
  self.content_type = content_type
159
132
  end
160
133
 
161
- headers.merge!(
162
- 'Content-Disposition' => disposition,
163
- 'Content-Transfer-Encoding' => 'binary'
164
- )
134
+ disposition = options.fetch(:disposition, DEFAULT_SEND_FILE_DISPOSITION)
135
+ unless disposition.nil?
136
+ disposition = disposition.to_s
137
+ disposition += %(; filename="#{options[:filename]}") if options[:filename]
138
+ headers["Content-Disposition"] = disposition
139
+ end
165
140
 
166
- response.sending_file = true
141
+ headers["Content-Transfer-Encoding"] = "binary"
167
142
 
168
143
  # Fix a problem with IE 6.0 on opening downloaded files:
169
144
  # If Cache-Control: no-cache is set (which Rails does by default),
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionController
4
+ # When you're using the flash, it's generally used as a conditional on the view.
5
+ # This means the content of the view depends on the flash. Which in turn means
6
+ # that the ETag for a response should be computed with the content of the flash
7
+ # in mind. This does that by including the content of the flash as a component
8
+ # in the ETag that's generated for a response.
9
+ module EtagWithFlash
10
+ extend ActiveSupport::Concern
11
+
12
+ include ActionController::ConditionalGet
13
+
14
+ included do
15
+ etag { flash unless flash.empty? }
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionController
4
+ # When our views change, they should bubble up into HTTP cache freshness
5
+ # and bust browser caches. So the template digest for the current action
6
+ # is automatically included in the ETag.
7
+ #
8
+ # Enabled by default for apps that use Action View. Disable by setting
9
+ #
10
+ # config.action_controller.etag_with_template_digest = false
11
+ #
12
+ # Override the template to digest by passing +:template+ to +fresh_when+
13
+ # and +stale?+ calls. For example:
14
+ #
15
+ # # We're going to render widgets/show, not posts/show
16
+ # fresh_when @post, template: 'widgets/show'
17
+ #
18
+ # # We're not going to render a template, so omit it from the ETag.
19
+ # fresh_when @post, template: false
20
+ #
21
+ module EtagWithTemplateDigest
22
+ extend ActiveSupport::Concern
23
+
24
+ include ActionController::ConditionalGet
25
+
26
+ included do
27
+ class_attribute :etag_with_template_digest, default: true
28
+
29
+ ActiveSupport.on_load :action_view, yield: true do
30
+ etag do |options|
31
+ determine_template_etag(options) if etag_with_template_digest
32
+ end
33
+ end
34
+ end
35
+
36
+ private
37
+ def determine_template_etag(options)
38
+ if template = pick_template_for_etag(options)
39
+ lookup_and_digest_template(template)
40
+ end
41
+ end
42
+
43
+ # Pick the template digest to include in the ETag. If the +:template+ option
44
+ # is present, use the named template. If +:template+ is +nil+ or absent, use
45
+ # the default controller/action template. If +:template+ is false, omit the
46
+ # template digest from the ETag.
47
+ def pick_template_for_etag(options)
48
+ unless options[:template] == false
49
+ options[:template] || "#{controller_path}/#{action_name}"
50
+ end
51
+ end
52
+
53
+ def lookup_and_digest_template(template)
54
+ ActionView::Digestor.digest name: template, finder: lookup_context
55
+ end
56
+ end
57
+ end