Close
Glad You're Ready. Let's Get Started!

Let us know how we can contact you.

Thank you!

We'll respond shortly.

LABS
Helpful Named-Route Error Messages

Sometimes I call a named route incorrectly: edit_user_project_path(project). And I get an illegible error message:

user_project_url failed to generate from {:action=>"show", :user_id=>#<Project id: 1, name: "Andy falls off a cliff", created_at: "2007-12-03 15:15:08", creator_id: 2, completed_at: nil, description: "", deleted_at: nil>, :controller=>"projects"}, expected: {:action=>"show", :controller=>"projects"}, diff: {:user_id=>#<Project id: 1, name: "Andy falls off a cliff", created_at: "2007-12-03 15:15:08", creator_id: 2, completed_at: nil, description: "", deleted_at: nil>}

I can’t read that. This, however, is much clearer:

user_project_url failed to generate from {:action=>"show", :user_id=>"1", :controller=>"projects"}, expected: {:action=>"show", :controller=>"projects"}, diff: {:user_id=>"1"}

The error message really ought to call #to_param on the path parts, don’t you think?

class ActionController::Routing::RouteSet
  # try to give a helpful error message when named route generation fails
  def raise_named_route_error(options, named_route, named_route_name)
    helpful_options = options.inject({}) {|hash, (key, value)| hash.merge(key => value.to_param) }
    diff = named_route.requirements.diff(options)
    unless diff.empty?
      raise RoutingError, "#{named_route_name}_url failed to generate  from #{helpful_options.inspect}, expected:  #{named_route.requirements.inspect}, diff:  #{named_route.requirements.diff(helpful_options).inspect}"
    else
      required_segments = named_route.segments.select {|seg| (!seg.optional?) && (!seg.is_a?(DividerSegment)) }
      required_keys_or_values = required_segments.map { |seg| seg.key rescue seg.value } # we want either the key or the value from the segment
      raise RoutingError, "#{named_route_name}_url failed to generate from #{helpful_options.inspect} - you may have ambiguous routes, or you may need to supply additional parameters for this route.  content_url has the following required parameters: #{required_keys_or_values.inspect} - are they all satisfied?"
    end
  end
end

Make it so!

Comments
  1. Nice! The stock named route error messages have been a big source of frustration for me recently while trying to get some tricky routing to work. I’ll definitely be giving this a try. Thanks!

  2. John Wyles says:

    Just a note to anyone encountering this error; I racked my brain for an hour trying to figure out what the problem was and why I was getting this message. As it turned out I had “:id => false” in the migration for my associative join model that had the polymorphic associations used by my named routes. Had the fix that was recommended in this article been in place I would not have discovered this. After inspecting this verbose output I discovered:

    edit_store_offering_url failed to generate from {:store_id=>#<Store id: 381345048, …>, :action=>”edit”, :controller=>”offerings”, :id=>#<Offering price: 3.99, offerable_id: 381345048, offerable_type: “Store”>}, expected: {:action=>”edit”, :controller=>”offerings”}, …

    If you look closely you see:
    :id=>#<Offering price: 3.99, offerable_id: 381345048, offerable_type: “Store”>} but no “id” column… This was the red herring and what led to the fix for my issue. In this one particular case I am thankful that the fix mentioned in this article was not committed to the rails core! I appreciate your efforts to clean things up, but I have to say: Thanks but no thanks!

  3. Brigitte says:

    Thanks for the patch, but it wasn’t helpful enough for me: I’m using param_to to create readable URLs:

    def to_param
    “#{id}-” + URI.encode(title)
    en

    As it turns out, uri-encoding is not enough to make an acceptable part of a route: some of my titles have dots, questionmarks or slashes in them.

    This problem triggered the named_route_error, but even in it’s most helpful form I didn’t understand that.

    Here’s how I fixed it:

    def to_param
    “#{id}-” + URI.encode(title).gsub(/(%20|.|?|/)+/, “+”)
    en

Post a Comment

Your Information (Name required. Email address will not be displayed with comment.)

* Copy This Password *

* Type Or Paste Password Here *