[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