Tips for data smugglers
While I was working on the acts_as_resource plugin trying to fix things up so that the resource finding side of things works neatly, I realised that I needed some way to get at the ordered list of parameter keys that were matched by the routing system.
One way to do it would have been to parse the path again, but that smacked a little too much of repetition, after all, the routing system knows this stuff already, but how to get at it?
What I needed was the route object that matches the current path.
I ended up modifying ActionController::Routing::Route::parameter_shell. In case you aren’t familiar with the innards of Rails’ routing system, parameter_shell is the ‘skeleton’ params hash that the route populates with information from the current url. So, if you have a route like:
then the generated route’s parameter shell would look like:
{:controller => ‘planets’, :action => ’index’}I needed to stuff the route into that hash, but what key should I use? My first thought was to use :matched_route, but what would happen if some future rails application decided that it needed the :matched_route for something it wanted to do?
So, then I thought of adding a ‘ghost’ key to the hash:
matched_route = self class << @parameter_shell def matched_route matched_route end endThat would have worked by hanging a method off the side of the parameter hash which, through the magic of closures would return the matching route. At least, I think so (I’m still not entirely sure when Ruby closes over things). Before I started to test it, I remembered that the first thing the recognize method does when it has a successful match is dup the parameter shell, and singleton class magic doesn’t survive dup.
Then, I remembered something lovely about the way Ruby’s hashes work; keys can be arbitrary objects! All I needed to do was to use a key that was neither a string nor a symbol to smuggle the data through, and there would be no way that it could clash with some future application. So, my current implementation of parameter_shell_with_matched_route looks something like:
It turns out that various of the Hash support methods in Rails don’t play that well with guarded hash keys like that, so I’ve adapted ActionController::AbstractRequest to pull the matched route out of the parameter hash at the earliest opportunity and stash it in a new method. Here’s the code that does that:
