actionpack 3.2.19 → 4.2.11.3
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/CHANGELOG.md +412 -503
- data/MIT-LICENSE +1 -1
- data/README.rdoc +11 -294
- data/lib/abstract_controller/asset_paths.rb +2 -2
- data/lib/abstract_controller/base.rb +52 -18
- data/lib/abstract_controller/callbacks.rb +87 -89
- data/lib/abstract_controller/collector.rb +17 -3
- data/lib/abstract_controller/helpers.rb +41 -14
- data/lib/abstract_controller/logger.rb +1 -2
- data/lib/abstract_controller/railties/routes_helpers.rb +3 -3
- data/lib/abstract_controller/rendering.rb +65 -118
- data/lib/abstract_controller/translation.rb +16 -1
- data/lib/abstract_controller/url_for.rb +7 -7
- data/lib/abstract_controller.rb +2 -10
- data/lib/action_controller/base.rb +61 -28
- data/lib/action_controller/caching/fragments.rb +30 -54
- data/lib/action_controller/caching.rb +38 -35
- data/lib/action_controller/log_subscriber.rb +35 -18
- data/lib/action_controller/metal/conditional_get.rb +103 -34
- data/lib/action_controller/metal/data_streaming.rb +20 -26
- data/lib/action_controller/metal/etag_with_template_digest.rb +50 -0
- data/lib/action_controller/metal/exceptions.rb +19 -6
- data/lib/action_controller/metal/flash.rb +41 -9
- data/lib/action_controller/metal/force_ssl.rb +70 -12
- data/lib/action_controller/metal/head.rb +30 -7
- data/lib/action_controller/metal/helpers.rb +11 -11
- data/lib/action_controller/metal/hide_actions.rb +0 -1
- data/lib/action_controller/metal/http_authentication.rb +140 -94
- data/lib/action_controller/metal/implicit_render.rb +1 -1
- data/lib/action_controller/metal/instrumentation.rb +11 -7
- data/lib/action_controller/metal/live.rb +328 -0
- data/lib/action_controller/metal/mime_responds.rb +161 -152
- data/lib/action_controller/metal/params_wrapper.rb +126 -81
- data/lib/action_controller/metal/rack_delegation.rb +10 -4
- data/lib/action_controller/metal/redirecting.rb +44 -41
- data/lib/action_controller/metal/renderers.rb +48 -19
- data/lib/action_controller/metal/rendering.rb +46 -11
- data/lib/action_controller/metal/request_forgery_protection.rb +250 -29
- data/lib/action_controller/metal/streaming.rb +30 -38
- data/lib/action_controller/metal/strong_parameters.rb +669 -0
- data/lib/action_controller/metal/testing.rb +12 -18
- data/lib/action_controller/metal/url_for.rb +31 -29
- data/lib/action_controller/metal.rb +31 -40
- data/lib/action_controller/model_naming.rb +12 -0
- data/lib/action_controller/railtie.rb +38 -18
- data/lib/action_controller/railties/helpers.rb +22 -0
- data/lib/action_controller/test_case.rb +359 -173
- data/lib/action_controller.rb +9 -16
- data/lib/action_dispatch/http/cache.rb +64 -11
- data/lib/action_dispatch/http/filter_parameters.rb +20 -10
- data/lib/action_dispatch/http/filter_redirect.rb +38 -0
- data/lib/action_dispatch/http/headers.rb +85 -17
- data/lib/action_dispatch/http/mime_negotiation.rb +55 -5
- data/lib/action_dispatch/http/mime_type.rb +167 -114
- data/lib/action_dispatch/http/mime_types.rb +2 -1
- data/lib/action_dispatch/http/parameter_filter.rb +44 -46
- data/lib/action_dispatch/http/parameters.rb +30 -46
- data/lib/action_dispatch/http/rack_cache.rb +2 -3
- data/lib/action_dispatch/http/request.rb +108 -45
- data/lib/action_dispatch/http/response.rb +247 -48
- data/lib/action_dispatch/http/upload.rb +60 -29
- data/lib/action_dispatch/http/url.rb +135 -45
- data/lib/action_dispatch/journey/backwards.rb +5 -0
- data/lib/action_dispatch/journey/formatter.rb +166 -0
- data/lib/action_dispatch/journey/gtg/builder.rb +162 -0
- data/lib/action_dispatch/journey/gtg/simulator.rb +47 -0
- data/lib/action_dispatch/journey/gtg/transition_table.rb +157 -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 +128 -0
- data/lib/action_dispatch/journey/parser.rb +198 -0
- data/lib/action_dispatch/journey/parser.y +49 -0
- data/lib/action_dispatch/journey/parser_extras.rb +23 -0
- data/lib/action_dispatch/journey/path/pattern.rb +193 -0
- data/lib/action_dispatch/journey/route.rb +125 -0
- data/lib/action_dispatch/journey/router/strexp.rb +27 -0
- data/lib/action_dispatch/journey/router/utils.rb +93 -0
- data/lib/action_dispatch/journey/router.rb +144 -0
- data/lib/action_dispatch/journey/routes.rb +80 -0
- data/lib/action_dispatch/journey/scanner.rb +61 -0
- data/lib/action_dispatch/journey/visitors.rb +221 -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 +5 -0
- data/lib/action_dispatch/middleware/callbacks.rb +16 -11
- data/lib/action_dispatch/middleware/cookies.rb +346 -125
- data/lib/action_dispatch/middleware/debug_exceptions.rb +52 -24
- data/lib/action_dispatch/middleware/exception_wrapper.rb +75 -9
- data/lib/action_dispatch/middleware/flash.rb +85 -72
- data/lib/action_dispatch/middleware/params_parser.rb +16 -31
- data/lib/action_dispatch/middleware/public_exceptions.rb +39 -14
- data/lib/action_dispatch/middleware/reloader.rb +16 -7
- data/lib/action_dispatch/middleware/remote_ip.rb +132 -40
- data/lib/action_dispatch/middleware/request_id.rb +3 -7
- data/lib/action_dispatch/middleware/session/abstract_store.rb +22 -20
- data/lib/action_dispatch/middleware/session/cache_store.rb +3 -3
- data/lib/action_dispatch/middleware/session/cookie_store.rb +84 -29
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -3
- data/lib/action_dispatch/middleware/show_exceptions.rb +15 -44
- data/lib/action_dispatch/middleware/ssl.rb +72 -0
- data/lib/action_dispatch/middleware/stack.rb +6 -1
- data/lib/action_dispatch/middleware/static.rb +80 -23
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +34 -0
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.erb +27 -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/layout.erb +133 -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 +19 -6
- data/lib/action_dispatch/request/session.rb +193 -0
- data/lib/action_dispatch/request/utils.rb +35 -0
- data/lib/action_dispatch/routing/endpoint.rb +10 -0
- data/lib/action_dispatch/routing/inspector.rb +234 -0
- data/lib/action_dispatch/routing/mapper.rb +897 -436
- data/lib/action_dispatch/routing/polymorphic_routes.rb +213 -92
- data/lib/action_dispatch/routing/redirection.rb +97 -37
- data/lib/action_dispatch/routing/route_set.rb +432 -239
- data/lib/action_dispatch/routing/routes_proxy.rb +7 -4
- data/lib/action_dispatch/routing/url_for.rb +63 -34
- data/lib/action_dispatch/routing.rb +57 -89
- data/lib/action_dispatch/testing/assertions/dom.rb +2 -36
- data/lib/action_dispatch/testing/assertions/response.rb +24 -38
- data/lib/action_dispatch/testing/assertions/routing.rb +55 -54
- data/lib/action_dispatch/testing/assertions/selector.rb +2 -434
- data/lib/action_dispatch/testing/assertions/tag.rb +2 -137
- data/lib/action_dispatch/testing/assertions.rb +11 -7
- data/lib/action_dispatch/testing/integration.rb +88 -72
- data/lib/action_dispatch/testing/test_process.rb +9 -6
- data/lib/action_dispatch/testing/test_request.rb +13 -9
- data/lib/action_dispatch/testing/test_response.rb +1 -5
- data/lib/action_dispatch.rb +24 -21
- data/lib/action_pack/gem_version.rb +15 -0
- data/lib/action_pack/version.rb +5 -7
- data/lib/action_pack.rb +1 -1
- metadata +181 -292
- 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/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/responder.rb +0 -286
- data/lib/action_controller/metal/session_management.rb +0 -14
- 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/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/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 -90
- data/lib/action_view/helpers/sanitize_helper.rb +0 -259
- data/lib/action_view/helpers/tag_helper.rb +0 -160
- 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 -254
- data/lib/action_view/path_set.rb +0 -89
- 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 -54
- data/lib/action_view/renderer/streaming_template_renderer.rb +0 -106
- data/lib/action_view/renderer/template_renderer.rb +0 -94
- 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 -272
- 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 -245
- data/lib/action_view/testing/resolvers.rb +0 -50
- 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
@@ -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
|
15
|
-
# config.action_controller.perform_caching = false
|
11
|
+
# Note: To turn off all caching, set
|
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
|
-
# Configuration examples (
|
19
|
+
# Configuration examples (FileStore 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,50 @@ 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
|
+
|
68
61
|
config_accessor :perform_caching
|
69
62
|
self.perform_caching = true if perform_caching.nil?
|
70
|
-
end
|
71
63
|
|
72
|
-
|
73
|
-
|
64
|
+
class_attribute :_view_cache_dependencies
|
65
|
+
self._view_cache_dependencies = []
|
66
|
+
helper_method :view_cache_dependencies if respond_to?(:helper_method)
|
74
67
|
end
|
75
68
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
if cache_configured?
|
80
|
-
cache_store.fetch(ActiveSupport::Cache.expand_cache_key(key, :controller), options, &block)
|
81
|
-
else
|
82
|
-
yield
|
69
|
+
module ClassMethods
|
70
|
+
def view_cache_dependency(&dependency)
|
71
|
+
self._view_cache_dependencies += [dependency]
|
83
72
|
end
|
84
73
|
end
|
74
|
+
|
75
|
+
def view_cache_dependencies
|
76
|
+
self.class._view_cache_dependencies.map { |dep| instance_exec(&dep) }.compact
|
77
|
+
end
|
78
|
+
|
79
|
+
protected
|
80
|
+
# Convenience accessor.
|
81
|
+
def cache(key, options = {}, &block)
|
82
|
+
if cache_configured?
|
83
|
+
cache_store.fetch(ActiveSupport::Cache.expand_cache_key(key, :controller), options, &block)
|
84
|
+
else
|
85
|
+
yield
|
86
|
+
end
|
87
|
+
end
|
85
88
|
end
|
86
89
|
end
|
@@ -1,10 +1,10 @@
|
|
1
|
-
require 'active_support/core_ext/object/blank'
|
2
|
-
|
3
1
|
module ActionController
|
4
2
|
class LogSubscriber < ActiveSupport::LogSubscriber
|
5
3
|
INTERNAL_PARAMS = %w(controller action format _method only_path)
|
6
4
|
|
7
5
|
def start_processing(event)
|
6
|
+
return unless logger.info?
|
7
|
+
|
8
8
|
payload = event.payload
|
9
9
|
params = payload[:params].except(*INTERNAL_PARAMS)
|
10
10
|
format = payload[:format]
|
@@ -15,44 +15,61 @@ module ActionController
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def process_action(event)
|
18
|
-
|
19
|
-
|
18
|
+
info do
|
19
|
+
payload = event.payload
|
20
|
+
additions = ActionController::Base.log_process_action(payload)
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
22
|
+
status = payload[:status]
|
23
|
+
if status.nil? && payload[:exception].present?
|
24
|
+
exception_class_name = payload[:exception].first
|
25
|
+
status = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name)
|
26
|
+
end
|
27
|
+
message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{event.duration.round}ms"
|
28
|
+
message << " (#{additions.join(" | ")})" unless additions.blank?
|
29
|
+
message
|
25
30
|
end
|
26
|
-
message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{format_duration(event.duration)}"
|
27
|
-
message << " (#{additions.join(" | ")})" unless additions.blank?
|
28
|
-
|
29
|
-
info(message)
|
30
31
|
end
|
31
32
|
|
32
33
|
def halted_callback(event)
|
33
|
-
info "Filter chain halted as #{event.payload[:filter]} rendered or redirected"
|
34
|
+
info { "Filter chain halted as #{event.payload[:filter].inspect} rendered or redirected" }
|
34
35
|
end
|
35
36
|
|
36
37
|
def send_file(event)
|
37
|
-
info
|
38
|
+
info { "Sent file #{event.payload[:path]} (#{event.duration.round(1)}ms)" }
|
38
39
|
end
|
39
40
|
|
40
41
|
def redirect_to(event)
|
41
|
-
info "Redirected to #{event.payload[:location]}"
|
42
|
+
info { "Redirected to #{event.payload[:location]}" }
|
42
43
|
end
|
43
44
|
|
44
45
|
def send_data(event)
|
45
|
-
info
|
46
|
+
info { "Sent data #{event.payload[:filename]} (#{event.duration.round(1)}ms)" }
|
47
|
+
end
|
48
|
+
|
49
|
+
def unpermitted_parameters(event)
|
50
|
+
debug do
|
51
|
+
unpermitted_keys = event.payload[:keys]
|
52
|
+
"Unpermitted parameter#{'s' if unpermitted_keys.size > 1}: #{unpermitted_keys.join(", ")}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def deep_munge(event)
|
57
|
+
debug do
|
58
|
+
"Value for params[:#{event.payload[:keys].join('][:')}] was set "\
|
59
|
+
"to nil, because it was one of [], [null] or [null, null, ...]. "\
|
60
|
+
"Go to http://guides.rubyonrails.org/security.html#unsafe-query-generation "\
|
61
|
+
"for more information."\
|
62
|
+
end
|
46
63
|
end
|
47
64
|
|
48
65
|
%w(write_fragment read_fragment exist_fragment?
|
49
66
|
expire_fragment expire_page write_page).each do |method|
|
50
67
|
class_eval <<-METHOD, __FILE__, __LINE__ + 1
|
51
68
|
def #{method}(event)
|
69
|
+
return unless logger.info?
|
52
70
|
key_or_path = event.payload[:key] || event.payload[:path]
|
53
71
|
human_name = #{method.to_s.humanize.inspect}
|
54
|
-
|
55
|
-
info("\#{human_name} \#{key_or_path} \#{duration}")
|
72
|
+
info("\#{human_name} \#{key_or_path} (\#{event.duration.round(1)}ms)")
|
56
73
|
end
|
57
74
|
METHOD
|
58
75
|
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,58 @@ 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
|
-
#
|
13
|
-
# * <tt>:
|
14
|
-
# * <tt>:
|
38
|
+
# === Parameters:
|
39
|
+
#
|
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
|
+
# * <tt>:template</tt> By default, the template digest for the current
|
45
|
+
# controller/action is included in ETags. If the action renders a
|
46
|
+
# different template, you can include its digest instead. If the action
|
47
|
+
# doesn't render a template at all, you can pass <tt>template: false</tt>
|
48
|
+
# to skip any attempt to check for a template digest.
|
15
49
|
#
|
16
|
-
# Example:
|
50
|
+
# === Example:
|
17
51
|
#
|
18
52
|
# def show
|
19
53
|
# @article = Article.find(params[:id])
|
20
|
-
# fresh_when(:
|
54
|
+
# fresh_when(etag: @article, last_modified: @article.created_at, public: true)
|
21
55
|
# end
|
22
56
|
#
|
23
|
-
# This will render the show template if the request isn't sending a matching
|
57
|
+
# This will render the show template if the request isn't sending a matching ETag or
|
24
58
|
# If-Modified-Since header and just a <tt>304 Not Modified</tt> response if there's a match.
|
25
59
|
#
|
26
|
-
# You can also just pass a record where last_modified will be set by calling
|
60
|
+
# You can also just pass a record where +last_modified+ will be set by calling
|
61
|
+
# +updated_at+ and the +etag+ by passing the object itself.
|
27
62
|
#
|
28
63
|
# def show
|
29
64
|
# @article = Article.find(params[:id])
|
@@ -34,40 +69,53 @@ module ActionController
|
|
34
69
|
#
|
35
70
|
# def show
|
36
71
|
# @article = Article.find(params[:id])
|
37
|
-
# fresh_when(@article, :
|
72
|
+
# fresh_when(@article, public: true)
|
38
73
|
# end
|
74
|
+
#
|
75
|
+
# When rendering a different template than the default controller/action
|
76
|
+
# style, you can indicate which digest to include in the ETag:
|
77
|
+
#
|
78
|
+
# before_action { fresh_when @article, template: 'widgets/show' }
|
79
|
+
#
|
39
80
|
def fresh_when(record_or_options, additional_options = {})
|
40
81
|
if record_or_options.is_a? Hash
|
41
82
|
options = record_or_options
|
42
|
-
options.assert_valid_keys(:etag, :last_modified, :public)
|
83
|
+
options.assert_valid_keys(:etag, :last_modified, :public, :template)
|
43
84
|
else
|
44
85
|
record = record_or_options
|
45
|
-
options = { :
|
86
|
+
options = { etag: record, last_modified: record.try(:updated_at) }.merge!(additional_options)
|
46
87
|
end
|
47
88
|
|
48
|
-
response.etag = options[:etag]
|
49
|
-
response.last_modified = options[:last_modified]
|
50
|
-
response.cache_control[:public] = true
|
89
|
+
response.etag = combine_etags(options) if options[:etag] || options[:template]
|
90
|
+
response.last_modified = options[:last_modified] if options[:last_modified]
|
91
|
+
response.cache_control[:public] = true if options[:public]
|
51
92
|
|
52
93
|
head :not_modified if request.fresh?(response)
|
53
94
|
end
|
54
95
|
|
55
|
-
# Sets the etag and/or last_modified on the response and checks it against
|
96
|
+
# Sets the +etag+ and/or +last_modified+ on the response and checks it against
|
56
97
|
# the client request. If the request doesn't match the options provided, the
|
57
98
|
# request is considered stale and should be generated from scratch. Otherwise,
|
58
99
|
# it's fresh and we don't need to generate anything and a reply of <tt>304 Not Modified</tt> is sent.
|
59
100
|
#
|
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).
|
101
|
+
# === Parameters:
|
64
102
|
#
|
65
|
-
#
|
103
|
+
# * <tt>:etag</tt>.
|
104
|
+
# * <tt>:last_modified</tt>.
|
105
|
+
# * <tt>:public</tt> By default the Cache-Control header is private, set this to
|
106
|
+
# +true+ if you want your application to be cachable by other devices (proxy caches).
|
107
|
+
# * <tt>:template</tt> By default, the template digest for the current
|
108
|
+
# controller/action is included in ETags. If the action renders a
|
109
|
+
# different template, you can include its digest instead. If the action
|
110
|
+
# doesn't render a template at all, you can pass <tt>template: false</tt>
|
111
|
+
# to skip any attempt to check for a template digest.
|
112
|
+
#
|
113
|
+
# === Example:
|
66
114
|
#
|
67
115
|
# def show
|
68
116
|
# @article = Article.find(params[:id])
|
69
117
|
#
|
70
|
-
# if stale?(:
|
118
|
+
# if stale?(etag: @article, last_modified: @article.created_at)
|
71
119
|
# @statistics = @article.really_expensive_call
|
72
120
|
# respond_to do |format|
|
73
121
|
# # all the supported formats
|
@@ -75,7 +123,8 @@ module ActionController
|
|
75
123
|
# end
|
76
124
|
# end
|
77
125
|
#
|
78
|
-
# You can also just pass a record where last_modified will be set by calling
|
126
|
+
# You can also just pass a record where +last_modified+ will be set by calling
|
127
|
+
# +updated_at+ and the +etag+ by passing the object itself.
|
79
128
|
#
|
80
129
|
# def show
|
81
130
|
# @article = Article.find(params[:id])
|
@@ -93,39 +142,59 @@ module ActionController
|
|
93
142
|
# def show
|
94
143
|
# @article = Article.find(params[:id])
|
95
144
|
#
|
96
|
-
# if stale?(@article, :
|
145
|
+
# if stale?(@article, public: true)
|
97
146
|
# @statistics = @article.really_expensive_call
|
98
147
|
# respond_to do |format|
|
99
148
|
# # all the supported formats
|
100
149
|
# end
|
101
150
|
# end
|
102
151
|
# end
|
152
|
+
#
|
153
|
+
# When rendering a different template than the default controller/action
|
154
|
+
# style, you can indicate which digest to include in the ETag:
|
155
|
+
#
|
156
|
+
# def show
|
157
|
+
# super if stale? @article, template: 'widgets/show'
|
158
|
+
# end
|
159
|
+
#
|
103
160
|
def stale?(record_or_options, additional_options = {})
|
104
161
|
fresh_when(record_or_options, additional_options)
|
105
162
|
!request.fresh?(response)
|
106
163
|
end
|
107
164
|
|
108
|
-
# Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a
|
109
|
-
# intermediate caches must not cache the response.
|
165
|
+
# Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a +private+
|
166
|
+
# instruction, so that intermediate caches must not cache the response.
|
110
167
|
#
|
111
|
-
# Examples:
|
112
168
|
# expires_in 20.minutes
|
113
|
-
# expires_in 3.hours, :
|
114
|
-
# expires_in 3.hours,
|
169
|
+
# expires_in 3.hours, public: true
|
170
|
+
# expires_in 3.hours, public: true, must_revalidate: true
|
115
171
|
#
|
116
172
|
# This method will overwrite an existing Cache-Control header.
|
117
173
|
# See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.
|
118
|
-
|
119
|
-
|
174
|
+
#
|
175
|
+
# The method will also ensure a HTTP Date header for client compatibility.
|
176
|
+
def expires_in(seconds, options = {})
|
177
|
+
response.cache_control.merge!(
|
178
|
+
:max_age => seconds,
|
179
|
+
:public => options.delete(:public),
|
180
|
+
:must_revalidate => options.delete(:must_revalidate)
|
181
|
+
)
|
120
182
|
options.delete(:private)
|
121
183
|
|
122
184
|
response.cache_control[:extras] = options.map {|k,v| "#{k}=#{v}"}
|
185
|
+
response.date = Time.now unless response.date?
|
123
186
|
end
|
124
187
|
|
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
|
188
|
+
# Sets a HTTP 1.1 Cache-Control header of <tt>no-cache</tt> so no caching should
|
189
|
+
# occur by the browser or intermediate caches (like caching proxy servers).
|
190
|
+
def expires_now
|
128
191
|
response.cache_control.replace(:no_cache => true)
|
129
192
|
end
|
193
|
+
|
194
|
+
private
|
195
|
+
def combine_etags(options)
|
196
|
+
etags = etaggers.map { |etagger| instance_exec(options, &etagger) }.compact
|
197
|
+
etags.unshift options[:etag]
|
198
|
+
end
|
130
199
|
end
|
131
200
|
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 plain: 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
|
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module ActionController
|
2
|
+
# When our views change, they should bubble up into HTTP cache freshness
|
3
|
+
# and bust browser caches. So the template digest for the current action
|
4
|
+
# is automatically included in the ETag.
|
5
|
+
#
|
6
|
+
# Enabled by default for apps that use Action View. Disable by setting
|
7
|
+
#
|
8
|
+
# config.action_controller.etag_with_template_digest = false
|
9
|
+
#
|
10
|
+
# Override the template to digest by passing +:template+ to +fresh_when+
|
11
|
+
# and +stale?+ calls. For example:
|
12
|
+
#
|
13
|
+
# # We're going to render widgets/show, not posts/show
|
14
|
+
# fresh_when @post, template: 'widgets/show'
|
15
|
+
#
|
16
|
+
# # We're not going to render a template, so omit it from the ETag.
|
17
|
+
# fresh_when @post, template: false
|
18
|
+
#
|
19
|
+
module EtagWithTemplateDigest
|
20
|
+
extend ActiveSupport::Concern
|
21
|
+
|
22
|
+
include ActionController::ConditionalGet
|
23
|
+
|
24
|
+
included do
|
25
|
+
class_attribute :etag_with_template_digest
|
26
|
+
self.etag_with_template_digest = true
|
27
|
+
|
28
|
+
ActiveSupport.on_load :action_view, yield: true do |action_view_base|
|
29
|
+
etag do |options|
|
30
|
+
determine_template_etag(options) if etag_with_template_digest
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def determine_template_etag(options)
|
37
|
+
if template = pick_template_for_etag(options)
|
38
|
+
lookup_and_digest_template(template)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def pick_template_for_etag(options)
|
43
|
+
options.fetch(:template) { "#{controller_name}/#{action_name}" }
|
44
|
+
end
|
45
|
+
|
46
|
+
def lookup_and_digest_template(template)
|
47
|
+
ActionView::Digestor.digest name: template, finder: lookup_context
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -2,6 +2,18 @@ module ActionController
|
|
2
2
|
class ActionControllerError < StandardError #:nodoc:
|
3
3
|
end
|
4
4
|
|
5
|
+
class BadRequest < ActionControllerError #:nodoc:
|
6
|
+
attr_reader :original_exception
|
7
|
+
|
8
|
+
def initialize(type = nil, e = nil)
|
9
|
+
return super() unless type && e
|
10
|
+
|
11
|
+
super("Invalid #{type} parameters: #{e.message}")
|
12
|
+
@original_exception = e
|
13
|
+
set_backtrace e.backtrace
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
5
17
|
class RenderError < ActionControllerError #:nodoc:
|
6
18
|
end
|
7
19
|
|
@@ -13,9 +25,10 @@ module ActionController
|
|
13
25
|
end
|
14
26
|
end
|
15
27
|
|
16
|
-
class
|
17
|
-
|
28
|
+
class ActionController::UrlGenerationError < ActionControllerError #:nodoc:
|
29
|
+
end
|
18
30
|
|
31
|
+
class MethodNotAllowed < ActionControllerError #:nodoc:
|
19
32
|
def initialize(*allowed_methods)
|
20
33
|
super("Only #{allowed_methods.to_sentence(:locale => :en)} requests are allowed.")
|
21
34
|
end
|
@@ -30,9 +43,6 @@ module ActionController
|
|
30
43
|
class MissingFile < ActionControllerError #:nodoc:
|
31
44
|
end
|
32
45
|
|
33
|
-
class RenderError < ActionControllerError #:nodoc:
|
34
|
-
end
|
35
|
-
|
36
46
|
class SessionOverflowError < ActionControllerError #:nodoc:
|
37
47
|
DEFAULT_MESSAGE = 'Your session data is larger than the data column in which it is to be stored. You must increase the size of your data column if you intend to store large data.'
|
38
48
|
|
@@ -43,4 +53,7 @@ module ActionController
|
|
43
53
|
|
44
54
|
class UnknownHttpMethod < ActionControllerError #:nodoc:
|
45
55
|
end
|
46
|
-
|
56
|
+
|
57
|
+
class UnknownFormat < ActionControllerError #:nodoc:
|
58
|
+
end
|
59
|
+
end
|