actionpack 5.2.7.1 → 6.1.4.6
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 +329 -352
- data/MIT-LICENSE +1 -1
- 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 +1 -2
- 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 +24 -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 +6 -6
- 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 +167 -58
- 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 +31 -27
- data/lib/action_dispatch/http/content_disposition.rb +45 -0
- data/lib/action_dispatch/http/content_security_policy.rb +39 -17
- 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 +128 -109
- 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 +141 -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 +3 -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 +54 -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 +29 -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 +4 -4
- data/lib/action_pack.rb +1 -1
- metadata +35 -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
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
|
@@ -22,11 +22,10 @@ module AbstractController
|
|
22
22
|
end
|
23
23
|
|
24
24
|
private
|
25
|
-
|
26
25
|
def method_missing(symbol, &block)
|
27
26
|
unless mime_constant = Mime[symbol]
|
28
27
|
raise NoMethodError, "To respond to a custom format, register it as a MIME type first: " \
|
29
|
-
"
|
28
|
+
"https://guides.rubyonrails.org/action_controller_overview.html#restful-downloads. " \
|
30
29
|
"If you meant to respond to a variant like :tablet or :phone, not a custom format, " \
|
31
30
|
"be sure to nest your variant response within a format response: " \
|
32
31
|
"format.html { |html| html.tablet { ... } }"
|
@@ -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
|