Rails URL Params as Types
Yesterday I (belatedly) took my friend Nola Stowe’s advice and picked up a book she tech-reviewed, The Rails Way by Obie Fernandez. It’s been a great read so far (about 85 pages in), and I just realized that routing is largely about type conversion.
In the explanation of named routes, there’s a series of examples demonstrating the increasing amounts of syntactic sugar Rails provides:
# The most explicit way, before you define the named route: <%= link_to “Auction for #{h(auction.item.description)}”, :controller => “auctions”, :action => “show”, :id => auction.id %>
# The first use of the named route, no need to specify controller or action: <%= link_to “Auction for #{h(auction.item.description)}”, auction_path(:id => auction.id) %>
# If you just have an :id argument to fill in, you can just pass the value: <%= link_to “Auction for #{h(auction.item.description)}”, auction_path(auction.id) %>
# And you could just pass the object and Rails will get its id: <%= link_to “Auction for #{h(auction.item.description)}”, auction_path(auction) %>
In Rails it’s just syntactic sugar, but the underlying mechanism here is type coercion. Instead of thinking of a URL having the type ‘string’, think of it having the type ‘URL’. (Here’s where all the Haskell guys are saying “Uh, it does already.” and rolling their eyes. You already get it, so this post is not for you.) The syntactic sugar is quietly converting an auction
object into a url
object (or part of one, really).
In Rails you can define a to_param
method on your object that’s called by url_for
(the method underlying link_to
), and the name gives away that you’re converting type like the to_i
, to_s
, to_f
methods you already know from Ruby’s builtin types.
The reverse is less seamless. There is no Model.from_param
. Rails’ routing unpacks the URL into variables for the code you write in your controller to find the object(s). In a RESTful style, it seems odd that Rails doesn’t default to doing something like @*model_name* = *Model*.find(params[:id])
if you don’t define your own load_object
before_filter
.
I’ve been thinking a lot about REST lately. The URL of a resource is that resource converted to a URL type. A view presenting an object could be considered converting it to an HTML type. Controllers and actions... I can’t quite see putting them into my models, but they seem much less distinct to me than they did a few hours ago.