actionpack 5.2.1 → 7.0.2.4
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +264 -220
- data/MIT-LICENSE +1 -1
- data/README.rdoc +6 -6
- data/lib/abstract_controller/asset_paths.rb +1 -1
- data/lib/abstract_controller/base.rb +24 -4
- data/lib/abstract_controller/caching/fragments.rb +8 -24
- data/lib/abstract_controller/caching.rb +2 -2
- data/lib/abstract_controller/callbacks.rb +34 -8
- data/lib/abstract_controller/collector.rb +5 -4
- data/lib/abstract_controller/error.rb +1 -1
- data/lib/abstract_controller/helpers.rb +107 -90
- data/lib/abstract_controller/logger.rb +1 -1
- data/lib/abstract_controller/railties/routes_helpers.rb +19 -1
- data/lib/abstract_controller/rendering.rb +9 -9
- data/lib/abstract_controller/translation.rb +12 -5
- data/lib/abstract_controller/url_for.rb +4 -6
- data/lib/abstract_controller.rb +2 -0
- data/lib/action_controller/api.rb +5 -4
- data/lib/action_controller/base.rb +6 -9
- data/lib/action_controller/caching.rb +1 -3
- data/lib/action_controller/log_subscriber.rb +13 -9
- data/lib/action_controller/metal/basic_implicit_render.rb +1 -1
- data/lib/action_controller/metal/conditional_get.rb +57 -6
- data/lib/action_controller/metal/content_security_policy.rb +2 -3
- data/lib/action_controller/metal/cookies.rb +4 -2
- data/lib/action_controller/metal/data_streaming.rb +9 -18
- data/lib/action_controller/metal/default_headers.rb +17 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +4 -6
- data/lib/action_controller/metal/exceptions.rb +55 -12
- data/lib/action_controller/metal/flash.rb +10 -6
- data/lib/action_controller/metal/head.rb +7 -4
- data/lib/action_controller/metal/helpers.rb +15 -6
- data/lib/action_controller/metal/http_authentication.rb +41 -39
- data/lib/action_controller/metal/implicit_render.rb +5 -15
- data/lib/action_controller/metal/instrumentation.rb +59 -55
- data/lib/action_controller/metal/live.rb +80 -33
- data/lib/action_controller/metal/logging.rb +20 -0
- data/lib/action_controller/metal/mime_responds.rb +22 -7
- data/lib/action_controller/metal/parameter_encoding.rb +35 -4
- data/lib/action_controller/metal/params_wrapper.rb +50 -31
- data/lib/action_controller/metal/permissions_policy.rb +46 -0
- data/lib/action_controller/metal/redirecting.rb +93 -23
- data/lib/action_controller/metal/renderers.rb +4 -4
- data/lib/action_controller/metal/rendering.rb +14 -9
- data/lib/action_controller/metal/request_forgery_protection.rb +160 -58
- data/lib/action_controller/metal/rescue.rb +2 -2
- data/lib/action_controller/metal/streaming.rb +1 -4
- data/lib/action_controller/metal/strong_parameters.rb +236 -88
- data/lib/action_controller/metal/testing.rb +9 -2
- data/lib/action_controller/metal/url_for.rb +1 -1
- data/lib/action_controller/metal.rb +16 -17
- data/lib/action_controller/railtie.rb +49 -6
- data/lib/action_controller/railties/helpers.rb +1 -1
- data/lib/action_controller/renderer.rb +37 -13
- data/lib/action_controller/template_assertions.rb +1 -1
- data/lib/action_controller/test_case.rb +98 -68
- data/lib/action_controller.rb +4 -5
- data/lib/action_dispatch/http/cache.rb +45 -32
- data/lib/action_dispatch/http/content_disposition.rb +45 -0
- data/lib/action_dispatch/http/content_security_policy.rb +69 -56
- data/lib/action_dispatch/http/filter_parameters.rb +14 -8
- data/lib/action_dispatch/http/filter_redirect.rb +2 -3
- data/lib/action_dispatch/http/headers.rb +4 -4
- data/lib/action_dispatch/http/mime_negotiation.rb +44 -16
- data/lib/action_dispatch/http/mime_type.rb +47 -30
- data/lib/action_dispatch/http/parameters.rb +18 -27
- data/lib/action_dispatch/http/permissions_policy.rb +173 -0
- data/lib/action_dispatch/http/request.rb +49 -35
- data/lib/action_dispatch/http/response.rb +34 -26
- data/lib/action_dispatch/http/upload.rb +9 -1
- data/lib/action_dispatch/http/url.rb +86 -94
- data/lib/action_dispatch/journey/formatter.rb +55 -31
- data/lib/action_dispatch/journey/gtg/builder.rb +30 -46
- data/lib/action_dispatch/journey/gtg/simulator.rb +15 -8
- data/lib/action_dispatch/journey/gtg/transition_table.rb +78 -21
- data/lib/action_dispatch/journey/nfa/dot.rb +0 -11
- data/lib/action_dispatch/journey/nodes/node.rb +83 -16
- data/lib/action_dispatch/journey/parser.rb +13 -13
- data/lib/action_dispatch/journey/parser.y +1 -1
- data/lib/action_dispatch/journey/path/pattern.rb +42 -34
- data/lib/action_dispatch/journey/route.rb +14 -31
- data/lib/action_dispatch/journey/router/utils.rb +16 -14
- data/lib/action_dispatch/journey/router.rb +27 -35
- data/lib/action_dispatch/journey/routes.rb +3 -5
- data/lib/action_dispatch/journey/scanner.rb +10 -4
- data/lib/action_dispatch/journey/visitors.rb +1 -4
- 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 +0 -2
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +45 -0
- data/lib/action_dispatch/middleware/callbacks.rb +2 -4
- data/lib/action_dispatch/middleware/cookies.rb +136 -113
- data/lib/action_dispatch/middleware/debug_exceptions.rb +47 -68
- data/lib/action_dispatch/middleware/debug_locks.rb +8 -8
- data/lib/action_dispatch/middleware/debug_view.rb +66 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +79 -30
- data/lib/action_dispatch/middleware/executor.rb +4 -1
- data/lib/action_dispatch/middleware/flash.rb +10 -12
- data/lib/action_dispatch/middleware/host_authorization.rb +159 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +6 -3
- data/lib/action_dispatch/middleware/remote_ip.rb +30 -20
- data/lib/action_dispatch/middleware/request_id.rb +5 -6
- data/lib/action_dispatch/middleware/server_timing.rb +33 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +16 -3
- data/lib/action_dispatch/middleware/session/cache_store.rb +11 -6
- data/lib/action_dispatch/middleware/session/cookie_store.rb +24 -19
- data/lib/action_dispatch/middleware/show_exceptions.rb +20 -11
- data/lib/action_dispatch/middleware/ssl.rb +20 -15
- data/lib/action_dispatch/middleware/stack.rb +79 -7
- data/lib/action_dispatch/middleware/static.rb +150 -94
- 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 +6 -11
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +4 -2
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +46 -36
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +7 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +25 -6
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +9 -6
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +4 -1
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +121 -15
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -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 +5 -5
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +5 -5
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +16 -2
- data/lib/action_dispatch/railtie.rb +16 -4
- data/lib/action_dispatch/request/session.rb +59 -22
- data/lib/action_dispatch/request/utils.rb +28 -2
- data/lib/action_dispatch/routing/inspector.rb +102 -54
- data/lib/action_dispatch/routing/mapper.rb +184 -156
- data/lib/action_dispatch/routing/polymorphic_routes.rb +21 -19
- data/lib/action_dispatch/routing/redirection.rb +4 -6
- data/lib/action_dispatch/routing/route_set.rb +83 -73
- data/lib/action_dispatch/routing/routes_proxy.rb +1 -1
- data/lib/action_dispatch/routing/url_for.rb +2 -3
- data/lib/action_dispatch/routing.rb +23 -22
- data/lib/action_dispatch/system_test_case.rb +65 -16
- data/lib/action_dispatch/system_testing/browser.rb +43 -16
- data/lib/action_dispatch/system_testing/driver.rb +42 -10
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +58 -12
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +3 -10
- data/lib/action_dispatch/testing/assertion_response.rb +0 -1
- data/lib/action_dispatch/testing/assertions/response.rb +4 -7
- data/lib/action_dispatch/testing/assertions/routing.rb +20 -8
- data/lib/action_dispatch/testing/assertions.rb +3 -6
- data/lib/action_dispatch/testing/integration.rb +61 -30
- data/lib/action_dispatch/testing/request_encoder.rb +2 -2
- data/lib/action_dispatch/testing/test_process.rb +8 -6
- data/lib/action_dispatch/testing/test_request.rb +3 -3
- data/lib/action_dispatch/testing/test_response.rb +4 -32
- data/lib/action_dispatch.rb +15 -7
- data/lib/action_pack/gem_version.rb +4 -4
- data/lib/action_pack.rb +1 -1
- metadata +44 -25
- data/lib/action_controller/metal/force_ssl.rb +0 -99
- data/lib/action_dispatch/http/parameter_filter.rb +0 -86
- data/lib/action_dispatch/journey/nfa/builder.rb +0 -78
- data/lib/action_dispatch/journey/nfa/simulator.rb +0 -49
- data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -120
- data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +0 -26
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
|
|
@@ -23,6 +22,7 @@ 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
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
|
|
@@ -32,7 +32,7 @@ The latest version of Action Pack can be installed with RubyGems:
|
|
32
32
|
|
33
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
|
@@ -46,7 +46,7 @@ Action Pack is released under the MIT license:
|
|
46
46
|
|
47
47
|
API documentation is at:
|
48
48
|
|
49
|
-
*
|
49
|
+
* https://api.rubyonrails.org
|
50
50
|
|
51
51
|
Bug reports for the Ruby on Rails project can be filed here:
|
52
52
|
|
@@ -54,4 +54,4 @@ Bug reports for the Ruby on Rails project can be filed here:
|
|
54
54
|
|
55
55
|
Feature requests should be discussed on the rails-core mailing list here:
|
56
56
|
|
57
|
-
* https://
|
57
|
+
* https://discuss.rubyonrails.org/c/rubyonrails-core
|
@@ -9,6 +9,21 @@ require "active_support/core_ext/module/attr_internal"
|
|
9
9
|
module AbstractController
|
10
10
|
# Raised when a non-existing controller action is triggered.
|
11
11
|
class ActionNotFound < StandardError
|
12
|
+
attr_reader :controller, :action # :nodoc:
|
13
|
+
|
14
|
+
def initialize(message = nil, controller = nil, action = nil) # :nodoc:
|
15
|
+
@controller = controller
|
16
|
+
@action = action
|
17
|
+
super(message)
|
18
|
+
end
|
19
|
+
|
20
|
+
if defined?(DidYouMean::Correctable) && defined?(DidYouMean::SpellChecker)
|
21
|
+
include DidYouMean::Correctable # :nodoc:
|
22
|
+
|
23
|
+
def corrections # :nodoc:
|
24
|
+
@corrections ||= DidYouMean::SpellChecker.new(dictionary: controller.class.action_methods).correct(action)
|
25
|
+
end
|
26
|
+
end
|
12
27
|
end
|
13
28
|
|
14
29
|
# AbstractController::Base is a low-level API. Nobody should be
|
@@ -78,7 +93,9 @@ module AbstractController
|
|
78
93
|
# Except for public instance methods of Base and its ancestors
|
79
94
|
internal_methods +
|
80
95
|
# Be sure to include shadowed public instance methods of this class
|
81
|
-
public_instance_methods(false))
|
96
|
+
public_instance_methods(false))
|
97
|
+
|
98
|
+
methods.map!(&:to_s)
|
82
99
|
|
83
100
|
methods.to_set
|
84
101
|
end
|
@@ -102,7 +119,7 @@ module AbstractController
|
|
102
119
|
# ==== Returns
|
103
120
|
# * <tt>String</tt>
|
104
121
|
def controller_path
|
105
|
-
@controller_path ||= name.
|
122
|
+
@controller_path ||= name.delete_suffix("Controller").underscore unless anonymous?
|
106
123
|
end
|
107
124
|
|
108
125
|
# Refresh the cached action_methods when a new action_method is added.
|
@@ -126,7 +143,7 @@ module AbstractController
|
|
126
143
|
@_action_name = action.to_s
|
127
144
|
|
128
145
|
unless action_name = _find_action_name(@_action_name)
|
129
|
-
raise ActionNotFound
|
146
|
+
raise ActionNotFound.new("The action '#{action}' could not be found for #{self.class.name}", self, action)
|
130
147
|
end
|
131
148
|
|
132
149
|
@_response_body = nil
|
@@ -173,8 +190,11 @@ module AbstractController
|
|
173
190
|
true
|
174
191
|
end
|
175
192
|
|
176
|
-
|
193
|
+
def inspect # :nodoc:
|
194
|
+
"#<#{self.class.name}:#{'%#016x' % (object_id << 1)}>"
|
195
|
+
end
|
177
196
|
|
197
|
+
private
|
178
198
|
# Returns true if the name can be considered an action because
|
179
199
|
# it has a method defined in the controller.
|
180
200
|
#
|
@@ -28,7 +28,6 @@ module AbstractController
|
|
28
28
|
self.fragment_cache_keys = []
|
29
29
|
|
30
30
|
if respond_to?(:helper_method)
|
31
|
-
helper_method :fragment_cache_key
|
32
31
|
helper_method :combined_fragment_cache_key
|
33
32
|
end
|
34
33
|
end
|
@@ -60,35 +59,20 @@ module AbstractController
|
|
60
59
|
end
|
61
60
|
end
|
62
61
|
|
63
|
-
# Given a key (as described in +expire_fragment+), returns
|
64
|
-
# a key suitable for use in reading, writing, or expiring a
|
65
|
-
# cached fragment. All keys begin with <tt>views/</tt>,
|
66
|
-
# followed by any controller-wide key prefix values, ending
|
67
|
-
# with the specified +key+ value. The key is expanded using
|
68
|
-
# ActiveSupport::Cache.expand_cache_key.
|
69
|
-
def fragment_cache_key(key)
|
70
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
71
|
-
Calling fragment_cache_key directly is deprecated and will be removed in Rails 6.0.
|
72
|
-
All fragment accessors now use the combined_fragment_cache_key method that retains the key as an array,
|
73
|
-
such that the caching stores can interrogate the parts for cache versions used in
|
74
|
-
recyclable cache keys.
|
75
|
-
MSG
|
76
|
-
|
77
|
-
head = self.class.fragment_cache_keys.map { |k| instance_exec(&k) }
|
78
|
-
tail = key.is_a?(Hash) ? url_for(key).split("://").last : key
|
79
|
-
ActiveSupport::Cache.expand_cache_key([*head, *tail], :views)
|
80
|
-
end
|
81
|
-
|
82
62
|
# Given a key (as described in +expire_fragment+), returns
|
83
63
|
# a key array suitable for use in reading, writing, or expiring a
|
84
64
|
# cached fragment. All keys begin with <tt>:views</tt>,
|
85
|
-
# followed by ENV["RAILS_CACHE_ID"] or ENV["RAILS_APP_VERSION"] if set,
|
65
|
+
# followed by <tt>ENV["RAILS_CACHE_ID"]</tt> or <tt>ENV["RAILS_APP_VERSION"]</tt> if set,
|
86
66
|
# followed by any controller-wide key prefix values, ending
|
87
67
|
# with the specified +key+ value.
|
88
68
|
def combined_fragment_cache_key(key)
|
89
69
|
head = self.class.fragment_cache_keys.map { |k| instance_exec(&k) }
|
90
70
|
tail = key.is_a?(Hash) ? url_for(key).split("://").last : key
|
91
|
-
|
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
|
92
76
|
end
|
93
77
|
|
94
78
|
# Writes +content+ to the location signified by
|
@@ -158,8 +142,8 @@ module AbstractController
|
|
158
142
|
end
|
159
143
|
end
|
160
144
|
|
161
|
-
def instrument_fragment_cache(name, key) # :nodoc:
|
162
|
-
ActiveSupport::Notifications.instrument("#{name}.#{instrument_name}", instrument_payload(key))
|
145
|
+
def instrument_fragment_cache(name, key, &block) # :nodoc:
|
146
|
+
ActiveSupport::Notifications.instrument("#{name}.#{instrument_name}", instrument_payload(key), &block)
|
163
147
|
end
|
164
148
|
end
|
165
149
|
end
|
@@ -15,7 +15,7 @@ module AbstractController
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def cache_store=(store)
|
18
|
-
config.cache_store = ActiveSupport::Cache.lookup_store(store)
|
18
|
+
config.cache_store = ActiveSupport::Cache.lookup_store(*store)
|
19
19
|
end
|
20
20
|
|
21
21
|
private
|
@@ -50,7 +50,7 @@ module AbstractController
|
|
50
50
|
end
|
51
51
|
|
52
52
|
def view_cache_dependencies
|
53
|
-
self.class._view_cache_dependencies.
|
53
|
+
self.class._view_cache_dependencies.filter_map { |dep| instance_exec(&dep) }
|
54
54
|
end
|
55
55
|
|
56
56
|
private
|
@@ -35,12 +35,18 @@ module AbstractController
|
|
35
35
|
skip_after_callbacks_if_terminated: true
|
36
36
|
end
|
37
37
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
run_callbacks(:process_action) do
|
42
|
-
super
|
38
|
+
class ActionFilter # :nodoc:
|
39
|
+
def initialize(actions)
|
40
|
+
@actions = Array(actions).map(&:to_s).to_set
|
43
41
|
end
|
42
|
+
|
43
|
+
def match?(controller)
|
44
|
+
@actions.include?(controller.action_name)
|
45
|
+
end
|
46
|
+
|
47
|
+
alias after match?
|
48
|
+
alias before match?
|
49
|
+
alias around match?
|
44
50
|
end
|
45
51
|
|
46
52
|
module ClassMethods
|
@@ -69,9 +75,8 @@ module AbstractController
|
|
69
75
|
end
|
70
76
|
|
71
77
|
def _normalize_callback_option(options, from, to) # :nodoc:
|
72
|
-
if from = options
|
73
|
-
|
74
|
-
from = proc { |c| _from.include? c.action_name }
|
78
|
+
if from = options.delete(from)
|
79
|
+
from = ActionFilter.new(from)
|
75
80
|
options[to] = Array(options[to]).unshift(from)
|
76
81
|
end
|
77
82
|
end
|
@@ -103,6 +108,10 @@ module AbstractController
|
|
103
108
|
# :call-seq: before_action(names, block)
|
104
109
|
#
|
105
110
|
# Append a callback before actions. See _insert_callbacks for parameter details.
|
111
|
+
#
|
112
|
+
# If the callback renders or redirects, the action will not run. If there
|
113
|
+
# are additional callbacks scheduled to run after that callback, they are
|
114
|
+
# also cancelled.
|
106
115
|
|
107
116
|
##
|
108
117
|
# :method: prepend_before_action
|
@@ -110,6 +119,10 @@ module AbstractController
|
|
110
119
|
# :call-seq: prepend_before_action(names, block)
|
111
120
|
#
|
112
121
|
# Prepend a callback before actions. See _insert_callbacks for parameter details.
|
122
|
+
#
|
123
|
+
# If the callback renders or redirects, the action will not run. If there
|
124
|
+
# are additional callbacks scheduled to run after that callback, they are
|
125
|
+
# also cancelled.
|
113
126
|
|
114
127
|
##
|
115
128
|
# :method: skip_before_action
|
@@ -124,6 +137,10 @@ module AbstractController
|
|
124
137
|
# :call-seq: append_before_action(names, block)
|
125
138
|
#
|
126
139
|
# Append a callback before actions. See _insert_callbacks for parameter details.
|
140
|
+
#
|
141
|
+
# If the callback renders or redirects, the action will not run. If there
|
142
|
+
# are additional callbacks scheduled to run after that callback, they are
|
143
|
+
# also cancelled.
|
127
144
|
|
128
145
|
##
|
129
146
|
# :method: after_action
|
@@ -208,5 +225,14 @@ module AbstractController
|
|
208
225
|
alias_method :"append_#{callback}_action", :"#{callback}_action"
|
209
226
|
end
|
210
227
|
end
|
228
|
+
|
229
|
+
private
|
230
|
+
# Override <tt>AbstractController::Base#process_action</tt> to run the
|
231
|
+
# <tt>process_action</tt> callbacks around the normal behavior.
|
232
|
+
def process_action(*)
|
233
|
+
run_callbacks(:process_action) do
|
234
|
+
super
|
235
|
+
end
|
236
|
+
end
|
211
237
|
end
|
212
238
|
end
|
@@ -10,6 +10,7 @@ module AbstractController
|
|
10
10
|
def #{sym}(*args, &block)
|
11
11
|
custom(Mime[:#{sym}], *args, &block)
|
12
12
|
end
|
13
|
+
ruby2_keywords(:#{sym})
|
13
14
|
RUBY
|
14
15
|
end
|
15
16
|
|
@@ -22,11 +23,10 @@ module AbstractController
|
|
22
23
|
end
|
23
24
|
|
24
25
|
private
|
25
|
-
|
26
|
-
def method_missing(symbol, &block)
|
26
|
+
def method_missing(symbol, *args, &block)
|
27
27
|
unless mime_constant = Mime[symbol]
|
28
28
|
raise NoMethodError, "To respond to a custom format, register it as a MIME type first: " \
|
29
|
-
"
|
29
|
+
"https://guides.rubyonrails.org/action_controller_overview.html#restful-downloads. " \
|
30
30
|
"If you meant to respond to a variant like :tablet or :phone, not a custom format, " \
|
31
31
|
"be sure to nest your variant response within a format response: " \
|
32
32
|
"format.html { |html| html.tablet { ... } }"
|
@@ -34,10 +34,11 @@ module AbstractController
|
|
34
34
|
|
35
35
|
if Mime::SET.include?(mime_constant)
|
36
36
|
AbstractController::Collector.generate_method_for_mime(mime_constant)
|
37
|
-
|
37
|
+
public_send(symbol, *args, &block)
|
38
38
|
else
|
39
39
|
super
|
40
40
|
end
|
41
41
|
end
|
42
|
+
ruby2_keywords(:method_missing)
|
42
43
|
end
|
43
44
|
end
|
@@ -1,14 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_support/dependencies"
|
4
|
+
require "active_support/core_ext/name_error"
|
4
5
|
|
5
6
|
module AbstractController
|
6
7
|
module Helpers
|
7
8
|
extend ActiveSupport::Concern
|
8
9
|
|
9
10
|
included do
|
10
|
-
class_attribute :_helpers, default: Module.new
|
11
11
|
class_attribute :_helper_methods, default: Array.new
|
12
|
+
|
13
|
+
# This is here so that it is always higher in the inheritance chain than
|
14
|
+
# the definition in lib/action_view/rendering.rb
|
15
|
+
redefine_singleton_method(:_helpers) do
|
16
|
+
if @_helpers ||= nil
|
17
|
+
@_helpers
|
18
|
+
else
|
19
|
+
superclass._helpers
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
self._helpers = define_helpers_module(self)
|
12
24
|
end
|
13
25
|
|
14
26
|
class MissingHelperError < LoadError
|
@@ -17,7 +29,7 @@ module AbstractController
|
|
17
29
|
@path = "helpers/#{path}.rb"
|
18
30
|
set_backtrace error.backtrace
|
19
31
|
|
20
|
-
if
|
32
|
+
if /^#{path}(\.rb)?$/.match?(error.path)
|
21
33
|
super("Missing helper file helpers/%s.rb" % path)
|
22
34
|
else
|
23
35
|
raise error
|
@@ -25,17 +37,24 @@ module AbstractController
|
|
25
37
|
end
|
26
38
|
end
|
27
39
|
|
40
|
+
def _helpers
|
41
|
+
self.class._helpers
|
42
|
+
end
|
43
|
+
|
28
44
|
module ClassMethods
|
29
45
|
# When a class is inherited, wrap its helper module in a new module.
|
30
46
|
# This ensures that the parent class's module can be changed
|
31
47
|
# independently of the child class's.
|
32
48
|
def inherited(klass)
|
33
|
-
|
34
|
-
klass._helpers =
|
49
|
+
# Inherited from parent by default
|
50
|
+
klass._helpers = nil
|
51
|
+
|
35
52
|
klass.class_eval { default_helper_module! } unless klass.anonymous?
|
36
53
|
super
|
37
54
|
end
|
38
55
|
|
56
|
+
attr_writer :_helpers
|
57
|
+
|
39
58
|
# Declare a controller method as a helper. For example, the following
|
40
59
|
# makes the +current_user+ and +logged_in?+ controller methods available
|
41
60
|
# to the view:
|
@@ -57,59 +76,81 @@ module AbstractController
|
|
57
76
|
# ==== Parameters
|
58
77
|
# * <tt>method[, method]</tt> - A name or names of a method on the controller
|
59
78
|
# to be made available on the view.
|
60
|
-
def helper_method(*
|
61
|
-
|
62
|
-
self._helper_methods +=
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
79
|
+
def helper_method(*methods)
|
80
|
+
methods.flatten!
|
81
|
+
self._helper_methods += methods
|
82
|
+
|
83
|
+
location = caller_locations(1, 1).first
|
84
|
+
file, line = location.path, location.lineno
|
85
|
+
|
86
|
+
methods.each do |method|
|
87
|
+
_helpers_for_modification.class_eval <<~ruby_eval, file, line
|
88
|
+
def #{method}(*args, &block) # def current_user(*args, &block)
|
89
|
+
controller.send(:'#{method}', *args, &block) # controller.send(:'current_user', *args, &block)
|
90
|
+
end # end
|
91
|
+
ruby2_keywords(:'#{method}')
|
69
92
|
ruby_eval
|
70
93
|
end
|
71
94
|
end
|
72
95
|
|
73
|
-
#
|
96
|
+
# Includes the given modules in the template class.
|
97
|
+
#
|
98
|
+
# Modules can be specified in different ways. All of the following calls
|
99
|
+
# include +FooHelper+:
|
100
|
+
#
|
101
|
+
# # Module, recommended.
|
102
|
+
# helper FooHelper
|
103
|
+
#
|
104
|
+
# # String/symbol without the "helper" suffix, camel or snake case.
|
105
|
+
# helper "Foo"
|
106
|
+
# helper :Foo
|
107
|
+
# helper "foo"
|
108
|
+
# helper :foo
|
109
|
+
#
|
110
|
+
# The last two assume that <tt>"foo".camelize</tt> returns "Foo".
|
74
111
|
#
|
75
|
-
#
|
76
|
-
#
|
77
|
-
#
|
112
|
+
# When strings or symbols are passed, the method finds the actual module
|
113
|
+
# object using +String#constantize+. Therefore, if the module has not been
|
114
|
+
# yet loaded, it has to be autoloadable, which is normally the case.
|
78
115
|
#
|
79
|
-
#
|
80
|
-
# helper FooHelper # => includes FooHelper
|
116
|
+
# Namespaces are supported. The following calls include +Foo::BarHelper+:
|
81
117
|
#
|
82
|
-
#
|
83
|
-
#
|
84
|
-
# when working with namespaced controllers, or other cases where the file containing the helper definition is not
|
85
|
-
# in one of Rails' standard load paths:
|
86
|
-
# helper :foo # => requires 'foo_helper' and includes FooHelper
|
87
|
-
# helper 'resources/foo' # => requires 'resources/foo_helper' and includes Resources::FooHelper
|
118
|
+
# # Module, recommended.
|
119
|
+
# helper Foo::BarHelper
|
88
120
|
#
|
89
|
-
#
|
90
|
-
#
|
121
|
+
# # String/symbol without the "helper" suffix, camel or snake case.
|
122
|
+
# helper "Foo::Bar"
|
123
|
+
# helper :"Foo::Bar"
|
124
|
+
# helper "foo/bar"
|
125
|
+
# helper :"foo/bar"
|
91
126
|
#
|
92
|
-
#
|
93
|
-
#
|
127
|
+
# The last two assume that <tt>"foo/bar".camelize</tt> returns "Foo::Bar".
|
128
|
+
#
|
129
|
+
# The method accepts a block too. If present, the block is evaluated in
|
130
|
+
# the context of the controller helper module. This simple call makes the
|
131
|
+
# +wadus+ method available in templates of the enclosing controller:
|
94
132
|
#
|
95
|
-
# # Multi-line
|
96
133
|
# helper do
|
97
|
-
# def
|
98
|
-
# "
|
134
|
+
# def wadus
|
135
|
+
# "wadus"
|
99
136
|
# end
|
100
137
|
# end
|
101
138
|
#
|
102
|
-
#
|
103
|
-
# +symbols+, +strings+, +modules+ and blocks.
|
139
|
+
# Furthermore, all the above styles can be mixed together:
|
104
140
|
#
|
105
|
-
# helper
|
141
|
+
# helper FooHelper, "woo", "bar/baz" do
|
142
|
+
# def wadus
|
143
|
+
# "wadus"
|
144
|
+
# end
|
145
|
+
# end
|
106
146
|
#
|
107
147
|
def helper(*args, &block)
|
108
148
|
modules_for_helpers(args).each do |mod|
|
109
|
-
|
149
|
+
next if _helpers.include?(mod)
|
150
|
+
_helpers_for_modification.include(mod)
|
110
151
|
end
|
111
152
|
|
112
|
-
|
153
|
+
_helpers_for_modification.module_eval(&block) if block_given?
|
113
154
|
end
|
114
155
|
|
115
156
|
# Clears up all existing helpers in this class, only keeping the helper
|
@@ -123,71 +164,47 @@ module AbstractController
|
|
123
164
|
default_helper_module! unless anonymous?
|
124
165
|
end
|
125
166
|
|
126
|
-
#
|
127
|
-
#
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
#
|
132
|
-
# Module:: No further processing
|
133
|
-
#
|
134
|
-
# After loading the appropriate files, the corresponding modules
|
135
|
-
# are returned.
|
136
|
-
#
|
137
|
-
# ==== Parameters
|
138
|
-
# * <tt>args</tt> - An array of helpers
|
139
|
-
#
|
140
|
-
# ==== Returns
|
141
|
-
# * <tt>Array</tt> - A normalized list of modules for the list of
|
142
|
-
# helpers provided.
|
143
|
-
def modules_for_helpers(args)
|
144
|
-
args.flatten.map! do |arg|
|
145
|
-
case arg
|
146
|
-
when String, Symbol
|
147
|
-
file_name = "#{arg.to_s.underscore}_helper"
|
148
|
-
begin
|
149
|
-
require_dependency(file_name)
|
150
|
-
rescue LoadError => e
|
151
|
-
raise AbstractController::Helpers::MissingHelperError.new(e, file_name)
|
152
|
-
end
|
153
|
-
|
154
|
-
mod_name = file_name.camelize
|
155
|
-
begin
|
156
|
-
mod_name.constantize
|
157
|
-
rescue LoadError
|
158
|
-
# dependencies.rb gives a similar error message but its wording is
|
159
|
-
# not as clear because it mentions autoloading. To the user all it
|
160
|
-
# matters is that a helper module couldn't be loaded, autoloading
|
161
|
-
# is an internal mechanism that should not leak.
|
162
|
-
raise NameError, "Couldn't find #{mod_name}, expected it to be defined in helpers/#{file_name}.rb"
|
163
|
-
end
|
167
|
+
# Given an array of values like the ones accepted by +helper+, this method
|
168
|
+
# returns an array with the corresponding modules, in the same order.
|
169
|
+
def modules_for_helpers(modules_or_helper_prefixes)
|
170
|
+
modules_or_helper_prefixes.flatten.map! do |module_or_helper_prefix|
|
171
|
+
case module_or_helper_prefix
|
164
172
|
when Module
|
165
|
-
|
173
|
+
module_or_helper_prefix
|
174
|
+
when String, Symbol
|
175
|
+
helper_prefix = module_or_helper_prefix.to_s
|
176
|
+
helper_prefix = helper_prefix.camelize unless helper_prefix.start_with?(/[A-Z]/)
|
177
|
+
"#{helper_prefix}Helper".constantize
|
166
178
|
else
|
167
179
|
raise ArgumentError, "helper must be a String, Symbol, or Module"
|
168
180
|
end
|
169
181
|
end
|
170
182
|
end
|
171
183
|
|
184
|
+
def _helpers_for_modification
|
185
|
+
unless @_helpers
|
186
|
+
self._helpers = define_helpers_module(self, superclass._helpers)
|
187
|
+
end
|
188
|
+
_helpers
|
189
|
+
end
|
190
|
+
|
172
191
|
private
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
192
|
+
def define_helpers_module(klass, helpers = nil)
|
193
|
+
# In some tests inherited is called explicitly. In that case, just
|
194
|
+
# return the module from the first time it was defined
|
195
|
+
return klass.const_get(:HelperMethods) if klass.const_defined?(:HelperMethods, false)
|
196
|
+
|
197
|
+
mod = Module.new
|
198
|
+
klass.const_set(:HelperMethods, mod)
|
199
|
+
mod.include(helpers) if helpers
|
200
|
+
mod
|
181
201
|
end
|
182
202
|
|
183
203
|
def default_helper_module!
|
184
|
-
|
185
|
-
|
186
|
-
helper module_path
|
187
|
-
rescue LoadError => e
|
188
|
-
raise e unless e.is_missing? "helpers/#{module_path}_helper"
|
204
|
+
helper_prefix = name.delete_suffix("Controller")
|
205
|
+
helper(helper_prefix)
|
189
206
|
rescue NameError => e
|
190
|
-
raise
|
207
|
+
raise unless e.missing_name?("#{helper_prefix}Helper")
|
191
208
|
end
|
192
209
|
end
|
193
210
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/core_ext/module/introspection"
|
4
|
+
|
3
5
|
module AbstractController
|
4
6
|
module Railties
|
5
7
|
module RoutesHelpers
|
@@ -7,11 +9,27 @@ module AbstractController
|
|
7
9
|
Module.new do
|
8
10
|
define_method(:inherited) do |klass|
|
9
11
|
super(klass)
|
10
|
-
|
12
|
+
|
13
|
+
namespace = klass.module_parents.detect { |m| m.respond_to?(:railtie_routes_url_helpers) }
|
14
|
+
actual_routes = namespace ? namespace.railtie_routes_url_helpers._routes : routes
|
15
|
+
|
16
|
+
if namespace
|
11
17
|
klass.include(namespace.railtie_routes_url_helpers(include_path_helpers))
|
12
18
|
else
|
13
19
|
klass.include(routes.url_helpers(include_path_helpers))
|
14
20
|
end
|
21
|
+
|
22
|
+
# In the case that we have ex.
|
23
|
+
# class A::Foo < ApplicationController
|
24
|
+
# class Bar < A::Foo
|
25
|
+
# We will need to redefine _routes because it will not be correct
|
26
|
+
# via inheritance.
|
27
|
+
unless klass._routes.equal?(actual_routes)
|
28
|
+
klass.redefine_singleton_method(:_routes) { actual_routes }
|
29
|
+
klass.include(Module.new do
|
30
|
+
define_method(:_routes) { @_routes || actual_routes }
|
31
|
+
end)
|
32
|
+
end
|
15
33
|
end
|
16
34
|
end
|
17
35
|
end
|