actionpack 3.2.19 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +850 -401
- data/MIT-LICENSE +1 -1
- data/README.rdoc +5 -288
- data/lib/abstract_controller/asset_paths.rb +2 -2
- data/lib/abstract_controller/base.rb +39 -37
- data/lib/abstract_controller/callbacks.rb +101 -82
- data/lib/abstract_controller/collector.rb +7 -3
- data/lib/abstract_controller/helpers.rb +25 -13
- data/lib/abstract_controller/layouts.rb +74 -74
- data/lib/abstract_controller/logger.rb +1 -2
- data/lib/abstract_controller/rendering.rb +30 -13
- data/lib/abstract_controller/translation.rb +16 -1
- data/lib/abstract_controller/url_for.rb +6 -6
- data/lib/abstract_controller/view_paths.rb +1 -1
- data/lib/abstract_controller.rb +1 -8
- data/lib/action_controller/base.rb +46 -22
- data/lib/action_controller/caching/fragments.rb +23 -53
- data/lib/action_controller/caching.rb +46 -33
- data/lib/action_controller/deprecated/integration_test.rb +3 -0
- data/lib/action_controller/deprecated.rb +5 -1
- data/lib/action_controller/log_subscriber.rb +16 -8
- data/lib/action_controller/metal/conditional_get.rb +76 -32
- data/lib/action_controller/metal/data_streaming.rb +20 -26
- data/lib/action_controller/metal/exceptions.rb +19 -6
- data/lib/action_controller/metal/flash.rb +24 -9
- data/lib/action_controller/metal/force_ssl.rb +70 -12
- data/lib/action_controller/metal/head.rb +25 -4
- data/lib/action_controller/metal/helpers.rb +5 -9
- data/lib/action_controller/metal/hide_actions.rb +0 -1
- data/lib/action_controller/metal/http_authentication.rb +107 -83
- data/lib/action_controller/metal/implicit_render.rb +1 -1
- data/lib/action_controller/metal/instrumentation.rb +2 -1
- data/lib/action_controller/metal/live.rb +175 -0
- data/lib/action_controller/metal/mime_responds.rb +161 -47
- data/lib/action_controller/metal/params_wrapper.rb +112 -74
- data/lib/action_controller/metal/rack_delegation.rb +9 -3
- data/lib/action_controller/metal/redirecting.rb +15 -20
- data/lib/action_controller/metal/renderers.rb +11 -9
- data/lib/action_controller/metal/rendering.rb +9 -1
- data/lib/action_controller/metal/request_forgery_protection.rb +112 -19
- data/lib/action_controller/metal/responder.rb +20 -19
- data/lib/action_controller/metal/streaming.rb +12 -18
- data/lib/action_controller/metal/strong_parameters.rb +520 -0
- data/lib/action_controller/metal/testing.rb +13 -18
- data/lib/action_controller/metal/url_for.rb +28 -25
- data/lib/action_controller/metal.rb +17 -32
- data/lib/action_controller/model_naming.rb +12 -0
- data/lib/action_controller/railtie.rb +33 -17
- data/lib/action_controller/railties/helpers.rb +22 -0
- data/lib/action_controller/record_identifier.rb +18 -72
- data/lib/action_controller/test_case.rb +251 -131
- data/lib/action_controller/vendor/html-scanner.rb +4 -19
- data/lib/action_controller.rb +15 -6
- data/lib/action_dispatch/http/cache.rb +63 -11
- data/lib/action_dispatch/http/filter_parameters.rb +18 -8
- data/lib/action_dispatch/http/filter_redirect.rb +37 -0
- data/lib/action_dispatch/http/headers.rb +49 -17
- data/lib/action_dispatch/http/mime_negotiation.rb +24 -1
- data/lib/action_dispatch/http/mime_type.rb +154 -100
- data/lib/action_dispatch/http/mime_types.rb +1 -1
- data/lib/action_dispatch/http/parameter_filter.rb +44 -46
- data/lib/action_dispatch/http/parameters.rb +28 -28
- data/lib/action_dispatch/http/rack_cache.rb +2 -3
- data/lib/action_dispatch/http/request.rb +64 -18
- data/lib/action_dispatch/http/response.rb +130 -35
- data/lib/action_dispatch/http/upload.rb +63 -20
- data/lib/action_dispatch/http/url.rb +98 -35
- data/lib/action_dispatch/journey/backwards.rb +5 -0
- data/lib/action_dispatch/journey/formatter.rb +146 -0
- data/lib/action_dispatch/journey/gtg/builder.rb +162 -0
- data/lib/action_dispatch/journey/gtg/simulator.rb +44 -0
- data/lib/action_dispatch/journey/gtg/transition_table.rb +156 -0
- data/lib/action_dispatch/journey/nfa/builder.rb +76 -0
- data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
- data/lib/action_dispatch/journey/nfa/simulator.rb +47 -0
- data/lib/action_dispatch/journey/nfa/transition_table.rb +163 -0
- data/lib/action_dispatch/journey/nodes/node.rb +124 -0
- data/lib/action_dispatch/journey/parser.rb +206 -0
- data/lib/action_dispatch/journey/parser.y +47 -0
- data/lib/action_dispatch/journey/parser_extras.rb +23 -0
- data/lib/action_dispatch/journey/path/pattern.rb +196 -0
- data/lib/action_dispatch/journey/route.rb +124 -0
- data/lib/action_dispatch/journey/router/strexp.rb +24 -0
- data/lib/action_dispatch/journey/router/utils.rb +54 -0
- data/lib/action_dispatch/journey/router.rb +166 -0
- data/lib/action_dispatch/journey/routes.rb +75 -0
- data/lib/action_dispatch/journey/scanner.rb +61 -0
- data/lib/action_dispatch/journey/visitors.rb +197 -0
- data/lib/action_dispatch/journey/visualizer/fsm.css +34 -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 +5 -0
- data/lib/action_dispatch/middleware/callbacks.rb +9 -4
- data/lib/action_dispatch/middleware/cookies.rb +259 -114
- data/lib/action_dispatch/middleware/debug_exceptions.rb +26 -17
- data/lib/action_dispatch/middleware/exception_wrapper.rb +29 -3
- data/lib/action_dispatch/middleware/flash.rb +58 -58
- data/lib/action_dispatch/middleware/params_parser.rb +14 -29
- data/lib/action_dispatch/middleware/public_exceptions.rb +30 -14
- data/lib/action_dispatch/middleware/reloader.rb +6 -6
- data/lib/action_dispatch/middleware/remote_ip.rb +145 -39
- data/lib/action_dispatch/middleware/request_id.rb +2 -6
- data/lib/action_dispatch/middleware/session/abstract_store.rb +22 -20
- data/lib/action_dispatch/middleware/session/cookie_store.rb +82 -28
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -3
- data/lib/action_dispatch/middleware/show_exceptions.rb +12 -45
- data/lib/action_dispatch/middleware/ssl.rb +70 -0
- data/lib/action_dispatch/middleware/stack.rb +6 -1
- data/lib/action_dispatch/middleware/static.rb +2 -1
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +14 -11
- data/lib/action_dispatch/middleware/templates/rescues/_source.erb +25 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +7 -9
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +15 -9
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +127 -5
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +7 -2
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +30 -15
- data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +39 -13
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +6 -2
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +144 -0
- data/lib/action_dispatch/railtie.rb +16 -6
- data/lib/action_dispatch/request/session.rb +181 -0
- data/lib/action_dispatch/routing/inspector.rb +240 -0
- data/lib/action_dispatch/routing/mapper.rb +540 -291
- data/lib/action_dispatch/routing/polymorphic_routes.rb +16 -20
- data/lib/action_dispatch/routing/redirection.rb +46 -29
- data/lib/action_dispatch/routing/route_set.rb +207 -164
- data/lib/action_dispatch/routing/routes_proxy.rb +2 -0
- data/lib/action_dispatch/routing/url_for.rb +48 -33
- data/lib/action_dispatch/routing.rb +48 -83
- data/lib/action_dispatch/testing/assertions/dom.rb +3 -13
- data/lib/action_dispatch/testing/assertions/response.rb +32 -40
- data/lib/action_dispatch/testing/assertions/routing.rb +42 -41
- data/lib/action_dispatch/testing/assertions/selector.rb +17 -22
- data/lib/action_dispatch/testing/assertions/tag.rb +20 -23
- data/lib/action_dispatch/testing/integration.rb +65 -51
- data/lib/action_dispatch/testing/test_process.rb +9 -6
- data/lib/action_dispatch/testing/test_request.rb +7 -3
- data/lib/action_dispatch.rb +21 -15
- data/lib/action_pack/version.rb +7 -6
- data/lib/action_pack.rb +1 -1
- data/lib/action_view/base.rb +15 -34
- data/lib/action_view/buffers.rb +7 -1
- data/lib/action_view/context.rb +4 -4
- data/lib/action_view/dependency_tracker.rb +93 -0
- data/lib/action_view/digestor.rb +85 -0
- data/lib/action_view/flows.rb +1 -4
- data/lib/action_view/helpers/active_model_helper.rb +3 -4
- data/lib/action_view/helpers/asset_tag_helper.rb +215 -352
- data/lib/action_view/helpers/asset_url_helper.rb +355 -0
- data/lib/action_view/helpers/atom_feed_helper.rb +13 -10
- data/lib/action_view/helpers/cache_helper.rb +150 -18
- data/lib/action_view/helpers/capture_helper.rb +44 -31
- data/lib/action_view/helpers/csrf_helper.rb +0 -2
- data/lib/action_view/helpers/date_helper.rb +269 -248
- data/lib/action_view/helpers/debug_helper.rb +10 -11
- data/lib/action_view/helpers/form_helper.rb +931 -537
- data/lib/action_view/helpers/form_options_helper.rb +341 -166
- data/lib/action_view/helpers/form_tag_helper.rb +190 -90
- data/lib/action_view/helpers/javascript_helper.rb +23 -16
- data/lib/action_view/helpers/number_helper.rb +148 -329
- data/lib/action_view/helpers/output_safety_helper.rb +3 -3
- data/lib/action_view/helpers/record_tag_helper.rb +17 -22
- data/lib/action_view/helpers/rendering_helper.rb +2 -2
- data/lib/action_view/helpers/sanitize_helper.rb +3 -6
- data/lib/action_view/helpers/tag_helper.rb +46 -33
- data/lib/action_view/helpers/tags/base.rb +147 -0
- data/lib/action_view/helpers/tags/check_box.rb +64 -0
- data/lib/action_view/helpers/tags/checkable.rb +16 -0
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +43 -0
- data/lib/action_view/helpers/tags/collection_helpers.rb +83 -0
- data/lib/action_view/helpers/tags/collection_radio_buttons.rb +36 -0
- data/lib/action_view/helpers/tags/collection_select.rb +28 -0
- data/lib/action_view/helpers/tags/color_field.rb +25 -0
- data/lib/action_view/helpers/tags/date_field.rb +13 -0
- data/lib/action_view/helpers/tags/date_select.rb +72 -0
- data/lib/action_view/helpers/tags/datetime_field.rb +22 -0
- data/lib/action_view/helpers/tags/datetime_local_field.rb +19 -0
- data/lib/action_view/helpers/tags/datetime_select.rb +8 -0
- data/lib/action_view/helpers/tags/email_field.rb +8 -0
- data/lib/action_view/helpers/tags/file_field.rb +8 -0
- data/lib/action_view/helpers/tags/grouped_collection_select.rb +29 -0
- data/lib/action_view/helpers/tags/hidden_field.rb +8 -0
- data/lib/action_view/helpers/tags/label.rb +65 -0
- data/lib/action_view/helpers/tags/month_field.rb +13 -0
- data/lib/action_view/helpers/tags/number_field.rb +18 -0
- data/lib/action_view/helpers/tags/password_field.rb +12 -0
- data/lib/action_view/helpers/tags/radio_button.rb +31 -0
- data/lib/action_view/helpers/tags/range_field.rb +8 -0
- data/lib/action_view/helpers/tags/search_field.rb +24 -0
- data/lib/action_view/helpers/tags/select.rb +40 -0
- data/lib/action_view/helpers/tags/tel_field.rb +8 -0
- data/lib/action_view/helpers/tags/text_area.rb +18 -0
- data/lib/action_view/helpers/tags/text_field.rb +29 -0
- data/lib/action_view/helpers/tags/time_field.rb +13 -0
- data/lib/action_view/helpers/tags/time_select.rb +8 -0
- data/lib/action_view/helpers/tags/time_zone_select.rb +20 -0
- data/lib/action_view/helpers/tags/url_field.rb +8 -0
- data/lib/action_view/helpers/tags/week_field.rb +13 -0
- data/lib/action_view/helpers/tags.rb +39 -0
- data/lib/action_view/helpers/text_helper.rb +130 -114
- data/lib/action_view/helpers/translation_helper.rb +32 -16
- data/lib/action_view/helpers/url_helper.rb +211 -270
- data/lib/action_view/helpers.rb +2 -4
- data/lib/action_view/locale/en.yml +1 -105
- data/lib/action_view/log_subscriber.rb +6 -4
- data/lib/action_view/lookup_context.rb +15 -28
- data/lib/action_view/model_naming.rb +12 -0
- data/lib/action_view/path_set.rb +8 -20
- data/lib/action_view/railtie.rb +6 -22
- data/lib/action_view/record_identifier.rb +84 -0
- data/lib/action_view/renderer/abstract_renderer.rb +25 -19
- data/lib/action_view/renderer/partial_renderer.rb +158 -81
- data/lib/action_view/renderer/renderer.rb +8 -12
- data/lib/action_view/renderer/streaming_template_renderer.rb +2 -5
- data/lib/action_view/renderer/template_renderer.rb +12 -10
- data/lib/action_view/routing_url_for.rb +107 -0
- data/lib/action_view/template/error.rb +22 -12
- data/lib/action_view/template/handlers/builder.rb +1 -1
- data/lib/action_view/template/handlers/erb.rb +40 -19
- data/lib/action_view/template/handlers/raw.rb +11 -0
- data/lib/action_view/template/handlers.rb +12 -9
- data/lib/action_view/template/resolver.rb +107 -53
- data/lib/action_view/template/text.rb +12 -8
- data/lib/action_view/template/types.rb +57 -0
- data/lib/action_view/template.rb +25 -23
- data/lib/action_view/test_case.rb +67 -42
- data/lib/{action_controller → action_view}/vendor/html-scanner/html/document.rb +0 -0
- data/lib/{action_controller → action_view}/vendor/html-scanner/html/node.rb +12 -12
- data/lib/{action_controller → action_view}/vendor/html-scanner/html/sanitizer.rb +13 -2
- data/lib/{action_controller → action_view}/vendor/html-scanner/html/selector.rb +9 -9
- data/lib/{action_controller → action_view}/vendor/html-scanner/html/tokenizer.rb +1 -1
- data/lib/{action_controller → action_view}/vendor/html-scanner/html/version.rb +0 -0
- data/lib/action_view/vendor/html-scanner.rb +20 -0
- data/lib/action_view.rb +17 -8
- metadata +184 -214
- data/lib/action_controller/caching/actions.rb +0 -185
- data/lib/action_controller/caching/pages.rb +0 -187
- data/lib/action_controller/caching/sweeping.rb +0 -97
- data/lib/action_controller/deprecated/performance_test.rb +0 -1
- data/lib/action_controller/metal/compatibility.rb +0 -65
- data/lib/action_controller/metal/session_management.rb +0 -14
- data/lib/action_controller/railties/paths.rb +0 -25
- 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/rescue.rb +0 -26
- data/lib/action_dispatch/testing/performance_test.rb +0 -10
- data/lib/action_view/asset_paths.rb +0 -142
- data/lib/action_view/helpers/asset_paths.rb +0 -7
- 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/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,59 +1,29 @@
|
|
|
1
|
-
module ActionController
|
|
1
|
+
module ActionController
|
|
2
2
|
module Caching
|
|
3
|
-
# Fragment caching is used for caching various blocks within
|
|
3
|
+
# Fragment caching is used for caching various blocks within
|
|
4
4
|
# views without caching the entire action as a whole. This is
|
|
5
|
-
# useful when certain elements of an action change frequently or
|
|
6
|
-
# depend on complicated state while other parts rarely change or
|
|
5
|
+
# useful when certain elements of an action change frequently or
|
|
6
|
+
# depend on complicated state while other parts rarely change or
|
|
7
7
|
# can be shared amongst multiple parties. The caching is done using
|
|
8
|
-
# the
|
|
9
|
-
#
|
|
8
|
+
# the +cache+ helper available in the Action View. See
|
|
9
|
+
# ActionView::Helpers::CacheHelper for more information.
|
|
10
10
|
#
|
|
11
|
-
#
|
|
11
|
+
# While it's strongly recommended that you use key-based cache
|
|
12
|
+
# expiration (see links in CacheHelper for more information),
|
|
13
|
+
# it is also possible to manually expire caches. For example:
|
|
12
14
|
#
|
|
13
|
-
#
|
|
14
|
-
# All the topics in the system:
|
|
15
|
-
# <%= render :partial => "topic", :collection => Topic.all %>
|
|
16
|
-
# <% end %>
|
|
17
|
-
#
|
|
18
|
-
# This cache will bind the name of the action that called it, so if
|
|
19
|
-
# this code was part of the view for the topics/list action, you
|
|
20
|
-
# would be able to invalidate it using:
|
|
21
|
-
#
|
|
22
|
-
# expire_fragment(:controller => "topics", :action => "list")
|
|
23
|
-
#
|
|
24
|
-
# This default behavior is limited if you need to cache multiple
|
|
25
|
-
# fragments per action or if the action itself is cached using
|
|
26
|
-
# <tt>caches_action</tt>. To remedy this, there is an option to
|
|
27
|
-
# qualify the name of the cached fragment by using the
|
|
28
|
-
# <tt>:action_suffix</tt> option:
|
|
29
|
-
#
|
|
30
|
-
# <% cache(:action => "list", :action_suffix => "all_topics") do %>
|
|
31
|
-
#
|
|
32
|
-
# That would result in a name such as
|
|
33
|
-
# <tt>/topics/list/all_topics</tt>, avoiding conflicts with the
|
|
34
|
-
# action cache and with any fragments that use a different suffix.
|
|
35
|
-
# Note that the URL doesn't have to really exist or be callable
|
|
36
|
-
# - the url_for system is just used to generate unique cache names
|
|
37
|
-
# that we can refer to when we need to expire the cache.
|
|
38
|
-
#
|
|
39
|
-
# The expiration call for this example is:
|
|
40
|
-
#
|
|
41
|
-
# expire_fragment(:controller => "topics",
|
|
42
|
-
# :action => "list",
|
|
43
|
-
# :action_suffix => "all_topics")
|
|
15
|
+
# expire_fragment('name_of_cache')
|
|
44
16
|
module Fragments
|
|
45
|
-
# Given a key (as described in
|
|
46
|
-
# a key suitable for use in reading, writing, or expiring a
|
|
47
|
-
# cached fragment.
|
|
48
|
-
# return value of url_for on that hash (without the protocol).
|
|
49
|
-
# All keys are prefixed with <tt>views/</tt> and uses
|
|
17
|
+
# Given a key (as described in +expire_fragment+), returns
|
|
18
|
+
# a key suitable for use in reading, writing, or expiring a
|
|
19
|
+
# cached fragment. All keys are prefixed with <tt>views/</tt> and uses
|
|
50
20
|
# ActiveSupport::Cache.expand_cache_key for the expansion.
|
|
51
21
|
def fragment_cache_key(key)
|
|
52
22
|
ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key).split("://").last : key, :views)
|
|
53
23
|
end
|
|
54
24
|
|
|
55
|
-
# Writes
|
|
56
|
-
#
|
|
25
|
+
# Writes +content+ to the location signified by
|
|
26
|
+
# +key+ (see +expire_fragment+ for acceptable formats).
|
|
57
27
|
def write_fragment(key, content, options = nil)
|
|
58
28
|
return content unless cache_configured?
|
|
59
29
|
|
|
@@ -65,8 +35,8 @@ module ActionController #:nodoc:
|
|
|
65
35
|
content
|
|
66
36
|
end
|
|
67
37
|
|
|
68
|
-
# Reads a cached fragment from the location signified by
|
|
69
|
-
# (see
|
|
38
|
+
# Reads a cached fragment from the location signified by +key+
|
|
39
|
+
# (see +expire_fragment+ for acceptable formats).
|
|
70
40
|
def read_fragment(key, options = nil)
|
|
71
41
|
return unless cache_configured?
|
|
72
42
|
|
|
@@ -77,8 +47,8 @@ module ActionController #:nodoc:
|
|
|
77
47
|
end
|
|
78
48
|
end
|
|
79
49
|
|
|
80
|
-
# Check if a cached fragment from the location signified by
|
|
81
|
-
#
|
|
50
|
+
# Check if a cached fragment from the location signified by
|
|
51
|
+
# +key+ exists (see +expire_fragment+ for acceptable formats).
|
|
82
52
|
def fragment_exist?(key, options = nil)
|
|
83
53
|
return unless cache_configured?
|
|
84
54
|
key = fragment_cache_key(key)
|
|
@@ -95,7 +65,7 @@ module ActionController #:nodoc:
|
|
|
95
65
|
# * String - This would normally take the form of a path, like
|
|
96
66
|
# <tt>pages/45/notes</tt>.
|
|
97
67
|
# * Hash - Treated as an implicit call to +url_for+, like
|
|
98
|
-
# <tt>{:
|
|
68
|
+
# <tt>{ controller: 'pages', action: 'notes', id: 45}</tt>
|
|
99
69
|
# * Regexp - Will remove any fragment that matches, so
|
|
100
70
|
# <tt>%r{pages/\d*/notes}</tt> might remove all notes. Make sure you
|
|
101
71
|
# don't use anchors in the regex (<tt>^</tt> or <tt>$</tt>) because
|
|
@@ -104,8 +74,8 @@ module ActionController #:nodoc:
|
|
|
104
74
|
# only supported on caches that can iterate over all keys (unlike
|
|
105
75
|
# memcached).
|
|
106
76
|
#
|
|
107
|
-
# +options+ is passed through to the cache store's
|
|
108
|
-
# method (or <tt>delete_matched</tt>, for Regexp keys.
|
|
77
|
+
# +options+ is passed through to the cache store's +delete+
|
|
78
|
+
# method (or <tt>delete_matched</tt>, for Regexp keys).
|
|
109
79
|
def expire_fragment(key, options = nil)
|
|
110
80
|
return unless cache_configured?
|
|
111
81
|
key = fragment_cache_key(key) unless key.is_a?(Regexp)
|
|
@@ -119,7 +89,7 @@ module ActionController #:nodoc:
|
|
|
119
89
|
end
|
|
120
90
|
end
|
|
121
91
|
|
|
122
|
-
def instrument_fragment_cache(name, key)
|
|
92
|
+
def instrument_fragment_cache(name, key) # :nodoc:
|
|
123
93
|
ActiveSupport::Notifications.instrument("#{name}.action_controller", :key => key){ yield }
|
|
124
94
|
end
|
|
125
95
|
end
|
|
@@ -2,41 +2,33 @@ require 'fileutils'
|
|
|
2
2
|
require 'uri'
|
|
3
3
|
require 'set'
|
|
4
4
|
|
|
5
|
-
module ActionController
|
|
5
|
+
module ActionController
|
|
6
6
|
# \Caching is a cheap way of speeding up slow applications by keeping the result of
|
|
7
7
|
# calculations, renderings, and database calls around for subsequent requests.
|
|
8
|
-
# Action Controller affords you three approaches in varying levels of granularity:
|
|
9
|
-
# Page, Action, Fragment.
|
|
10
8
|
#
|
|
11
|
-
# You can read more about each approach
|
|
12
|
-
# modules below.
|
|
9
|
+
# You can read more about each approach by clicking the modules below.
|
|
13
10
|
#
|
|
14
|
-
# Note: To turn off all caching
|
|
11
|
+
# Note: To turn off all caching, set
|
|
15
12
|
# config.action_controller.perform_caching = false.
|
|
16
13
|
#
|
|
17
14
|
# == \Caching stores
|
|
18
15
|
#
|
|
19
16
|
# All the caching stores from ActiveSupport::Cache are available to be used as backends
|
|
20
|
-
# for Action Controller caching.
|
|
21
|
-
# as page caching is always written to disk.
|
|
17
|
+
# for Action Controller caching.
|
|
22
18
|
#
|
|
23
19
|
# Configuration examples (MemoryStore is the default):
|
|
24
20
|
#
|
|
25
21
|
# config.action_controller.cache_store = :memory_store
|
|
26
|
-
# config.action_controller.cache_store = :file_store,
|
|
27
|
-
# config.action_controller.cache_store = :mem_cache_store,
|
|
28
|
-
# config.action_controller.cache_store = :mem_cache_store, Memcached::Rails.new(
|
|
29
|
-
# config.action_controller.cache_store = MyOwnStore.new(
|
|
22
|
+
# config.action_controller.cache_store = :file_store, '/path/to/cache/directory'
|
|
23
|
+
# config.action_controller.cache_store = :mem_cache_store, 'localhost'
|
|
24
|
+
# config.action_controller.cache_store = :mem_cache_store, Memcached::Rails.new('localhost:11211')
|
|
25
|
+
# config.action_controller.cache_store = MyOwnStore.new('parameter')
|
|
30
26
|
module Caching
|
|
31
27
|
extend ActiveSupport::Concern
|
|
32
28
|
extend ActiveSupport::Autoload
|
|
33
29
|
|
|
34
30
|
eager_autoload do
|
|
35
|
-
autoload :Actions
|
|
36
31
|
autoload :Fragments
|
|
37
|
-
autoload :Pages
|
|
38
|
-
autoload :Sweeper, 'action_controller/caching/sweeping'
|
|
39
|
-
autoload :Sweeping, 'action_controller/caching/sweeping'
|
|
40
32
|
end
|
|
41
33
|
|
|
42
34
|
module ConfigMethods
|
|
@@ -48,39 +40,60 @@ module ActionController #:nodoc:
|
|
|
48
40
|
config.cache_store = ActiveSupport::Cache.lookup_store(store)
|
|
49
41
|
end
|
|
50
42
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
end
|
|
43
|
+
private
|
|
44
|
+
def cache_configured?
|
|
45
|
+
perform_caching && cache_store
|
|
46
|
+
end
|
|
56
47
|
end
|
|
57
48
|
|
|
58
49
|
include RackDelegation
|
|
59
50
|
include AbstractController::Callbacks
|
|
60
51
|
|
|
61
52
|
include ConfigMethods
|
|
62
|
-
include
|
|
63
|
-
include Sweeping if defined?(ActiveRecord)
|
|
53
|
+
include Fragments
|
|
64
54
|
|
|
65
55
|
included do
|
|
66
56
|
extend ConfigMethods
|
|
67
57
|
|
|
58
|
+
config_accessor :default_static_extension
|
|
59
|
+
self.default_static_extension ||= '.html'
|
|
60
|
+
|
|
61
|
+
def self.page_cache_extension=(extension)
|
|
62
|
+
ActiveSupport::Deprecation.deprecation_warning(:page_cache_extension, :default_static_extension)
|
|
63
|
+
self.default_static_extension = extension
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def self.page_cache_extension
|
|
67
|
+
ActiveSupport::Deprecation.deprecation_warning(:page_cache_extension, :default_static_extension)
|
|
68
|
+
default_static_extension
|
|
69
|
+
end
|
|
70
|
+
|
|
68
71
|
config_accessor :perform_caching
|
|
69
72
|
self.perform_caching = true if perform_caching.nil?
|
|
70
|
-
end
|
|
71
73
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
+
class_attribute :_view_cache_dependencies
|
|
75
|
+
self._view_cache_dependencies = []
|
|
76
|
+
helper_method :view_cache_dependencies if respond_to?(:helper_method)
|
|
74
77
|
end
|
|
75
78
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if cache_configured?
|
|
80
|
-
cache_store.fetch(ActiveSupport::Cache.expand_cache_key(key, :controller), options, &block)
|
|
81
|
-
else
|
|
82
|
-
yield
|
|
79
|
+
module ClassMethods
|
|
80
|
+
def view_cache_dependency(&dependency)
|
|
81
|
+
self._view_cache_dependencies += [dependency]
|
|
83
82
|
end
|
|
84
83
|
end
|
|
84
|
+
|
|
85
|
+
def view_cache_dependencies
|
|
86
|
+
self.class._view_cache_dependencies.map { |dep| instance_exec(&dep) }.compact
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
protected
|
|
90
|
+
# Convenience accessor.
|
|
91
|
+
def cache(key, options = {}, &block)
|
|
92
|
+
if cache_configured?
|
|
93
|
+
cache_store.fetch(ActiveSupport::Cache.expand_cache_key(key, :controller), options, &block)
|
|
94
|
+
else
|
|
95
|
+
yield
|
|
96
|
+
end
|
|
97
|
+
end
|
|
85
98
|
end
|
|
86
99
|
end
|
|
@@ -1,2 +1,5 @@
|
|
|
1
1
|
ActionController::Integration = ActionDispatch::Integration
|
|
2
2
|
ActionController::IntegrationTest = ActionDispatch::IntegrationTest
|
|
3
|
+
|
|
4
|
+
ActiveSupport::Deprecation.warn 'ActionController::Integration is deprecated and will be removed, use ActionDispatch::Integration instead.'
|
|
5
|
+
ActiveSupport::Deprecation.warn 'ActionController::IntegrationTest is deprecated and will be removed, use ActionDispatch::IntegrationTest instead.'
|
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
ActionController::AbstractRequest = ActionController::Request = ActionDispatch::Request
|
|
2
2
|
ActionController::AbstractResponse = ActionController::Response = ActionDispatch::Response
|
|
3
|
-
ActionController::Routing = ActionDispatch::Routing
|
|
3
|
+
ActionController::Routing = ActionDispatch::Routing
|
|
4
|
+
|
|
5
|
+
ActiveSupport::Deprecation.warn 'ActionController::AbstractRequest and ActionController::Request are deprecated and will be removed, use ActionDispatch::Request instead.'
|
|
6
|
+
ActiveSupport::Deprecation.warn 'ActionController::AbstractResponse and ActionController::Response are deprecated and will be removed, use ActionDispatch::Response instead.'
|
|
7
|
+
ActiveSupport::Deprecation.warn 'ActionController::Routing is deprecated and will be removed, use ActionDispatch::Routing instead.'
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
require 'active_support/core_ext/object/blank'
|
|
2
1
|
|
|
3
2
|
module ActionController
|
|
4
3
|
class LogSubscriber < ActiveSupport::LogSubscriber
|
|
5
4
|
INTERNAL_PARAMS = %w(controller action format _method only_path)
|
|
6
5
|
|
|
7
6
|
def start_processing(event)
|
|
7
|
+
return unless logger.info?
|
|
8
|
+
|
|
8
9
|
payload = event.payload
|
|
9
10
|
params = payload[:params].except(*INTERNAL_PARAMS)
|
|
10
11
|
format = payload[:format]
|
|
@@ -15,6 +16,8 @@ module ActionController
|
|
|
15
16
|
end
|
|
16
17
|
|
|
17
18
|
def process_action(event)
|
|
19
|
+
return unless logger.info?
|
|
20
|
+
|
|
18
21
|
payload = event.payload
|
|
19
22
|
additions = ActionController::Base.log_process_action(payload)
|
|
20
23
|
|
|
@@ -23,36 +26,41 @@ module ActionController
|
|
|
23
26
|
exception_class_name = payload[:exception].first
|
|
24
27
|
status = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name)
|
|
25
28
|
end
|
|
26
|
-
message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{
|
|
29
|
+
message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{event.duration.round}ms"
|
|
27
30
|
message << " (#{additions.join(" | ")})" unless additions.blank?
|
|
28
31
|
|
|
29
32
|
info(message)
|
|
30
33
|
end
|
|
31
34
|
|
|
32
35
|
def halted_callback(event)
|
|
33
|
-
info
|
|
36
|
+
info("Filter chain halted as #{event.payload[:filter]} rendered or redirected")
|
|
34
37
|
end
|
|
35
38
|
|
|
36
39
|
def send_file(event)
|
|
37
|
-
info("Sent file #{event.payload[:path]} (#{
|
|
40
|
+
info("Sent file #{event.payload[:path]} (#{event.duration.round(1)}ms)")
|
|
38
41
|
end
|
|
39
42
|
|
|
40
43
|
def redirect_to(event)
|
|
41
|
-
info
|
|
44
|
+
info("Redirected to #{event.payload[:location]}")
|
|
42
45
|
end
|
|
43
46
|
|
|
44
47
|
def send_data(event)
|
|
45
|
-
info("Sent data #{event.payload[:filename]}
|
|
48
|
+
info("Sent data #{event.payload[:filename]} (#{event.duration.round(1)}ms)")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def unpermitted_parameters(event)
|
|
52
|
+
unpermitted_keys = event.payload[:keys]
|
|
53
|
+
debug("Unpermitted parameters: #{unpermitted_keys.join(", ")}")
|
|
46
54
|
end
|
|
47
55
|
|
|
48
56
|
%w(write_fragment read_fragment exist_fragment?
|
|
49
57
|
expire_fragment expire_page write_page).each do |method|
|
|
50
58
|
class_eval <<-METHOD, __FILE__, __LINE__ + 1
|
|
51
59
|
def #{method}(event)
|
|
60
|
+
return unless logger.info?
|
|
52
61
|
key_or_path = event.payload[:key] || event.payload[:path]
|
|
53
62
|
human_name = #{method.to_s.humanize.inspect}
|
|
54
|
-
|
|
55
|
-
info("\#{human_name} \#{key_or_path} \#{duration}")
|
|
63
|
+
info("\#{human_name} \#{key_or_path} (\#{event.duration.round(1)}ms)")
|
|
56
64
|
end
|
|
57
65
|
METHOD
|
|
58
66
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require 'active_support/core_ext/hash/keys'
|
|
2
|
+
|
|
1
3
|
module ActionController
|
|
2
4
|
module ConditionalGet
|
|
3
5
|
extend ActiveSupport::Concern
|
|
@@ -5,25 +7,53 @@ module ActionController
|
|
|
5
7
|
include RackDelegation
|
|
6
8
|
include Head
|
|
7
9
|
|
|
8
|
-
|
|
10
|
+
included do
|
|
11
|
+
class_attribute :etaggers
|
|
12
|
+
self.etaggers = []
|
|
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 authorized 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:
|
|
15
39
|
#
|
|
16
|
-
#
|
|
40
|
+
# * <tt>:etag</tt>.
|
|
41
|
+
# * <tt>:last_modified</tt>.
|
|
42
|
+
# * <tt>:public</tt> By default the Cache-Control header is private, set this to
|
|
43
|
+
# +true+ if you want your application to be cachable by other devices (proxy caches).
|
|
44
|
+
#
|
|
45
|
+
# === Example:
|
|
17
46
|
#
|
|
18
47
|
# def show
|
|
19
48
|
# @article = Article.find(params[:id])
|
|
20
|
-
# fresh_when(:
|
|
49
|
+
# fresh_when(etag: @article, last_modified: @article.created_at, public: true)
|
|
21
50
|
# end
|
|
22
51
|
#
|
|
23
52
|
# This will render the show template if the request isn't sending a matching etag or
|
|
24
53
|
# If-Modified-Since header and just a <tt>304 Not Modified</tt> response if there's a match.
|
|
25
54
|
#
|
|
26
|
-
# You can also just pass a record where last_modified will be set by calling
|
|
55
|
+
# You can also just pass a record where +last_modified+ will be set by calling
|
|
56
|
+
# +updated_at+ and the etag by passing the object itself.
|
|
27
57
|
#
|
|
28
58
|
# def show
|
|
29
59
|
# @article = Article.find(params[:id])
|
|
@@ -34,7 +64,7 @@ module ActionController
|
|
|
34
64
|
#
|
|
35
65
|
# def show
|
|
36
66
|
# @article = Article.find(params[:id])
|
|
37
|
-
# fresh_when(@article, :
|
|
67
|
+
# fresh_when(@article, public: true)
|
|
38
68
|
# end
|
|
39
69
|
def fresh_when(record_or_options, additional_options = {})
|
|
40
70
|
if record_or_options.is_a? Hash
|
|
@@ -42,32 +72,34 @@ module ActionController
|
|
|
42
72
|
options.assert_valid_keys(:etag, :last_modified, :public)
|
|
43
73
|
else
|
|
44
74
|
record = record_or_options
|
|
45
|
-
options = { :
|
|
75
|
+
options = { etag: record, last_modified: record.try(:updated_at) }.merge!(additional_options)
|
|
46
76
|
end
|
|
47
77
|
|
|
48
|
-
response.etag = options[:etag]
|
|
49
|
-
response.last_modified = options[:last_modified]
|
|
50
|
-
response.cache_control[:public] = true
|
|
78
|
+
response.etag = combine_etags(options[:etag]) if options[:etag]
|
|
79
|
+
response.last_modified = options[:last_modified] if options[:last_modified]
|
|
80
|
+
response.cache_control[:public] = true if options[:public]
|
|
51
81
|
|
|
52
82
|
head :not_modified if request.fresh?(response)
|
|
53
83
|
end
|
|
54
84
|
|
|
55
|
-
# Sets the etag and/or last_modified on the response and checks it against
|
|
85
|
+
# Sets the +etag+ and/or +last_modified+ on the response and checks it against
|
|
56
86
|
# the client request. If the request doesn't match the options provided, the
|
|
57
87
|
# request is considered stale and should be generated from scratch. Otherwise,
|
|
58
88
|
# it's fresh and we don't need to generate anything and a reply of <tt>304 Not Modified</tt> is sent.
|
|
59
89
|
#
|
|
60
|
-
# Parameters:
|
|
61
|
-
#
|
|
62
|
-
# * <tt>:
|
|
63
|
-
# * <tt>:
|
|
90
|
+
# === Parameters:
|
|
91
|
+
#
|
|
92
|
+
# * <tt>:etag</tt>.
|
|
93
|
+
# * <tt>:last_modified</tt>.
|
|
94
|
+
# * <tt>:public</tt> By default the Cache-Control header is private, set this to
|
|
95
|
+
# +true+ if you want your application to be cachable by other devices (proxy caches).
|
|
64
96
|
#
|
|
65
|
-
# Example:
|
|
97
|
+
# === Example:
|
|
66
98
|
#
|
|
67
99
|
# def show
|
|
68
100
|
# @article = Article.find(params[:id])
|
|
69
101
|
#
|
|
70
|
-
# if stale?(:
|
|
102
|
+
# if stale?(etag: @article, last_modified: @article.created_at)
|
|
71
103
|
# @statistics = @article.really_expensive_call
|
|
72
104
|
# respond_to do |format|
|
|
73
105
|
# # all the supported formats
|
|
@@ -75,7 +107,8 @@ module ActionController
|
|
|
75
107
|
# end
|
|
76
108
|
# end
|
|
77
109
|
#
|
|
78
|
-
# You can also just pass a record where last_modified will be set by calling
|
|
110
|
+
# You can also just pass a record where +last_modified+ will be set by calling
|
|
111
|
+
# updated_at and the etag by passing the object itself.
|
|
79
112
|
#
|
|
80
113
|
# def show
|
|
81
114
|
# @article = Article.find(params[:id])
|
|
@@ -93,7 +126,7 @@ module ActionController
|
|
|
93
126
|
# def show
|
|
94
127
|
# @article = Article.find(params[:id])
|
|
95
128
|
#
|
|
96
|
-
# if stale?(@article, :
|
|
129
|
+
# if stale?(@article, public: true)
|
|
97
130
|
# @statistics = @article.really_expensive_call
|
|
98
131
|
# respond_to do |format|
|
|
99
132
|
# # all the supported formats
|
|
@@ -105,27 +138,38 @@ module ActionController
|
|
|
105
138
|
!request.fresh?(response)
|
|
106
139
|
end
|
|
107
140
|
|
|
108
|
-
# Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a
|
|
109
|
-
# intermediate caches must not cache the response.
|
|
141
|
+
# Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a +private+
|
|
142
|
+
# instruction, so that intermediate caches must not cache the response.
|
|
110
143
|
#
|
|
111
|
-
# Examples:
|
|
112
144
|
# expires_in 20.minutes
|
|
113
|
-
# expires_in 3.hours, :
|
|
114
|
-
# expires_in 3.hours,
|
|
145
|
+
# expires_in 3.hours, public: true
|
|
146
|
+
# expires_in 3.hours, public: true, must_revalidate: true
|
|
115
147
|
#
|
|
116
148
|
# This method will overwrite an existing Cache-Control header.
|
|
117
149
|
# See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.
|
|
118
|
-
|
|
119
|
-
|
|
150
|
+
#
|
|
151
|
+
# The method will also ensure a HTTP Date header for client compatibility.
|
|
152
|
+
def expires_in(seconds, options = {})
|
|
153
|
+
response.cache_control.merge!(
|
|
154
|
+
:max_age => seconds,
|
|
155
|
+
:public => options.delete(:public),
|
|
156
|
+
:must_revalidate => options.delete(:must_revalidate)
|
|
157
|
+
)
|
|
120
158
|
options.delete(:private)
|
|
121
159
|
|
|
122
160
|
response.cache_control[:extras] = options.map {|k,v| "#{k}=#{v}"}
|
|
161
|
+
response.date = Time.now unless response.date?
|
|
123
162
|
end
|
|
124
163
|
|
|
125
|
-
# Sets a HTTP 1.1 Cache-Control header of <tt>no-cache</tt> so no caching should
|
|
126
|
-
# intermediate caches (like caching proxy servers).
|
|
127
|
-
def expires_now
|
|
164
|
+
# Sets a HTTP 1.1 Cache-Control header of <tt>no-cache</tt> so no caching should
|
|
165
|
+
# occur by the browser or intermediate caches (like caching proxy servers).
|
|
166
|
+
def expires_now
|
|
128
167
|
response.cache_control.replace(:no_cache => true)
|
|
129
168
|
end
|
|
169
|
+
|
|
170
|
+
private
|
|
171
|
+
def combine_etags(etag)
|
|
172
|
+
[ etag, *etaggers.map { |etagger| instance_exec(&etagger) }.compact ]
|
|
173
|
+
end
|
|
130
174
|
end
|
|
131
175
|
end
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
require 'active_support/core_ext/file/path'
|
|
2
1
|
require 'action_controller/metal/exceptions'
|
|
3
2
|
|
|
4
3
|
module ActionController #:nodoc:
|
|
@@ -9,15 +8,13 @@ module ActionController #:nodoc:
|
|
|
9
8
|
|
|
10
9
|
include ActionController::Rendering
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
:disposition => 'attachment'.freeze,
|
|
15
|
-
}.freeze
|
|
11
|
+
DEFAULT_SEND_FILE_TYPE = 'application/octet-stream'.freeze #:nodoc:
|
|
12
|
+
DEFAULT_SEND_FILE_DISPOSITION = 'attachment'.freeze #:nodoc:
|
|
16
13
|
|
|
17
14
|
protected
|
|
18
15
|
# Sends the file. This uses a server-appropriate method (such as X-Sendfile)
|
|
19
16
|
# via the Rack::Sendfile middleware. The header to use is set via
|
|
20
|
-
# config.action_dispatch.x_sendfile_header
|
|
17
|
+
# +config.action_dispatch.x_sendfile_header+.
|
|
21
18
|
# Your server can also configure this for you by setting the X-Sendfile-Type header.
|
|
22
19
|
#
|
|
23
20
|
# Be careful to sanitize the path parameter if it is coming from a web
|
|
@@ -50,11 +47,11 @@ module ActionController #:nodoc:
|
|
|
50
47
|
#
|
|
51
48
|
# Show a JPEG in the browser:
|
|
52
49
|
#
|
|
53
|
-
# send_file '/path/to.jpeg', :
|
|
50
|
+
# send_file '/path/to.jpeg', type: 'image/jpeg', disposition: 'inline'
|
|
54
51
|
#
|
|
55
52
|
# Show a 404 page in the browser:
|
|
56
53
|
#
|
|
57
|
-
# send_file '/path/to/404.html', :
|
|
54
|
+
# send_file '/path/to/404.html', type: 'text/html; charset=utf-8', status: 404
|
|
58
55
|
#
|
|
59
56
|
# Read about the other Content-* HTTP headers if you'd like to
|
|
60
57
|
# provide the user with more information (such as Content-Description) in
|
|
@@ -99,7 +96,7 @@ module ActionController #:nodoc:
|
|
|
99
96
|
end
|
|
100
97
|
|
|
101
98
|
# Sends the given binary data to the browser. This method is similar to
|
|
102
|
-
# <tt>render :
|
|
99
|
+
# <tt>render text: data</tt>, but also allows you to specify whether
|
|
103
100
|
# the browser should display the response as a file attachment (i.e. in a
|
|
104
101
|
# download dialog) or as inline data. You may also set the content type,
|
|
105
102
|
# the apparent file name, and other things.
|
|
@@ -120,15 +117,15 @@ module ActionController #:nodoc:
|
|
|
120
117
|
#
|
|
121
118
|
# Download a dynamically-generated tarball:
|
|
122
119
|
#
|
|
123
|
-
# send_data generate_tgz('dir'), :
|
|
120
|
+
# send_data generate_tgz('dir'), filename: 'dir.tgz'
|
|
124
121
|
#
|
|
125
122
|
# Display an image Active Record in the browser:
|
|
126
123
|
#
|
|
127
|
-
# send_data image.data, :
|
|
124
|
+
# send_data image.data, type: image.content_type, disposition: 'inline'
|
|
128
125
|
#
|
|
129
126
|
# See +send_file+ for more information on HTTP Content-* headers and caching.
|
|
130
127
|
def send_data(data, options = {}) #:doc:
|
|
131
|
-
send_file_headers! options
|
|
128
|
+
send_file_headers! options
|
|
132
129
|
render options.slice(:status, :content_type).merge(:text => data)
|
|
133
130
|
end
|
|
134
131
|
|
|
@@ -136,15 +133,8 @@ module ActionController #:nodoc:
|
|
|
136
133
|
def send_file_headers!(options)
|
|
137
134
|
type_provided = options.has_key?(:type)
|
|
138
135
|
|
|
139
|
-
options.
|
|
140
|
-
|
|
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]
|
|
146
|
-
|
|
147
|
-
content_type = options[:type]
|
|
136
|
+
content_type = options.fetch(:type, DEFAULT_SEND_FILE_TYPE)
|
|
137
|
+
raise ArgumentError, ":type option required" if content_type.nil?
|
|
148
138
|
|
|
149
139
|
if content_type.is_a?(Symbol)
|
|
150
140
|
extension = Mime[content_type]
|
|
@@ -153,15 +143,19 @@ module ActionController #:nodoc:
|
|
|
153
143
|
else
|
|
154
144
|
if !type_provided && options[:filename]
|
|
155
145
|
# If type wasn't provided, try guessing from file extension.
|
|
156
|
-
content_type = Mime::Type.lookup_by_extension(File.extname(options[:filename]).downcase.
|
|
146
|
+
content_type = Mime::Type.lookup_by_extension(File.extname(options[:filename]).downcase.delete('.')) || content_type
|
|
157
147
|
end
|
|
158
148
|
self.content_type = content_type
|
|
159
149
|
end
|
|
160
150
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
151
|
+
disposition = options.fetch(:disposition, DEFAULT_SEND_FILE_DISPOSITION)
|
|
152
|
+
unless disposition.nil?
|
|
153
|
+
disposition = disposition.to_s
|
|
154
|
+
disposition += %(; filename="#{options[:filename]}") if options[:filename]
|
|
155
|
+
headers['Content-Disposition'] = disposition
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
headers['Content-Transfer-Encoding'] = 'binary'
|
|
165
159
|
|
|
166
160
|
response.sending_file = true
|
|
167
161
|
|