actionpack 5.2.7.1 → 6.1.7.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +427 -338
- data/MIT-LICENSE +1 -2
- data/README.rdoc +4 -3
- data/lib/abstract_controller/base.rb +38 -4
- data/lib/abstract_controller/caching/fragments.rb +6 -22
- data/lib/abstract_controller/caching.rb +1 -1
- data/lib/abstract_controller/callbacks.rb +14 -2
- data/lib/abstract_controller/collector.rb +5 -4
- data/lib/abstract_controller/helpers.rb +106 -90
- data/lib/abstract_controller/railties/routes_helpers.rb +17 -1
- data/lib/abstract_controller/rendering.rb +9 -9
- data/lib/abstract_controller/translation.rb +11 -5
- data/lib/abstract_controller.rb +1 -0
- data/lib/action_controller/api.rb +4 -3
- data/lib/action_controller/base.rb +6 -9
- data/lib/action_controller/caching.rb +1 -3
- data/lib/action_controller/log_subscriber.rb +10 -7
- data/lib/action_controller/metal/basic_implicit_render.rb +1 -1
- data/lib/action_controller/metal/conditional_get.rb +19 -5
- data/lib/action_controller/metal/content_security_policy.rb +1 -2
- data/lib/action_controller/metal/cookies.rb +3 -1
- data/lib/action_controller/metal/data_streaming.rb +6 -7
- 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 +56 -2
- data/lib/action_controller/metal/flash.rb +5 -5
- data/lib/action_controller/metal/head.rb +7 -4
- data/lib/action_controller/metal/helpers.rb +14 -5
- data/lib/action_controller/metal/http_authentication.rb +25 -23
- data/lib/action_controller/metal/implicit_render.rb +5 -15
- data/lib/action_controller/metal/instrumentation.rb +13 -14
- data/lib/action_controller/metal/live.rb +39 -32
- data/lib/action_controller/metal/logging.rb +20 -0
- data/lib/action_controller/metal/mime_responds.rb +19 -4
- data/lib/action_controller/metal/parameter_encoding.rb +35 -4
- data/lib/action_controller/metal/params_wrapper.rb +32 -22
- data/lib/action_controller/metal/permissions_policy.rb +46 -0
- data/lib/action_controller/metal/redirecting.rb +26 -7
- data/lib/action_controller/metal/renderers.rb +4 -4
- data/lib/action_controller/metal/rendering.rb +8 -3
- data/lib/action_controller/metal/request_forgery_protection.rb +26 -49
- data/lib/action_controller/metal/rescue.rb +1 -1
- data/lib/action_controller/metal/streaming.rb +0 -1
- data/lib/action_controller/metal/strong_parameters.rb +168 -59
- data/lib/action_controller/metal/url_for.rb +1 -1
- data/lib/action_controller/metal.rb +10 -8
- 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 +71 -63
- data/lib/action_controller.rb +7 -4
- data/lib/action_dispatch/http/cache.rb +32 -28
- data/lib/action_dispatch/http/content_disposition.rb +45 -0
- data/lib/action_dispatch/http/content_security_policy.rb +34 -18
- data/lib/action_dispatch/http/filter_parameters.rb +9 -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 +26 -13
- data/lib/action_dispatch/http/mime_type.rb +43 -24
- data/lib/action_dispatch/http/parameters.rb +14 -23
- data/lib/action_dispatch/http/permissions_policy.rb +173 -0
- data/lib/action_dispatch/http/request.rb +45 -22
- data/lib/action_dispatch/http/response.rb +45 -25
- data/lib/action_dispatch/http/upload.rb +9 -1
- data/lib/action_dispatch/http/url.rb +82 -82
- data/lib/action_dispatch/journey/formatter.rb +55 -31
- data/lib/action_dispatch/journey/gtg/builder.rb +22 -37
- data/lib/action_dispatch/journey/gtg/simulator.rb +8 -7
- data/lib/action_dispatch/journey/gtg/transition_table.rb +6 -5
- data/lib/action_dispatch/journey/nfa/dot.rb +0 -11
- data/lib/action_dispatch/journey/nodes/node.rb +13 -11
- 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 +19 -21
- data/lib/action_dispatch/journey/route.rb +10 -20
- data/lib/action_dispatch/journey/router/utils.rb +14 -12
- data/lib/action_dispatch/journey/router.rb +26 -34
- data/lib/action_dispatch/journey/routes.rb +0 -2
- data/lib/action_dispatch/journey/scanner.rb +10 -4
- data/lib/action_dispatch/journey/visitors.rb +1 -4
- data/lib/action_dispatch/journey.rb +0 -2
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
- data/lib/action_dispatch/middleware/callbacks.rb +2 -4
- data/lib/action_dispatch/middleware/cookies.rb +150 -123
- data/lib/action_dispatch/middleware/debug_exceptions.rb +43 -66
- data/lib/action_dispatch/middleware/debug_locks.rb +5 -5
- data/lib/action_dispatch/middleware/debug_view.rb +66 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +75 -30
- data/lib/action_dispatch/middleware/flash.rb +1 -1
- data/lib/action_dispatch/middleware/host_authorization.rb +170 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +6 -3
- data/lib/action_dispatch/middleware/remote_ip.rb +14 -16
- data/lib/action_dispatch/middleware/request_id.rb +5 -6
- data/lib/action_dispatch/middleware/session/abstract_store.rb +2 -3
- data/lib/action_dispatch/middleware/session/cookie_store.rb +3 -9
- data/lib/action_dispatch/middleware/show_exceptions.rb +13 -2
- data/lib/action_dispatch/middleware/ssl.rb +20 -15
- data/lib/action_dispatch/middleware/stack.rb +56 -2
- data/lib/action_dispatch/middleware/static.rb +153 -93
- 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 +3 -1
- 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 +45 -35
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +23 -4
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +6 -3
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +4 -1
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +104 -8
- 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 +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +24 -1
- data/lib/action_dispatch/railtie.rb +8 -2
- data/lib/action_dispatch/request/session.rb +11 -10
- data/lib/action_dispatch/request/utils.rb +26 -2
- data/lib/action_dispatch/routing/inspector.rb +100 -52
- data/lib/action_dispatch/routing/mapper.rb +155 -103
- data/lib/action_dispatch/routing/polymorphic_routes.rb +13 -15
- data/lib/action_dispatch/routing/redirection.rb +4 -4
- data/lib/action_dispatch/routing/route_set.rb +71 -69
- data/lib/action_dispatch/routing/url_for.rb +2 -2
- data/lib/action_dispatch/routing.rb +21 -20
- data/lib/action_dispatch/system_test_case.rb +60 -11
- data/lib/action_dispatch/system_testing/browser.rb +53 -16
- data/lib/action_dispatch/system_testing/driver.rb +11 -3
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +49 -7
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +8 -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 +1 -1
- data/lib/action_dispatch/testing/integration.rb +60 -28
- data/lib/action_dispatch/testing/request_encoder.rb +2 -2
- data/lib/action_dispatch/testing/test_process.rb +32 -4
- 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 +9 -3
- data/lib/action_pack/gem_version.rb +3 -3
- data/lib/action_pack.rb +1 -1
- metadata +36 -23
- 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/MIT-LICENSE
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c) 2004-
|
1
|
+
Copyright (c) 2004-2022 David Heinemeier Hansson
|
2
2
|
|
3
3
|
Permission is hereby granted, free of charge, to any person obtaining
|
4
4
|
a copy of this software and associated documentation files (the
|
@@ -18,4 +18,3 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
18
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
19
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
20
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
-
|
data/README.rdoc
CHANGED
@@ -23,6 +23,7 @@ by default and Action View rendering is implicitly triggered by Action
|
|
23
23
|
Controller. However, these modules are designed to function on their own and
|
24
24
|
can be used outside of Rails.
|
25
25
|
|
26
|
+
You can read more about Action Pack in the {Action Controller Overview}[https://guides.rubyonrails.org/action_controller_overview.html] guide.
|
26
27
|
|
27
28
|
== Download and installation
|
28
29
|
|
@@ -32,7 +33,7 @@ The latest version of Action Pack can be installed with RubyGems:
|
|
32
33
|
|
33
34
|
Source code can be downloaded as part of the Rails project on GitHub:
|
34
35
|
|
35
|
-
* https://github.com/rails/rails/tree/
|
36
|
+
* https://github.com/rails/rails/tree/main/actionpack
|
36
37
|
|
37
38
|
|
38
39
|
== License
|
@@ -46,7 +47,7 @@ Action Pack is released under the MIT license:
|
|
46
47
|
|
47
48
|
API documentation is at:
|
48
49
|
|
49
|
-
*
|
50
|
+
* https://api.rubyonrails.org
|
50
51
|
|
51
52
|
Bug reports for the Ruby on Rails project can be filed here:
|
52
53
|
|
@@ -54,4 +55,4 @@ Bug reports for the Ruby on Rails project can be filed here:
|
|
54
55
|
|
55
56
|
Feature requests should be discussed on the rails-core mailing list here:
|
56
57
|
|
57
|
-
* https://
|
58
|
+
* https://discuss.rubyonrails.org/c/rubyonrails-core
|
@@ -9,6 +9,35 @@ 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
|
13
|
+
def initialize(message = nil, controller = nil, action = nil)
|
14
|
+
@controller = controller
|
15
|
+
@action = action
|
16
|
+
super(message)
|
17
|
+
end
|
18
|
+
|
19
|
+
class Correction
|
20
|
+
def initialize(error)
|
21
|
+
@error = error
|
22
|
+
end
|
23
|
+
|
24
|
+
def corrections
|
25
|
+
if @error.action
|
26
|
+
maybe_these = @error.controller.class.action_methods
|
27
|
+
|
28
|
+
maybe_these.sort_by { |n|
|
29
|
+
DidYouMean::Jaro.distance(@error.action.to_s, n)
|
30
|
+
}.reverse.first(4)
|
31
|
+
else
|
32
|
+
[]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# We may not have DYM, and DYM might not let us register error handlers
|
38
|
+
if defined?(DidYouMean) && DidYouMean.respond_to?(:correct_error)
|
39
|
+
DidYouMean.correct_error(self, Correction)
|
40
|
+
end
|
12
41
|
end
|
13
42
|
|
14
43
|
# AbstractController::Base is a low-level API. Nobody should be
|
@@ -78,7 +107,9 @@ module AbstractController
|
|
78
107
|
# Except for public instance methods of Base and its ancestors
|
79
108
|
internal_methods +
|
80
109
|
# Be sure to include shadowed public instance methods of this class
|
81
|
-
public_instance_methods(false))
|
110
|
+
public_instance_methods(false))
|
111
|
+
|
112
|
+
methods.map!(&:to_s)
|
82
113
|
|
83
114
|
methods.to_set
|
84
115
|
end
|
@@ -102,7 +133,7 @@ module AbstractController
|
|
102
133
|
# ==== Returns
|
103
134
|
# * <tt>String</tt>
|
104
135
|
def controller_path
|
105
|
-
@controller_path ||= name.
|
136
|
+
@controller_path ||= name.delete_suffix("Controller").underscore unless anonymous?
|
106
137
|
end
|
107
138
|
|
108
139
|
# Refresh the cached action_methods when a new action_method is added.
|
@@ -126,7 +157,7 @@ module AbstractController
|
|
126
157
|
@_action_name = action.to_s
|
127
158
|
|
128
159
|
unless action_name = _find_action_name(@_action_name)
|
129
|
-
raise ActionNotFound
|
160
|
+
raise ActionNotFound.new("The action '#{action}' could not be found for #{self.class.name}", self, action)
|
130
161
|
end
|
131
162
|
|
132
163
|
@_response_body = nil
|
@@ -173,8 +204,11 @@ module AbstractController
|
|
173
204
|
true
|
174
205
|
end
|
175
206
|
|
176
|
-
|
207
|
+
def inspect # :nodoc:
|
208
|
+
"#<#{self.class.name}:#{'%#016x' % (object_id << 1)}>"
|
209
|
+
end
|
177
210
|
|
211
|
+
private
|
178
212
|
# Returns true if the name can be considered an action because
|
179
213
|
# it has a method defined in the controller.
|
180
214
|
#
|
@@ -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
|
@@ -37,7 +37,7 @@ module AbstractController
|
|
37
37
|
|
38
38
|
# Override <tt>AbstractController::Base#process_action</tt> to run the
|
39
39
|
# <tt>process_action</tt> callbacks around the normal behavior.
|
40
|
-
def process_action(*
|
40
|
+
def process_action(*)
|
41
41
|
run_callbacks(:process_action) do
|
42
42
|
super
|
43
43
|
end
|
@@ -69,7 +69,7 @@ module AbstractController
|
|
69
69
|
end
|
70
70
|
|
71
71
|
def _normalize_callback_option(options, from, to) # :nodoc:
|
72
|
-
if from = options
|
72
|
+
if from = options.delete(from)
|
73
73
|
_from = Array(from).map(&:to_s).to_set
|
74
74
|
from = proc { |c| _from.include? c.action_name }
|
75
75
|
options[to] = Array(options[to]).unshift(from)
|
@@ -103,6 +103,10 @@ module AbstractController
|
|
103
103
|
# :call-seq: before_action(names, block)
|
104
104
|
#
|
105
105
|
# Append a callback before actions. See _insert_callbacks for parameter details.
|
106
|
+
#
|
107
|
+
# If the callback renders or redirects, the action will not run. If there
|
108
|
+
# are additional callbacks scheduled to run after that callback, they are
|
109
|
+
# also cancelled.
|
106
110
|
|
107
111
|
##
|
108
112
|
# :method: prepend_before_action
|
@@ -110,6 +114,10 @@ module AbstractController
|
|
110
114
|
# :call-seq: prepend_before_action(names, block)
|
111
115
|
#
|
112
116
|
# Prepend a callback before actions. See _insert_callbacks for parameter details.
|
117
|
+
#
|
118
|
+
# If the callback renders or redirects, the action will not run. If there
|
119
|
+
# are additional callbacks scheduled to run after that callback, they are
|
120
|
+
# also cancelled.
|
113
121
|
|
114
122
|
##
|
115
123
|
# :method: skip_before_action
|
@@ -124,6 +132,10 @@ module AbstractController
|
|
124
132
|
# :call-seq: append_before_action(names, block)
|
125
133
|
#
|
126
134
|
# Append a callback before actions. See _insert_callbacks for parameter details.
|
135
|
+
#
|
136
|
+
# If the callback renders or redirects, the action will not run. If there
|
137
|
+
# are additional callbacks scheduled to run after that callback, they are
|
138
|
+
# also cancelled.
|
127
139
|
|
128
140
|
##
|
129
141
|
# :method: after_action
|
@@ -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}) if respond_to?(:ruby2_keywords, true)
|
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) if respond_to?(:ruby2_keywords, true)
|
42
43
|
end
|
43
44
|
end
|
@@ -7,8 +7,19 @@ module AbstractController
|
|
7
7
|
extend ActiveSupport::Concern
|
8
8
|
|
9
9
|
included do
|
10
|
-
class_attribute :_helpers, default: Module.new
|
11
10
|
class_attribute :_helper_methods, default: Array.new
|
11
|
+
|
12
|
+
# This is here so that it is always higher in the inheritance chain than
|
13
|
+
# the definition in lib/action_view/rendering.rb
|
14
|
+
redefine_singleton_method(:_helpers) do
|
15
|
+
if @_helpers ||= nil
|
16
|
+
@_helpers
|
17
|
+
else
|
18
|
+
superclass._helpers
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
self._helpers = define_helpers_module(self)
|
12
23
|
end
|
13
24
|
|
14
25
|
class MissingHelperError < LoadError
|
@@ -17,7 +28,7 @@ module AbstractController
|
|
17
28
|
@path = "helpers/#{path}.rb"
|
18
29
|
set_backtrace error.backtrace
|
19
30
|
|
20
|
-
if
|
31
|
+
if /^#{path}(\.rb)?$/.match?(error.path)
|
21
32
|
super("Missing helper file helpers/%s.rb" % path)
|
22
33
|
else
|
23
34
|
raise error
|
@@ -25,17 +36,24 @@ module AbstractController
|
|
25
36
|
end
|
26
37
|
end
|
27
38
|
|
39
|
+
def _helpers
|
40
|
+
self.class._helpers
|
41
|
+
end
|
42
|
+
|
28
43
|
module ClassMethods
|
29
44
|
# When a class is inherited, wrap its helper module in a new module.
|
30
45
|
# This ensures that the parent class's module can be changed
|
31
46
|
# independently of the child class's.
|
32
47
|
def inherited(klass)
|
33
|
-
|
34
|
-
klass._helpers =
|
48
|
+
# Inherited from parent by default
|
49
|
+
klass._helpers = nil
|
50
|
+
|
35
51
|
klass.class_eval { default_helper_module! } unless klass.anonymous?
|
36
52
|
super
|
37
53
|
end
|
38
54
|
|
55
|
+
attr_writer :_helpers
|
56
|
+
|
39
57
|
# Declare a controller method as a helper. For example, the following
|
40
58
|
# makes the +current_user+ and +logged_in?+ controller methods available
|
41
59
|
# to the view:
|
@@ -57,59 +75,81 @@ module AbstractController
|
|
57
75
|
# ==== Parameters
|
58
76
|
# * <tt>method[, method]</tt> - A name or names of a method on the controller
|
59
77
|
# to be made available on the view.
|
60
|
-
def helper_method(*
|
61
|
-
|
62
|
-
self._helper_methods +=
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
78
|
+
def helper_method(*methods)
|
79
|
+
methods.flatten!
|
80
|
+
self._helper_methods += methods
|
81
|
+
|
82
|
+
location = caller_locations(1, 1).first
|
83
|
+
file, line = location.path, location.lineno
|
84
|
+
|
85
|
+
methods.each do |method|
|
86
|
+
_helpers_for_modification.class_eval <<-ruby_eval, file, line
|
87
|
+
def #{method}(*args, &block) # def current_user(*args, &block)
|
88
|
+
controller.send(:'#{method}', *args, &block) # controller.send(:'current_user', *args, &block)
|
89
|
+
end # end
|
90
|
+
ruby2_keywords(:'#{method}') if respond_to?(:ruby2_keywords, true)
|
69
91
|
ruby_eval
|
70
92
|
end
|
71
93
|
end
|
72
94
|
|
73
|
-
#
|
95
|
+
# Includes the given modules in the template class.
|
96
|
+
#
|
97
|
+
# Modules can be specified in different ways. All of the following calls
|
98
|
+
# include +FooHelper+:
|
99
|
+
#
|
100
|
+
# # Module, recommended.
|
101
|
+
# helper FooHelper
|
102
|
+
#
|
103
|
+
# # String/symbol without the "helper" suffix, camel or snake case.
|
104
|
+
# helper "Foo"
|
105
|
+
# helper :Foo
|
106
|
+
# helper "foo"
|
107
|
+
# helper :foo
|
108
|
+
#
|
109
|
+
# The last two assume that <tt>"foo".camelize</tt> returns "Foo".
|
74
110
|
#
|
75
|
-
#
|
76
|
-
#
|
77
|
-
#
|
111
|
+
# When strings or symbols are passed, the method finds the actual module
|
112
|
+
# object using +String#constantize+. Therefore, if the module has not been
|
113
|
+
# yet loaded, it has to be autoloadable, which is normally the case.
|
78
114
|
#
|
79
|
-
#
|
80
|
-
# helper FooHelper # => includes FooHelper
|
115
|
+
# Namespaces are supported. The following calls include +Foo::BarHelper+:
|
81
116
|
#
|
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
|
117
|
+
# # Module, recommended.
|
118
|
+
# helper Foo::BarHelper
|
88
119
|
#
|
89
|
-
#
|
90
|
-
#
|
120
|
+
# # String/symbol without the "helper" suffix, camel or snake case.
|
121
|
+
# helper "Foo::Bar"
|
122
|
+
# helper :"Foo::Bar"
|
123
|
+
# helper "foo/bar"
|
124
|
+
# helper :"foo/bar"
|
91
125
|
#
|
92
|
-
#
|
93
|
-
#
|
126
|
+
# The last two assume that <tt>"foo/bar".camelize</tt> returns "Foo::Bar".
|
127
|
+
#
|
128
|
+
# The method accepts a block too. If present, the block is evaluated in
|
129
|
+
# the context of the controller helper module. This simple call makes the
|
130
|
+
# +wadus+ method available in templates of the enclosing controller:
|
94
131
|
#
|
95
|
-
# # Multi-line
|
96
132
|
# helper do
|
97
|
-
# def
|
98
|
-
# "
|
133
|
+
# def wadus
|
134
|
+
# "wadus"
|
99
135
|
# end
|
100
136
|
# end
|
101
137
|
#
|
102
|
-
#
|
103
|
-
# +symbols+, +strings+, +modules+ and blocks.
|
138
|
+
# Furthermore, all the above styles can be mixed together:
|
104
139
|
#
|
105
|
-
# helper
|
140
|
+
# helper FooHelper, "woo", "bar/baz" do
|
141
|
+
# def wadus
|
142
|
+
# "wadus"
|
143
|
+
# end
|
144
|
+
# end
|
106
145
|
#
|
107
146
|
def helper(*args, &block)
|
108
147
|
modules_for_helpers(args).each do |mod|
|
109
|
-
|
148
|
+
next if _helpers.include?(mod)
|
149
|
+
_helpers_for_modification.include(mod)
|
110
150
|
end
|
111
151
|
|
112
|
-
|
152
|
+
_helpers_for_modification.module_eval(&block) if block_given?
|
113
153
|
end
|
114
154
|
|
115
155
|
# Clears up all existing helpers in this class, only keeping the helper
|
@@ -123,71 +163,47 @@ module AbstractController
|
|
123
163
|
default_helper_module! unless anonymous?
|
124
164
|
end
|
125
165
|
|
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
|
166
|
+
# Given an array of values like the ones accepted by +helper+, this method
|
167
|
+
# returns an array with the corresponding modules, in the same order.
|
168
|
+
def modules_for_helpers(modules_or_helper_prefixes)
|
169
|
+
modules_or_helper_prefixes.flatten.map! do |module_or_helper_prefix|
|
170
|
+
case module_or_helper_prefix
|
164
171
|
when Module
|
165
|
-
|
172
|
+
module_or_helper_prefix
|
173
|
+
when String, Symbol
|
174
|
+
helper_prefix = module_or_helper_prefix.to_s
|
175
|
+
helper_prefix = helper_prefix.camelize unless helper_prefix.start_with?(/[A-Z]/)
|
176
|
+
"#{helper_prefix}Helper".constantize
|
166
177
|
else
|
167
178
|
raise ArgumentError, "helper must be a String, Symbol, or Module"
|
168
179
|
end
|
169
180
|
end
|
170
181
|
end
|
171
182
|
|
183
|
+
def _helpers_for_modification
|
184
|
+
unless @_helpers
|
185
|
+
self._helpers = define_helpers_module(self, superclass._helpers)
|
186
|
+
end
|
187
|
+
_helpers
|
188
|
+
end
|
189
|
+
|
172
190
|
private
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
191
|
+
def define_helpers_module(klass, helpers = nil)
|
192
|
+
# In some tests inherited is called explicitly. In that case, just
|
193
|
+
# return the module from the first time it was defined
|
194
|
+
return klass.const_get(:HelperMethods) if klass.const_defined?(:HelperMethods, false)
|
195
|
+
|
196
|
+
mod = Module.new
|
197
|
+
klass.const_set(:HelperMethods, mod)
|
198
|
+
mod.include(helpers) if helpers
|
199
|
+
mod
|
181
200
|
end
|
182
201
|
|
183
202
|
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"
|
203
|
+
helper_prefix = name.delete_suffix("Controller")
|
204
|
+
helper(helper_prefix)
|
189
205
|
rescue NameError => e
|
190
|
-
raise
|
206
|
+
raise unless e.missing_name?("#{helper_prefix}Helper")
|
191
207
|
end
|
192
208
|
end
|
193
209
|
end
|
@@ -7,11 +7,27 @@ module AbstractController
|
|
7
7
|
Module.new do
|
8
8
|
define_method(:inherited) do |klass|
|
9
9
|
super(klass)
|
10
|
-
|
10
|
+
|
11
|
+
namespace = klass.module_parents.detect { |m| m.respond_to?(:railtie_routes_url_helpers) }
|
12
|
+
actual_routes = namespace ? namespace.railtie_routes_url_helpers._routes : routes
|
13
|
+
|
14
|
+
if namespace
|
11
15
|
klass.include(namespace.railtie_routes_url_helpers(include_path_helpers))
|
12
16
|
else
|
13
17
|
klass.include(routes.url_helpers(include_path_helpers))
|
14
18
|
end
|
19
|
+
|
20
|
+
# In the case that we have ex.
|
21
|
+
# class A::Foo < ApplicationController
|
22
|
+
# class Bar < A::Foo
|
23
|
+
# We will need to redefine _routes because it will not be correct
|
24
|
+
# via inheritance.
|
25
|
+
unless klass._routes.equal?(actual_routes)
|
26
|
+
klass.redefine_singleton_method(:_routes) { actual_routes }
|
27
|
+
klass.include(Module.new do
|
28
|
+
define_method(:_routes) { @_routes || actual_routes }
|
29
|
+
end)
|
30
|
+
end
|
15
31
|
end
|
16
32
|
end
|
17
33
|
end
|
@@ -28,6 +28,7 @@ module AbstractController
|
|
28
28
|
else
|
29
29
|
_set_rendered_content_type rendered_format
|
30
30
|
end
|
31
|
+
_set_vary_header
|
31
32
|
self.response_body = rendered_body
|
32
33
|
end
|
33
34
|
|
@@ -55,20 +56,16 @@ module AbstractController
|
|
55
56
|
Mime[:text]
|
56
57
|
end
|
57
58
|
|
58
|
-
DEFAULT_PROTECTED_INSTANCE_VARIABLES =
|
59
|
-
@_action_name @_response_body @_formats @_prefixes
|
60
|
-
)
|
59
|
+
DEFAULT_PROTECTED_INSTANCE_VARIABLES = %i(@_action_name @_response_body @_formats @_prefixes)
|
61
60
|
|
62
61
|
# This method should return a hash with assigns.
|
63
62
|
# You can overwrite this configuration per controller.
|
64
63
|
def view_assigns
|
65
|
-
|
66
|
-
variables = instance_variables
|
64
|
+
variables = instance_variables - _protected_ivars
|
67
65
|
|
68
|
-
variables.
|
69
|
-
variables.each_with_object({}) { |name, hash|
|
66
|
+
variables.each_with_object({}) do |name, hash|
|
70
67
|
hash[name.slice(1, name.length)] = instance_variable_get(name)
|
71
|
-
|
68
|
+
end
|
72
69
|
end
|
73
70
|
|
74
71
|
private
|
@@ -109,6 +106,9 @@ module AbstractController
|
|
109
106
|
def _set_html_content_type # :nodoc:
|
110
107
|
end
|
111
108
|
|
109
|
+
def _set_vary_header # :nodoc:
|
110
|
+
end
|
111
|
+
|
112
112
|
def _set_rendered_content_type(format) # :nodoc:
|
113
113
|
end
|
114
114
|
|
@@ -120,7 +120,7 @@ module AbstractController
|
|
120
120
|
options
|
121
121
|
end
|
122
122
|
|
123
|
-
def _protected_ivars
|
123
|
+
def _protected_ivars
|
124
124
|
DEFAULT_PROTECTED_INSTANCE_VARIABLES
|
125
125
|
end
|
126
126
|
end
|
@@ -1,7 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/core_ext/symbol/starts_ends_with"
|
4
|
+
|
3
5
|
module AbstractController
|
4
6
|
module Translation
|
7
|
+
mattr_accessor :raise_on_missing_translations, default: false
|
8
|
+
|
5
9
|
# Delegates to <tt>I18n.translate</tt>. Also aliased as <tt>t</tt>.
|
6
10
|
#
|
7
11
|
# When the given key starts with a period, it will be scoped by the current
|
@@ -10,21 +14,23 @@ module AbstractController
|
|
10
14
|
# <tt>I18n.translate("people.index.foo")</tt>. This makes it less repetitive
|
11
15
|
# to translate many keys within the same controller / action and gives you a
|
12
16
|
# simple framework for scoping them consistently.
|
13
|
-
def translate(key, options
|
14
|
-
if key
|
17
|
+
def translate(key, **options)
|
18
|
+
if key&.start_with?(".")
|
15
19
|
path = controller_path.tr("/", ".")
|
16
20
|
defaults = [:"#{path}#{key}"]
|
17
21
|
defaults << options[:default] if options[:default]
|
18
22
|
options[:default] = defaults.flatten
|
19
23
|
key = "#{path}.#{action_name}#{key}"
|
20
24
|
end
|
21
|
-
|
25
|
+
|
26
|
+
i18n_raise = options.fetch(:raise, self.raise_on_missing_translations)
|
27
|
+
I18n.translate(key, **options, raise: i18n_raise)
|
22
28
|
end
|
23
29
|
alias :t :translate
|
24
30
|
|
25
31
|
# Delegates to <tt>I18n.localize</tt>. Also aliased as <tt>l</tt>.
|
26
|
-
def localize(
|
27
|
-
I18n.localize(
|
32
|
+
def localize(object, **options)
|
33
|
+
I18n.localize(object, **options)
|
28
34
|
end
|
29
35
|
alias :l :localize
|
30
36
|
end
|