aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_dispatch/journey/formatter.rb
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib/action_dispatch/journey/formatter.rb')
-rw-r--r--actionpack/lib/action_dispatch/journey/formatter.rb74
1 files changed, 48 insertions, 26 deletions
diff --git a/actionpack/lib/action_dispatch/journey/formatter.rb b/actionpack/lib/action_dispatch/journey/formatter.rb
index 4a344f71af..177f586c0e 100644
--- a/actionpack/lib/action_dispatch/journey/formatter.rb
+++ b/actionpack/lib/action_dispatch/journey/formatter.rb
@@ -1,7 +1,10 @@
+require 'action_controller/metal/exceptions'
+require 'active_support/deprecation'
+
module ActionDispatch
module Journey
# The Formatter class is used for formatting URLs. For example, parameters
- # passed to +url_for+ in rails will eventually call Formatter#generate.
+ # passed to +url_for+ in Rails will eventually call Formatter#generate.
class Formatter # :nodoc:
attr_reader :routes
@@ -10,13 +13,17 @@ module ActionDispatch
@cache = nil
end
- def generate(type, name, options, recall = {}, parameterize = nil)
- constraints = recall.merge(options)
+ def generate(name, options, path_parameters, parameterize = nil)
+ constraints = path_parameters.merge(options)
missing_keys = []
match_route(name, constraints) do |route|
- parameterized_parts = extract_parameterized_parts(route, options, recall, parameterize)
- next if !name && route.requirements.empty? && route.parts.empty?
+ parameterized_parts = extract_parameterized_parts(route, options, path_parameters, parameterize)
+
+ # Skip this route unless a name has been provided or it is a
+ # standard Rails route since we can't determine whether an options
+ # hash passed to url_for matches a Rack application or a redirect.
+ next unless name || route.dispatcher?
missing_keys = missing_keys(route, parameterized_parts)
next unless missing_keys.empty?
@@ -24,10 +31,19 @@ module ActionDispatch
parameterized_parts.key?(key) || route.defaults.key?(key)
end
+ defaults = route.defaults
+ required_parts = route.required_parts
+ parameterized_parts.delete_if do |key, value|
+ value.to_s == defaults[key].to_s && !required_parts.include?(key)
+ end
+
return [route.format(parameterized_parts), params]
end
- raise Router::RoutingError.new "missing required keys: #{missing_keys}"
+ message = "No route matches #{Hash[constraints.sort].inspect}"
+ message << " missing required keys: #{missing_keys.sort.inspect}" unless missing_keys.empty?
+
+ raise ActionController::UrlGenerationError, message
end
def clear
@@ -37,26 +53,23 @@ module ActionDispatch
private
def extract_parameterized_parts(route, options, recall, parameterize = nil)
- constraints = recall.merge(options)
- data = constraints.dup
+ parameterized_parts = recall.merge(options)
keys_to_keep = route.parts.reverse.drop_while { |part|
!options.key?(part) || (options[part] || recall[part]).nil?
} | route.required_parts
- (data.keys - keys_to_keep).each do |bad_key|
- data.delete(bad_key)
+ (parameterized_parts.keys - keys_to_keep).each do |bad_key|
+ parameterized_parts.delete(bad_key)
end
- parameterized_parts = data.dup
-
if parameterize
parameterized_parts.each do |k, v|
parameterized_parts[k] = parameterize.call(k, v)
end
end
- parameterized_parts.keep_if { |_, v| v }
+ parameterized_parts.keep_if { |_, v| v }
parameterized_parts
end
@@ -68,14 +81,28 @@ module ActionDispatch
if named_routes.key?(name)
yield named_routes[name]
else
- routes = non_recursive(cache, options.to_a)
+ # Make sure we don't show the deprecation warning more than once
+ warned = false
+
+ routes = non_recursive(cache, options)
hash = routes.group_by { |_, r| r.score(options) }
hash.keys.sort.reverse_each do |score|
- next if score < 0
+ break if score < 0
hash[score].sort_by { |i, _| i }.each do |_, route|
+ if name && !warned
+ ActiveSupport::Deprecation.warn <<-MSG.squish
+ You are trying to generate the URL for a named route called
+ #{name.inspect} but no such route was found. In the future,
+ this will result in an `ActionController::UrlGenerationError`
+ exception.
+ MSG
+
+ warned = true
+ end
+
yield route
end
end
@@ -84,14 +111,14 @@ module ActionDispatch
def non_recursive(cache, options)
routes = []
- stack = [cache]
+ queue = [cache]
- while stack.any?
- c = stack.shift
+ while queue.any?
+ c = queue.shift
routes.concat(c[:___routes]) if c.key?(:___routes)
options.each do |pair|
- stack << c[pair] if c.key?(pair)
+ queue << c[pair] if c.key?(pair)
end
end
@@ -115,14 +142,9 @@ module ActionDispatch
def possibles(cache, options, depth = 0)
cache.fetch(:___routes) { [] } + options.find_all { |pair|
cache.key?(pair)
- }.map { |pair|
+ }.flat_map { |pair|
possibles(cache[pair], options, depth + 1)
- }.flatten(1)
- end
-
- # Returns +true+ if no missing keys are present, otherwise +false+.
- def verify_required_parts!(route, parts)
- missing_keys(route, parts).empty?
+ }
end
def build_cache