diff options
Diffstat (limited to 'actionpack/lib/action_dispatch/journey/formatter.rb')
-rw-r--r-- | actionpack/lib/action_dispatch/journey/formatter.rb | 33 |
1 files changed, 25 insertions, 8 deletions
diff --git a/actionpack/lib/action_dispatch/journey/formatter.rb b/actionpack/lib/action_dispatch/journey/formatter.rb index 0323360faa..52396ec901 100644 --- a/actionpack/lib/action_dispatch/journey/formatter.rb +++ b/actionpack/lib/action_dispatch/journey/formatter.rb @@ -1,10 +1,13 @@ -require 'action_controller/metal/exceptions' +# frozen_string_literal: true + +require "action_controller/metal/exceptions" module ActionDispatch + # :stopdoc: module Journey # The Formatter class is used for formatting URLs. For example, parameters # passed to +url_for+ in Rails will eventually call Formatter#generate. - class Formatter # :nodoc: + class Formatter attr_reader :routes def initialize(routes) @@ -14,7 +17,7 @@ module ActionDispatch def generate(name, options, path_parameters, parameterize = nil) constraints = path_parameters.merge(options) - missing_keys = nil # need for variable scope + missing_keys = nil match_route(name, constraints) do |route| parameterized_parts = extract_parameterized_parts(route, options, path_parameters, parameterize) @@ -32,15 +35,24 @@ module ActionDispatch defaults = route.defaults required_parts = route.required_parts - parameterized_parts.keep_if do |key, value| - (defaults[key].nil? && value.present?) || value.to_s != defaults[key].to_s || required_parts.include?(key) + + route.parts.reverse_each do |key| + break if defaults[key].nil? && parameterized_parts[key].present? + next if parameterized_parts[key].to_s != defaults[key].to_s + break if required_parts.include?(key) + + parameterized_parts.delete(key) end return [route.format(parameterized_parts), params] end - 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? + 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, message end @@ -82,7 +94,11 @@ module ActionDispatch else routes = non_recursive(cache, options) - hash = routes.group_by { |_, r| r.score(options) } + supplied_keys = options.each_with_object({}) do |(k, v), h| + h[k.to_s] = true if v + end + + hash = routes.group_by { |_, r| r.score(supplied_keys) } hash.keys.sort.reverse_each do |score| break if score < 0 @@ -169,4 +185,5 @@ module ActionDispatch end end end + # :startdoc: end |