actionpack 5.2.0 → 6.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +408 -190
- 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 +2 -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 -24
- 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 +33 -23
- data/lib/action_controller/metal/permissions_policy.rb +46 -0
- data/lib/action_controller/metal/redirecting.rb +7 -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 +89 -36
- 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 +181 -69
- data/lib/action_controller/metal/url_for.rb +1 -1
- data/lib/action_controller/metal.rb +12 -10
- 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 +81 -70
- data/lib/action_controller.rb +7 -4
- data/lib/action_dispatch/http/cache.rb +34 -28
- data/lib/action_dispatch/http/content_disposition.rb +45 -0
- data/lib/action_dispatch/http/content_security_policy.rb +47 -24
- 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 +31 -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 +21 -22
- 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 +1 -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 +2 -2
- data/lib/action_dispatch/middleware/host_authorization.rb +130 -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 +15 -2
- data/lib/action_dispatch/middleware/session/cache_store.rb +11 -6
- data/lib/action_dispatch/middleware/session/cookie_store.rb +24 -19
- data/lib/action_dispatch/middleware/show_exceptions.rb +3 -2
- data/lib/action_dispatch/middleware/ssl.rb +20 -15
- data/lib/action_dispatch/middleware/stack.rb +57 -3
- 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 +17 -10
- data/lib/action_dispatch/request/utils.rb +28 -2
- data/lib/action_dispatch/routing/inspector.rb +101 -53
- data/lib/action_dispatch/routing/mapper.rb +156 -103
- data/lib/action_dispatch/routing/polymorphic_routes.rb +21 -19
- 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 +3 -3
- 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 -6
- 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 +61 -28
- data/lib/action_dispatch/testing/request_encoder.rb +3 -3
- 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 +14 -7
- data/lib/action_pack/gem_version.rb +3 -3
- data/lib/action_pack.rb +1 -1
- metadata +39 -22
- 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
|
|
@@ -664,10 +678,11 @@ module ActionDispatch
|
|
|
664
678
|
def define_generate_prefix(app, name)
|
|
665
679
|
_route = @set.named_routes.get name
|
|
666
680
|
_routes = @set
|
|
681
|
+
_url_helpers = @set.url_helpers
|
|
667
682
|
|
|
668
683
|
script_namer = ->(options) do
|
|
669
684
|
prefix_options = options.slice(*_route.segment_keys)
|
|
670
|
-
prefix_options[:relative_url_root] = ""
|
|
685
|
+
prefix_options[:relative_url_root] = ""
|
|
671
686
|
|
|
672
687
|
if options[:_recall]
|
|
673
688
|
prefix_options.reverse_merge!(options[:_recall].slice(*_route.segment_keys))
|
|
@@ -675,7 +690,7 @@ module ActionDispatch
|
|
|
675
690
|
|
|
676
691
|
# We must actually delete prefix segment keys to avoid passing them to next url_for.
|
|
677
692
|
_route.segment_keys.each { |k| options.delete(k) }
|
|
678
|
-
|
|
693
|
+
_url_helpers.public_send("#{name}_path", prefix_options)
|
|
679
694
|
end
|
|
680
695
|
|
|
681
696
|
app.routes.define_mounted_helper(name, script_namer)
|
|
@@ -735,6 +750,14 @@ module ActionDispatch
|
|
|
735
750
|
map_method(:delete, args, &block)
|
|
736
751
|
end
|
|
737
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
|
+
|
|
738
761
|
private
|
|
739
762
|
def map_method(method, args, &block)
|
|
740
763
|
options = args.extract_options!
|
|
@@ -982,7 +1005,7 @@ module ActionDispatch
|
|
|
982
1005
|
#
|
|
983
1006
|
# Requests to routes can be constrained based on specific criteria:
|
|
984
1007
|
#
|
|
985
|
-
# constraints(-> (req) { req.env["HTTP_USER_AGENT"]
|
|
1008
|
+
# constraints(-> (req) { /iPhone/.match?(req.env["HTTP_USER_AGENT"]) }) do
|
|
986
1009
|
# resources :iphones
|
|
987
1010
|
# end
|
|
988
1011
|
#
|
|
@@ -992,7 +1015,7 @@ module ActionDispatch
|
|
|
992
1015
|
#
|
|
993
1016
|
# class Iphone
|
|
994
1017
|
# def self.matches?(request)
|
|
995
|
-
# request.env["HTTP_USER_AGENT"]
|
|
1018
|
+
# /iPhone/.match?(request.env["HTTP_USER_AGENT"])
|
|
996
1019
|
# end
|
|
997
1020
|
# end
|
|
998
1021
|
#
|
|
@@ -1137,6 +1160,10 @@ module ActionDispatch
|
|
|
1137
1160
|
attr_reader :controller, :path, :param
|
|
1138
1161
|
|
|
1139
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
|
+
|
|
1140
1167
|
@name = entities.to_s
|
|
1141
1168
|
@path = (options[:path] || @name).to_s
|
|
1142
1169
|
@controller = (options[:controller] || @name).to_s
|
|
@@ -1158,10 +1185,16 @@ module ActionDispatch
|
|
|
1158
1185
|
end
|
|
1159
1186
|
|
|
1160
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
|
|
1161
1196
|
if @only
|
|
1162
1197
|
Array(@only).map(&:to_sym)
|
|
1163
|
-
elsif @except
|
|
1164
|
-
default_actions - Array(@except).map(&:to_sym)
|
|
1165
1198
|
else
|
|
1166
1199
|
default_actions
|
|
1167
1200
|
end
|
|
@@ -1388,6 +1421,8 @@ module ActionDispatch
|
|
|
1388
1421
|
# as a comment on a blog post like <tt>/posts/a-long-permalink/comments/1234</tt>
|
|
1389
1422
|
# to be shortened to just <tt>/comments/1234</tt>.
|
|
1390
1423
|
#
|
|
1424
|
+
# Set <tt>shallow: false</tt> on a child resource to ignore a parent's shallow parameter.
|
|
1425
|
+
#
|
|
1391
1426
|
# [:shallow_path]
|
|
1392
1427
|
# Prefixes nested shallow routes with the specified path.
|
|
1393
1428
|
#
|
|
@@ -1430,6 +1465,9 @@ module ActionDispatch
|
|
|
1430
1465
|
# Allows you to specify the default value for optional +format+
|
|
1431
1466
|
# segment or disable it by supplying +false+.
|
|
1432
1467
|
#
|
|
1468
|
+
# [:param]
|
|
1469
|
+
# Allows you to override the default param name of +:id+ in the URL.
|
|
1470
|
+
#
|
|
1433
1471
|
# === Examples
|
|
1434
1472
|
#
|
|
1435
1473
|
# # routes call <tt>Admin::PostsController</tt>
|
|
@@ -1570,6 +1608,22 @@ module ActionDispatch
|
|
|
1570
1608
|
!parent_resource.singleton? && @scope[:shallow]
|
|
1571
1609
|
end
|
|
1572
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
|
+
|
|
1573
1627
|
# Matches a URL pattern to one or more routes.
|
|
1574
1628
|
# For more information, see match[rdoc-ref:Base#match].
|
|
1575
1629
|
#
|
|
@@ -1587,7 +1641,7 @@ module ActionDispatch
|
|
|
1587
1641
|
when Symbol
|
|
1588
1642
|
options[:action] = to
|
|
1589
1643
|
when String
|
|
1590
|
-
if to
|
|
1644
|
+
if /#/.match?(to)
|
|
1591
1645
|
options[:to] = to
|
|
1592
1646
|
else
|
|
1593
1647
|
options[:controller] = to
|
|
@@ -1644,26 +1698,26 @@ module ActionDispatch
|
|
|
1644
1698
|
end
|
|
1645
1699
|
|
|
1646
1700
|
private
|
|
1647
|
-
|
|
1648
1701
|
def parent_resource
|
|
1649
1702
|
@scope[:scope_level_resource]
|
|
1650
1703
|
end
|
|
1651
1704
|
|
|
1652
1705
|
def apply_common_behavior_for(method, resources, options, &block)
|
|
1653
1706
|
if resources.length > 1
|
|
1654
|
-
resources.each { |r|
|
|
1707
|
+
resources.each { |r| public_send(method, r, options, &block) }
|
|
1655
1708
|
return true
|
|
1656
1709
|
end
|
|
1657
1710
|
|
|
1658
|
-
if options
|
|
1711
|
+
if options[:shallow]
|
|
1712
|
+
options.delete(:shallow)
|
|
1659
1713
|
shallow do
|
|
1660
|
-
|
|
1714
|
+
public_send(method, resources.pop, options, &block)
|
|
1661
1715
|
end
|
|
1662
1716
|
return true
|
|
1663
1717
|
end
|
|
1664
1718
|
|
|
1665
1719
|
if resource_scope?
|
|
1666
|
-
nested {
|
|
1720
|
+
nested { public_send(method, resources.pop, options, &block) }
|
|
1667
1721
|
return true
|
|
1668
1722
|
end
|
|
1669
1723
|
|
|
@@ -1674,7 +1728,7 @@ module ActionDispatch
|
|
|
1674
1728
|
scope_options = options.slice!(*RESOURCE_OPTIONS)
|
|
1675
1729
|
unless scope_options.empty?
|
|
1676
1730
|
scope(scope_options) do
|
|
1677
|
-
|
|
1731
|
+
public_send(method, resources.pop, options, &block)
|
|
1678
1732
|
end
|
|
1679
1733
|
return true
|
|
1680
1734
|
end
|
|
@@ -1804,7 +1858,7 @@ module ActionDispatch
|
|
|
1804
1858
|
# and return nil in case it isn't. Otherwise, we pass the invalid name
|
|
1805
1859
|
# forward so the underlying router engine treats it and raises an exception.
|
|
1806
1860
|
if as.nil?
|
|
1807
|
-
candidate unless candidate
|
|
1861
|
+
candidate unless !candidate.match?(/\A[_a-z]/i) || has_named_route?(candidate)
|
|
1808
1862
|
else
|
|
1809
1863
|
candidate
|
|
1810
1864
|
end
|
|
@@ -1858,7 +1912,7 @@ module ActionDispatch
|
|
|
1858
1912
|
options_constraints = options.delete(:constraints) || {}
|
|
1859
1913
|
|
|
1860
1914
|
path_types = paths.group_by(&:class)
|
|
1861
|
-
path_types
|
|
1915
|
+
(path_types[String] || []).each do |_path|
|
|
1862
1916
|
route_options = options.dup
|
|
1863
1917
|
if _path && option_path
|
|
1864
1918
|
raise ArgumentError, "Ambiguous route definition. Both :path and the route path were specified as strings."
|
|
@@ -1867,7 +1921,7 @@ module ActionDispatch
|
|
|
1867
1921
|
decomposed_match(_path, controller, route_options, _path, to, via, formatted, anchor, options_constraints)
|
|
1868
1922
|
end
|
|
1869
1923
|
|
|
1870
|
-
path_types
|
|
1924
|
+
(path_types[Symbol] || []).each do |action|
|
|
1871
1925
|
route_options = options.dup
|
|
1872
1926
|
decomposed_match(action, controller, route_options, option_path, to, via, formatted, anchor, options_constraints)
|
|
1873
1927
|
end
|
|
@@ -1880,14 +1934,14 @@ module ActionDispatch
|
|
|
1880
1934
|
|
|
1881
1935
|
path_without_format = path.sub(/\(\.:format\)$/, "")
|
|
1882
1936
|
if using_match_shorthand?(path_without_format)
|
|
1883
|
-
path_without_format.
|
|
1937
|
+
path_without_format.delete_prefix("/").sub(%r{/([^/]*)$}, '#\1').tr("-", "_")
|
|
1884
1938
|
else
|
|
1885
1939
|
nil
|
|
1886
1940
|
end
|
|
1887
1941
|
end
|
|
1888
1942
|
|
|
1889
1943
|
def using_match_shorthand?(path)
|
|
1890
|
-
|
|
1944
|
+
%r{^/?[-\w]+/[-\w/]+$}.match?(path)
|
|
1891
1945
|
end
|
|
1892
1946
|
|
|
1893
1947
|
def decomposed_match(path, controller, options, _path, to, via, formatted, anchor, options_constraints)
|
|
@@ -1913,7 +1967,7 @@ module ActionDispatch
|
|
|
1913
1967
|
|
|
1914
1968
|
default_action = options.delete(:action) || @scope[:action]
|
|
1915
1969
|
|
|
1916
|
-
if
|
|
1970
|
+
if /^[\w\-\/]+$/.match?(action)
|
|
1917
1971
|
default_action ||= action.tr("-", "_") unless action.include?("/")
|
|
1918
1972
|
else
|
|
1919
1973
|
action = nil
|
|
@@ -1925,7 +1979,7 @@ module ActionDispatch
|
|
|
1925
1979
|
name_for_action(options.delete(:as), action)
|
|
1926
1980
|
end
|
|
1927
1981
|
|
|
1928
|
-
path = Mapping.normalize_path URI.
|
|
1982
|
+
path = Mapping.normalize_path URI::DEFAULT_PARSER.escape(path), formatted
|
|
1929
1983
|
ast = Journey::Parser.parse path
|
|
1930
1984
|
|
|
1931
1985
|
mapping = Mapping.build(@scope, @set, ast, controller, default_action, to, via, formatted, options_constraints, anchor, options)
|
|
@@ -1933,9 +1987,7 @@ module ActionDispatch
|
|
|
1933
1987
|
end
|
|
1934
1988
|
|
|
1935
1989
|
def match_root_route(options)
|
|
1936
|
-
|
|
1937
|
-
args = ["/", { as: name, via: :get }.merge!(options)]
|
|
1938
|
-
|
|
1990
|
+
args = ["/", { as: :root, via: :get }.merge(options)]
|
|
1939
1991
|
match(*args)
|
|
1940
1992
|
end
|
|
1941
1993
|
end
|
|
@@ -2051,7 +2103,7 @@ module ActionDispatch
|
|
|
2051
2103
|
# of routing helpers, e.g:
|
|
2052
2104
|
#
|
|
2053
2105
|
# direct :homepage do
|
|
2054
|
-
# "
|
|
2106
|
+
# "https://rubyonrails.org"
|
|
2055
2107
|
# end
|
|
2056
2108
|
#
|
|
2057
2109
|
# direct :commentable do |model|
|
|
@@ -2250,6 +2302,7 @@ module ActionDispatch
|
|
|
2250
2302
|
|
|
2251
2303
|
def initialize(set) #:nodoc:
|
|
2252
2304
|
@set = set
|
|
2305
|
+
@draw_paths = set.draw_paths
|
|
2253
2306
|
@scope = Scope.new(path_names: @set.resources_path_names)
|
|
2254
2307
|
@concerns = {}
|
|
2255
2308
|
end
|