actionpack 5.2.8.1 → 6.1.6.1
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 +383 -346
- 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 +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 +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 +31 -27
- 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 +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 +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 +34 -21
- 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
@@ -4,6 +4,7 @@ require "active_support/core_ext/hash/slice"
|
|
4
4
|
require "active_support/core_ext/enumerable"
|
5
5
|
require "active_support/core_ext/array/extract_options"
|
6
6
|
require "active_support/core_ext/regexp"
|
7
|
+
require "active_support/core_ext/symbol/starts_ends_with"
|
7
8
|
require "action_dispatch/routing/redirection"
|
8
9
|
require "action_dispatch/routing/endpoint"
|
9
10
|
|
@@ -50,7 +51,19 @@ module ActionDispatch
|
|
50
51
|
|
51
52
|
private
|
52
53
|
def constraint_args(constraint, request)
|
53
|
-
|
54
|
+
arity = if constraint.respond_to?(:arity)
|
55
|
+
constraint.arity
|
56
|
+
else
|
57
|
+
constraint.method(:call).arity
|
58
|
+
end
|
59
|
+
|
60
|
+
if arity < 1
|
61
|
+
[]
|
62
|
+
elsif arity == 1
|
63
|
+
[request]
|
64
|
+
else
|
65
|
+
[request.path_parameters, request]
|
66
|
+
end
|
54
67
|
end
|
55
68
|
end
|
56
69
|
|
@@ -58,17 +71,21 @@ module ActionDispatch
|
|
58
71
|
ANCHOR_CHARACTERS_REGEX = %r{\A(\\A|\^)|(\\Z|\\z|\$)\Z}
|
59
72
|
OPTIONAL_FORMAT_REGEX = %r{(?:\(\.:format\)+|\.:format|/)\Z}
|
60
73
|
|
61
|
-
attr_reader :requirements, :defaults
|
62
|
-
|
63
|
-
attr_reader :required_defaults, :ast
|
74
|
+
attr_reader :requirements, :defaults, :to, :default_controller,
|
75
|
+
:default_action, :required_defaults, :ast, :scope_options
|
64
76
|
|
65
77
|
def self.build(scope, set, ast, controller, default_action, to, via, formatted, options_constraints, anchor, options)
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
78
|
+
scope_params = {
|
79
|
+
blocks: scope[:blocks] || [],
|
80
|
+
constraints: scope[:constraints] || {},
|
81
|
+
defaults: (scope[:defaults] || {}).dup,
|
82
|
+
module: scope[:module],
|
83
|
+
options: scope[:options] || {}
|
84
|
+
}
|
70
85
|
|
71
|
-
new set, ast
|
86
|
+
new set: set, ast: ast, controller: controller, default_action: default_action,
|
87
|
+
to: to, formatted: formatted, via: via, options_constraints: options_constraints,
|
88
|
+
anchor: anchor, scope_params: scope_params, options: scope_params[:options].merge(options)
|
72
89
|
end
|
73
90
|
|
74
91
|
def self.check_via(via)
|
@@ -96,36 +113,48 @@ module ActionDispatch
|
|
96
113
|
end
|
97
114
|
|
98
115
|
def self.optional_format?(path, format)
|
99
|
-
format != false && path
|
116
|
+
format != false && !path.match?(OPTIONAL_FORMAT_REGEX)
|
100
117
|
end
|
101
118
|
|
102
|
-
def initialize(set
|
103
|
-
@defaults
|
104
|
-
@set
|
105
|
-
|
106
|
-
@
|
107
|
-
@
|
108
|
-
@default_action = default_action
|
119
|
+
def initialize(set:, ast:, controller:, default_action:, to:, formatted:, via:, options_constraints:, anchor:, scope_params:, options:)
|
120
|
+
@defaults = scope_params[:defaults]
|
121
|
+
@set = set
|
122
|
+
@to = intern(to)
|
123
|
+
@default_controller = intern(controller)
|
124
|
+
@default_action = intern(default_action)
|
109
125
|
@ast = ast
|
110
126
|
@anchor = anchor
|
111
127
|
@via = via
|
112
128
|
@internal = options.delete(:internal)
|
129
|
+
@scope_options = scope_params[:options]
|
130
|
+
|
131
|
+
path_params = []
|
132
|
+
wildcard_options = {}
|
133
|
+
ast.each do |node|
|
134
|
+
if node.symbol?
|
135
|
+
path_params << node.to_sym
|
136
|
+
elsif formatted != false && node.star?
|
137
|
+
# Add a constraint for wildcard route to make it non-greedy and match the
|
138
|
+
# optional format part of the route by default.
|
139
|
+
wildcard_options[node.name.to_sym] ||= /.+?/
|
140
|
+
elsif node.cat?
|
141
|
+
alter_regex_for_custom_routes(node)
|
142
|
+
end
|
143
|
+
end
|
113
144
|
|
114
|
-
|
115
|
-
|
116
|
-
options = add_wildcard_options(options, formatted, ast)
|
145
|
+
options = wildcard_options.merge!(options)
|
117
146
|
|
118
|
-
options = normalize_options!(options, path_params,
|
147
|
+
options = normalize_options!(options, path_params, scope_params[:module])
|
119
148
|
|
120
149
|
split_options = constraints(options, path_params)
|
121
150
|
|
122
|
-
constraints =
|
151
|
+
constraints = scope_params[:constraints].merge Hash[split_options[:constraints] || []]
|
123
152
|
|
124
153
|
if options_constraints.is_a?(Hash)
|
125
154
|
@defaults = Hash[options_constraints.find_all { |key, default|
|
126
155
|
URL_OPTIONS.include?(key) && (String === default || Integer === default)
|
127
156
|
}].merge @defaults
|
128
|
-
@blocks = blocks
|
157
|
+
@blocks = scope_params[:blocks]
|
129
158
|
constraints.merge! options_constraints
|
130
159
|
else
|
131
160
|
@blocks = blocks(options_constraints)
|
@@ -148,25 +177,20 @@ module ActionDispatch
|
|
148
177
|
end
|
149
178
|
|
150
179
|
def make_route(name, precedence)
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
required_defaults,
|
156
|
-
defaults,
|
157
|
-
request_method,
|
158
|
-
precedence,
|
159
|
-
@internal)
|
160
|
-
|
161
|
-
route
|
180
|
+
Journey::Route.new(name: name, app: application, path: path, constraints: conditions,
|
181
|
+
required_defaults: required_defaults, defaults: defaults,
|
182
|
+
request_method_match: request_method, precedence: precedence,
|
183
|
+
scope_options: scope_options, internal: @internal)
|
162
184
|
end
|
163
185
|
|
164
186
|
def application
|
165
187
|
app(@blocks)
|
166
188
|
end
|
167
189
|
|
190
|
+
JOINED_SEPARATORS = SEPARATORS.join # :nodoc:
|
191
|
+
|
168
192
|
def path
|
169
|
-
|
193
|
+
Journey::Path::Pattern.new(@ast, requirements, JOINED_SEPARATORS, @anchor)
|
170
194
|
end
|
171
195
|
|
172
196
|
def conditions
|
@@ -187,16 +211,10 @@ module ActionDispatch
|
|
187
211
|
end
|
188
212
|
private :request_method
|
189
213
|
|
190
|
-
|
191
|
-
|
192
|
-
def build_path(ast, requirements, anchor)
|
193
|
-
pattern = Journey::Path::Pattern.new(ast, requirements, JOINED_SEPARATORS, anchor)
|
194
|
-
|
214
|
+
private
|
195
215
|
# Find all the symbol nodes that are adjacent to literal nodes and alter
|
196
216
|
# the regexp so that Journey will partition them into custom routes.
|
197
|
-
|
198
|
-
next unless node.cat?
|
199
|
-
|
217
|
+
def alter_regex_for_custom_routes(node)
|
200
218
|
if node.left.literal? && node.right.symbol?
|
201
219
|
symbol = node.right
|
202
220
|
elsif node.left.literal? && node.right.cat? && node.right.left.symbol?
|
@@ -205,30 +223,15 @@ module ActionDispatch
|
|
205
223
|
symbol = node.left
|
206
224
|
elsif node.left.symbol? && node.right.cat? && node.right.left.literal?
|
207
225
|
symbol = node.left
|
208
|
-
else
|
209
|
-
next
|
210
226
|
end
|
211
227
|
|
212
228
|
if symbol
|
213
229
|
symbol.regexp = /(?:#{Regexp.union(symbol.regexp, '-')})+/
|
214
230
|
end
|
215
|
-
|
216
|
-
|
217
|
-
pattern
|
218
|
-
end
|
219
|
-
private :build_path
|
231
|
+
end
|
220
232
|
|
221
|
-
|
222
|
-
|
223
|
-
# Add a constraint for wildcard route to make it non-greedy and match the
|
224
|
-
# optional format part of the route by default.
|
225
|
-
if formatted != false
|
226
|
-
path_ast.grep(Journey::Nodes::Star).each_with_object({}) { |node, hash|
|
227
|
-
hash[node.name.to_sym] ||= /.+?/
|
228
|
-
}.merge options
|
229
|
-
else
|
230
|
-
options
|
231
|
-
end
|
233
|
+
def intern(object)
|
234
|
+
object.is_a?(String) ? -object : object
|
232
235
|
end
|
233
236
|
|
234
237
|
def normalize_options!(options, path_params, modyoule)
|
@@ -279,7 +282,7 @@ module ActionDispatch
|
|
279
282
|
|
280
283
|
def verify_regexp_requirements(requirements)
|
281
284
|
requirements.each do |requirement|
|
282
|
-
if requirement.source
|
285
|
+
if ANCHOR_CHARACTERS_REGEX.match?(requirement.source)
|
283
286
|
raise ArgumentError, "Regexp anchor characters are not allowed in routing requirements: #{requirement.inspect}"
|
284
287
|
end
|
285
288
|
|
@@ -308,8 +311,8 @@ module ActionDispatch
|
|
308
311
|
def check_controller_and_action(path_params, controller, action)
|
309
312
|
hash = check_part(:controller, controller, path_params, {}) do |part|
|
310
313
|
translate_controller(part) {
|
311
|
-
message = "'#{part}' is not a supported controller name. This can lead to potential routing problems."
|
312
|
-
message << " See
|
314
|
+
message = +"'#{part}' is not a supported controller name. This can lead to potential routing problems."
|
315
|
+
message << " See https://guides.rubyonrails.org/routing.html#specifying-a-controller-to-use"
|
313
316
|
|
314
317
|
raise ArgumentError, message
|
315
318
|
}
|
@@ -333,8 +336,8 @@ module ActionDispatch
|
|
333
336
|
end
|
334
337
|
|
335
338
|
def split_to(to)
|
336
|
-
if to
|
337
|
-
to.split("#")
|
339
|
+
if /#/.match?(to)
|
340
|
+
to.split("#").map!(&:-@)
|
338
341
|
else
|
339
342
|
[]
|
340
343
|
end
|
@@ -342,10 +345,10 @@ module ActionDispatch
|
|
342
345
|
|
343
346
|
def add_controller_module(controller, modyoule)
|
344
347
|
if modyoule && !controller.is_a?(Regexp)
|
345
|
-
if controller
|
346
|
-
controller[1..-1]
|
348
|
+
if controller&.start_with?("/")
|
349
|
+
-controller[1..-1]
|
347
350
|
else
|
348
|
-
[modyoule, controller].compact.join("/")
|
351
|
+
-[modyoule, controller].compact.join("/")
|
349
352
|
end
|
350
353
|
else
|
351
354
|
controller
|
@@ -354,7 +357,7 @@ module ActionDispatch
|
|
354
357
|
|
355
358
|
def translate_controller(controller)
|
356
359
|
return controller if Regexp === controller
|
357
|
-
return controller.to_s if
|
360
|
+
return controller.to_s if /\A[a-z_0-9][a-z_0-9\/]*\z/.match?(controller)
|
358
361
|
|
359
362
|
yield
|
360
363
|
end
|
@@ -385,12 +388,23 @@ module ActionDispatch
|
|
385
388
|
end
|
386
389
|
end
|
387
390
|
|
388
|
-
# Invokes Journey::Router::Utils.normalize_path
|
389
|
-
# (:locale) becomes (/:locale)
|
390
|
-
#
|
391
|
+
# Invokes Journey::Router::Utils.normalize_path, then ensures that
|
392
|
+
# /(:locale) becomes (/:locale). Except for root cases, where the
|
393
|
+
# former is the correct one.
|
391
394
|
def self.normalize_path(path)
|
392
395
|
path = Journey::Router::Utils.normalize_path(path)
|
393
|
-
|
396
|
+
|
397
|
+
# the path for a root URL at this point can be something like
|
398
|
+
# "/(/:locale)(/:platform)/(:browser)", and we would want
|
399
|
+
# "/(:locale)(/:platform)(/:browser)"
|
400
|
+
|
401
|
+
# reverse "/(", "/((" etc to "(/", "((/" etc
|
402
|
+
path.gsub!(%r{/(\(+)/?}, '\1/')
|
403
|
+
# if a path is all optional segments, change the leading "(/" back to
|
404
|
+
# "/(" so it evaluates to "/" when interpreted with no options.
|
405
|
+
# Unless, however, at least one secondary segment consists of a static
|
406
|
+
# part, ex. "(/:locale)(/pages/:page)"
|
407
|
+
path.sub!(%r{^(\(+)/}, '/\1') if %r{^(\(+[^)]+\))(\(+/:[^)]+\))*$}.match?(path)
|
394
408
|
path
|
395
409
|
end
|
396
410
|
|
@@ -547,16 +561,16 @@ module ActionDispatch
|
|
547
561
|
# Constrains parameters with a hash of regular expressions
|
548
562
|
# or an object that responds to <tt>matches?</tt>. In addition, constraints
|
549
563
|
# other than path can also be specified with any object
|
550
|
-
# that responds to <tt>===</tt> (
|
564
|
+
# that responds to <tt>===</tt> (e.g. String, Array, Range, etc.).
|
551
565
|
#
|
552
566
|
# match 'path/:id', constraints: { id: /[A-Z]\d{5}/ }, via: :get
|
553
567
|
#
|
554
568
|
# match 'json_only', constraints: { format: 'json' }, via: :get
|
555
569
|
#
|
556
|
-
# class
|
570
|
+
# class PermitList
|
557
571
|
# def matches?(request) request.remote_ip == '1.2.3.4' end
|
558
572
|
# end
|
559
|
-
# match 'path', to: 'c#a', constraints:
|
573
|
+
# match 'path', to: 'c#a', constraints: PermitList.new, via: :get
|
560
574
|
#
|
561
575
|
# See <tt>Scoping#constraints</tt> for more examples with its scope
|
562
576
|
# equivalent.
|
@@ -611,7 +625,7 @@ module ActionDispatch
|
|
611
625
|
end
|
612
626
|
|
613
627
|
raise ArgumentError, "A rack application must be specified" unless app.respond_to?(:call)
|
614
|
-
raise ArgumentError,
|
628
|
+
raise ArgumentError, <<~MSG unless path
|
615
629
|
Must be called with mount point
|
616
630
|
|
617
631
|
mount SomeRackApp, at: "some_route"
|
@@ -644,7 +658,7 @@ module ActionDispatch
|
|
644
658
|
|
645
659
|
# Query if the following named route was already defined.
|
646
660
|
def has_named_route?(name)
|
647
|
-
@set.named_routes.key?
|
661
|
+
@set.named_routes.key?(name)
|
648
662
|
end
|
649
663
|
|
650
664
|
private
|
@@ -668,7 +682,7 @@ module ActionDispatch
|
|
668
682
|
|
669
683
|
script_namer = ->(options) do
|
670
684
|
prefix_options = options.slice(*_route.segment_keys)
|
671
|
-
prefix_options[:relative_url_root] = ""
|
685
|
+
prefix_options[:relative_url_root] = ""
|
672
686
|
|
673
687
|
if options[:_recall]
|
674
688
|
prefix_options.reverse_merge!(options[:_recall].slice(*_route.segment_keys))
|
@@ -676,7 +690,7 @@ module ActionDispatch
|
|
676
690
|
|
677
691
|
# We must actually delete prefix segment keys to avoid passing them to next url_for.
|
678
692
|
_route.segment_keys.each { |k| options.delete(k) }
|
679
|
-
_url_helpers.
|
693
|
+
_url_helpers.public_send("#{name}_path", prefix_options)
|
680
694
|
end
|
681
695
|
|
682
696
|
app.routes.define_mounted_helper(name, script_namer)
|
@@ -736,6 +750,14 @@ module ActionDispatch
|
|
736
750
|
map_method(:delete, args, &block)
|
737
751
|
end
|
738
752
|
|
753
|
+
# Define a route that only recognizes HTTP OPTIONS.
|
754
|
+
# For supported arguments, see match[rdoc-ref:Base#match]
|
755
|
+
#
|
756
|
+
# options 'carrots', to: 'food#carrots'
|
757
|
+
def options(*args, &block)
|
758
|
+
map_method(:options, args, &block)
|
759
|
+
end
|
760
|
+
|
739
761
|
private
|
740
762
|
def map_method(method, args, &block)
|
741
763
|
options = args.extract_options!
|
@@ -983,7 +1005,7 @@ module ActionDispatch
|
|
983
1005
|
#
|
984
1006
|
# Requests to routes can be constrained based on specific criteria:
|
985
1007
|
#
|
986
|
-
# constraints(-> (req) { req.env["HTTP_USER_AGENT"]
|
1008
|
+
# constraints(-> (req) { /iPhone/.match?(req.env["HTTP_USER_AGENT"]) }) do
|
987
1009
|
# resources :iphones
|
988
1010
|
# end
|
989
1011
|
#
|
@@ -993,7 +1015,7 @@ module ActionDispatch
|
|
993
1015
|
#
|
994
1016
|
# class Iphone
|
995
1017
|
# def self.matches?(request)
|
996
|
-
# request.env["HTTP_USER_AGENT"]
|
1018
|
+
# /iPhone/.match?(request.env["HTTP_USER_AGENT"])
|
997
1019
|
# end
|
998
1020
|
# end
|
999
1021
|
#
|
@@ -1138,6 +1160,10 @@ module ActionDispatch
|
|
1138
1160
|
attr_reader :controller, :path, :param
|
1139
1161
|
|
1140
1162
|
def initialize(entities, api_only, shallow, options = {})
|
1163
|
+
if options[:param].to_s.include?(":")
|
1164
|
+
raise ArgumentError, ":param option can't contain colons"
|
1165
|
+
end
|
1166
|
+
|
1141
1167
|
@name = entities.to_s
|
1142
1168
|
@path = (options[:path] || @name).to_s
|
1143
1169
|
@controller = (options[:controller] || @name).to_s
|
@@ -1159,10 +1185,16 @@ module ActionDispatch
|
|
1159
1185
|
end
|
1160
1186
|
|
1161
1187
|
def actions
|
1188
|
+
if @except
|
1189
|
+
available_actions - Array(@except).map(&:to_sym)
|
1190
|
+
else
|
1191
|
+
available_actions
|
1192
|
+
end
|
1193
|
+
end
|
1194
|
+
|
1195
|
+
def available_actions
|
1162
1196
|
if @only
|
1163
1197
|
Array(@only).map(&:to_sym)
|
1164
|
-
elsif @except
|
1165
|
-
default_actions - Array(@except).map(&:to_sym)
|
1166
1198
|
else
|
1167
1199
|
default_actions
|
1168
1200
|
end
|
@@ -1389,6 +1421,8 @@ module ActionDispatch
|
|
1389
1421
|
# as a comment on a blog post like <tt>/posts/a-long-permalink/comments/1234</tt>
|
1390
1422
|
# to be shortened to just <tt>/comments/1234</tt>.
|
1391
1423
|
#
|
1424
|
+
# Set <tt>shallow: false</tt> on a child resource to ignore a parent's shallow parameter.
|
1425
|
+
#
|
1392
1426
|
# [:shallow_path]
|
1393
1427
|
# Prefixes nested shallow routes with the specified path.
|
1394
1428
|
#
|
@@ -1431,6 +1465,9 @@ module ActionDispatch
|
|
1431
1465
|
# Allows you to specify the default value for optional +format+
|
1432
1466
|
# segment or disable it by supplying +false+.
|
1433
1467
|
#
|
1468
|
+
# [:param]
|
1469
|
+
# Allows you to override the default param name of +:id+ in the URL.
|
1470
|
+
#
|
1434
1471
|
# === Examples
|
1435
1472
|
#
|
1436
1473
|
# # routes call <tt>Admin::PostsController</tt>
|
@@ -1571,6 +1608,22 @@ module ActionDispatch
|
|
1571
1608
|
!parent_resource.singleton? && @scope[:shallow]
|
1572
1609
|
end
|
1573
1610
|
|
1611
|
+
def draw(name)
|
1612
|
+
path = @draw_paths.find do |_path|
|
1613
|
+
File.exist? "#{_path}/#{name}.rb"
|
1614
|
+
end
|
1615
|
+
|
1616
|
+
unless path
|
1617
|
+
msg = "Your router tried to #draw the external file #{name}.rb,\n" \
|
1618
|
+
"but the file was not found in:\n\n"
|
1619
|
+
msg += @draw_paths.map { |_path| " * #{_path}" }.join("\n")
|
1620
|
+
raise ArgumentError, msg
|
1621
|
+
end
|
1622
|
+
|
1623
|
+
route_path = "#{path}/#{name}.rb"
|
1624
|
+
instance_eval(File.read(route_path), route_path.to_s)
|
1625
|
+
end
|
1626
|
+
|
1574
1627
|
# Matches a URL pattern to one or more routes.
|
1575
1628
|
# For more information, see match[rdoc-ref:Base#match].
|
1576
1629
|
#
|
@@ -1588,7 +1641,7 @@ module ActionDispatch
|
|
1588
1641
|
when Symbol
|
1589
1642
|
options[:action] = to
|
1590
1643
|
when String
|
1591
|
-
if to
|
1644
|
+
if /#/.match?(to)
|
1592
1645
|
options[:to] = to
|
1593
1646
|
else
|
1594
1647
|
options[:controller] = to
|
@@ -1645,26 +1698,26 @@ module ActionDispatch
|
|
1645
1698
|
end
|
1646
1699
|
|
1647
1700
|
private
|
1648
|
-
|
1649
1701
|
def parent_resource
|
1650
1702
|
@scope[:scope_level_resource]
|
1651
1703
|
end
|
1652
1704
|
|
1653
1705
|
def apply_common_behavior_for(method, resources, options, &block)
|
1654
1706
|
if resources.length > 1
|
1655
|
-
resources.each { |r|
|
1707
|
+
resources.each { |r| public_send(method, r, options, &block) }
|
1656
1708
|
return true
|
1657
1709
|
end
|
1658
1710
|
|
1659
|
-
if options
|
1711
|
+
if options[:shallow]
|
1712
|
+
options.delete(:shallow)
|
1660
1713
|
shallow do
|
1661
|
-
|
1714
|
+
public_send(method, resources.pop, options, &block)
|
1662
1715
|
end
|
1663
1716
|
return true
|
1664
1717
|
end
|
1665
1718
|
|
1666
1719
|
if resource_scope?
|
1667
|
-
nested {
|
1720
|
+
nested { public_send(method, resources.pop, options, &block) }
|
1668
1721
|
return true
|
1669
1722
|
end
|
1670
1723
|
|
@@ -1675,7 +1728,7 @@ module ActionDispatch
|
|
1675
1728
|
scope_options = options.slice!(*RESOURCE_OPTIONS)
|
1676
1729
|
unless scope_options.empty?
|
1677
1730
|
scope(scope_options) do
|
1678
|
-
|
1731
|
+
public_send(method, resources.pop, options, &block)
|
1679
1732
|
end
|
1680
1733
|
return true
|
1681
1734
|
end
|
@@ -1805,7 +1858,7 @@ module ActionDispatch
|
|
1805
1858
|
# and return nil in case it isn't. Otherwise, we pass the invalid name
|
1806
1859
|
# forward so the underlying router engine treats it and raises an exception.
|
1807
1860
|
if as.nil?
|
1808
|
-
candidate unless candidate
|
1861
|
+
candidate unless !candidate.match?(/\A[_a-z]/i) || has_named_route?(candidate)
|
1809
1862
|
else
|
1810
1863
|
candidate
|
1811
1864
|
end
|
@@ -1859,7 +1912,7 @@ module ActionDispatch
|
|
1859
1912
|
options_constraints = options.delete(:constraints) || {}
|
1860
1913
|
|
1861
1914
|
path_types = paths.group_by(&:class)
|
1862
|
-
path_types
|
1915
|
+
(path_types[String] || []).each do |_path|
|
1863
1916
|
route_options = options.dup
|
1864
1917
|
if _path && option_path
|
1865
1918
|
raise ArgumentError, "Ambiguous route definition. Both :path and the route path were specified as strings."
|
@@ -1868,7 +1921,7 @@ module ActionDispatch
|
|
1868
1921
|
decomposed_match(_path, controller, route_options, _path, to, via, formatted, anchor, options_constraints)
|
1869
1922
|
end
|
1870
1923
|
|
1871
|
-
path_types
|
1924
|
+
(path_types[Symbol] || []).each do |action|
|
1872
1925
|
route_options = options.dup
|
1873
1926
|
decomposed_match(action, controller, route_options, option_path, to, via, formatted, anchor, options_constraints)
|
1874
1927
|
end
|
@@ -1881,14 +1934,14 @@ module ActionDispatch
|
|
1881
1934
|
|
1882
1935
|
path_without_format = path.sub(/\(\.:format\)$/, "")
|
1883
1936
|
if using_match_shorthand?(path_without_format)
|
1884
|
-
path_without_format.
|
1937
|
+
path_without_format.delete_prefix("/").sub(%r{/([^/]*)$}, '#\1').tr("-", "_")
|
1885
1938
|
else
|
1886
1939
|
nil
|
1887
1940
|
end
|
1888
1941
|
end
|
1889
1942
|
|
1890
1943
|
def using_match_shorthand?(path)
|
1891
|
-
|
1944
|
+
%r{^/?[-\w]+/[-\w/]+$}.match?(path)
|
1892
1945
|
end
|
1893
1946
|
|
1894
1947
|
def decomposed_match(path, controller, options, _path, to, via, formatted, anchor, options_constraints)
|
@@ -1914,7 +1967,7 @@ module ActionDispatch
|
|
1914
1967
|
|
1915
1968
|
default_action = options.delete(:action) || @scope[:action]
|
1916
1969
|
|
1917
|
-
if
|
1970
|
+
if /^[\w\-\/]+$/.match?(action)
|
1918
1971
|
default_action ||= action.tr("-", "_") unless action.include?("/")
|
1919
1972
|
else
|
1920
1973
|
action = nil
|
@@ -1926,7 +1979,7 @@ module ActionDispatch
|
|
1926
1979
|
name_for_action(options.delete(:as), action)
|
1927
1980
|
end
|
1928
1981
|
|
1929
|
-
path = Mapping.normalize_path URI.
|
1982
|
+
path = Mapping.normalize_path URI::DEFAULT_PARSER.escape(path), formatted
|
1930
1983
|
ast = Journey::Parser.parse path
|
1931
1984
|
|
1932
1985
|
mapping = Mapping.build(@scope, @set, ast, controller, default_action, to, via, formatted, options_constraints, anchor, options)
|
@@ -1934,9 +1987,7 @@ module ActionDispatch
|
|
1934
1987
|
end
|
1935
1988
|
|
1936
1989
|
def match_root_route(options)
|
1937
|
-
|
1938
|
-
args = ["/", { as: name, via: :get }.merge!(options)]
|
1939
|
-
|
1990
|
+
args = ["/", { as: :root, via: :get }.merge(options)]
|
1940
1991
|
match(*args)
|
1941
1992
|
end
|
1942
1993
|
end
|
@@ -2052,7 +2103,7 @@ module ActionDispatch
|
|
2052
2103
|
# of routing helpers, e.g:
|
2053
2104
|
#
|
2054
2105
|
# direct :homepage do
|
2055
|
-
# "
|
2106
|
+
# "https://rubyonrails.org"
|
2056
2107
|
# end
|
2057
2108
|
#
|
2058
2109
|
# direct :commentable do |model|
|
@@ -2251,6 +2302,7 @@ module ActionDispatch
|
|
2251
2302
|
|
2252
2303
|
def initialize(set) #:nodoc:
|
2253
2304
|
@set = set
|
2305
|
+
@draw_paths = set.draw_paths
|
2254
2306
|
@scope = Scope.new(path_names: @set.resources_path_names)
|
2255
2307
|
@concerns = {}
|
2256
2308
|
end
|
@@ -120,8 +120,7 @@ module ActionDispatch
|
|
120
120
|
opts
|
121
121
|
end
|
122
122
|
|
123
|
-
# Returns the path component of a URL for the given record.
|
124
|
-
# <tt>polymorphic_url</tt> with <tt>routing_type: :path</tt>.
|
123
|
+
# Returns the path component of a URL for the given record.
|
125
124
|
def polymorphic_path(record_or_hash_or_array, options = {})
|
126
125
|
if Hash === record_or_hash_or_array
|
127
126
|
options = record_or_hash_or_array.merge(options)
|
@@ -146,6 +145,7 @@ module ActionDispatch
|
|
146
145
|
|
147
146
|
%w(edit new).each do |action|
|
148
147
|
module_eval <<-EOT, __FILE__, __LINE__ + 1
|
148
|
+
# frozen_string_literal: true
|
149
149
|
def #{action}_polymorphic_url(record_or_hash, options = {})
|
150
150
|
polymorphic_url_for_action("#{action}", record_or_hash, options)
|
151
151
|
end
|
@@ -157,7 +157,6 @@ module ActionDispatch
|
|
157
157
|
end
|
158
158
|
|
159
159
|
private
|
160
|
-
|
161
160
|
def polymorphic_url_for_action(action, record_or_hash, options)
|
162
161
|
polymorphic_url(record_or_hash, options.merge(action: action))
|
163
162
|
end
|
@@ -175,15 +174,15 @@ module ActionDispatch
|
|
175
174
|
end
|
176
175
|
|
177
176
|
class HelperMethodBuilder # :nodoc:
|
178
|
-
CACHE = {
|
177
|
+
CACHE = { path: {}, url: {} }
|
179
178
|
|
180
179
|
def self.get(action, type)
|
181
|
-
type = type.
|
180
|
+
type = type.to_sym
|
182
181
|
CACHE[type].fetch(action) { build action, type }
|
183
182
|
end
|
184
183
|
|
185
|
-
def self.url; CACHE[
|
186
|
-
def self.path; CACHE[
|
184
|
+
def self.url; CACHE[:url][nil]; end
|
185
|
+
def self.path; CACHE[:path][nil]; end
|
187
186
|
|
188
187
|
def self.build(action, type)
|
189
188
|
prefix = action ? "#{action}_" : ""
|
@@ -229,9 +228,9 @@ module ActionDispatch
|
|
229
228
|
end
|
230
229
|
|
231
230
|
if options.empty?
|
232
|
-
recipient.
|
231
|
+
recipient.public_send(method, *args)
|
233
232
|
else
|
234
|
-
recipient.
|
233
|
+
recipient.public_send(method, *args, options)
|
235
234
|
end
|
236
235
|
end
|
237
236
|
|
@@ -248,7 +247,7 @@ module ActionDispatch
|
|
248
247
|
end
|
249
248
|
|
250
249
|
def handle_string_call(target, str)
|
251
|
-
target.
|
250
|
+
target.public_send get_method_for_string str
|
252
251
|
end
|
253
252
|
|
254
253
|
def handle_class(klass)
|
@@ -256,7 +255,7 @@ module ActionDispatch
|
|
256
255
|
end
|
257
256
|
|
258
257
|
def handle_class_call(target, klass)
|
259
|
-
target.
|
258
|
+
target.public_send get_method_for_class klass
|
260
259
|
end
|
261
260
|
|
262
261
|
def handle_model(record)
|
@@ -278,7 +277,7 @@ module ActionDispatch
|
|
278
277
|
mapping.call(target, [record], suffix == "path")
|
279
278
|
else
|
280
279
|
method, args = handle_model(record)
|
281
|
-
target.
|
280
|
+
target.public_send(method, *args)
|
282
281
|
end
|
283
282
|
end
|
284
283
|
|
@@ -328,7 +327,6 @@ module ActionDispatch
|
|
328
327
|
end
|
329
328
|
|
330
329
|
private
|
331
|
-
|
332
330
|
def polymorphic_mapping(target, record)
|
333
331
|
if record.respond_to?(:to_model)
|
334
332
|
target._routes.polymorphic_mappings[record.to_model.model_name.name]
|
@@ -347,8 +345,8 @@ module ActionDispatch
|
|
347
345
|
end
|
348
346
|
|
349
347
|
[nil, "new", "edit"].each do |action|
|
350
|
-
CACHE[
|
351
|
-
CACHE[
|
348
|
+
CACHE[:url][action] = build action, "url"
|
349
|
+
CACHE[:path][action] = build action, "path"
|
352
350
|
end
|
353
351
|
end
|
354
352
|
end
|