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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +279 -603
- data/MIT-LICENSE +1 -1
- data/README.rdoc +13 -297
- data/lib/abstract_controller/asset_paths.rb +4 -2
- data/lib/abstract_controller/base.rb +82 -52
- data/lib/abstract_controller/caching/fragments.rb +166 -0
- data/lib/abstract_controller/caching.rb +66 -0
- data/lib/abstract_controller/callbacks.rb +117 -103
- data/lib/abstract_controller/collector.rb +18 -7
- data/lib/abstract_controller/error.rb +6 -0
- data/lib/abstract_controller/helpers.rb +65 -38
- data/lib/abstract_controller/logger.rb +3 -2
- data/lib/abstract_controller/railties/routes_helpers.rb +5 -3
- data/lib/abstract_controller/rendering.rb +77 -129
- data/lib/abstract_controller/translation.rb +21 -3
- data/lib/abstract_controller/url_for.rb +9 -7
- data/lib/abstract_controller.rb +12 -13
- data/lib/action_controller/api/api_rendering.rb +16 -0
- data/lib/action_controller/api.rb +149 -0
- data/lib/action_controller/base.rb +81 -40
- data/lib/action_controller/caching.rb +22 -62
- data/lib/action_controller/form_builder.rb +50 -0
- data/lib/action_controller/log_subscriber.rb +30 -18
- data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
- data/lib/action_controller/metal/conditional_get.rb +190 -47
- data/lib/action_controller/metal/content_security_policy.rb +52 -0
- data/lib/action_controller/metal/cookies.rb +3 -3
- data/lib/action_controller/metal/data_streaming.rb +40 -65
- data/lib/action_controller/metal/etag_with_flash.rb +18 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +57 -0
- data/lib/action_controller/metal/exceptions.rb +19 -12
- data/lib/action_controller/metal/flash.rb +42 -9
- data/lib/action_controller/metal/force_ssl.rb +79 -19
- data/lib/action_controller/metal/head.rb +35 -10
- data/lib/action_controller/metal/helpers.rb +31 -21
- data/lib/action_controller/metal/http_authentication.rb +182 -134
- data/lib/action_controller/metal/implicit_render.rb +62 -8
- data/lib/action_controller/metal/instrumentation.rb +28 -26
- data/lib/action_controller/metal/live.rb +312 -0
- data/lib/action_controller/metal/mime_responds.rb +159 -163
- data/lib/action_controller/metal/parameter_encoding.rb +51 -0
- data/lib/action_controller/metal/params_wrapper.rb +146 -93
- data/lib/action_controller/metal/redirecting.rb +80 -56
- data/lib/action_controller/metal/renderers.rb +119 -47
- data/lib/action_controller/metal/rendering.rb +89 -32
- data/lib/action_controller/metal/request_forgery_protection.rb +373 -41
- data/lib/action_controller/metal/rescue.rb +9 -16
- data/lib/action_controller/metal/streaming.rb +39 -45
- data/lib/action_controller/metal/strong_parameters.rb +1086 -0
- data/lib/action_controller/metal/testing.rb +8 -29
- data/lib/action_controller/metal/url_for.rb +43 -32
- data/lib/action_controller/metal.rb +112 -106
- data/lib/action_controller/railtie.rb +56 -18
- data/lib/action_controller/railties/helpers.rb +24 -0
- data/lib/action_controller/renderer.rb +117 -0
- data/lib/action_controller/template_assertions.rb +11 -0
- data/lib/action_controller/test_case.rb +402 -347
- data/lib/action_controller.rb +31 -30
- data/lib/action_dispatch/http/cache.rb +133 -34
- data/lib/action_dispatch/http/content_security_policy.rb +272 -0
- data/lib/action_dispatch/http/filter_parameters.rb +40 -24
- data/lib/action_dispatch/http/filter_redirect.rb +37 -0
- data/lib/action_dispatch/http/headers.rb +117 -16
- data/lib/action_dispatch/http/mime_negotiation.rb +98 -33
- data/lib/action_dispatch/http/mime_type.rb +198 -146
- data/lib/action_dispatch/http/mime_types.rb +22 -7
- data/lib/action_dispatch/http/parameter_filter.rb +61 -49
- data/lib/action_dispatch/http/parameters.rb +94 -51
- data/lib/action_dispatch/http/rack_cache.rb +4 -3
- data/lib/action_dispatch/http/request.rb +262 -117
- data/lib/action_dispatch/http/response.rb +400 -86
- data/lib/action_dispatch/http/upload.rb +66 -29
- data/lib/action_dispatch/http/url.rb +232 -60
- data/lib/action_dispatch/journey/formatter.rb +189 -0
- data/lib/action_dispatch/journey/gtg/builder.rb +164 -0
- data/lib/action_dispatch/journey/gtg/simulator.rb +41 -0
- data/lib/action_dispatch/journey/gtg/transition_table.rb +158 -0
- data/lib/action_dispatch/journey/nfa/builder.rb +78 -0
- data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
- data/lib/action_dispatch/journey/nfa/simulator.rb +49 -0
- data/lib/action_dispatch/journey/nfa/transition_table.rb +120 -0
- data/lib/action_dispatch/journey/nodes/node.rb +140 -0
- data/lib/action_dispatch/journey/parser.rb +199 -0
- data/lib/action_dispatch/journey/parser.y +50 -0
- data/lib/action_dispatch/journey/parser_extras.rb +31 -0
- data/lib/action_dispatch/journey/path/pattern.rb +199 -0
- data/lib/action_dispatch/journey/route.rb +203 -0
- data/lib/action_dispatch/journey/router/utils.rb +102 -0
- data/lib/action_dispatch/journey/router.rb +156 -0
- data/lib/action_dispatch/journey/routes.rb +82 -0
- data/lib/action_dispatch/journey/scanner.rb +64 -0
- data/lib/action_dispatch/journey/visitors.rb +268 -0
- data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
- data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
- data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
- data/lib/action_dispatch/journey.rb +7 -0
- data/lib/action_dispatch/middleware/callbacks.rb +17 -13
- data/lib/action_dispatch/middleware/cookies.rb +494 -162
- data/lib/action_dispatch/middleware/debug_exceptions.rb +176 -53
- data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +103 -38
- data/lib/action_dispatch/middleware/executor.rb +21 -0
- data/lib/action_dispatch/middleware/flash.rb +128 -91
- data/lib/action_dispatch/middleware/public_exceptions.rb +43 -16
- data/lib/action_dispatch/middleware/reloader.rb +6 -83
- data/lib/action_dispatch/middleware/remote_ip.rb +151 -49
- data/lib/action_dispatch/middleware/request_id.rb +19 -15
- data/lib/action_dispatch/middleware/session/abstract_store.rb +38 -34
- data/lib/action_dispatch/middleware/session/cache_store.rb +14 -9
- data/lib/action_dispatch/middleware/session/cookie_store.rb +94 -44
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +15 -4
- data/lib/action_dispatch/middleware/show_exceptions.rb +36 -61
- data/lib/action_dispatch/middleware/ssl.rb +150 -0
- data/lib/action_dispatch/middleware/stack.rb +33 -41
- data/lib/action_dispatch/middleware/static.rb +92 -48
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +22 -0
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +27 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +52 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +16 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +21 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +13 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +134 -5
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +200 -0
- data/lib/action_dispatch/railtie.rb +29 -8
- data/lib/action_dispatch/request/session.rb +234 -0
- data/lib/action_dispatch/request/utils.rb +78 -0
- data/lib/action_dispatch/routing/endpoint.rb +17 -0
- data/lib/action_dispatch/routing/inspector.rb +225 -0
- data/lib/action_dispatch/routing/mapper.rb +1329 -582
- data/lib/action_dispatch/routing/polymorphic_routes.rb +237 -94
- data/lib/action_dispatch/routing/redirection.rb +120 -50
- data/lib/action_dispatch/routing/route_set.rb +545 -322
- data/lib/action_dispatch/routing/routes_proxy.rb +37 -7
- data/lib/action_dispatch/routing/url_for.rb +103 -34
- data/lib/action_dispatch/routing.rb +66 -99
- data/lib/action_dispatch/system_test_case.rb +147 -0
- data/lib/action_dispatch/system_testing/browser.rb +49 -0
- data/lib/action_dispatch/system_testing/driver.rb +59 -0
- data/lib/action_dispatch/system_testing/server.rb +31 -0
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +96 -0
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +31 -0
- data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
- data/lib/action_dispatch/testing/assertion_response.rb +47 -0
- data/lib/action_dispatch/testing/assertions/response.rb +53 -42
- data/lib/action_dispatch/testing/assertions/routing.rb +79 -74
- data/lib/action_dispatch/testing/assertions.rb +15 -9
- data/lib/action_dispatch/testing/integration.rb +361 -207
- data/lib/action_dispatch/testing/request_encoder.rb +55 -0
- data/lib/action_dispatch/testing/test_process.rb +28 -19
- data/lib/action_dispatch/testing/test_request.rb +30 -33
- data/lib/action_dispatch/testing/test_response.rb +35 -11
- data/lib/action_dispatch.rb +42 -32
- data/lib/action_pack/gem_version.rb +17 -0
- data/lib/action_pack/version.rb +7 -7
- data/lib/action_pack.rb +4 -2
- metadata +116 -175
- data/lib/abstract_controller/layouts.rb +0 -423
- data/lib/abstract_controller/view_paths.rb +0 -96
- data/lib/action_controller/caching/actions.rb +0 -185
- data/lib/action_controller/caching/fragments.rb +0 -127
- data/lib/action_controller/caching/pages.rb +0 -187
- data/lib/action_controller/caching/sweeping.rb +0 -97
- data/lib/action_controller/deprecated/integration_test.rb +0 -2
- data/lib/action_controller/deprecated/performance_test.rb +0 -1
- data/lib/action_controller/deprecated.rb +0 -3
- data/lib/action_controller/metal/compatibility.rb +0 -65
- data/lib/action_controller/metal/hide_actions.rb +0 -41
- data/lib/action_controller/metal/rack_delegation.rb +0 -26
- data/lib/action_controller/metal/responder.rb +0 -286
- data/lib/action_controller/metal/session_management.rb +0 -14
- data/lib/action_controller/middleware.rb +0 -39
- data/lib/action_controller/railties/paths.rb +0 -25
- data/lib/action_controller/record_identifier.rb +0 -85
- data/lib/action_controller/vendor/html-scanner/html/document.rb +0 -68
- data/lib/action_controller/vendor/html-scanner/html/node.rb +0 -532
- data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +0 -177
- data/lib/action_controller/vendor/html-scanner/html/selector.rb +0 -830
- data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +0 -107
- data/lib/action_controller/vendor/html-scanner/html/version.rb +0 -11
- data/lib/action_controller/vendor/html-scanner.rb +0 -20
- data/lib/action_dispatch/middleware/best_standards_support.rb +0 -30
- data/lib/action_dispatch/middleware/body_proxy.rb +0 -30
- data/lib/action_dispatch/middleware/head.rb +0 -18
- data/lib/action_dispatch/middleware/params_parser.rb +0 -75
- data/lib/action_dispatch/middleware/rescue.rb +0 -26
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +0 -31
- data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +0 -26
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +0 -10
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +0 -2
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +0 -15
- data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +0 -17
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +0 -2
- data/lib/action_dispatch/testing/assertions/dom.rb +0 -37
- data/lib/action_dispatch/testing/assertions/selector.rb +0 -435
- data/lib/action_dispatch/testing/assertions/tag.rb +0 -138
- data/lib/action_dispatch/testing/performance_test.rb +0 -10
- data/lib/action_view/asset_paths.rb +0 -142
- data/lib/action_view/base.rb +0 -220
- data/lib/action_view/buffers.rb +0 -43
- data/lib/action_view/context.rb +0 -36
- data/lib/action_view/flows.rb +0 -79
- data/lib/action_view/helpers/active_model_helper.rb +0 -50
- data/lib/action_view/helpers/asset_paths.rb +0 -7
- data/lib/action_view/helpers/asset_tag_helper.rb +0 -457
- data/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +0 -146
- data/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +0 -93
- data/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +0 -193
- data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +0 -148
- data/lib/action_view/helpers/atom_feed_helper.rb +0 -200
- data/lib/action_view/helpers/cache_helper.rb +0 -64
- data/lib/action_view/helpers/capture_helper.rb +0 -203
- data/lib/action_view/helpers/controller_helper.rb +0 -25
- data/lib/action_view/helpers/csrf_helper.rb +0 -32
- data/lib/action_view/helpers/date_helper.rb +0 -1062
- data/lib/action_view/helpers/debug_helper.rb +0 -40
- data/lib/action_view/helpers/form_helper.rb +0 -1486
- data/lib/action_view/helpers/form_options_helper.rb +0 -658
- data/lib/action_view/helpers/form_tag_helper.rb +0 -685
- data/lib/action_view/helpers/javascript_helper.rb +0 -110
- data/lib/action_view/helpers/number_helper.rb +0 -622
- data/lib/action_view/helpers/output_safety_helper.rb +0 -38
- data/lib/action_view/helpers/record_tag_helper.rb +0 -111
- data/lib/action_view/helpers/rendering_helper.rb +0 -92
- data/lib/action_view/helpers/sanitize_helper.rb +0 -259
- data/lib/action_view/helpers/tag_helper.rb +0 -167
- data/lib/action_view/helpers/text_helper.rb +0 -426
- data/lib/action_view/helpers/translation_helper.rb +0 -91
- data/lib/action_view/helpers/url_helper.rb +0 -693
- data/lib/action_view/helpers.rb +0 -60
- data/lib/action_view/locale/en.yml +0 -160
- data/lib/action_view/log_subscriber.rb +0 -28
- data/lib/action_view/lookup_context.rb +0 -258
- data/lib/action_view/path_set.rb +0 -101
- data/lib/action_view/railtie.rb +0 -55
- data/lib/action_view/renderer/abstract_renderer.rb +0 -41
- data/lib/action_view/renderer/partial_renderer.rb +0 -415
- data/lib/action_view/renderer/renderer.rb +0 -61
- data/lib/action_view/renderer/streaming_template_renderer.rb +0 -106
- data/lib/action_view/renderer/template_renderer.rb +0 -95
- data/lib/action_view/template/error.rb +0 -128
- data/lib/action_view/template/handlers/builder.rb +0 -26
- data/lib/action_view/template/handlers/erb.rb +0 -125
- data/lib/action_view/template/handlers.rb +0 -50
- data/lib/action_view/template/resolver.rb +0 -298
- data/lib/action_view/template/text.rb +0 -30
- data/lib/action_view/template.rb +0 -337
- data/lib/action_view/test_case.rb +0 -246
- data/lib/action_view/testing/resolvers.rb +0 -49
- data/lib/action_view.rb +0 -84
- data/lib/sprockets/assets.rake +0 -99
- data/lib/sprockets/bootstrap.rb +0 -37
- data/lib/sprockets/compressors.rb +0 -83
- data/lib/sprockets/helpers/isolated_helper.rb +0 -13
- data/lib/sprockets/helpers/rails_helper.rb +0 -182
- data/lib/sprockets/helpers.rb +0 -6
- data/lib/sprockets/railtie.rb +0 -62
- 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
|
-
|
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
|
-
#
|
13
|
-
# * <tt>:
|
14
|
-
#
|
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(:
|
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
|
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
|
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
|
-
#
|
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, :
|
97
|
+
# fresh_when(@article, public: true)
|
38
98
|
# end
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
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.
|
49
|
-
response.
|
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
|
-
#
|
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?(:
|
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
|
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
|
-
#
|
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, :
|
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
|
-
|
104
|
-
|
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
|
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, :
|
114
|
-
# expires_in 3.hours,
|
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
|
118
|
-
|
119
|
-
|
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
|
126
|
-
#
|
127
|
-
|
128
|
-
|
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
|
-
|
2
|
-
|
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
|
-
|
13
|
-
|
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
|
-
|
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
|
32
|
-
# <tt
|
33
|
-
# If
|
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', :
|
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', :
|
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
|
-
#
|
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
|
-
#
|
68
|
-
#
|
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)
|
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
|
-
|
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 :
|
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
|
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'.
|
110
|
-
# either a string or a symbol for a registered type
|
111
|
-
# If omitted, type will be
|
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'), :
|
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, :
|
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
|
132
|
-
render options.slice(:status, :content_type).merge(:
|
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.
|
140
|
-
|
141
|
-
|
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
|
-
|
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.
|
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
|
-
|
162
|
-
|
163
|
-
|
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
|
-
|
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
|