1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Convert route params array into object

This PR converts the route params array into an object and moves the
error generation code into its own object as well. This is a refactoring
of internal code to make it easier to change and simplier for internals
to interface with. We have two new objects:

1) `RouteWithParams`

Previously `#generate` returned an array of parameterized parts for a
route. We have now turned this into an object with a `path` and a
`params methods` to be able to access the parts we need.

2) `MissingRoute`

`#generate` was also responsible for constructing the message for the
error. We've moved this code into the new `MissingRoute` object so it
does that work instead.

This change makes these methods reusable throughout the code and easier
for future refactoring we plan to do.

Co-authored-by: Aaron Patterson <aaron.patterson@gmail.com>
This commit is contained in:
eileencodes 2020-05-15 15:56:27 -04:00
parent 4654f4aab6
commit 437ab2031e
No known key found for this signature in database
GPG key ID: BA5C575120BBE8DF
4 changed files with 66 additions and 20 deletions

View file

@ -15,7 +15,48 @@ module ActionDispatch
@cache = nil
end
def generate(name, options, path_parameters, method_name = nil)
class RouteWithParams
attr_reader :params
def initialize(route, parameterized_parts, params)
@route = route
@parameterized_parts = parameterized_parts
@params = params
end
def path(_)
@route.format(@parameterized_parts)
end
end
class MissingRoute
attr_reader :routes, :name, :constraints, :missing_keys, :unmatched_keys
def initialize(constraints, missing_keys, unmatched_keys, routes, name)
@constraints = constraints
@missing_keys = missing_keys
@unmatched_keys = unmatched_keys
@routes = routes
@name = name
end
def path(method_name)
raise ActionController::UrlGenerationError.new(message, routes, name, method_name)
end
def params
path("unknown")
end
def message
message = +"No route matches #{Hash[constraints.sort_by { |k, v| k.to_s }].inspect}"
message << ", missing required keys: #{missing_keys.sort.inspect}" if missing_keys && !missing_keys.empty?
message << ", possible unmatched constraints: #{unmatched_keys.sort.inspect}" if unmatched_keys && !unmatched_keys.empty?
message
end
end
def generate(name, options, path_parameters)
constraints = path_parameters.merge(options)
missing_keys = nil
@ -44,17 +85,13 @@ module ActionDispatch
parameterized_parts.delete(key)
end
return [route.format(parameterized_parts), params]
return RouteWithParams.new(route, parameterized_parts, params)
end
unmatched_keys = (missing_keys || []) & constraints.keys
missing_keys = (missing_keys || []) - unmatched_keys
message = +"No route matches #{Hash[constraints.sort_by { |k, v| k.to_s }].inspect}"
message << ", missing required keys: #{missing_keys.sort.inspect}" if missing_keys && !missing_keys.empty?
message << ", possible unmatched constraints: #{unmatched_keys.sort.inspect}" if unmatched_keys && !unmatched_keys.empty?
raise ActionController::UrlGenerationError.new(message, routes, name, method_name)
MissingRoute.new(constraints, missing_keys, unmatched_keys, routes, name)
end
def clear

View file

@ -733,10 +733,10 @@ module ActionDispatch
end
end
# Generates a path from routes, returns [path, params].
# If no route is generated the formatter will raise ActionController::UrlGenerationError
def generate(method_name)
@set.formatter.generate(named_route, options, recall, method_name)
# Generates a path from routes, returns a RouteWithParams or MissingRoute.
# MissingRoute will raise ActionController::UrlGenerationError.
def generate
@set.formatter.generate(named_route, options, recall)
end
def different_controller?
@ -761,13 +761,20 @@ module ActionDispatch
end
def generate_extras(options, recall = {})
route_key = options.delete :use_route
path, params = generate(route_key, options, recall)
return path, params.keys
if recall
options = options.merge(_recall: recall)
end
route_name = options.delete :use_route
path = path_for(options, route_name)
uri = URI.parse(path)
params = Rack::Utils.parse_nested_query(uri.query).symbolize_keys
[uri.path, params.keys]
end
def generate(route_key, options, recall = {}, method_name = nil)
Generator.new(route_key, options, recall, self).generate(method_name)
def generate(route_name, options, recall = {}, method_name = nil)
Generator.new(route_name, options, recall, self).generate
end
private :generate
@ -814,7 +821,9 @@ module ActionDispatch
path_options = options.dup
RESERVED_OPTIONS.each { |ro| path_options.delete ro }
path, params = generate(route_name, path_options, recall, method_name)
route_with_params = generate(route_name, path_options, recall)
path = route_with_params.path(method_name)
params = route_with_params.params
if options.key? :params
params.merge! options[:params]

View file

@ -2076,11 +2076,11 @@ class RackMountIntegrationTests < ActiveSupport::TestCase
def test_extras
params = { controller: "people" }
assert_equal [], @routes.extra_keys(params)
assert_equal({ controller: "people", action: "index" }, params)
assert_equal({ controller: "people" }, params)
params = { controller: "people", foo: "bar" }
assert_equal [:foo], @routes.extra_keys(params)
assert_equal({ controller: "people", action: "index", foo: "bar" }, params)
assert_equal({ controller: "people", foo: "bar" }, params)
params = { controller: "people", action: "create", person: { name: "Josh" } }
assert_equal [:person], @routes.extra_keys(params)

View file

@ -61,7 +61,7 @@ module ActionDispatch
get "/foo/:id", id: /\d/, anchor: false, to: "foo#bar"
assert_raises(ActionController::UrlGenerationError) do
_generate(nil, { controller: "foo", action: "bar", id: "10" }, {})
@route_set.url_for({ controller: "foo", action: "bar", id: "10" }, nil)
end
end