Rerouting Rails Part II

I wrote about my initial experiments with reimplementing Rails’s routing yesterday; this post continues the story.

I’ve started running the tests from Rails’s Action Pack against my routing implementation. It forced me to change some names to match the existing API. So far, so good.

One obvious failure occurs when the controller is namespaced: Rails allows you to give a route like ':controller/:action/:id' but to have two segments in the controller, such that the URL '/foo/bar/baz/1' will match controller Foo::Bar with action baz and id 1if and only if such a controller exists.

Now, I could implement this fairly easily. For a start, I’d have to change my tree to branch on strings instead of route segments, and to start using left-anchored substring matches against the remaining request URL at each comparison. In fact, regardless of the namespace problem, that might well be a better method than the one I’m currently using, as it would preserve the original path more accurately when this is passed as a parameter to an action.

But there’s a problem: the router still needs to know the name of every valid controller in the application. Another possibility would be to make :controller a special case, with an extra branch for a possible namespace. Once again, though, the router needs to know what those are.

I dislike the tight coupling of the current routing implementation, and I don’t think that the router should have to know which controllers are valid (and especially not by scanning through source directories as it does in Rails at present). The trade-off is that routes files would have to specify namespaced controllers more explicitly, with a

map.connect 'namespace/:controller/:action/:id' ...

line for each namespaced controller. I don’t really see this as a bad thing, considering how it helps in making the code cleaner and better decoupled, but I’d be interested to hear any points of view on the subject.

As I was rummaging through the routing tests, I found that there’s a benchmarking ‘test’ in there, although (a) it only runs when a specific command line option is passed to the test runner, and (b) doesn’t actually work after all that! I ported it over to benchmark my routing file, adding explicit routing for the namespaced controllers. (I also pulled it out of the test file where it didn’t really belong.) With the requisite quantities of salt, here are the numbers on my MacBook:

Recognition (RouteSet):
0.0537548184394836 ms/url
18602.9834911597 url/s

As I haven’t managed to get the same benchmark running against the Rails code, I can’t compare the speed, but it’s fast enough: 50 μs on a request is really not going to be any kind of bottleneck. And it’s all done in 169 lines of code, including blank lines.

Let me spell that out clearly: It’s possible to implement route recognition that is clean, fast, and doesn’t use any magic in a small number of lines of code.


  1. Jamis Buck

    Wrote at 2006-11-19 03:42 UTC using Firefox 2.0 on Mac OS X:

    Great work, this is good stuff. I’m very curious to see how your route generation stuff goes, since that’s the primary cause of most of the complexity in the current implementation. Keep it up!
  2. Dr Nic

    Wrote at 2006-11-19 09:38 UTC using Firefox 2.0 on Windows XP:

    Agreed, keep up the great work.
  3. Tim

    Wrote at 2006-11-19 13:18 UTC using Firefox on Windows XP:

    Awesome. Any chance of a looky at the code yet?
  4. Olle Jonsson

    Wrote at 2006-11-22 08:03 UTC using Firefox 2.0 on Mac OS X:

    Thanks for writing about your complexity-reducing adventures, Paul.

    Excited yelps from the sidelines.
  5. Jamie Hill

    Wrote at 2006-11-30 16:46 UTC using Safari 419.3 on Mac OS X:

    This could simplify adding routes via plug-ins as currently that is a nightmare!
  6. Alex Payne

    Wrote at 2006-11-30 17:34 UTC using Safari 419.3 on Mac OS X:

    Very cool! How will it interact with RESTful routes?
  7. Paul Battley

    Wrote at 2006-12-01 00:04 UTC using Firefox 2.0 on Mac OS X:

    It doesn’t know anything about RESTful routes as such, but there’s nothing technically preventing them—or at least there won’t be once I’ve finished implementing the string-based branching I mentioned.

    I envisage that the RESTful syntax will be implemented as a translation layer on top, so that

    map.resources :comments, :member => { :approve => :post }

    will be expanded to

    map.connect ‘comments/:id;:approve’, ...

    etc. I think it’s useful to keep the syntactic sugar out of the core implementation.
  8. MimliEsmim

    Wrote at 2008-04-19 00:25 UTC using Internet Explorer 6.0 on Windows XP:

    male celebrity smokers , hermione granger xxx , payton milf ,