Positive Incline Mike Burrows (@asplake) moving on up, positively

February 10, 2010

Experimental {.format} in Routes

Filed under: Web Integration — Tags: , , , — Mike @ 7:34 pm

A while back I raised on the URI Template mailing list the idea of a {.var} syntax that generates optional path extensions, “.html”, “.json”, etc.

For example, given a path template of /releases/{id}/notes{.format} and an id parameter of “1”, paths of /releases/1/notes or /releases/1/notes.pdf will be generated depending on whether the supplied format parameters are undefined or “pdf” respectively.

It’s not a completely original idea.  Rails has a (.:format) syntax that DescribedRoutes maps to the flexible but somewhat clunky {-prefix|.|format} syntax of the old draft URI Template spec. The proposed syntax seems more in keeping with current draft (very much a work in progress) though.

Today my Routes fork gained support for the syntax and tweaks to the new SubMapper helpers so that duplicate “formatted routes” aren’t generated (something that happened to Rails a long time ago).

Example config (<code>config/routing.py</code> in your Pylons app):

with mapper.collection(
                'releases',
                'release',
                requirements={'id': 'd+'}) as c:
    c.member.link(rel='notes', name='release_notes')

And in the paster shell:

>>> print mapper
Route name     Methods Path
releases       GET     /releases{.format}
create_release POST    /releases{.format}
new_release    GET     /releases/new{.format}
release        GET     /releases/{id}{.format}
update_release PUT     /releases/{id}{.format}
delete_release DELETE  /releases/{id}{.format}
edit_release   GET     /releases/{id}/edit{.format}
release_notes  GET     /releases/{id}/notes{.format}
>>> url('release_notes', id=1, format='pdf')
'/releases/1/notes.pdf'
>>> url('release_notes', id=1)
'/releases/1/notes'
>>> mapper.match('/releases/1/notes')
{'action': u'notes', 'controller': u'release', 'id': u'1', 'format': None}
>>> mapper.match('/releases/1/notes.pdf')
{'action': u'notes', 'controller': u'release', 'id': u'1', 'format': u'pdf'}

Just add formatted=False to your collection() call if you don’t want this new behaviour.

I haven’t sent the Routes guys a pull request yet. I’d like some feedback first, and meanwhile I will try to come up with a nice helper that takes the format extension to override content negotiation. My app has some basic header-based conneg already but I would like the convenience of being able to override it in the URL.

3 Comments

  1. Hi Mike,

    I’ve been following your work on Routes with some interest. I’m puzzled that in your various posts, you haven’t mentioned the {format} options that Routes’s map.resource() [1] generates.

    I’ve found it quite useful even though the auto-generated mapping isn’t as comprehensive as the one you describe above, it omits the POST and PUT (but they can trivially be hand-added).

    My inherent laziness steers me towards the use of html forms so that I can take advantage of tw and formencode for validating inputs. Have you got anything specific in mind to play this role in a {format} context, or is it mostly a roll-your-own situation?

    Cheers,

    Graham.

    [1] http://routes.groovie.org/manual.html#restful-services

    Comment by Graham Higgins — February 11, 2010 @ 1:57 am

  2. Hi Graham,

    Thank you very much for the comment. You may wish also to comment on @validate revisited, JSON support, content negotiation [pylons-discuss] which covers some of the same ground. The short version is that I have refactored @validate (just within the scope my work project currently) and extended it with just a few lines of code to add json support.

    > you haven’t mentioned the {format} options

    Not directly, but I alluded to them when I referred to duplicate “formatted routes”. What map.resource() does is to generate near-duplicate routes that differ only in name and {format}:
    foos: /foos
    formatted_foos: /foo.{format}
    foo: /foos/{id}
    formatted_foo: /foos/{id}.{format}
    and so on, whereas map.collection() will generate
    foos: /foo{.format}
    foo: /foos/{id}{.format}
    i.e. half the number of routes, with the format extension being optional. This makes for a shorter/nicer config if routes are specified individually, and when Rails made a similar change in (I think) 2.3, a significant memory saving was claimed (not measured here).

    Regards,
    Mike

    Comment by Mike — February 11, 2010 @ 10:24 am

  3. […] Positive Incline Mike Burrows (asplake) moving on up « Experimental {.format} in Routes […]

    Pingback by Experimental: Pylons validation (un)decorator with JSON support « Positive Incline — February 13, 2010 @ 12:24 pm

RSS feed for comments on this post.

Sorry, the comment form is closed at this time.

Powered by WordPress