actionpack 4.2.10 → 7.2.0.rc1
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 +5 -5
- data/CHANGELOG.md +86 -600
- data/MIT-LICENSE +1 -1
- data/README.rdoc +13 -14
- data/lib/abstract_controller/asset_paths.rb +5 -1
- data/lib/abstract_controller/base.rb +166 -136
- data/lib/abstract_controller/caching/fragments.rb +149 -0
- data/lib/abstract_controller/caching.rb +68 -0
- data/lib/abstract_controller/callbacks.rb +126 -57
- data/lib/abstract_controller/collector.rb +13 -15
- data/lib/abstract_controller/deprecator.rb +9 -0
- data/lib/abstract_controller/error.rb +8 -0
- data/lib/abstract_controller/helpers.rb +181 -132
- data/lib/abstract_controller/logger.rb +5 -1
- data/lib/abstract_controller/railties/routes_helpers.rb +10 -3
- data/lib/abstract_controller/rendering.rb +56 -56
- data/lib/abstract_controller/translation.rb +29 -15
- data/lib/abstract_controller/url_for.rb +15 -11
- data/lib/abstract_controller.rb +21 -5
- data/lib/action_controller/api/api_rendering.rb +18 -0
- data/lib/action_controller/api.rb +154 -0
- data/lib/action_controller/base.rb +219 -155
- data/lib/action_controller/caching.rb +28 -68
- data/lib/action_controller/deprecator.rb +9 -0
- data/lib/action_controller/form_builder.rb +55 -0
- data/lib/action_controller/log_subscriber.rb +35 -22
- data/lib/action_controller/metal/allow_browser.rb +119 -0
- data/lib/action_controller/metal/basic_implicit_render.rb +17 -0
- data/lib/action_controller/metal/conditional_get.rb +259 -122
- data/lib/action_controller/metal/content_security_policy.rb +86 -0
- data/lib/action_controller/metal/cookies.rb +9 -5
- data/lib/action_controller/metal/data_streaming.rb +87 -104
- data/lib/action_controller/metal/default_headers.rb +21 -0
- data/lib/action_controller/metal/etag_with_flash.rb +22 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +35 -26
- data/lib/action_controller/metal/exceptions.rb +71 -24
- data/lib/action_controller/metal/flash.rb +26 -19
- data/lib/action_controller/metal/head.rb +45 -36
- data/lib/action_controller/metal/helpers.rb +80 -64
- data/lib/action_controller/metal/http_authentication.rb +297 -244
- data/lib/action_controller/metal/implicit_render.rb +57 -9
- data/lib/action_controller/metal/instrumentation.rb +76 -64
- data/lib/action_controller/metal/live.rb +238 -176
- data/lib/action_controller/metal/logging.rb +22 -0
- data/lib/action_controller/metal/mime_responds.rb +177 -166
- data/lib/action_controller/metal/parameter_encoding.rb +84 -0
- data/lib/action_controller/metal/params_wrapper.rb +145 -118
- data/lib/action_controller/metal/permissions_policy.rb +38 -0
- data/lib/action_controller/metal/rate_limiting.rb +62 -0
- data/lib/action_controller/metal/redirecting.rb +203 -64
- data/lib/action_controller/metal/renderers.rb +108 -65
- data/lib/action_controller/metal/rendering.rb +216 -56
- data/lib/action_controller/metal/request_forgery_protection.rb +496 -163
- data/lib/action_controller/metal/rescue.rb +19 -21
- data/lib/action_controller/metal/streaming.rb +179 -138
- data/lib/action_controller/metal/strong_parameters.rb +1058 -382
- data/lib/action_controller/metal/testing.rb +11 -17
- data/lib/action_controller/metal/url_for.rb +37 -21
- data/lib/action_controller/metal.rb +236 -138
- data/lib/action_controller/railtie.rb +89 -11
- data/lib/action_controller/railties/helpers.rb +5 -1
- data/lib/action_controller/renderer.rb +161 -0
- data/lib/action_controller/template_assertions.rb +13 -0
- data/lib/action_controller/test_case.rb +425 -497
- data/lib/action_controller.rb +44 -22
- data/lib/action_dispatch/constants.rb +34 -0
- data/lib/action_dispatch/deprecator.rb +9 -0
- data/lib/action_dispatch/http/cache.rb +119 -63
- data/lib/action_dispatch/http/content_disposition.rb +47 -0
- data/lib/action_dispatch/http/content_security_policy.rb +364 -0
- data/lib/action_dispatch/http/filter_parameters.rb +36 -34
- data/lib/action_dispatch/http/filter_redirect.rb +24 -12
- data/lib/action_dispatch/http/headers.rb +66 -31
- data/lib/action_dispatch/http/mime_negotiation.rb +106 -75
- data/lib/action_dispatch/http/mime_type.rb +196 -136
- data/lib/action_dispatch/http/mime_types.rb +25 -7
- data/lib/action_dispatch/http/parameters.rb +97 -45
- data/lib/action_dispatch/http/permissions_policy.rb +187 -0
- data/lib/action_dispatch/http/rack_cache.rb +6 -0
- data/lib/action_dispatch/http/request.rb +299 -170
- data/lib/action_dispatch/http/response.rb +311 -160
- data/lib/action_dispatch/http/upload.rb +52 -23
- data/lib/action_dispatch/http/url.rb +201 -125
- data/lib/action_dispatch/journey/formatter.rb +110 -50
- data/lib/action_dispatch/journey/gtg/builder.rb +37 -50
- data/lib/action_dispatch/journey/gtg/simulator.rb +20 -17
- data/lib/action_dispatch/journey/gtg/transition_table.rb +96 -36
- data/lib/action_dispatch/journey/nfa/dot.rb +5 -14
- data/lib/action_dispatch/journey/nodes/node.rb +100 -20
- data/lib/action_dispatch/journey/parser.rb +19 -17
- data/lib/action_dispatch/journey/parser.y +4 -3
- data/lib/action_dispatch/journey/parser_extras.rb +14 -4
- data/lib/action_dispatch/journey/path/pattern.rb +79 -63
- data/lib/action_dispatch/journey/route.rb +108 -44
- data/lib/action_dispatch/journey/router/utils.rb +41 -29
- data/lib/action_dispatch/journey/router.rb +64 -57
- data/lib/action_dispatch/journey/routes.rb +23 -21
- data/lib/action_dispatch/journey/scanner.rb +28 -17
- data/lib/action_dispatch/journey/visitors.rb +100 -54
- data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
- data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
- data/lib/action_dispatch/journey.rb +7 -5
- data/lib/action_dispatch/log_subscriber.rb +25 -0
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
- data/lib/action_dispatch/middleware/assume_ssl.rb +27 -0
- data/lib/action_dispatch/middleware/callbacks.rb +7 -6
- data/lib/action_dispatch/middleware/cookies.rb +471 -328
- data/lib/action_dispatch/middleware/debug_exceptions.rb +149 -66
- data/lib/action_dispatch/middleware/debug_locks.rb +129 -0
- data/lib/action_dispatch/middleware/debug_view.rb +73 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +275 -73
- data/lib/action_dispatch/middleware/executor.rb +32 -0
- data/lib/action_dispatch/middleware/flash.rb +143 -101
- data/lib/action_dispatch/middleware/host_authorization.rb +171 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +36 -27
- data/lib/action_dispatch/middleware/reloader.rb +10 -92
- data/lib/action_dispatch/middleware/remote_ip.rb +133 -107
- data/lib/action_dispatch/middleware/request_id.rb +29 -15
- data/lib/action_dispatch/middleware/server_timing.rb +78 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +49 -27
- data/lib/action_dispatch/middleware/session/cache_store.rb +33 -16
- data/lib/action_dispatch/middleware/session/cookie_store.rb +86 -80
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +15 -3
- data/lib/action_dispatch/middleware/show_exceptions.rb +66 -36
- data/lib/action_dispatch/middleware/ssl.rb +134 -36
- data/lib/action_dispatch/middleware/stack.rb +109 -44
- data/lib/action_dispatch/middleware/static.rb +159 -90
- 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 +7 -24
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +36 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +46 -36
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +12 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +26 -7
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +16 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +139 -15
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +23 -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 +6 -6
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +7 -7
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +9 -9
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +7 -4
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +125 -93
- data/lib/action_dispatch/railtie.rb +44 -16
- data/lib/action_dispatch/request/session.rb +159 -69
- data/lib/action_dispatch/request/utils.rb +97 -23
- data/lib/action_dispatch/routing/endpoint.rb +11 -2
- data/lib/action_dispatch/routing/inspector.rb +195 -106
- data/lib/action_dispatch/routing/mapper.rb +1338 -955
- data/lib/action_dispatch/routing/polymorphic_routes.rb +234 -201
- data/lib/action_dispatch/routing/redirection.rb +78 -51
- data/lib/action_dispatch/routing/route_set.rb +460 -374
- data/lib/action_dispatch/routing/routes_proxy.rb +36 -12
- data/lib/action_dispatch/routing/url_for.rb +172 -124
- data/lib/action_dispatch/routing.rb +159 -158
- data/lib/action_dispatch/system_test_case.rb +206 -0
- data/lib/action_dispatch/system_testing/browser.rb +84 -0
- data/lib/action_dispatch/system_testing/driver.rb +85 -0
- data/lib/action_dispatch/system_testing/server.rb +33 -0
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +164 -0
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +23 -0
- data/lib/action_dispatch/testing/assertion_response.rb +48 -0
- data/lib/action_dispatch/testing/assertions/response.rb +71 -39
- data/lib/action_dispatch/testing/assertions/routing.rb +228 -103
- data/lib/action_dispatch/testing/assertions.rb +9 -6
- data/lib/action_dispatch/testing/integration.rb +486 -306
- data/lib/action_dispatch/testing/request_encoder.rb +60 -0
- data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
- data/lib/action_dispatch/testing/test_process.rb +35 -22
- data/lib/action_dispatch/testing/test_request.rb +29 -34
- data/lib/action_dispatch/testing/test_response.rb +48 -15
- data/lib/action_dispatch.rb +82 -40
- data/lib/action_pack/gem_version.rb +8 -4
- data/lib/action_pack/version.rb +6 -2
- data/lib/action_pack.rb +21 -18
- metadata +146 -56
- data/lib/action_controller/caching/fragments.rb +0 -103
- data/lib/action_controller/metal/force_ssl.rb +0 -97
- data/lib/action_controller/metal/hide_actions.rb +0 -40
- data/lib/action_controller/metal/rack_delegation.rb +0 -32
- data/lib/action_controller/middleware.rb +0 -39
- data/lib/action_controller/model_naming.rb +0 -12
- data/lib/action_dispatch/http/parameter_filter.rb +0 -72
- data/lib/action_dispatch/journey/backwards.rb +0 -5
- data/lib/action_dispatch/journey/nfa/builder.rb +0 -76
- data/lib/action_dispatch/journey/nfa/simulator.rb +0 -47
- data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -163
- data/lib/action_dispatch/journey/router/strexp.rb +0 -27
- data/lib/action_dispatch/middleware/params_parser.rb +0 -60
- data/lib/action_dispatch/middleware/templates/rescues/_source.erb +0 -27
- data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
- data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
- data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
@@ -1,86 +1,81 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :markup: markdown
|
4
|
+
|
5
|
+
require "action_dispatch/journey"
|
6
|
+
require "active_support/core_ext/object/to_query"
|
7
|
+
require "active_support/core_ext/module/redefine_method"
|
8
|
+
require "active_support/core_ext/module/remove_method"
|
9
|
+
require "active_support/core_ext/array/extract_options"
|
10
|
+
require "action_controller/metal/exceptions"
|
11
|
+
require "action_dispatch/routing/endpoint"
|
12
12
|
|
13
13
|
module ActionDispatch
|
14
14
|
module Routing
|
15
|
-
#
|
15
|
+
# The RouteSet contains a collection of Route instances, representing the routes
|
16
|
+
# typically defined in `config/routes.rb`.
|
16
17
|
class RouteSet
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
|
18
|
+
# Returns a Route matching the given requirements, or `nil` if none are found.
|
19
|
+
#
|
20
|
+
# This is intended for use by tools such as Language Servers.
|
21
|
+
#
|
22
|
+
# Given the routes are defined as:
|
23
|
+
#
|
24
|
+
# resources :posts
|
25
|
+
#
|
26
|
+
# Then the following will return the Route for the `show` action:
|
27
|
+
#
|
28
|
+
# Rails.application.routes.from_requirements(controller: "posts", action: "show")
|
29
|
+
def from_requirements(requirements)
|
30
|
+
routes.find { |route| route.requirements == requirements }
|
31
|
+
end
|
32
|
+
# :stopdoc:
|
22
33
|
|
23
|
-
|
34
|
+
# Since the router holds references to many parts of the system like engines,
|
35
|
+
# controllers and the application itself, inspecting the route set can actually
|
36
|
+
# be really slow, therefore we default alias inspect to to_s.
|
37
|
+
alias inspect to_s
|
24
38
|
|
25
39
|
class Dispatcher < Routing::Endpoint
|
26
|
-
def initialize(
|
27
|
-
@
|
40
|
+
def initialize(raise_on_name_error)
|
41
|
+
@raise_on_name_error = raise_on_name_error
|
28
42
|
end
|
29
43
|
|
30
44
|
def dispatcher?; true; end
|
31
45
|
|
32
46
|
def serve(req)
|
33
|
-
req.
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
47
|
+
params = req.path_parameters
|
48
|
+
controller = controller req
|
49
|
+
res = controller.make_response! req
|
50
|
+
dispatch(controller, params[:action], req, res)
|
51
|
+
rescue ActionController::RoutingError
|
52
|
+
if @raise_on_name_error
|
53
|
+
raise
|
54
|
+
else
|
55
|
+
[404, { Constants::X_CASCADE => "pass" }, []]
|
41
56
|
end
|
42
|
-
|
43
|
-
dispatch(controller, params[:action], req.env)
|
44
|
-
end
|
45
|
-
|
46
|
-
def prepare_params!(params)
|
47
|
-
normalize_controller!(params)
|
48
|
-
merge_default_action!(params)
|
49
57
|
end
|
50
58
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
# controller, it means we should respect the @scope[:module] parameter.
|
57
|
-
def controller(params, default_controller=true)
|
58
|
-
if params && params.key?(:controller)
|
59
|
-
controller_param = params[:controller]
|
60
|
-
controller_reference(controller_param)
|
59
|
+
private
|
60
|
+
def controller(req)
|
61
|
+
req.controller_class
|
62
|
+
rescue NameError => e
|
63
|
+
raise ActionController::RoutingError, e.message, e.backtrace
|
61
64
|
end
|
62
|
-
rescue NameError => e
|
63
|
-
raise ActionController::RoutingError, e.message, e.backtrace if default_controller
|
64
|
-
end
|
65
|
-
|
66
|
-
private
|
67
65
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
def dispatch(controller, action, env)
|
74
|
-
controller.action(action).call(env)
|
75
|
-
end
|
66
|
+
def dispatch(controller, action, req, res)
|
67
|
+
controller.dispatch(action, req, res)
|
68
|
+
end
|
69
|
+
end
|
76
70
|
|
77
|
-
|
78
|
-
|
71
|
+
class StaticDispatcher < Dispatcher
|
72
|
+
def initialize(controller_class)
|
73
|
+
super(false)
|
74
|
+
@controller_class = controller_class
|
79
75
|
end
|
80
76
|
|
81
|
-
|
82
|
-
|
83
|
-
end
|
77
|
+
private
|
78
|
+
def controller(_); @controller_class; end
|
84
79
|
end
|
85
80
|
|
86
81
|
# A NamedRouteCollection instance is a collection of named routes, and also
|
@@ -88,10 +83,11 @@ module ActionDispatch
|
|
88
83
|
# named routes.
|
89
84
|
class NamedRouteCollection
|
90
85
|
include Enumerable
|
91
|
-
attr_reader :routes, :url_helpers_module
|
86
|
+
attr_reader :routes, :url_helpers_module, :path_helpers_module
|
87
|
+
private :routes
|
92
88
|
|
93
89
|
def initialize
|
94
|
-
@routes
|
90
|
+
@routes = {}
|
95
91
|
@path_helpers = Set.new
|
96
92
|
@url_helpers = Set.new
|
97
93
|
@url_helpers_module = Module.new
|
@@ -103,25 +99,17 @@ module ActionDispatch
|
|
103
99
|
@path_helpers.include?(key) || @url_helpers.include?(key)
|
104
100
|
end
|
105
101
|
|
106
|
-
def helpers
|
107
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
108
|
-
`named_routes.helpers` is deprecated, please use `route_defined?(route_name)`
|
109
|
-
to see if a named route was defined.
|
110
|
-
MSG
|
111
|
-
@path_helpers + @url_helpers
|
112
|
-
end
|
113
|
-
|
114
102
|
def helper_names
|
115
103
|
@path_helpers.map(&:to_s) + @url_helpers.map(&:to_s)
|
116
104
|
end
|
117
105
|
|
118
106
|
def clear!
|
119
107
|
@path_helpers.each do |helper|
|
120
|
-
@path_helpers_module.
|
108
|
+
@path_helpers_module.remove_method helper
|
121
109
|
end
|
122
110
|
|
123
111
|
@url_helpers.each do |helper|
|
124
|
-
@url_helpers_module.
|
112
|
+
@url_helpers_module.remove_method helper
|
125
113
|
end
|
126
114
|
|
127
115
|
@routes.clear
|
@@ -135,12 +123,14 @@ module ActionDispatch
|
|
135
123
|
url_name = :"#{name}_url"
|
136
124
|
|
137
125
|
if routes.key? key
|
138
|
-
@path_helpers_module.
|
139
|
-
@url_helpers_module.
|
126
|
+
@path_helpers_module.undef_method path_name
|
127
|
+
@url_helpers_module.undef_method url_name
|
140
128
|
end
|
141
129
|
routes[key] = route
|
142
|
-
|
143
|
-
|
130
|
+
|
131
|
+
helper = UrlHelper.create(route, route.defaults, name)
|
132
|
+
define_url_helper @path_helpers_module, path_name, helper, PATH
|
133
|
+
define_url_helper @url_helpers_module, url_name, helper, UNKNOWN
|
144
134
|
|
145
135
|
@path_helpers << path_name
|
146
136
|
@url_helpers << url_name
|
@@ -151,6 +141,7 @@ module ActionDispatch
|
|
151
141
|
end
|
152
142
|
|
153
143
|
def key?(name)
|
144
|
+
return unless name
|
154
145
|
routes.key? name.to_sym
|
155
146
|
end
|
156
147
|
|
@@ -158,8 +149,8 @@ module ActionDispatch
|
|
158
149
|
alias [] get
|
159
150
|
alias clear clear!
|
160
151
|
|
161
|
-
def each
|
162
|
-
routes.each
|
152
|
+
def each(&block)
|
153
|
+
routes.each(&block)
|
163
154
|
self
|
164
155
|
end
|
165
156
|
|
@@ -171,53 +162,71 @@ module ActionDispatch
|
|
171
162
|
routes.length
|
172
163
|
end
|
173
164
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
165
|
+
# Given a `name`, defines name_path and name_url helpers. Used by 'direct',
|
166
|
+
# 'resolve', and 'polymorphic' route helpers.
|
167
|
+
def add_url_helper(name, defaults, &block)
|
168
|
+
helper = CustomUrlHelper.new(name, defaults, &block)
|
169
|
+
path_name = :"#{name}_path"
|
170
|
+
url_name = :"#{name}_url"
|
180
171
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
172
|
+
@path_helpers_module.module_eval do
|
173
|
+
redefine_method(path_name) do |*args|
|
174
|
+
helper.call(self, args, true)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
@url_helpers_module.module_eval do
|
179
|
+
redefine_method(url_name) do |*args|
|
180
|
+
helper.call(self, args, false)
|
187
181
|
end
|
188
|
-
else
|
189
|
-
@path_helpers_module
|
190
182
|
end
|
183
|
+
|
184
|
+
@path_helpers << path_name
|
185
|
+
@url_helpers << url_name
|
186
|
+
|
187
|
+
self
|
191
188
|
end
|
192
189
|
|
193
190
|
class UrlHelper
|
194
|
-
def self.create(route, options, route_name
|
191
|
+
def self.create(route, options, route_name)
|
195
192
|
if optimize_helper?(route)
|
196
|
-
OptimizedUrlHelper.new(route, options, route_name
|
193
|
+
OptimizedUrlHelper.new(route, options, route_name)
|
197
194
|
else
|
198
|
-
new
|
195
|
+
new(route, options, route_name)
|
199
196
|
end
|
200
197
|
end
|
201
198
|
|
202
199
|
def self.optimize_helper?(route)
|
203
|
-
|
200
|
+
route.path.requirements.empty? && !route.glob?
|
204
201
|
end
|
205
202
|
|
206
|
-
attr_reader :
|
203
|
+
attr_reader :route_name
|
207
204
|
|
208
205
|
class OptimizedUrlHelper < UrlHelper
|
209
206
|
attr_reader :arg_size
|
210
207
|
|
211
|
-
def initialize(route, options, route_name
|
208
|
+
def initialize(route, options, route_name)
|
212
209
|
super
|
213
210
|
@required_parts = @route.required_parts
|
214
211
|
@arg_size = @required_parts.size
|
215
212
|
end
|
216
213
|
|
217
|
-
def call(t, args, inner_options)
|
214
|
+
def call(t, method_name, args, inner_options, url_strategy)
|
218
215
|
if args.size == arg_size && !inner_options && optimize_routes_generation?(t)
|
219
216
|
options = t.url_options.merge @options
|
220
|
-
|
217
|
+
path = optimized_helper(args)
|
218
|
+
path << "/" if options[:trailing_slash] && !path.end_with?("/")
|
219
|
+
options[:path] = path
|
220
|
+
|
221
|
+
original_script_name = options.delete(:original_script_name)
|
222
|
+
script_name = t._routes.find_script_name(options)
|
223
|
+
|
224
|
+
if original_script_name
|
225
|
+
script_name = original_script_name + script_name
|
226
|
+
end
|
227
|
+
|
228
|
+
options[:script_name] = script_name
|
229
|
+
|
221
230
|
url_strategy.call options
|
222
231
|
else
|
223
232
|
super
|
@@ -225,59 +234,59 @@ module ActionDispatch
|
|
225
234
|
end
|
226
235
|
|
227
236
|
private
|
237
|
+
def optimized_helper(args)
|
238
|
+
params = parameterize_args(args) do
|
239
|
+
raise_generation_error(args)
|
240
|
+
end
|
228
241
|
|
229
|
-
|
230
|
-
params = parameterize_args(args)
|
231
|
-
missing_keys = missing_keys(params)
|
232
|
-
|
233
|
-
unless missing_keys.empty?
|
234
|
-
raise_generation_error(params, missing_keys)
|
242
|
+
@route.format params
|
235
243
|
end
|
236
244
|
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
def optimize_routes_generation?(t)
|
241
|
-
t.send(:optimize_routes_generation?)
|
242
|
-
end
|
243
|
-
|
244
|
-
def parameterize_args(args)
|
245
|
-
params = {}
|
246
|
-
@required_parts.zip(args.map(&:to_param)) { |k,v| params[k] = v }
|
247
|
-
params
|
248
|
-
end
|
245
|
+
def optimize_routes_generation?(t)
|
246
|
+
t.send(:optimize_routes_generation?)
|
247
|
+
end
|
249
248
|
|
250
|
-
|
251
|
-
|
252
|
-
|
249
|
+
def parameterize_args(args)
|
250
|
+
params = {}
|
251
|
+
@arg_size.times { |i|
|
252
|
+
key = @required_parts[i]
|
253
|
+
value = args[i].to_param
|
254
|
+
yield key if value.nil? || value.empty?
|
255
|
+
params[key] = value
|
256
|
+
}
|
257
|
+
params
|
258
|
+
end
|
253
259
|
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
260
|
+
def raise_generation_error(args)
|
261
|
+
missing_keys = []
|
262
|
+
params = parameterize_args(args) { |missing_key|
|
263
|
+
missing_keys << missing_key
|
264
|
+
}
|
265
|
+
constraints = Hash[@route.requirements.merge(params).sort_by { |k, v| k.to_s }]
|
266
|
+
message = +"No route matches #{constraints.inspect}"
|
267
|
+
message << ", missing required keys: #{missing_keys.sort.inspect}"
|
258
268
|
|
259
|
-
|
260
|
-
|
269
|
+
raise ActionController::UrlGenerationError, message
|
270
|
+
end
|
261
271
|
end
|
262
272
|
|
263
|
-
def initialize(route, options, route_name
|
273
|
+
def initialize(route, options, route_name)
|
264
274
|
@options = options
|
265
275
|
@segment_keys = route.segment_keys.uniq
|
266
276
|
@route = route
|
267
|
-
@url_strategy = url_strategy
|
268
277
|
@route_name = route_name
|
269
278
|
end
|
270
279
|
|
271
|
-
def call(t, args, inner_options)
|
280
|
+
def call(t, method_name, args, inner_options, url_strategy)
|
272
281
|
controller_options = t.url_options
|
273
282
|
options = controller_options.merge @options
|
274
283
|
hash = handle_positional_args(controller_options,
|
275
|
-
|
284
|
+
inner_options || {},
|
276
285
|
args,
|
277
286
|
options,
|
278
287
|
@segment_keys)
|
279
288
|
|
280
|
-
t._routes.url_for(hash, route_name, url_strategy)
|
289
|
+
t._routes.url_for(hash, route_name, url_strategy, method_name)
|
281
290
|
end
|
282
291
|
|
283
292
|
def handle_positional_args(controller_options, inner_options, args, result, path_params)
|
@@ -291,118 +300,148 @@ module ActionDispatch
|
|
291
300
|
|
292
301
|
if args.size < path_params_size
|
293
302
|
path_params -= controller_options.keys
|
294
|
-
path_params -= result.keys
|
303
|
+
path_params -= (result[:path_params] || {}).merge(result).keys
|
304
|
+
else
|
305
|
+
path_params = path_params.dup
|
306
|
+
end
|
307
|
+
inner_options.each_key do |key|
|
308
|
+
path_params.delete(key)
|
295
309
|
end
|
296
|
-
path_params.each { |param|
|
297
|
-
value = inner_options.fetch(param) { args.shift }
|
298
310
|
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
311
|
+
args.each_with_index do |arg, index|
|
312
|
+
param = path_params[index]
|
313
|
+
result[param] = arg if param
|
314
|
+
end
|
303
315
|
end
|
304
316
|
|
305
317
|
result.merge!(inner_options)
|
306
318
|
end
|
307
|
-
|
308
|
-
DEPRECATED_STRING_OPTIONS = %w[controller action]
|
309
|
-
|
310
|
-
def deprecate_string_options(options)
|
311
|
-
options ||= {}
|
312
|
-
deprecated_string_options = options.keys & DEPRECATED_STRING_OPTIONS
|
313
|
-
if deprecated_string_options.any?
|
314
|
-
msg = "Calling URL helpers with string keys #{deprecated_string_options.join(", ")} is deprecated. Use symbols instead."
|
315
|
-
ActiveSupport::Deprecation.warn(msg)
|
316
|
-
deprecated_string_options.each do |option|
|
317
|
-
value = options.delete(option)
|
318
|
-
options[option.to_sym] = value
|
319
|
-
end
|
320
|
-
end
|
321
|
-
options
|
322
|
-
end
|
323
319
|
end
|
324
320
|
|
325
321
|
private
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
322
|
+
# Create a URL helper allowing ordered parameters to be associated with
|
323
|
+
# corresponding dynamic segments, so you can do:
|
324
|
+
#
|
325
|
+
# foo_url(bar, baz, bang)
|
326
|
+
#
|
327
|
+
# Instead of:
|
328
|
+
#
|
329
|
+
# foo_url(bar: bar, baz: baz, bang: bang)
|
330
|
+
#
|
331
|
+
# Also allow options hash, so you can do:
|
332
|
+
#
|
333
|
+
# foo_url(bar, baz, bang, sort_by: 'baz')
|
334
|
+
#
|
335
|
+
def define_url_helper(mod, name, helper, url_strategy)
|
336
|
+
mod.define_method(name) do |*args|
|
337
|
+
last = args.last
|
338
|
+
options = \
|
339
|
+
case last
|
340
|
+
when Hash
|
341
|
+
args.pop
|
342
|
+
when ActionController::Parameters
|
343
|
+
args.pop.to_h
|
344
|
+
end
|
345
|
+
helper.call(self, name, args, options, url_strategy)
|
346
346
|
end
|
347
347
|
end
|
348
|
-
end
|
349
348
|
end
|
350
349
|
|
351
|
-
# strategy for building
|
350
|
+
# strategy for building URLs to send to the client
|
352
351
|
PATH = ->(options) { ActionDispatch::Http::URL.path_for(options) }
|
353
|
-
FULL = ->(options) { ActionDispatch::Http::URL.full_url_for(options) }
|
354
352
|
UNKNOWN = ->(options) { ActionDispatch::Http::URL.url_for(options) }
|
355
|
-
LEGACY = ->(options) {
|
356
|
-
if options.key?(:only_path)
|
357
|
-
if options[:only_path]
|
358
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
359
|
-
You are calling a `*_path` helper with the `only_path` option
|
360
|
-
explicitly set to `true`. This option will stop working on
|
361
|
-
path helpers in Rails 5. Simply remove the `only_path: true`
|
362
|
-
argument from your call as it is redundant when applied to a
|
363
|
-
path helper.
|
364
|
-
MSG
|
365
|
-
|
366
|
-
PATH.call(options)
|
367
|
-
else
|
368
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
369
|
-
You are calling a `*_path` helper with the `only_path` option
|
370
|
-
explicitly set to `false`. This option will stop working on
|
371
|
-
path helpers in Rails 5. Use the corresponding `*_url` helper
|
372
|
-
instead.
|
373
|
-
MSG
|
374
|
-
|
375
|
-
FULL.call(options)
|
376
|
-
end
|
377
|
-
else
|
378
|
-
PATH.call(options)
|
379
|
-
end
|
380
|
-
}
|
381
353
|
|
382
354
|
attr_accessor :formatter, :set, :named_routes, :default_scope, :router
|
383
355
|
attr_accessor :disable_clear_and_finalize, :resources_path_names
|
384
|
-
attr_accessor :default_url_options, :
|
356
|
+
attr_accessor :default_url_options, :draw_paths
|
357
|
+
attr_reader :env_key, :polymorphic_mappings
|
385
358
|
|
386
359
|
alias :routes :set
|
387
360
|
|
388
361
|
def self.default_resources_path_names
|
389
|
-
{ :
|
362
|
+
{ new: "new", edit: "edit" }
|
363
|
+
end
|
364
|
+
|
365
|
+
def self.new_with_config(config)
|
366
|
+
route_set_config = DEFAULT_CONFIG
|
367
|
+
|
368
|
+
# engines apparently don't have this set
|
369
|
+
if config.respond_to? :relative_url_root
|
370
|
+
route_set_config.relative_url_root = config.relative_url_root
|
371
|
+
end
|
372
|
+
|
373
|
+
if config.respond_to? :api_only
|
374
|
+
route_set_config.api_only = config.api_only
|
375
|
+
end
|
376
|
+
|
377
|
+
new route_set_config
|
390
378
|
end
|
391
379
|
|
392
|
-
|
380
|
+
Config = Struct.new :relative_url_root, :api_only
|
381
|
+
|
382
|
+
DEFAULT_CONFIG = Config.new(nil, false)
|
383
|
+
|
384
|
+
def initialize(config = DEFAULT_CONFIG)
|
393
385
|
self.named_routes = NamedRouteCollection.new
|
394
386
|
self.resources_path_names = self.class.default_resources_path_names
|
395
387
|
self.default_url_options = {}
|
396
|
-
self.
|
388
|
+
self.draw_paths = []
|
397
389
|
|
390
|
+
@config = config
|
398
391
|
@append = []
|
399
392
|
@prepend = []
|
400
393
|
@disable_clear_and_finalize = false
|
401
394
|
@finalized = false
|
395
|
+
@env_key = "ROUTES_#{object_id}_SCRIPT_NAME"
|
396
|
+
@default_env = nil
|
402
397
|
|
403
398
|
@set = Journey::Routes.new
|
404
399
|
@router = Journey::Router.new @set
|
405
|
-
@formatter = Journey::Formatter.new
|
400
|
+
@formatter = Journey::Formatter.new self
|
401
|
+
@polymorphic_mappings = {}
|
402
|
+
end
|
403
|
+
|
404
|
+
def eager_load!
|
405
|
+
router.eager_load!
|
406
|
+
routes.each(&:eager_load!)
|
407
|
+
formatter.eager_load!
|
408
|
+
nil
|
409
|
+
end
|
410
|
+
|
411
|
+
def relative_url_root
|
412
|
+
@config.relative_url_root
|
413
|
+
end
|
414
|
+
|
415
|
+
def api_only?
|
416
|
+
@config.api_only
|
417
|
+
end
|
418
|
+
|
419
|
+
def request_class
|
420
|
+
ActionDispatch::Request
|
421
|
+
end
|
422
|
+
|
423
|
+
def make_request(env)
|
424
|
+
request_class.new env
|
425
|
+
end
|
426
|
+
private :make_request
|
427
|
+
|
428
|
+
def default_env
|
429
|
+
if default_url_options != @default_env&.[]("action_dispatch.routes.default_url_options")
|
430
|
+
url_options = default_url_options.dup.freeze
|
431
|
+
uri = URI(ActionDispatch::Http::URL.full_url_for(host: "example.org", **url_options))
|
432
|
+
|
433
|
+
@default_env = {
|
434
|
+
"action_dispatch.routes" => self,
|
435
|
+
"action_dispatch.routes.default_url_options" => url_options,
|
436
|
+
"HTTPS" => uri.scheme == "https" ? "on" : "off",
|
437
|
+
"rack.url_scheme" => uri.scheme,
|
438
|
+
"HTTP_HOST" => uri.port == uri.default_port ? uri.host : "#{uri.host}:#{uri.port}",
|
439
|
+
"SCRIPT_NAME" => uri.path.chomp("/"),
|
440
|
+
"rack.input" => "",
|
441
|
+
}.freeze
|
442
|
+
end
|
443
|
+
|
444
|
+
@default_env
|
406
445
|
end
|
407
446
|
|
408
447
|
def draw(&block)
|
@@ -421,10 +460,6 @@ module ActionDispatch
|
|
421
460
|
end
|
422
461
|
|
423
462
|
def eval_block(block)
|
424
|
-
if block.arity == 1
|
425
|
-
raise "You are using the old router DSL which has been removed in Rails 3.1. " <<
|
426
|
-
"Please check how to update your routes file at: http://www.engineyard.com/blog/2010/the-lowdown-on-routes-in-rails-3/"
|
427
|
-
end
|
428
463
|
mapper = Mapper.new(self)
|
429
464
|
if default_scope
|
430
465
|
mapper.with_default_scope(default_scope, &block)
|
@@ -445,27 +480,23 @@ module ActionDispatch
|
|
445
480
|
named_routes.clear
|
446
481
|
set.clear
|
447
482
|
formatter.clear
|
483
|
+
@polymorphic_mappings.clear
|
448
484
|
@prepend.each { |blk| eval_block(blk) }
|
449
485
|
end
|
450
486
|
|
451
|
-
def dispatcher(defaults)
|
452
|
-
Routing::RouteSet::Dispatcher.new(defaults)
|
453
|
-
end
|
454
|
-
|
455
487
|
module MountedHelpers
|
456
488
|
extend ActiveSupport::Concern
|
457
489
|
include UrlFor
|
458
490
|
end
|
459
491
|
|
460
|
-
# Contains all the mounted helpers across different
|
461
|
-
#
|
462
|
-
#
|
463
|
-
# access routes for other engines.
|
492
|
+
# Contains all the mounted helpers across different engines and the `main_app`
|
493
|
+
# helper for the application. You can include this in your classes if you want
|
494
|
+
# to access routes for other engines.
|
464
495
|
def mounted_helpers
|
465
496
|
MountedHelpers
|
466
497
|
end
|
467
498
|
|
468
|
-
def define_mounted_helper(name)
|
499
|
+
def define_mounted_helper(name, script_namer = nil)
|
469
500
|
return if MountedHelpers.method_defined?(name)
|
470
501
|
|
471
502
|
routes = self
|
@@ -473,7 +504,7 @@ module ActionDispatch
|
|
473
504
|
|
474
505
|
MountedHelpers.class_eval do
|
475
506
|
define_method "_#{name}" do
|
476
|
-
RoutesProxy.new(routes, _routes_context, helpers)
|
507
|
+
RoutesProxy.new(routes, _routes_context, helpers, script_namer)
|
477
508
|
end
|
478
509
|
end
|
479
510
|
|
@@ -485,6 +516,14 @@ module ActionDispatch
|
|
485
516
|
end
|
486
517
|
|
487
518
|
def url_helpers(supports_path = true)
|
519
|
+
if supports_path
|
520
|
+
@url_helpers_with_paths ||= generate_url_helpers(true)
|
521
|
+
else
|
522
|
+
@url_helpers_without_paths ||= generate_url_helpers(false)
|
523
|
+
end
|
524
|
+
end
|
525
|
+
|
526
|
+
def generate_url_helpers(supports_path)
|
488
527
|
routes = self
|
489
528
|
|
490
529
|
Module.new do
|
@@ -493,41 +532,76 @@ module ActionDispatch
|
|
493
532
|
|
494
533
|
# Define url_for in the singleton level so one can do:
|
495
534
|
# Rails.application.routes.url_helpers.url_for(args)
|
496
|
-
|
497
|
-
|
498
|
-
|
535
|
+
proxy_class = Class.new do
|
536
|
+
include UrlFor
|
537
|
+
include routes.named_routes.path_helpers_module
|
538
|
+
include routes.named_routes.url_helpers_module
|
539
|
+
|
499
540
|
attr_reader :_routes
|
541
|
+
|
542
|
+
def initialize(routes)
|
543
|
+
@_routes = routes
|
544
|
+
end
|
545
|
+
|
546
|
+
def optimize_routes_generation?
|
547
|
+
@_routes.optimize_routes_generation?
|
548
|
+
end
|
549
|
+
end
|
550
|
+
|
551
|
+
@_proxy = proxy_class.new(routes)
|
552
|
+
|
553
|
+
class << self
|
554
|
+
def url_for(options)
|
555
|
+
@_proxy.url_for(options)
|
556
|
+
end
|
557
|
+
|
558
|
+
def full_url_for(options)
|
559
|
+
@_proxy.full_url_for(options)
|
560
|
+
end
|
561
|
+
|
562
|
+
def route_for(name, *args)
|
563
|
+
@_proxy.route_for(name, *args)
|
564
|
+
end
|
565
|
+
|
566
|
+
def optimize_routes_generation?
|
567
|
+
@_proxy.optimize_routes_generation?
|
568
|
+
end
|
569
|
+
|
570
|
+
def polymorphic_url(record_or_hash_or_array, options = {})
|
571
|
+
@_proxy.polymorphic_url(record_or_hash_or_array, options)
|
572
|
+
end
|
573
|
+
|
574
|
+
def polymorphic_path(record_or_hash_or_array, options = {})
|
575
|
+
@_proxy.polymorphic_path(record_or_hash_or_array, options)
|
576
|
+
end
|
577
|
+
|
578
|
+
def _routes; @_proxy._routes; end
|
500
579
|
def url_options; {}; end
|
501
580
|
end
|
502
581
|
|
503
582
|
url_helpers = routes.named_routes.url_helpers_module
|
504
583
|
|
505
|
-
# Make named_routes available in the module singleton
|
506
|
-
# as well, so one can do:
|
584
|
+
# Make named_routes available in the module singleton as well, so one can do:
|
507
585
|
# Rails.application.routes.url_helpers.posts_path
|
508
586
|
extend url_helpers
|
509
587
|
|
510
|
-
# Any class that includes this module will get all
|
511
|
-
# named routes...
|
588
|
+
# Any class that includes this module will get all named routes...
|
512
589
|
include url_helpers
|
513
590
|
|
514
591
|
if supports_path
|
515
592
|
path_helpers = routes.named_routes.path_helpers_module
|
516
|
-
else
|
517
|
-
path_helpers = routes.named_routes.path_helpers_module(true)
|
518
|
-
end
|
519
593
|
|
520
|
-
|
521
|
-
|
594
|
+
include path_helpers
|
595
|
+
extend path_helpers
|
596
|
+
end
|
522
597
|
|
523
598
|
# plus a singleton class method called _routes ...
|
524
599
|
included do
|
525
|
-
|
600
|
+
redefine_singleton_method(:_routes) { routes }
|
526
601
|
end
|
527
602
|
|
528
|
-
# And an instance method _routes. Note that
|
529
|
-
#
|
530
|
-
# conveniences for working with @_routes.
|
603
|
+
# And an instance method _routes. Note that UrlFor (included in this module) add
|
604
|
+
# extra conveniences for working with @_routes.
|
531
605
|
define_method(:_routes) { @_routes || routes }
|
532
606
|
|
533
607
|
define_method(:_generate_paths_by_default) do
|
@@ -535,6 +609,20 @@ module ActionDispatch
|
|
535
609
|
end
|
536
610
|
|
537
611
|
private :_generate_paths_by_default
|
612
|
+
|
613
|
+
# If the module is included more than once (for example, in a subclass of an
|
614
|
+
# ancestor that includes the module), ensure that the `_routes` singleton and
|
615
|
+
# instance methods return the desired route set by including a new copy of the
|
616
|
+
# module (recursively if necessary). Note that this method is called for each
|
617
|
+
# inclusion, whereas the above `included` block is run only for the initial
|
618
|
+
# inclusion of each copy.
|
619
|
+
def self.included(base)
|
620
|
+
super
|
621
|
+
if base.respond_to?(:_routes) && !base._routes.equal?(@_proxy._routes)
|
622
|
+
@dup_for_reinclude ||= self.dup
|
623
|
+
base.include @dup_for_reinclude
|
624
|
+
end
|
625
|
+
end
|
538
626
|
end
|
539
627
|
end
|
540
628
|
|
@@ -542,7 +630,7 @@ module ActionDispatch
|
|
542
630
|
routes.empty?
|
543
631
|
end
|
544
632
|
|
545
|
-
def add_route(
|
633
|
+
def add_route(mapping, name)
|
546
634
|
raise ArgumentError, "Invalid route name: '#{name}'" unless name.blank? || name.to_s.match(/^[_a-z]\w*$/i)
|
547
635
|
|
548
636
|
if name && named_routes[name]
|
@@ -550,94 +638,80 @@ module ActionDispatch
|
|
550
638
|
"You may have defined two routes with the same name using the `:as` option, or " \
|
551
639
|
"you may be overriding a route already defined by a resource with the same naming. " \
|
552
640
|
"For the latter, you can restrict the routes created with `resources` as explained here: \n" \
|
553
|
-
"
|
641
|
+
"https://guides.rubyonrails.org/routing.html#restricting-the-routes-created"
|
554
642
|
end
|
555
643
|
|
556
|
-
|
557
|
-
ast = conditions.delete :parsed_path_info
|
558
|
-
path = build_path(path, ast, requirements, anchor)
|
559
|
-
conditions = build_conditions(conditions, path.names.map { |x| x.to_sym })
|
560
|
-
|
561
|
-
route = @set.add_route(app, path, conditions, defaults, name)
|
644
|
+
route = @set.add_route(name, mapping)
|
562
645
|
named_routes[name] = route if name
|
563
|
-
route
|
564
|
-
end
|
565
646
|
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
anchor)
|
573
|
-
|
574
|
-
pattern = Journey::Path::Pattern.new(strexp)
|
575
|
-
|
576
|
-
builder = Journey::GTG::Builder.new pattern.spec
|
647
|
+
if route.segment_keys.include?(:controller)
|
648
|
+
ActionDispatch.deprecator.warn(<<-MSG.squish)
|
649
|
+
Using a dynamic :controller segment in a route is deprecated and
|
650
|
+
will be removed in Rails 7.2.
|
651
|
+
MSG
|
652
|
+
end
|
577
653
|
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
654
|
+
if route.segment_keys.include?(:action)
|
655
|
+
ActionDispatch.deprecator.warn(<<-MSG.squish)
|
656
|
+
Using a dynamic :action segment in a route is deprecated and
|
657
|
+
will be removed in Rails 7.2.
|
658
|
+
MSG
|
659
|
+
end
|
583
660
|
|
584
|
-
|
585
|
-
|
586
|
-
builder.followpos(n).first
|
587
|
-
}.find_all(&:symbol?)
|
661
|
+
route
|
662
|
+
end
|
588
663
|
|
589
|
-
|
590
|
-
|
591
|
-
|
664
|
+
def add_polymorphic_mapping(klass, options, &block)
|
665
|
+
@polymorphic_mappings[klass] = CustomUrlHelper.new(klass, options, &block)
|
666
|
+
end
|
592
667
|
|
593
|
-
|
668
|
+
def add_url_helper(name, options, &block)
|
669
|
+
named_routes.add_url_helper(name, options, &block)
|
594
670
|
end
|
595
|
-
private :build_path
|
596
671
|
|
597
|
-
|
598
|
-
|
672
|
+
class CustomUrlHelper
|
673
|
+
attr_reader :name, :defaults, :block
|
599
674
|
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
verbs = conditions[:request_method] || []
|
605
|
-
unless verbs.empty?
|
606
|
-
conditions[:request_method] = %r[^#{verbs.join('|')}$]
|
675
|
+
def initialize(name, defaults, &block)
|
676
|
+
@name = name
|
677
|
+
@defaults = defaults
|
678
|
+
@block = block
|
607
679
|
end
|
608
680
|
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
end
|
613
|
-
end
|
614
|
-
private :build_conditions
|
681
|
+
def call(t, args, only_path = false)
|
682
|
+
options = args.extract_options!
|
683
|
+
url = t.full_url_for(eval_block(t, args, options))
|
615
684
|
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
elsif value.is_a?(Array)
|
621
|
-
value.map { |v| v.to_param }.join('/')
|
622
|
-
elsif param = value.to_param
|
623
|
-
param
|
685
|
+
if only_path
|
686
|
+
"/" + url.partition(%r{(?<!/)/(?!/)}).last
|
687
|
+
else
|
688
|
+
url
|
624
689
|
end
|
625
690
|
end
|
626
691
|
|
692
|
+
private
|
693
|
+
def eval_block(t, args, options)
|
694
|
+
t.instance_exec(*args, merge_defaults(options), &block)
|
695
|
+
end
|
696
|
+
|
697
|
+
def merge_defaults(options)
|
698
|
+
defaults ? defaults.merge(options) : options
|
699
|
+
end
|
700
|
+
end
|
701
|
+
|
702
|
+
class Generator
|
627
703
|
attr_reader :options, :recall, :set, :named_route
|
628
704
|
|
629
705
|
def initialize(named_route, options, recall, set)
|
630
706
|
@named_route = named_route
|
631
|
-
@options = options
|
632
|
-
@recall = recall
|
707
|
+
@options = options
|
708
|
+
@recall = recall
|
633
709
|
@set = set
|
634
710
|
|
635
|
-
normalize_recall!
|
636
711
|
normalize_options!
|
637
712
|
normalize_controller_action_id!
|
638
713
|
use_relative_controller!
|
639
714
|
normalize_controller!
|
640
|
-
normalize_action!
|
641
715
|
end
|
642
716
|
|
643
717
|
def controller
|
@@ -651,52 +725,46 @@ module ActionDispatch
|
|
651
725
|
def use_recall_for(key)
|
652
726
|
if @recall[key] && (!@options.key?(key) || @options[key] == @recall[key])
|
653
727
|
if !named_route_exists? || segment_keys.include?(key)
|
654
|
-
@options[key] = @recall
|
728
|
+
@options[key] = @recall[key]
|
655
729
|
end
|
656
730
|
end
|
657
731
|
end
|
658
732
|
|
659
|
-
# Set 'index' as default action for recall
|
660
|
-
def normalize_recall!
|
661
|
-
@recall[:action] ||= 'index'
|
662
|
-
end
|
663
|
-
|
664
733
|
def normalize_options!
|
665
|
-
# If an explicit :controller was given, always make :action explicit
|
666
|
-
#
|
734
|
+
# If an explicit :controller was given, always make :action explicit too, so
|
735
|
+
# that action expiry works as expected for things like
|
667
736
|
#
|
668
|
-
#
|
737
|
+
# generate({controller: 'content'}, {controller: 'content', action: 'show'})
|
669
738
|
#
|
670
|
-
# (the above is from the unit tests). In the above case, because the
|
671
|
-
#
|
672
|
-
#
|
739
|
+
# (the above is from the unit tests). In the above case, because the controller
|
740
|
+
# was explicitly given, but no action, the action is implied to be "index", not
|
741
|
+
# the recalled action of "show".
|
673
742
|
|
674
743
|
if options[:controller]
|
675
|
-
options[:action] ||=
|
744
|
+
options[:action] ||= "index"
|
676
745
|
options[:controller] = options[:controller].to_s
|
677
746
|
end
|
678
747
|
|
679
748
|
if options.key?(:action)
|
680
|
-
options[:action] = (options[:action] ||
|
749
|
+
options[:action] = (options[:action] || "index").to_s
|
681
750
|
end
|
682
751
|
end
|
683
752
|
|
684
|
-
# This pulls :controller, :action, and :id out of the recall.
|
685
|
-
#
|
686
|
-
#
|
687
|
-
# :controller, :action or :id is not found, don't pull any
|
753
|
+
# This pulls :controller, :action, and :id out of the recall. The recall key is
|
754
|
+
# only used if there is no key in the options or if the key in the options is
|
755
|
+
# identical. If any of :controller, :action or :id is not found, don't pull any
|
688
756
|
# more keys from the recall.
|
689
757
|
def normalize_controller_action_id!
|
690
|
-
use_recall_for(:controller)
|
691
|
-
use_recall_for(:action)
|
758
|
+
use_recall_for(:controller) || return
|
759
|
+
use_recall_for(:action) || return
|
692
760
|
use_recall_for(:id)
|
693
761
|
end
|
694
762
|
|
695
|
-
# if the current controller is "foo/bar/baz" and controller: "baz/bat"
|
696
|
-
#
|
763
|
+
# if the current controller is "foo/bar/baz" and controller: "baz/bat" is
|
764
|
+
# specified, the controller becomes "foo/baz/bat"
|
697
765
|
def use_relative_controller!
|
698
766
|
if !named_route && different_controller? && !controller.start_with?("/")
|
699
|
-
old_parts = current_controller.split(
|
767
|
+
old_parts = current_controller.split("/")
|
700
768
|
size = controller.count("/") + 1
|
701
769
|
parts = old_parts[0...-size] << controller
|
702
770
|
@options[:controller] = parts.join("/")
|
@@ -705,20 +773,19 @@ module ActionDispatch
|
|
705
773
|
|
706
774
|
# Remove leading slashes from controllers
|
707
775
|
def normalize_controller!
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
@recall[:action] = @options.delete(:action)
|
776
|
+
if controller
|
777
|
+
if controller.start_with?("/")
|
778
|
+
@options[:controller] = controller[1..-1]
|
779
|
+
else
|
780
|
+
@options[:controller] = controller
|
781
|
+
end
|
715
782
|
end
|
716
783
|
end
|
717
784
|
|
718
|
-
# Generates a path from routes, returns
|
719
|
-
#
|
785
|
+
# Generates a path from routes, returns a RouteWithParams or MissingRoute.
|
786
|
+
# MissingRoute will raise ActionController::UrlGenerationError.
|
720
787
|
def generate
|
721
|
-
@set.formatter.generate(named_route, options, recall
|
788
|
+
@set.formatter.generate(named_route, options, recall)
|
722
789
|
end
|
723
790
|
|
724
791
|
def different_controller?
|
@@ -736,45 +803,46 @@ module ActionDispatch
|
|
736
803
|
end
|
737
804
|
end
|
738
805
|
|
739
|
-
# Generate the path indicated by the arguments, and return an array of
|
740
|
-
#
|
741
|
-
def extra_keys(options, recall={})
|
806
|
+
# Generate the path indicated by the arguments, and return an array of the keys
|
807
|
+
# that were not used to generate it.
|
808
|
+
def extra_keys(options, recall = {})
|
742
809
|
generate_extras(options, recall).last
|
743
810
|
end
|
744
811
|
|
745
|
-
def generate_extras(options, recall={})
|
746
|
-
|
747
|
-
|
748
|
-
|
812
|
+
def generate_extras(options, recall = {})
|
813
|
+
if recall
|
814
|
+
options = options.merge(_recall: recall)
|
815
|
+
end
|
816
|
+
|
817
|
+
route_name = options.delete :use_route
|
818
|
+
generator = generate(route_name, options, recall)
|
819
|
+
path_info = path_for(options, route_name, [])
|
820
|
+
[URI(path_info).path, generator.params.except(:_recall).keys]
|
749
821
|
end
|
750
822
|
|
751
|
-
def generate(
|
752
|
-
Generator.new(
|
823
|
+
def generate(route_name, options, recall = {}, method_name = nil)
|
824
|
+
Generator.new(route_name, options, recall, self).generate
|
753
825
|
end
|
754
826
|
private :generate
|
755
827
|
|
756
828
|
RESERVED_OPTIONS = [:host, :protocol, :port, :subdomain, :domain, :tld_length,
|
757
829
|
:trailing_slash, :anchor, :params, :only_path, :script_name,
|
758
|
-
:original_script_name
|
830
|
+
:original_script_name]
|
759
831
|
|
760
832
|
def optimize_routes_generation?
|
761
833
|
default_url_options.empty?
|
762
834
|
end
|
763
835
|
|
764
836
|
def find_script_name(options)
|
765
|
-
options.delete(:script_name) ||
|
766
|
-
end
|
767
|
-
|
768
|
-
def find_relative_url_root(options)
|
769
|
-
options.delete(:relative_url_root) || relative_url_root
|
837
|
+
options.delete(:script_name) || relative_url_root || ""
|
770
838
|
end
|
771
839
|
|
772
|
-
def path_for(options, route_name = nil)
|
773
|
-
url_for(options, route_name, PATH)
|
840
|
+
def path_for(options, route_name = nil, reserved = RESERVED_OPTIONS)
|
841
|
+
url_for(options, route_name, PATH, nil, reserved)
|
774
842
|
end
|
775
843
|
|
776
|
-
# The
|
777
|
-
def url_for(options, route_name = nil, url_strategy = UNKNOWN)
|
844
|
+
# The `options` argument must be a hash whose keys are **symbols**.
|
845
|
+
def url_for(options, route_name = nil, url_strategy = UNKNOWN, method_name = nil, reserved = RESERVED_OPTIONS)
|
778
846
|
options = default_url_options.merge options
|
779
847
|
|
780
848
|
user = password = nil
|
@@ -784,7 +852,7 @@ module ActionDispatch
|
|
784
852
|
password = options.delete :password
|
785
853
|
end
|
786
854
|
|
787
|
-
recall
|
855
|
+
recall = options.delete(:_recall) { {} }
|
788
856
|
|
789
857
|
original_script_name = options.delete(:original_script_name)
|
790
858
|
script_name = find_script_name options
|
@@ -794,12 +862,23 @@ module ActionDispatch
|
|
794
862
|
end
|
795
863
|
|
796
864
|
path_options = options.dup
|
797
|
-
|
865
|
+
reserved.each { |ro| path_options.delete ro }
|
798
866
|
|
799
|
-
|
867
|
+
route_with_params = generate(route_name, path_options, recall)
|
868
|
+
path = route_with_params.path(method_name)
|
869
|
+
|
870
|
+
if options[:trailing_slash] && !options[:format] && !path.end_with?("/")
|
871
|
+
path += "/"
|
872
|
+
end
|
873
|
+
|
874
|
+
params = route_with_params.params
|
800
875
|
|
801
876
|
if options.key? :params
|
802
|
-
|
877
|
+
if options[:params].respond_to?(:to_hash)
|
878
|
+
params.merge! options[:params]
|
879
|
+
else
|
880
|
+
params[:params] = options[:params]
|
881
|
+
end
|
803
882
|
end
|
804
883
|
|
805
884
|
options[:path] = path
|
@@ -812,47 +891,54 @@ module ActionDispatch
|
|
812
891
|
end
|
813
892
|
|
814
893
|
def call(env)
|
815
|
-
req =
|
894
|
+
req = make_request(env)
|
816
895
|
req.path_info = Journey::Router::Utils.normalize_path(req.path_info)
|
817
896
|
@router.serve(req)
|
818
897
|
end
|
819
898
|
|
820
899
|
def recognize_path(path, environment = {})
|
821
900
|
method = (environment[:method] || "GET").to_s.upcase
|
822
|
-
path = Journey::Router::Utils.normalize_path(path) unless path
|
901
|
+
path = Journey::Router::Utils.normalize_path(path) unless path&.include?("://")
|
823
902
|
extras = environment[:extras] || {}
|
824
903
|
|
825
904
|
begin
|
826
|
-
env = Rack::MockRequest.env_for(path,
|
905
|
+
env = Rack::MockRequest.env_for(path, method: method)
|
827
906
|
rescue URI::InvalidURIError => e
|
828
907
|
raise ActionController::RoutingError, e.message
|
829
908
|
end
|
830
909
|
|
831
|
-
req =
|
910
|
+
req = make_request(env)
|
911
|
+
recognize_path_with_request(req, path, extras)
|
912
|
+
end
|
913
|
+
|
914
|
+
def recognize_path_with_request(req, path, extras, raise_on_missing: true)
|
832
915
|
@router.recognize(req) do |route, params|
|
833
916
|
params.merge!(extras)
|
834
917
|
params.each do |key, value|
|
835
918
|
if value.is_a?(String)
|
836
919
|
value = value.dup.force_encoding(Encoding::BINARY)
|
837
|
-
params[key] = URI.
|
920
|
+
params[key] = URI::DEFAULT_PARSER.unescape(value)
|
838
921
|
end
|
839
922
|
end
|
840
|
-
|
841
|
-
req.path_parameters = old_params.merge params
|
923
|
+
req.path_parameters = params
|
842
924
|
app = route.app
|
843
925
|
if app.matches?(req) && app.dispatcher?
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
dispatcher.prepare_params!(params)
|
848
|
-
return params
|
849
|
-
else
|
926
|
+
begin
|
927
|
+
req.controller_class
|
928
|
+
rescue NameError
|
850
929
|
raise ActionController::RoutingError, "A route matches #{path.inspect}, but references missing controller: #{params[:controller].camelize}Controller"
|
851
930
|
end
|
931
|
+
|
932
|
+
return req.path_parameters
|
933
|
+
elsif app.matches?(req) && app.engine?
|
934
|
+
path_parameters = app.rack_app.routes.recognize_path_with_request(req, path, extras, raise_on_missing: false)
|
935
|
+
return path_parameters if path_parameters
|
852
936
|
end
|
853
937
|
end
|
854
938
|
|
855
|
-
|
939
|
+
if raise_on_missing
|
940
|
+
raise ActionController::RoutingError, "No route matches #{path.inspect}"
|
941
|
+
end
|
856
942
|
end
|
857
943
|
end
|
858
944
|
# :startdoc:
|