-----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: [EMAIL PROTECTED]
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-----
_______________________________________________
microformats-rest mailing list
[email protected]
http://microformats.org/mailman/listinfo/microformats-rest

Reply via email to