actionpack 4.2.10 → 7.2.0.rc1
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.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/CHANGELOG.md +86 -600
- data/MIT-LICENSE +1 -1
- data/README.rdoc +13 -14
- data/lib/abstract_controller/asset_paths.rb +5 -1
- data/lib/abstract_controller/base.rb +166 -136
- data/lib/abstract_controller/caching/fragments.rb +149 -0
- data/lib/abstract_controller/caching.rb +68 -0
- data/lib/abstract_controller/callbacks.rb +126 -57
- data/lib/abstract_controller/collector.rb +13 -15
- data/lib/abstract_controller/deprecator.rb +9 -0
- data/lib/abstract_controller/error.rb +8 -0
- data/lib/abstract_controller/helpers.rb +181 -132
- data/lib/abstract_controller/logger.rb +5 -1
- data/lib/abstract_controller/railties/routes_helpers.rb +10 -3
- data/lib/abstract_controller/rendering.rb +56 -56
- data/lib/abstract_controller/translation.rb +29 -15
- data/lib/abstract_controller/url_for.rb +15 -11
- data/lib/abstract_controller.rb +21 -5
- data/lib/action_controller/api/api_rendering.rb +18 -0
- data/lib/action_controller/api.rb +154 -0
- data/lib/action_controller/base.rb +219 -155
- data/lib/action_controller/caching.rb +28 -68
- data/lib/action_controller/deprecator.rb +9 -0
- data/lib/action_controller/form_builder.rb +55 -0
- data/lib/action_controller/log_subscriber.rb +35 -22
- data/lib/action_controller/metal/allow_browser.rb +119 -0
- data/lib/action_controller/metal/basic_implicit_render.rb +17 -0
- data/lib/action_controller/metal/conditional_get.rb +259 -122
- data/lib/action_controller/metal/content_security_policy.rb +86 -0
- data/lib/action_controller/metal/cookies.rb +9 -5
- data/lib/action_controller/metal/data_streaming.rb +87 -104
- data/lib/action_controller/metal/default_headers.rb +21 -0
- data/lib/action_controller/metal/etag_with_flash.rb +22 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +35 -26
- data/lib/action_controller/metal/exceptions.rb +71 -24
- data/lib/action_controller/metal/flash.rb +26 -19
- data/lib/action_controller/metal/head.rb +45 -36
- data/lib/action_controller/metal/helpers.rb +80 -64
- data/lib/action_controller/metal/http_authentication.rb +297 -244
- data/lib/action_controller/metal/implicit_render.rb +57 -9
- data/lib/action_controller/metal/instrumentation.rb +76 -64
- data/lib/action_controller/metal/live.rb +238 -176
- data/lib/action_controller/metal/logging.rb +22 -0
- data/lib/action_controller/metal/mime_responds.rb +177 -166
- data/lib/action_controller/metal/parameter_encoding.rb +84 -0
- data/lib/action_controller/metal/params_wrapper.rb +145 -118
- data/lib/action_controller/metal/permissions_policy.rb +38 -0
- data/lib/action_controller/metal/rate_limiting.rb +62 -0
- data/lib/action_controller/metal/redirecting.rb +203 -64
- data/lib/action_controller/metal/renderers.rb +108 -65
- data/lib/action_controller/metal/rendering.rb +216 -56
- data/lib/action_controller/metal/request_forgery_protection.rb +496 -163
- data/lib/action_controller/metal/rescue.rb +19 -21
- data/lib/action_controller/metal/streaming.rb +179 -138
- data/lib/action_controller/metal/strong_parameters.rb +1058 -382
- data/lib/action_controller/metal/testing.rb +11 -17
- data/lib/action_controller/metal/url_for.rb +37 -21
- data/lib/action_controller/metal.rb +236 -138
- data/lib/action_controller/railtie.rb +89 -11
- data/lib/action_controller/railties/helpers.rb +5 -1
- data/lib/action_controller/renderer.rb +161 -0
- data/lib/action_controller/template_assertions.rb +13 -0
- data/lib/action_controller/test_case.rb +425 -497
- data/lib/action_controller.rb +44 -22
- data/lib/action_dispatch/constants.rb +34 -0
- data/lib/action_dispatch/deprecator.rb +9 -0
- data/lib/action_dispatch/http/cache.rb +119 -63
- data/lib/action_dispatch/http/content_disposition.rb +47 -0
- data/lib/action_dispatch/http/content_security_policy.rb +364 -0
- data/lib/action_dispatch/http/filter_parameters.rb +36 -34
- data/lib/action_dispatch/http/filter_redirect.rb +24 -12
- data/lib/action_dispatch/http/headers.rb +66 -31
- data/lib/action_dispatch/http/mime_negotiation.rb +106 -75
- data/lib/action_dispatch/http/mime_type.rb +196 -136
- data/lib/action_dispatch/http/mime_types.rb +25 -7
- data/lib/action_dispatch/http/parameters.rb +97 -45
- data/lib/action_dispatch/http/permissions_policy.rb +187 -0
- data/lib/action_dispatch/http/rack_cache.rb +6 -0
- data/lib/action_dispatch/http/request.rb +299 -170
- data/lib/action_dispatch/http/response.rb +311 -160
- data/lib/action_dispatch/http/upload.rb +52 -23
- data/lib/action_dispatch/http/url.rb +201 -125
- data/lib/action_dispatch/journey/formatter.rb +110 -50
- data/lib/action_dispatch/journey/gtg/builder.rb +37 -50
- data/lib/action_dispatch/journey/gtg/simulator.rb +20 -17
- data/lib/action_dispatch/journey/gtg/transition_table.rb +96 -36
- data/lib/action_dispatch/journey/nfa/dot.rb +5 -14
- data/lib/action_dispatch/journey/nodes/node.rb +100 -20
- data/lib/action_dispatch/journey/parser.rb +19 -17
- data/lib/action_dispatch/journey/parser.y +4 -3
- data/lib/action_dispatch/journey/parser_extras.rb +14 -4
- data/lib/action_dispatch/journey/path/pattern.rb +79 -63
- data/lib/action_dispatch/journey/route.rb +108 -44
- data/lib/action_dispatch/journey/router/utils.rb +41 -29
- data/lib/action_dispatch/journey/router.rb +64 -57
- data/lib/action_dispatch/journey/routes.rb +23 -21
- data/lib/action_dispatch/journey/scanner.rb +28 -17
- data/lib/action_dispatch/journey/visitors.rb +100 -54
- data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
- data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
- data/lib/action_dispatch/journey.rb +7 -5
- data/lib/action_dispatch/log_subscriber.rb +25 -0
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
- data/lib/action_dispatch/middleware/assume_ssl.rb +27 -0
- data/lib/action_dispatch/middleware/callbacks.rb +7 -6
- data/lib/action_dispatch/middleware/cookies.rb +471 -328
- data/lib/action_dispatch/middleware/debug_exceptions.rb +149 -66
- data/lib/action_dispatch/middleware/debug_locks.rb +129 -0
- data/lib/action_dispatch/middleware/debug_view.rb +73 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +275 -73
- data/lib/action_dispatch/middleware/executor.rb +32 -0
- data/lib/action_dispatch/middleware/flash.rb +143 -101
- data/lib/action_dispatch/middleware/host_authorization.rb +171 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +36 -27
- data/lib/action_dispatch/middleware/reloader.rb +10 -92
- data/lib/action_dispatch/middleware/remote_ip.rb +133 -107
- data/lib/action_dispatch/middleware/request_id.rb +29 -15
- data/lib/action_dispatch/middleware/server_timing.rb +78 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +49 -27
- data/lib/action_dispatch/middleware/session/cache_store.rb +33 -16
- data/lib/action_dispatch/middleware/session/cookie_store.rb +86 -80
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +15 -3
- data/lib/action_dispatch/middleware/show_exceptions.rb +66 -36
- data/lib/action_dispatch/middleware/ssl.rb +134 -36
- data/lib/action_dispatch/middleware/stack.rb +109 -44
- data/lib/action_dispatch/middleware/static.rb +159 -90
- data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
- data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +7 -24
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +36 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +46 -36
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +12 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +26 -7
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +16 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +139 -15
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +23 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +6 -6
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +7 -7
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +9 -9
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +7 -4
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +125 -93
- data/lib/action_dispatch/railtie.rb +44 -16
- data/lib/action_dispatch/request/session.rb +159 -69
- data/lib/action_dispatch/request/utils.rb +97 -23
- data/lib/action_dispatch/routing/endpoint.rb +11 -2
- data/lib/action_dispatch/routing/inspector.rb +195 -106
- data/lib/action_dispatch/routing/mapper.rb +1338 -955
- data/lib/action_dispatch/routing/polymorphic_routes.rb +234 -201
- data/lib/action_dispatch/routing/redirection.rb +78 -51
- data/lib/action_dispatch/routing/route_set.rb +460 -374
- data/lib/action_dispatch/routing/routes_proxy.rb +36 -12
- data/lib/action_dispatch/routing/url_for.rb +172 -124
- data/lib/action_dispatch/routing.rb +159 -158
- data/lib/action_dispatch/system_test_case.rb +206 -0
- data/lib/action_dispatch/system_testing/browser.rb +84 -0
- data/lib/action_dispatch/system_testing/driver.rb +85 -0
- data/lib/action_dispatch/system_testing/server.rb +33 -0
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +164 -0
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +23 -0
- data/lib/action_dispatch/testing/assertion_response.rb +48 -0
- data/lib/action_dispatch/testing/assertions/response.rb +71 -39
- data/lib/action_dispatch/testing/assertions/routing.rb +228 -103
- data/lib/action_dispatch/testing/assertions.rb +9 -6
- data/lib/action_dispatch/testing/integration.rb +486 -306
- data/lib/action_dispatch/testing/request_encoder.rb +60 -0
- data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
- data/lib/action_dispatch/testing/test_process.rb +35 -22
- data/lib/action_dispatch/testing/test_request.rb +29 -34
- data/lib/action_dispatch/testing/test_response.rb +48 -15
- data/lib/action_dispatch.rb +82 -40
- data/lib/action_pack/gem_version.rb +8 -4
- data/lib/action_pack/version.rb +6 -2
- data/lib/action_pack.rb +21 -18
- metadata +146 -56
- data/lib/action_controller/caching/fragments.rb +0 -103
- data/lib/action_controller/metal/force_ssl.rb +0 -97
- data/lib/action_controller/metal/hide_actions.rb +0 -40
- data/lib/action_controller/metal/rack_delegation.rb +0 -32
- data/lib/action_controller/middleware.rb +0 -39
- data/lib/action_controller/model_naming.rb +0 -12
- data/lib/action_dispatch/http/parameter_filter.rb +0 -72
- data/lib/action_dispatch/journey/backwards.rb +0 -5
- data/lib/action_dispatch/journey/nfa/builder.rb +0 -76
- data/lib/action_dispatch/journey/nfa/simulator.rb +0 -47
- data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -163
- data/lib/action_dispatch/journey/router/strexp.rb +0 -27
- data/lib/action_dispatch/middleware/params_parser.rb +0 -60
- data/lib/action_dispatch/middleware/templates/rescues/_source.erb +0 -27
- data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
- data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
- data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
data/MIT-LICENSE
CHANGED
data/README.rdoc
CHANGED
@@ -2,9 +2,8 @@
|
|
2
2
|
|
3
3
|
Action Pack is a framework for handling and responding to web requests. It
|
4
4
|
provides mechanisms for *routing* (mapping request URLs to actions), defining
|
5
|
-
*controllers* that implement actions, and generating responses
|
6
|
-
|
7
|
-
provides the view and controller layers in the MVC paradigm.
|
5
|
+
*controllers* that implement actions, and generating responses. In short, Action Pack
|
6
|
+
provides the controller layer in the MVC paradigm.
|
8
7
|
|
9
8
|
It consists of several modules:
|
10
9
|
|
@@ -17,42 +16,42 @@ It consists of several modules:
|
|
17
16
|
subclassed to implement filters and actions to handle requests. The result
|
18
17
|
of an action is typically content generated from views.
|
19
18
|
|
20
|
-
With the Ruby on Rails framework, users only directly interface with the
|
19
|
+
With the Ruby on \Rails framework, users only directly interface with the
|
21
20
|
Action Controller module. Necessary Action Dispatch functionality is activated
|
22
21
|
by default and Action View rendering is implicitly triggered by Action
|
23
22
|
Controller. However, these modules are designed to function on their own and
|
24
|
-
can be used outside of Rails.
|
23
|
+
can be used outside of \Rails.
|
25
24
|
|
25
|
+
You can read more about Action Pack in the {Action Controller Overview}[https://guides.rubyonrails.org/action_controller_overview.html] guide.
|
26
26
|
|
27
27
|
== Download and installation
|
28
28
|
|
29
29
|
The latest version of Action Pack can be installed with RubyGems:
|
30
30
|
|
31
|
-
|
31
|
+
$ gem install actionpack
|
32
32
|
|
33
|
-
Source code can be downloaded as part of the Rails project on GitHub
|
33
|
+
Source code can be downloaded as part of the \Rails project on GitHub:
|
34
34
|
|
35
|
-
* https://github.com/rails/rails/tree/
|
35
|
+
* https://github.com/rails/rails/tree/main/actionpack
|
36
36
|
|
37
37
|
|
38
38
|
== License
|
39
39
|
|
40
40
|
Action Pack is released under the MIT license:
|
41
41
|
|
42
|
-
*
|
42
|
+
* https://opensource.org/licenses/MIT
|
43
43
|
|
44
44
|
|
45
45
|
== Support
|
46
46
|
|
47
|
-
API documentation is at
|
47
|
+
API documentation is at:
|
48
48
|
|
49
|
-
*
|
49
|
+
* https://api.rubyonrails.org
|
50
50
|
|
51
|
-
Bug reports
|
51
|
+
Bug reports for the Ruby on \Rails project can be filed here:
|
52
52
|
|
53
53
|
* https://github.com/rails/rails/issues
|
54
54
|
|
55
55
|
Feature requests should be discussed on the rails-core mailing list here:
|
56
56
|
|
57
|
-
* https://
|
58
|
-
|
57
|
+
* https://discuss.rubyonrails.org/c/rubyonrails-core
|
@@ -1,111 +1,131 @@
|
|
1
|
-
|
2
|
-
require 'set'
|
3
|
-
require 'active_support/configurable'
|
4
|
-
require 'active_support/descendants_tracker'
|
5
|
-
require 'active_support/core_ext/module/anonymous'
|
1
|
+
# frozen_string_literal: true
|
6
2
|
|
7
|
-
|
8
|
-
|
9
|
-
|
3
|
+
# :markup: markdown
|
4
|
+
|
5
|
+
require "abstract_controller/error"
|
6
|
+
require "active_support/configurable"
|
7
|
+
require "active_support/descendants_tracker"
|
8
|
+
require "active_support/core_ext/module/anonymous"
|
9
|
+
require "active_support/core_ext/module/attr_internal"
|
10
10
|
|
11
|
+
module AbstractController
|
11
12
|
# Raised when a non-existing controller action is triggered.
|
12
13
|
class ActionNotFound < StandardError
|
14
|
+
attr_reader :controller, :action # :nodoc:
|
15
|
+
|
16
|
+
def initialize(message = nil, controller = nil, action = nil) # :nodoc:
|
17
|
+
@controller = controller
|
18
|
+
@action = action
|
19
|
+
super(message)
|
20
|
+
end
|
21
|
+
|
22
|
+
if defined?(DidYouMean::Correctable) && defined?(DidYouMean::SpellChecker)
|
23
|
+
include DidYouMean::Correctable # :nodoc:
|
24
|
+
|
25
|
+
def corrections # :nodoc:
|
26
|
+
@corrections ||= DidYouMean::SpellChecker.new(dictionary: controller.class.action_methods).correct(action)
|
27
|
+
end
|
28
|
+
end
|
13
29
|
end
|
14
30
|
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
31
|
+
# # Abstract Controller Base
|
32
|
+
#
|
33
|
+
# AbstractController::Base is a low-level API. Nobody should be using it
|
34
|
+
# directly, and subclasses (like ActionController::Base) are expected to provide
|
35
|
+
# their own `render` method, since rendering means different things depending on
|
36
|
+
# the context.
|
19
37
|
class Base
|
38
|
+
##
|
39
|
+
# Returns the body of the HTTP response sent by the controller.
|
20
40
|
attr_internal :response_body
|
41
|
+
|
42
|
+
##
|
43
|
+
# Returns the name of the action this controller is processing.
|
21
44
|
attr_internal :action_name
|
45
|
+
|
46
|
+
##
|
47
|
+
# Returns the formats that can be processed by the controller.
|
22
48
|
attr_internal :formats
|
23
49
|
|
24
50
|
include ActiveSupport::Configurable
|
25
51
|
extend ActiveSupport::DescendantsTracker
|
26
52
|
|
27
|
-
undef_method :not_implemented
|
28
53
|
class << self
|
29
54
|
attr_reader :abstract
|
30
55
|
alias_method :abstract?, :abstract
|
31
56
|
|
32
|
-
# Define a controller as abstract. See internal_methods for more
|
33
|
-
# details.
|
57
|
+
# Define a controller as abstract. See internal_methods for more details.
|
34
58
|
def abstract!
|
35
59
|
@abstract = true
|
36
60
|
end
|
37
61
|
|
38
62
|
def inherited(klass) # :nodoc:
|
39
|
-
# Define the abstract ivar on subclasses so that we don't get
|
40
|
-
#
|
63
|
+
# Define the abstract ivar on subclasses so that we don't get uninitialized ivar
|
64
|
+
# warnings
|
41
65
|
unless klass.instance_variable_defined?(:@abstract)
|
42
66
|
klass.instance_variable_set(:@abstract, false)
|
43
67
|
end
|
44
68
|
super
|
45
69
|
end
|
46
70
|
|
47
|
-
# A list of all internal methods for a controller. This finds the first
|
48
|
-
#
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
52
|
-
#
|
71
|
+
# A list of all internal methods for a controller. This finds the first abstract
|
72
|
+
# superclass of a controller, and gets a list of all public instance methods on
|
73
|
+
# that abstract class. Public instance methods of a controller would normally be
|
74
|
+
# considered action methods, so methods declared on abstract classes are being
|
75
|
+
# removed. (ActionController::Metal and ActionController::Base are defined as
|
76
|
+
# abstract)
|
53
77
|
def internal_methods
|
54
78
|
controller = self
|
79
|
+
methods = []
|
55
80
|
|
56
|
-
|
57
|
-
|
58
|
-
|
81
|
+
until controller.abstract?
|
82
|
+
methods += controller.public_instance_methods(false)
|
83
|
+
controller = controller.superclass
|
84
|
+
end
|
59
85
|
|
60
|
-
|
61
|
-
# This can be modified by other modules or subclasses
|
62
|
-
# to specify particular actions as hidden.
|
63
|
-
#
|
64
|
-
# ==== Returns
|
65
|
-
# * <tt>Array</tt> - An array of method names that should not be considered actions.
|
66
|
-
def hidden_actions
|
67
|
-
[]
|
86
|
+
controller.public_instance_methods(true) - methods
|
68
87
|
end
|
69
88
|
|
70
|
-
# A list of method names that should be considered actions. This
|
71
|
-
#
|
72
|
-
#
|
73
|
-
#
|
74
|
-
#
|
89
|
+
# A list of method names that should be considered actions. This includes all
|
90
|
+
# public instance methods on a controller, less any internal methods (see
|
91
|
+
# internal_methods), adding back in any methods that are internal, but still
|
92
|
+
# exist on the class itself.
|
93
|
+
#
|
94
|
+
# #### Returns
|
95
|
+
# * `Set` - A set of all methods that should be considered actions.
|
75
96
|
#
|
76
|
-
# ==== Returns
|
77
|
-
# * <tt>Set</tt> - A set of all methods that should be considered actions.
|
78
97
|
def action_methods
|
79
98
|
@action_methods ||= begin
|
80
|
-
# All public instance methods of this class, including ancestors
|
81
|
-
methods
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
hidden_actions.to_a
|
88
|
-
|
89
|
-
# Clear out AS callback method pollution
|
90
|
-
Set.new(methods.reject { |method| method =~ /_one_time_conditions/ })
|
99
|
+
# All public instance methods of this class, including ancestors except for
|
100
|
+
# public instance methods of Base and its ancestors.
|
101
|
+
methods = public_instance_methods(true) - internal_methods
|
102
|
+
# Be sure to include shadowed public instance methods of this class.
|
103
|
+
methods.concat(public_instance_methods(false))
|
104
|
+
methods.map!(&:to_s)
|
105
|
+
methods.to_set
|
91
106
|
end
|
92
107
|
end
|
93
108
|
|
94
|
-
# action_methods are cached and there is sometimes need to refresh
|
95
|
-
#
|
96
|
-
#
|
109
|
+
# action_methods are cached and there is sometimes a need to refresh them.
|
110
|
+
# ::clear_action_methods! allows you to do that, so next time you run
|
111
|
+
# action_methods, they will be recalculated.
|
97
112
|
def clear_action_methods!
|
98
113
|
@action_methods = nil
|
99
114
|
end
|
100
115
|
|
101
116
|
# Returns the full controller name, underscored, without the ending Controller.
|
102
|
-
# For instance, MyApp::MyPostsController would return "my_app/my_posts" for
|
103
|
-
# controller_path.
|
104
117
|
#
|
105
|
-
#
|
106
|
-
#
|
118
|
+
# class MyApp::MyPostsController < AbstractController::Base
|
119
|
+
#
|
120
|
+
# end
|
121
|
+
#
|
122
|
+
# MyApp::MyPostsController.controller_path # => "my_app/my_posts"
|
123
|
+
#
|
124
|
+
# #### Returns
|
125
|
+
# * `String`
|
126
|
+
#
|
107
127
|
def controller_path
|
108
|
-
@controller_path ||= name.
|
128
|
+
@controller_path ||= name.delete_suffix("Controller").underscore unless anonymous?
|
109
129
|
end
|
110
130
|
|
111
131
|
# Refresh the cached action_methods when a new action_method is added.
|
@@ -113,146 +133,156 @@ module AbstractController
|
|
113
133
|
super
|
114
134
|
clear_action_methods!
|
115
135
|
end
|
136
|
+
|
137
|
+
def eager_load! # :nodoc:
|
138
|
+
action_methods
|
139
|
+
nil
|
140
|
+
end
|
116
141
|
end
|
117
142
|
|
118
143
|
abstract!
|
119
144
|
|
120
|
-
# Calls the action going through the entire
|
145
|
+
# Calls the action going through the entire Action Dispatch stack.
|
146
|
+
#
|
147
|
+
# The actual method that is called is determined by calling #method_for_action.
|
148
|
+
# If no method can handle the action, then an AbstractController::ActionNotFound
|
149
|
+
# error is raised.
|
121
150
|
#
|
122
|
-
#
|
123
|
-
#
|
124
|
-
# AbstractController::ActionNotFound error is raised.
|
151
|
+
# #### Returns
|
152
|
+
# * `self`
|
125
153
|
#
|
126
|
-
|
127
|
-
# * <tt>self</tt>
|
128
|
-
def process(action, *args)
|
154
|
+
def process(action, ...)
|
129
155
|
@_action_name = action.to_s
|
130
156
|
|
131
157
|
unless action_name = _find_action_name(@_action_name)
|
132
|
-
raise ActionNotFound
|
158
|
+
raise ActionNotFound.new("The action '#{action}' could not be found for #{self.class.name}", self, action)
|
133
159
|
end
|
134
160
|
|
135
161
|
@_response_body = nil
|
136
162
|
|
137
|
-
process_action(action_name,
|
163
|
+
process_action(action_name, ...)
|
138
164
|
end
|
139
165
|
|
140
|
-
# Delegates to the class'
|
166
|
+
# Delegates to the class's ::controller_path.
|
141
167
|
def controller_path
|
142
168
|
self.class.controller_path
|
143
169
|
end
|
144
170
|
|
145
|
-
# Delegates to the class'
|
171
|
+
# Delegates to the class's ::action_methods.
|
146
172
|
def action_methods
|
147
173
|
self.class.action_methods
|
148
174
|
end
|
149
175
|
|
150
|
-
# Returns true if a method for the action is available and
|
151
|
-
#
|
176
|
+
# Returns true if a method for the action is available and can be dispatched,
|
177
|
+
# false otherwise.
|
152
178
|
#
|
153
|
-
# Notice that
|
154
|
-
#
|
155
|
-
#
|
156
|
-
#
|
179
|
+
# Notice that `action_methods.include?("foo")` may return false and
|
180
|
+
# `available_action?("foo")` returns true because this method considers actions
|
181
|
+
# that are also available through other means, for example, implicit render
|
182
|
+
# ones.
|
157
183
|
#
|
158
|
-
#
|
159
|
-
# *
|
184
|
+
# #### Parameters
|
185
|
+
# * `action_name` - The name of an action to be tested
|
160
186
|
#
|
161
|
-
# ==== Returns
|
162
|
-
# * <tt>TrueClass</tt>, <tt>FalseClass</tt>
|
163
187
|
def available_action?(action_name)
|
164
|
-
_find_action_name(action_name)
|
188
|
+
_find_action_name(action_name)
|
189
|
+
end
|
190
|
+
|
191
|
+
# Tests if a response body is set. Used to determine if the `process_action`
|
192
|
+
# callback needs to be terminated in AbstractController::Callbacks.
|
193
|
+
def performed?
|
194
|
+
response_body
|
165
195
|
end
|
166
196
|
|
167
|
-
# Returns true if the given controller is capable of rendering
|
168
|
-
#
|
169
|
-
#
|
170
|
-
# support paths, only full URLs.
|
197
|
+
# Returns true if the given controller is capable of rendering a path. A
|
198
|
+
# subclass of `AbstractController::Base` may return false. An Email controller
|
199
|
+
# for example does not support paths, only full URLs.
|
171
200
|
def self.supports_path?
|
172
201
|
true
|
173
202
|
end
|
174
203
|
|
175
|
-
|
204
|
+
def inspect # :nodoc:
|
205
|
+
"#<#{self.class.name}:#{'%#016x' % (object_id << 1)}>"
|
206
|
+
end
|
176
207
|
|
177
|
-
|
178
|
-
# it has a method
|
179
|
-
#
|
180
|
-
# ==== Parameters
|
181
|
-
# * <tt>name</tt> - The name of an action to be tested
|
208
|
+
private
|
209
|
+
# Returns true if the name can be considered an action because it has a method
|
210
|
+
# defined in the controller.
|
182
211
|
#
|
183
|
-
#
|
184
|
-
# *
|
212
|
+
# #### Parameters
|
213
|
+
# * `name` - The name of an action to be tested
|
185
214
|
#
|
186
|
-
# :api: private
|
187
215
|
def action_method?(name)
|
188
216
|
self.class.action_methods.include?(name)
|
189
217
|
end
|
190
218
|
|
191
|
-
# Call the action. Override this in a subclass to modify the
|
192
|
-
#
|
193
|
-
#
|
219
|
+
# Call the action. Override this in a subclass to modify the behavior around
|
220
|
+
# processing an action. This, and not #process, is the intended way to override
|
221
|
+
# action dispatching.
|
194
222
|
#
|
195
|
-
# Notice that the first argument is the method to be dispatched
|
196
|
-
#
|
197
|
-
def process_action(
|
198
|
-
send_action(
|
223
|
+
# Notice that the first argument is the method to be dispatched which is **not**
|
224
|
+
# necessarily the same as the action name.
|
225
|
+
def process_action(...)
|
226
|
+
send_action(...)
|
199
227
|
end
|
200
228
|
|
201
|
-
# Actually call the method associated with the action. Override
|
202
|
-
#
|
203
|
-
#
|
204
|
-
#
|
205
|
-
# method.
|
229
|
+
# Actually call the method associated with the action. Override this method if
|
230
|
+
# you wish to change how action methods are called, not to add additional
|
231
|
+
# behavior around it. For example, you would override #send_action if you want
|
232
|
+
# to inject arguments into the method.
|
206
233
|
alias send_action send
|
207
234
|
|
208
|
-
# If the action name was not found, but a method called "action_missing"
|
209
|
-
#
|
210
|
-
#
|
235
|
+
# If the action name was not found, but a method called "action_missing" was
|
236
|
+
# found, #method_for_action will return "_handle_action_missing". This method
|
237
|
+
# calls #action_missing with the current action name.
|
211
238
|
def _handle_action_missing(*args)
|
212
239
|
action_missing(@_action_name, *args)
|
213
240
|
end
|
214
241
|
|
215
|
-
# Takes an action name and returns the name of the method that will
|
216
|
-
#
|
242
|
+
# Takes an action name and returns the name of the method that will handle the
|
243
|
+
# action.
|
217
244
|
#
|
218
245
|
# It checks if the action name is valid and returns false otherwise.
|
219
246
|
#
|
220
247
|
# See method_for_action for more information.
|
221
248
|
#
|
222
|
-
#
|
223
|
-
# *
|
249
|
+
# #### Parameters
|
250
|
+
# * `action_name` - An action name to find a method name for
|
224
251
|
#
|
225
|
-
#
|
226
|
-
#
|
227
|
-
# *
|
228
|
-
#
|
252
|
+
#
|
253
|
+
# #### Returns
|
254
|
+
# * `string` - The name of the method that handles the action
|
255
|
+
# * false - No valid method name could be found.
|
256
|
+
#
|
257
|
+
# Raise `AbstractController::ActionNotFound`.
|
229
258
|
def _find_action_name(action_name)
|
230
259
|
_valid_action_name?(action_name) && method_for_action(action_name)
|
231
260
|
end
|
232
261
|
|
233
|
-
# Takes an action name and returns the name of the method that will
|
234
|
-
#
|
235
|
-
#
|
236
|
-
#
|
237
|
-
#
|
262
|
+
# Takes an action name and returns the name of the method that will handle the
|
263
|
+
# action. In normal cases, this method returns the same name as it receives. By
|
264
|
+
# default, if #method_for_action receives a name that is not an action, it will
|
265
|
+
# look for an #action_missing method and return "_handle_action_missing" if one
|
266
|
+
# is found.
|
267
|
+
#
|
268
|
+
# Subclasses may override this method to add additional conditions that should
|
269
|
+
# be considered an action. For instance, an HTTP controller with a template
|
270
|
+
# matching the action name is considered to exist.
|
271
|
+
#
|
272
|
+
# If you override this method to handle additional cases, you may also provide a
|
273
|
+
# method (like `_handle_method_missing`) to handle the case.
|
238
274
|
#
|
239
|
-
#
|
240
|
-
#
|
241
|
-
# with a template matching the action name is considered to exist.
|
275
|
+
# If none of these conditions are true, and `method_for_action` returns `nil`,
|
276
|
+
# an `AbstractController::ActionNotFound` exception will be raised.
|
242
277
|
#
|
243
|
-
#
|
244
|
-
#
|
245
|
-
# the case.
|
278
|
+
# #### Parameters
|
279
|
+
# * `action_name` - An action name to find a method name for
|
246
280
|
#
|
247
|
-
# If none of these conditions are true, and method_for_action
|
248
|
-
# returns nil, an AbstractController::ActionNotFound exception will be raised.
|
249
281
|
#
|
250
|
-
#
|
251
|
-
# *
|
282
|
+
# #### Returns
|
283
|
+
# * `string` - The name of the method that handles the action
|
284
|
+
# * `nil` - No method name could be found.
|
252
285
|
#
|
253
|
-
# ==== Returns
|
254
|
-
# * <tt>string</tt> - The name of the method that handles the action
|
255
|
-
# * <tt>nil</tt> - No method name could be found.
|
256
286
|
def method_for_action(action_name)
|
257
287
|
if action_method?(action_name)
|
258
288
|
action_name
|
@@ -0,0 +1,149 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :markup: markdown
|
4
|
+
|
5
|
+
module AbstractController
|
6
|
+
module Caching
|
7
|
+
# # Abstract Controller Caching Fragments
|
8
|
+
#
|
9
|
+
# Fragment caching is used for caching various blocks within views without
|
10
|
+
# caching the entire action as a whole. This is useful when certain elements of
|
11
|
+
# an action change frequently or depend on complicated state while other parts
|
12
|
+
# rarely change or can be shared amongst multiple parties. The caching is done
|
13
|
+
# using the `cache` helper available in the Action View. See
|
14
|
+
# ActionView::Helpers::CacheHelper for more information.
|
15
|
+
#
|
16
|
+
# While it's strongly recommended that you use key-based cache expiration (see
|
17
|
+
# links in CacheHelper for more information), it is also possible to manually
|
18
|
+
# expire caches. For example:
|
19
|
+
#
|
20
|
+
# expire_fragment('name_of_cache')
|
21
|
+
module Fragments
|
22
|
+
extend ActiveSupport::Concern
|
23
|
+
|
24
|
+
included do
|
25
|
+
if respond_to?(:class_attribute)
|
26
|
+
class_attribute :fragment_cache_keys
|
27
|
+
else
|
28
|
+
mattr_writer :fragment_cache_keys
|
29
|
+
end
|
30
|
+
|
31
|
+
self.fragment_cache_keys = []
|
32
|
+
|
33
|
+
if respond_to?(:helper_method)
|
34
|
+
helper_method :combined_fragment_cache_key
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
module ClassMethods
|
39
|
+
# Allows you to specify controller-wide key prefixes for cache fragments. Pass
|
40
|
+
# either a constant `value`, or a block which computes a value each time a cache
|
41
|
+
# key is generated.
|
42
|
+
#
|
43
|
+
# For example, you may want to prefix all fragment cache keys with a global
|
44
|
+
# version identifier, so you can easily invalidate all caches.
|
45
|
+
#
|
46
|
+
# class ApplicationController
|
47
|
+
# fragment_cache_key "v1"
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# When it's time to invalidate all fragments, simply change the string constant.
|
51
|
+
# Or, progressively roll out the cache invalidation using a computed value:
|
52
|
+
#
|
53
|
+
# class ApplicationController
|
54
|
+
# fragment_cache_key do
|
55
|
+
# @account.id.odd? ? "v1" : "v2"
|
56
|
+
# end
|
57
|
+
# end
|
58
|
+
def fragment_cache_key(value = nil, &key)
|
59
|
+
self.fragment_cache_keys += [key || -> { value }]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Given a key (as described in `expire_fragment`), returns a key array suitable
|
64
|
+
# for use in reading, writing, or expiring a cached fragment. All keys begin
|
65
|
+
# with `:views`, followed by `ENV["RAILS_CACHE_ID"]` or
|
66
|
+
# `ENV["RAILS_APP_VERSION"]` if set, followed by any controller-wide key prefix
|
67
|
+
# values, ending with the specified `key` value.
|
68
|
+
def combined_fragment_cache_key(key)
|
69
|
+
head = self.class.fragment_cache_keys.map { |k| instance_exec(&k) }
|
70
|
+
tail = key.is_a?(Hash) ? url_for(key).split("://").last : key
|
71
|
+
|
72
|
+
cache_key = [:views, ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"], head, tail]
|
73
|
+
cache_key.flatten!(1)
|
74
|
+
cache_key.compact!
|
75
|
+
cache_key
|
76
|
+
end
|
77
|
+
|
78
|
+
# Writes `content` to the location signified by `key` (see `expire_fragment` for
|
79
|
+
# acceptable formats).
|
80
|
+
def write_fragment(key, content, options = nil)
|
81
|
+
return content unless cache_configured?
|
82
|
+
|
83
|
+
key = combined_fragment_cache_key(key)
|
84
|
+
instrument_fragment_cache :write_fragment, key do
|
85
|
+
content = content.to_str
|
86
|
+
cache_store.write(key, content, options)
|
87
|
+
end
|
88
|
+
content
|
89
|
+
end
|
90
|
+
|
91
|
+
# Reads a cached fragment from the location signified by `key` (see
|
92
|
+
# `expire_fragment` for acceptable formats).
|
93
|
+
def read_fragment(key, options = nil)
|
94
|
+
return unless cache_configured?
|
95
|
+
|
96
|
+
key = combined_fragment_cache_key(key)
|
97
|
+
instrument_fragment_cache :read_fragment, key do
|
98
|
+
result = cache_store.read(key, options)
|
99
|
+
result.respond_to?(:html_safe) ? result.html_safe : result
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Check if a cached fragment from the location signified by `key` exists (see
|
104
|
+
# `expire_fragment` for acceptable formats).
|
105
|
+
def fragment_exist?(key, options = nil)
|
106
|
+
return unless cache_configured?
|
107
|
+
key = combined_fragment_cache_key(key)
|
108
|
+
|
109
|
+
instrument_fragment_cache :exist_fragment?, key do
|
110
|
+
cache_store.exist?(key, options)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Removes fragments from the cache.
|
115
|
+
#
|
116
|
+
# `key` can take one of three forms:
|
117
|
+
#
|
118
|
+
# * String - This would normally take the form of a path, like
|
119
|
+
# `pages/45/notes`.
|
120
|
+
# * Hash - Treated as an implicit call to `url_for`, like `{ controller:
|
121
|
+
# 'pages', action: 'notes', id: 45}`
|
122
|
+
# * Regexp - Will remove any fragment that matches, so `%r{pages/\d*/notes}`
|
123
|
+
# might remove all notes. Make sure you don't use anchors in the regex (`^`
|
124
|
+
# or `$`) because the actual filename matched looks like
|
125
|
+
# `./cache/filename/path.cache`. Note: Regexp expiration is only supported
|
126
|
+
# on caches that can iterate over all keys (unlike memcached).
|
127
|
+
#
|
128
|
+
#
|
129
|
+
# `options` is passed through to the cache store's `delete` method (or
|
130
|
+
# `delete_matched`, for Regexp keys).
|
131
|
+
def expire_fragment(key, options = nil)
|
132
|
+
return unless cache_configured?
|
133
|
+
key = combined_fragment_cache_key(key) unless key.is_a?(Regexp)
|
134
|
+
|
135
|
+
instrument_fragment_cache :expire_fragment, key do
|
136
|
+
if key.is_a?(Regexp)
|
137
|
+
cache_store.delete_matched(key, options)
|
138
|
+
else
|
139
|
+
cache_store.delete(key, options)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def instrument_fragment_cache(name, key, &block) # :nodoc:
|
145
|
+
ActiveSupport::Notifications.instrument("#{name}.#{instrument_name}", instrument_payload(key), &block)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|