Announcing resource_hacks

Posted by Jeremy Voorhis Tue, 01 Aug 2006 04:02:00 GMT

resource_hacks is a plugin that extends edge Rails’ new restful routes implementation. While the current opinion of the core team is that we should only use member resources with a numeric id, resource_hacks allows Rails to support arbitrary an arbitrary member path. Allow me to illustrate:


  map.resources :entries

gives us the member path /entries/1 where 1 is the value of params[:id]. With resource_hacks, you can still use a numeric id, but also declare an arbitrary member path like so:


map.resources :entries,
  :member_path => '/entries/:year/:month/:day/:permalink'

This gives us the member path /entries/2006/8/1/announcing_resource_hacks rather than /entries/1.

With this routing definition, you may send an HTTP request with the PUT method to update the Entry via EntriesController#update. The same goes for DELETE. The collection found at /entries will not be affected by resource_hacks.

You can install the plugin by running


  $ script/plugin install http://svn.planetargon.org/rails/plugins/resource_hacks

from your application’s base directory.

One final note: I cannot take full credit for this plugin. The basic implementation came from Andrew Grim, PLANET ARGON’s latest developer. Welcome, Andrew!

Comments

  1. Alisdair McDiarmid said 1 day later:

    Really nicely done. Are you going to try to get this into trunk?

  2. JV said 1 day later:

    As much as I’d like to, I’m not holding my breath. The plugin is a manifestation of a simple difference of opinion between the core team and myself. The best way to advocate for its inclusion in core, of course, would be to use the plugin and talk about your experience with it.

    Cheers

  3. kogent said 1 day later:

    Outstanding! This is one area where I also disagree with the core-philosophy.

    The beauty of ruby (& rails) is it is readable to the point that most people with no ruby experience can read and figure what the code is up to.

    Why should our code be readable but our URLs not? Thanks for putting together a plugin that caters to those of us that believe that URLs should be readable to our users.

  4. Aslak Hellesoy said 1 day later:

    Nested routes are great, but there is something I don’t understand about how core rails does it. Let me give an example to illustrate why:

    I’m writing an app for XP iteration story management. The app can manage many projects, and each project can have many iterations. With core nested routes the iteration URL would be something like:

    /projects/:project_id/iterations/:iteration_id

    This is not DRY . If the URL specifies the primary key of an iteration (:iteration_id), then the /projects/:project_id part of the URL is redundant. I don’t need to know the project_id in order to find the iteration – I have the iteration_id.

    What I’d rather have is this:

    /projects/:project_id/iterations/:iteration_number

    iteration_number is not globally unique in the database – each project will have an iteration 1. However, it will be unique within the scope of a given project.

    Resource hacks allows me to use nested routes while staying DRY , which is great.

    Am I missing something about the core team’s philosophy on this?

  5. Tim Lucas said 2 days later:

    Aslak Hellesoy: I guess it depends on how you want to refer to your “resource”. If you move an iteration, should the URL point to the old resource, or the one now at it’s old location? Is the “resource” the actual database record, or just “the 2nd iteration of project X”. How will this affect clients of say, an API for your application? The resource itself and the ordering of it within the project be separated, so other clients of the API have a consistent pointer to that actual resource?

    Again, this is probably why this makes most sense as a plugin!

  6. Alisdair McDiarmid said 2 days later:

    Aslak: I think the purpose of path_prefix is more for operating on the collection, rather than the individual. For example, finding the iterations of a project:

    /projects/:project_id/iterations

    looks much nicer than:

    /iterations?project_id=#{project_id}

    The same applies for creating new iterations. Instead of either having a URL parameter (as above), or a hidden field in the form, the path scopes where the iteration is to be created.

  7. Aslak Hellesoy said 2 days later:

    Tim, Alisdair, others,

    What I still don’t understand is this: In a Rails core nested route (numbers are primary keys), how are the following two URLS /resources semantically different?

    <pre> /projects/3/iterations/2 /projects/1/iterations/2 </pre>

    Wouldn’t they both point to iteration with pk 2? What does the project id mean in this example?

    Is it anti RES Tful to define an iteration resource as “the 2nd iteration of project X” (given the constraint that an iteration never moves to a different project).

    Aslak

  8. rick said 2 days later:

    aslak: simply_restful is only a routes macro. Your controller can do whatever it wants with the :id field. I have an app that uses numbers the way you described (think blog comments).

    The default way would be:

    @post.comments.find(params[:id]

    But you can just as easily do:

    @post.comments.find_by_number(params[:id])

    Hell, I’m using strings in one project :) Sometimes the readability of your URLs is more important (SEO for example) .

    For a blogging system, however, I’d probably just define my own route with year/month/permalink URLs on top of the named routes. Use the named routes for simple API stuff and keep the GET url for SEO visability.

  9. JV said 2 days later:

    Rick: I understand that approach, and I did toy around with overloading the :id param – to the extend of also redefining #to_param on some of my models. I have no argument against its effectiveness.

    I do find defining an additional route less aesthetically pleasing to my sensibilities. If i have a year/month/permalink route for displaying a resource, I would like to be able to PUT to and DELETE that resource as well; there should be one and only one path.

    Part of this aesthetic is inherited from the Atom Publishing Protocol: a collection should be allowed to create resource paths as it chooses. The collection then provides references to its members. If that collection constrains the names of its members to a numeric sequence, that is fine. Even though I find that technique to be useful, I often find other naming schemes to be desirable. I want routes to reflect my design decisions, and I want a single path that I can perform the various HTTP methods on.

(leave url/email »)