This one cost me at least an hour of frustration.

So apparently the Rails router has considered the dot (“.”) to be a “separator” character along with the slash (“/”) since version 1.2. I don’t know in what context this ever seemed like a good idea, but whatever. It’s not the sort of thing that’s going to bite you every day, but when it does it will be in very weird ways. To wit:

First, a simple routes.rb.

  resources :users do
    resources :projects
  end

Fill in some typical values, and you get a path:

irb(main):009:0> app.user_projects_path("avdi")
=> "/users/avdi/projects"

Now fill in a value with a period in it, and watch it explode:

irb(main):010:0> app.user_projects_path("avdi.grimm")
ActionController::RoutingError: No route matches {
  :user_id=>"avdi.grimm", :action=>"create", :controller=>"projects"
}

:action => "create"? What?!! Who said anything about create?!

As it turns out, there is an invocation in your routes file which will fix this:

  resources :users, :constraints => { :id => /.*/ } do
    resources :projects
  end
irb(main):013:0> app.user_projects_path("avdi.grimm")
=> "/users/avdi.grimm/projects"

Now I know what you’re thinking. “That’s so obvious, why didn’t he think of that immediately?” What can I say, some days I’m slow.

UPDATE: Here’s the Rails 2.* version:

  resources :users, :requirements => { :id => /.*/ } do
    resources :projects
  end

And here’s a version that accepts anything BUT slashes for the parameter value:

  resources :users, :requirements => { :id => /[^/]+/ } do
    resources :projects
  end

Published by Avdi Grimm

11 Comments

  1. I guess the “.” became all about response_for and response formats. I find Rails routing awkward often, though like the arch of your conundrum above, I can usually get the routing the way I want it after a brief visit or two to Hades on the way.

    Reply
  2. I guess the “.” became all about response_for and response formats. I find Rails routing awkward often, though like the arch of your conundrum above, I can usually get the routing the way I want it after a brief visit or two to Hades on the way.

    Reply
  3. THANK YOU! I'm glad I found this post quickly, I was prepared to beat my head against this for quite a while.

    Reply
  4. Thank you so much! Exactly what I needed.

    Reply
  5. thanks! for many resourses we can do something like:

    [:users, ::posts, ::comments].each { |r| resources r, :constraints => { :id => /.*/ } }

    Reply
  6. By allowing /.*/ you bust your regular respond_to blocks though, don’t you? I mean if “foo” is a valid userid and the url is “foo.xml”, it’s going to try to find a “foo.xml” user instead of rendering xml for the foo user.

    Is there a catch-all respond_to we can use instead?

    Reply
  7. Thanks for sharing this. I learned not one, but two things from your post!.

    Reply
  8. Not particularly germane to the subject at hand, but in Rails 3.1.x (3.0.x?) you can tighten up your route constraint:

    resources :users, :constraints => { :id => /.*/ } do

    like so:

    resources :users, :id => /.*/ do

    Reply
  9. Thanks Avdi – just ran into this

    Reply
  10. Thank you bro I was using some unique string to replace with dot :).

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *