mike enriquez

Mac/iOS/Web Developer

Technical stuff

Namespaced controller and url generation gotcha in Rails

I ran into a gotcha while working on a Rails app that was written a few years ago, before the idea of REST came about.

What is the path generated from the following?

<%= link_to "Clicky", :controller => "posts" %>

Normally, you would expect /posts, but that’s not always the case. It depends upon the context of where this is being called. If you’re inside a namespaced controller such as Admin::CommentsController, then the generated url is /admin/posts.

It turns out that Rails assumes that if you’re within the context of a namespace that you want to stay in that namespace. You can read more about it in the documentation for url_for. It’s a bit of a WTF moment when you specify a controller and you end up with something else.

Here are 2 ways to get around this behavior.

Workaround #1: Add a leading slash

The leading slash forces the url generation to ignore the defaults.

<%= link_to "Clicky", :controller => "/posts" %>

Workaround #2: Override url_for

In the app I was working on, changing every generated url wasn’t doable given time constraints. I came up with a little hack to override url_for in the controller to always add the leading slash.

# app/controllers/admin/comments_controller.rb

protected
def url_for(options = {})
  if options.is_a?(Hash) && options.has_key?(:controller)
    options[:controller] = "/#{options[:controller]}"
  end
  super(options)
end

I haven’t investigated any side effects to this, so be warned and run your tests.