[uf-rest] RESTifying RAILs

Dan Kubb dan.kubb at autopilotmarketing.com
Sun Nov 6 22:58:31 PST 2005

Hi David,

> class PeopleController
>   verbs_for :person do
>     def post
>     end
>     def get
>     end
>   end
>   verbs_for :friend do
>     def post
>     end
>     def get
>     end
>   end
> end
> This would internally get translated to something like:
> class PeopleController
>   def person
>     # check for methods, standard responses
>     # delegate to person_verb_post when person is request through POST
>   end
>   private
>     def person_verb_post
>     end
> end

I can't claim to be a ruby expert (I've only been at it for a couple of
weeks), but this looks fine to me -- probably more readable than my  
suggestion of just using a naming convention like person_post to handle
POSTs to the person action.

Obviously the final API is still up for discussion, but I think we're
starting to get a better idea on how a RESTful Rails app will handle:

   - clean RESTful URIs
   - routing via the HTTP method (verb)
   - access control by verbs
   - OPTIONS support

REST is all about using the HTTP protocol to its fullest, and I'm
sure there are thing we haven't discussed that HTTP can do, but
that frameworks don't make easy to do yet.

One thing that comes to mind is that its hard to do the
right thing when it comes to caching.  When responding
(ideally) you want to send an ETag and Last-Modified header
for a resource.  Its also a good idea to send a Cache-Control
header so the client knows how long (or if) you want the
resource to be cached.  Then there's the Expires header
which (I think) when subtracted by the Date header to get
the number of seconds difference, should pretty much be in
sync with Cache-Control's max-age parameter -- anyone
know if this is true 100% of the time?

Given proper instructions a browser can wait a specific period
of time before contacting the server and just use what it has
in-cache, thus reducing traffic.  And when it does contact the
server it can send headers like If-Modified-Since, or If-Match
to ask if the resource has changed at all and get a 304 with
no response body if it hasn't.   This can speed up the client
AND the server since the client doesn't have to download and
render anything on the screen, and the server doesn't have to
spend time rendering the view.

The problem is that you have to remember to do all this stuff,
and the way you configure the Cache-Control header really
isn't that user friendly, except maybe in the simplest cases.

It would be really nice to be able to easily say:

   "Cache this resource for 10 minutes, and when you do
    attempt to retrieve it, the resource etag is ABCD-EFGH
    and it was last modified on Nov 6, 2005 at 9:54pm.
    The resource is public and can be shared freely."

To do this maybe we could do something like:

   def get
     # .. set up @resource, an AR object

     cache @resource, :as => :public, :for => 10.minutes

The following could then happen:

   1. The attribute values of @resource would be SHA1
      hashed to create an ETag.   Another option is
      that a Model method could be executed to generate
      the ETag value.

   2. The modified_at attribute (if exists) would be
      used to set the Last-Modified header

   3. The Expires header could be set for a date
      10 minutes from now.

   4. The Cache-Control would be public and the
      max-age would be 600 to be in sync with Expires.

   5. For responses to GET/HEAD requests AND before
      the view is rendered, the If-Match, If-Modified-Since,
      If-None-Match, and If-Unmodified-Since request headers
      would be inspected.  If according to these headers
      the resource has not changed since the client viewed it,
      a simple "304 Not Modified" response could be returned.

      This saves all the work of having to render the view
      when it isn't necessary.

   6. I don't know alot about AR's caching mechanisms,
      but perhaps this might be able to tell memcached
      to keep this resource cached for the next 10
      minutes and then expire it from the cache.

   7. This might provide hooks to implement a type of
      page caching.  It could output the file in the
      cache if it is less than 10 minutes old and just
      skip rendering.  If the file is more than 10
      minutes old it could delete it, and then
      regenerate it.

With one line of code would allow caching to be more
easily used and allow conditional GETs to be used by
Rails apps.

Another plus is that the code that handles comparing the
If-* headers vs the @resource could be reused to implement
conditional PUT and conditional DELETE requests.  This
would be super-cool for web services and AJAX apps to use.




Dan Kubb                  Email: dan.kubb at autopilotmarketing.com
Autopilot Marketing Inc.  Phone: 1 (604) 820-0212
                             Web: http://www.autopilotmarketing.com

More information about the microformats-rest mailing list