[uf-rest] Fwd: REST and Rails

Dan Kubb dan.kubb at autopilotmarketing.com
Sat Apr 1 17:22:41 PST 2006


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Hi Charlie,

> ====  Syntax ==========
> The currently proposed syntax is:
>
>   resource :collection do |r|
>     r.post do
>     end
>   end
>
> Where 'r' is an instance of a Resource class.  At first I preferred  
> a simpler syntax, like this:
>
>   resource :collection do
>     method.post do
>     end
>   end
>
> Where method represents the HTTP verb (GET, POST, PUT)?  Or maybe  
> even:
>
>   resource :collection do
>     request.post? do
>     end
>   end
>
> However, do you have to have an instance of the resource class to  
> achieve the 4 points you brought up (more details in original email):
>
>   1. Handling OPTIONS requests.
>   2. Returning 405 when appropriate
>   3. Return 501 when appropriate
>   4. Checking If-* request
>
> These all would be good features to have.  If they require having a  
> resource class passed as "r" then I can agree to this approach.

To do the first three of the above you need to keep track of which
HTTP methods have handlers defined for them.  I thought the most
obvious place would be in a "resource" object, since in REST terms
you call methods on a resource.

To do the fourth option you need to check the If-* headers AFTER
you've instantiated all the models that could affect the output of
the representation (view) or the logic in the per-method handler,
but BEFORE you actually run the per-method specific code.

So the pattern is:

   resource :collection do |r|
     # instantiate models

     # register models as conditions affecting the display of
     # the representation or affecting the logic in the per-method  
handler

     # check the If-* headers against the server state before executing
     # the per-method handler

     # execute per method handler
   end

I still think I prefer the top-most syntax.  In fact, I think the third
one could potentially introduce bugs because of the 4 steps that are
performed.  I suppose you could get around that by explicitly testing
the conditions, but its not really necessary, most people will forget,
and should ALWAYS be performed before executing the per-method handler.

> ====  Templates   ==========
> You mentioned that you'd expect these responses for different methods:
>
>   GET    - 200 OK        - Response Body
>   POST   - 303 See Other - No Response Body, Location header to  
> newly created  resource
>   PUT    - 303 See Other - No Response Body, Location header to  
> resource
>   DELETE - 303 See Other - No Response Body, Location header to  
> collection resource
>
> Using regular HTML forms I'd agree.  However, this breaks down when  
> using XmlHttpRequest.

This response pattern is not what I'd recommend when doing AJAX  
stuff.  I would only
do this when forced to tunnel PUT/DELETE over top of POST when using  
HTML forms,
which is an altogether different scenario than an AJAX client.  An  
AJAX client doesn't
have to do any tunneling, since it can issue a true PUT or DELETE on  
its own.

The response pattern I normally use for web services (including AJAX  
clients) is the
following:

   GET    - 200 OK         - Response Body
   POST   - 201 Created    - No Response Body, Location header to  
newly created resource
   PUT    - 204 No Content - No Response Body
   DELETE - 204 No Content - No Response Body

I'm very particular about what headers and response body I return for
specific methods.  I suppose some people would execute a PUT, and then
return the same response body as you'd get when issuing a GET in order
to cut out a round-trip to the server.  To me that smells like a  
premature
optimization of sorts.   I'd prefer to keep the API simple and  
consistent,
and if I need to know the current state of the resource I'll perform a
Conditional GET on the resource, not rely on a "PUTGET" to know the  
state.

I'm not saying these two patterns are the best ways to respond to  
specific
HTTP methods, I'm just saying that they've worked well in all the cases
I've personally tested.

> Thus, I think we must provide a system where you can provide a  
> response body regardless of the method.  One approach is Peter's  
> proposal of just using a standardized directory structure:
>
> view
>     my_controller
>         resource1
>             get
>             put
>        resource2
>            post
>           delete
>
> I like this concept.  However, maybe it would be confusing since  
> Rails uses directories for nested classes  
> (MyNamespace::MySubNamespace::MyController).  Thus, we could go use  
> the name mangling I currently do now, i.e., get_resource1, or  
> resource1_get (which I like better because it groups the templates  
> for a resource together a bit more clearly).

Since I follow the response patterns I outlined above, I'd never need  
this
sort of directory structure, so to me it adds unnecessary  
complexity.  I'd
personally hold off on this until a pattern emerges in working code and
ONLY if adopting this convention reduces code consistently across the
code base.

For example, if you had a ton of code that looked like this:

   resource :collection do |r|
     # ... get the models ...

     r.post do
       # ... do some work ...
       render :template => 'collection_post'
     end

     r.put do
       # ... do some work ...
       render :template => 'collection_put'
     end
   end

Then I'd say go with it.  But if you attempt to use the patterns I  
outlined
above, I don't think you'll see it too often.

I've mentioned before, but I'm always cautious about breaking  
fundamental Rails
conventions for our own purposes, like the naming and location of  
templates.
There's so much already out in the wild that relies on those  
conventions that
to break one means that you suddenly have to re-implement other  
functionality
that rely on those conventions.  Maybe in this particular case it  
won't be
a big deal (I don't know), but we get alot for free with rails and  
I'd like
to keep as much as I can by sticking with conventions by default.

> ====  Top Level Resources  ==========
>
> Here is something Peter and I disagree about.  Do you prefer this  
> style:
>
> def MyController
>    resource :collection do |r|
>       ...
>    end
>
>    resource :member do |r|
>       ...
>    end
>
>    resource :editor do |r|
>       ...
>    end
> end
>
> Or this:
>
> def MyController
>    r.get
>       ...
>    end
>
>    r.post
>       ...
>    end
>    resource :member do |r|
>       ...
>    end
>
>    resource :editor do |r|
>       ...
>    end
> end
>
> The difference being in the first case you insist every resource  
> must be defined via "resource" while in the second you say that the  
> controller is the top level resource.  When I first did my  
> RestController I went with option #1 because it seemed more  
> elegant.  However, I quickly changed my mind when actually writing  
> code because it turned out to be annoying having to add the extra  
> syntax.  This was particularly true for controllers that are just  
> single resources.  For example, say you have geocoding  
> functionality so you have a GeocoderController.  It does just one  
> thing, GETs geocoding results so there is no concept of collection/ 
> member.  Thus I favor option #2 as a syntax shortcut.

I prefer the first style for a couple of reasons.

The first one is esthetics.  I like my APIs to be consistent, whether  
in web
services or programming.  I don't like the idea of having one way to  
define
a "default" resource as opposed to a normal resource.  Maybe I'm taking
a page out of DHH's Emo Programmer, but it gives me cold shivers rather
than warm fuzzies ;)

The second one is that its really close to the rails way of doing  
things.  If
we tell developers "the resource method creates normal rails actions  
named
collection, member and editor in this case."  Simple.  Its so easy to  
tell
people who are trying to wrap their heads around it to do a s/ 
resource/action/
in order form the mental bridge between Rails and REST.

The third one seems to be sort of an optimization again.  It cuts out  
only
two lines of code and I can't help but feeling it would add a little  
bit of
cognitive overhead to others having to maintain it.. I know I had to  
do a
double-take when I first saw the syntax on your blog last week.

And fourthly it means you can't gather up all the models before  
executing
the per-method HTTP handler.  That means you'll either have duplicate  
code
in each of the handlers, or you'll be using a before_filter to do the  
work.
And if you're not using the same model objects in each of the per-method
handlers then I would argue that you don't have a single resource, but
rather two or more distinct resources that should have their own
per-method handlers.

> ====  Caching ==========
> Sounds like we've agreed that caching is important, but is not part  
> of the Rest Controller.  Thus, you'll split that off the caching  
> functionality into another plugin that can work with a Rest  
> Controller.

Yes, I will work on this next week.  It should be relatively simple.   
I'm
probably going to do it after I update before_filter to add a way to
specify them on a per HTTP method basis.

>  ====  Implementation ==========
>
> I see that your implementation is significantly different than  
> mine.  I map :resource to a Ruby module, which gets included in a  
> controller.  In contrast, you map :resource to a Ruby method.   
> Within that method you create an instance of a Ruby class called  
> Resource and then execute the provided block in its context.  This  
> disadvantage of my approach is that there is method renaming that  
> goes on under the scenes, which you have to know about in order to  
> get filters to work correctly.  The disadvantage I see to your  
> implementation is that all the methods for a resource get mapped  
> under a single Ruby method.  It seems that would make it hard to  
> apply filters to specific methods.


> I.E., how could I apply a filter to a GET method of the resource  
> Member but not to the POST?  It would be great if we could use the  
> existing filter syntax without change, but I'm guessing that might  
> not be reasonable because now we are dealing with Resources and  
> Methods as opposed to Actions.  Anyway, this is something I think  
> is important to solve.

It should be relatively simple.  A filter runs before the action ever  
executes, so
it doesn't have to know anything about the way the action is being  
handled.  It should
simply be a matter of wrapping an old before_filter in a Proc that  
checks to see
if @request.method matches a list of allowed methods.  Maybe 2 or 3  
lines of code.

I was thinking of using Peter's proposed syntax, which adds  
the :methods condition:

   before_filter :auth, :methods => [ :post, :put, :delete ]

So in this example we'd run the rails auth method whenever the HTTP  
request
method is either POST, PUT or DELETE.  Of course, you could still  
restrict
it further by using the :only condition to specify exactly which
actions/resources to apply the filter to.

I think this would be nice to add to Rails core, but for now I'll add  
it to my
Rails Controller.  I'd also make sure after and around filters could  
use the
same syntax.

> Also, how does your Rest controller interact with the base  
> ActionController?  I'm not seeing it being include anywhere...could  
> you point me to the appropriate code.

Its supposed to be included at the top of the controller (just like  
yours)
with "include RestController::Base".

> Last, it would be good to have an example controller for people to  
> play with.  Something simple, like a ProductController or mabye  
> copying one of the examples from the Agile Web Development book.

For the most part the example at
http://microformats.org/discuss/mail/microformats-rest/2006-March/ 
000158.html
should work.

People can use the original syntax:

   def new
     resource.post do
     end
   end

Or the proposed syntax, which I added yesterday so people could try  
it out now
while we thresh out the design on this list:

   resource :new do |r|
     r.post do
     end
   end

- --

Thanks,

Dan
__________________________________________________________________

Dan Kubb
Autopilot Marketing Inc.

Email: dan.kubb at autopilotmarketing.com
Phone: 1 (604) 820-0212
Web:   http://autopilotmarketing.com/
vCard: http://autopilotmarketing.com/~dan.kubb/vcard
__________________________________________________________________



-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.2.2 (Darwin)

iD8DBQFELydh4DfZD7OEWk0RApinAKCfLe+LM9oo1XhpQcz58LlE0QjJNQCgp1RP
lEnzmkMObzdguxA1zu1e9aM=
=6tBC
-----END PGP SIGNATURE-----


More information about the microformats-rest mailing list