[LRUG] REST and Associations

Eleanor eleanor at goth-chic.org
Mon Feb 26 21:10:21 PST 2007


Hi Roland,

Apologies in advance but this is going to be a long reply, and  
probably not that helpful >8p

On 22 Feb 2007, at 22:02, Roland Swingler wrote:
> 1. From what I've read, I thought all urls were meant to be nouns in
> restland - how does having /posts/1;edit give you any advantage over
> /posts/edit/1 or /posts/1/edit ? A semicolon does not a noun make. For
> me this highlights a possible weakness in thinking of everything as
> nouns - see Steve Yegge's "Kingdom of Nouns" article -
> http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of- 
> nouns.html

<rant>
Rails' RESTful is a good approximation of the real thing, geared to  
an (X)HTML environment. Using raw HTTP (i.e. GET/PUT/POST/DELETE  
headers) there really is no problem with edits - an edit would just  
be a GET existing resource followed by a POST updated resource, and a  
new resource would be created using a PUT resource request. There  
would be no such thing as

	/posts/1;edit

because that's unnecessary at the HTTP level.

However in (X)HTML land your application has to provide an interface  
that allows a resource to be presented in an editable manner, hence  
the use of a parameter to a GET request to return a representation  
that can be edited by the browser. In this case you are not using  
'edit' as a verb, but as a noun identifying the edit form itself. Of  
course the

	/posts/1;edit

URI is a very non-standard usage as most people wouldn't think of  
embedding a semicolon in the URI itself but would instead use a GET  
parameter:

	/posts/1?edit=
or
	/posts/1?action=edit

which has implications for caching of the content. Blame the W3C for  
this annoyance - they've had more than enough years to add DELETE and  
PUT into the HTML specs, instead of arguing over <BLINK>, the correct  
usage of <I> and <B>, and all the other trivia that seems to obsess  
them.
</rant>

Of course these limitations don't apply if you're working on an AJAX  
application as it's then possible to send the full range of HTTP  
verbs :)

> 2. What is the right way to do search? Are query strings still ok in
> urls? Is search a seperate controller?

A RESTful search should consist of a URI approximately similar to the  
search criterion, and your app would transparently do the search  
routing in the background, returning an index page listing the top  
hits. So for example:

	/books/authors/a-z/country/us	
and
	/books/country/us/authors/a-z

would both perform the same search and return the same listing of  
authors. This reflects the fact that both URIs refer to the same  
resource and that its index page is the natural place to list all of  
the matching authors. However nobody does this - probably because  
there is a common misconception that REST somehow mandates unique  
URIs for resources, which is not the case. Hence the usual  
alternative of:

	/books/search?authors=a-z;country=us

which is not a valid REST request ('search' is a redundant verb), or  
the somewhat more RESTful

	/books?authors=a-z;country=us

where GET parameters are being used appropriately to specify  
criterion. Of course parameters in GET requests have a habit of  
confusing caching servers, increasing traffic for popular searches on  
the originating server...

> 3. Similarly what is the right way to do sorting/pagination? For
> example when I GET /posts I may want to only return the top ten in
> html - but what about in the xml respond_to do I return all of them or
> just ten? Is the idea that *exactly* the same resource(s) are
> returned, no matter the mime type or can _what_ is returned vary as
> well as _how_ it is returned?

Pagination could be handled using a URI such as:

	/recipes/1/pages/3?page_entries=30

which makes proper use of HTTP parameters with a GET request. There  
is of course a Pagination object in Rails, but for many tasks it's  
somewhat restrictive and it's simple enough to roll your own.

A common mistake with pagination schemes is to mandate the number of  
page entries on the server side, which is not where responsibility  
should lie (HTTP servers should not maintain state, even if it is one- 
size-fits-all 'default' state). With vanilla (X)HTML the client may  
need to be told the number of entries by scripts on the server as  
part of creating appropriate browsing links for the page, but web  
services and URI interfaces should be more flexible than that. After  
all, it's the client that understands the concept of pages and the  
client should therefore be able to specify how large it believes  
pages should be and which page it thinks it wants in the sequence -  
or some other meaningful pagination specifier. This would suggest  
alternative URIs of the form:

	/books/1?page=3&paragraphs_per_page=10
or
	/recipes/1?start_item=91&items_to_list=30
or
	/recipes/1/items/91-120

and the server would either return valid content or a resource not  
found error (either a standard 404 or a redirect to an appropriate  
'sensible' error response for an HTML browser).

> 4. The urls nested resources produces only seems to make sense for the
> new / index /create actions - by the time the resource has a unique id
> it already has a unique url, because the id is unique - but there
> doesn't seem to be any way to disable the urls you don't want.

You've just eluded to the single biggest flaw in Rails' URI  
conventions. Exposing database IDs over the network is a very bad  
idea and everyone should write their applications to use public  
resource names rather than IDs. In real-world applications it will  
have minimal impact on database query performance (after all, you are  
using indexes I hope!) and at the same time will make your URIs much  
more robust. Otherwise you're handing implementation-dependent detail  
to any random third-party that wants to explore your application, and  
constraining your public interface to a static view of your data.  
You're also breaking a key tenet of user interface design - a URI is  
intended to be a human-friendly resource identifier and as such  
should use meaningful semantics wherever possible to simplify the  
user experience!

Instead of:

	/posts/1

you should favour:

	/posts/my-views-on-REST
or
	/posts?title=my-views-on-REST&version=0.1
or
	/posts?year=2006&month=December&category=REST&author=all
or
	/posts/2006/December/REST

none of which expose your database IDs, and all of which can point to  
the same resource, allowing users to remember whichever style of URI  
makes most sense to them as well as making educated guesses about how  
to search for other resources in your application.

This is where the environment/routes.rb file comes in to play. Some  
useful resources on Rails routes include:

	http://www.pinupgeek.com/2006/5/24/rails-routing-demystified
	http://www.afreshcup.com/2007/2/9/basic-routing-in-rails

but these all tend to be pretty basic. I've yet to find any really  
deep geek articles on the subject, although LRUGer Paul Battley has  
done some interesting work on a procedural alternative to the  
standard routing model so he might be able to suggest appropriate  
resources.

> 5. For nested resources, how do you avoid a dirty great switch
> statement in your controller if things can be nested under many
> different resources?

Rails tends to encourage one controller per view so if you've lots of  
big case statements in your controllers the chances are that you've  
too few controllers relative to your views. Also remember that  
multiple routes can all lead to the same controller/view combination...

> 6. What do you do when you want to create multiple objects at once?
> What do you return in the location field in the xml response? or
> update or destroy many objects at once? What happens if you want to do
> ANYTHING to more than one resource at once?

You could try using a similar approach to the one suggested above for  
search, in that effectively a multi-resource delete or update is  
analogous to a GET, so performing a POST with a form returned by the  
query:

	/books/authors/a-z;edit?theme=&

would change the theme attributes on the specified range of  
resources. The returned URI for the location should probably be:

	/books/authors/a-z

as that's the resource that in this case has been updated and an  
index.html would be returned to a browser (or index.xml, index.jpg,  
whatever other MIMI-type is requested).

With regard to how many items to return in a request, this in general  
should be specified by the client (as mentioned above).

> 7. How useful is ActiveResource - is anyone here using it? can you do
> much more than find a list of resources/resources by id - like the sql
> options in find()?

The demo of it at RCE looked interesting as it'll allow complex  
automation pipelines to be built using a very similar API to that  
provided by ActiveRecord. However if it does rely exclusively on  
resource IDs I have a feeling it's going to cause almost as many  
problems as it will solve.

> Any thoughts on any of these very very welcome - and as a call for
> talk ideas was raised earlier I would really appreciate a talk on REST
> that goes beyond the basics of here is a resource generated with the
> scaffold - "look now I can find a record with an id with
> ActiveResource" - anyone offering...?
>
> On a side note does anyone know if there is a group/mailing list
> devoted to REST - I tried to find one the other day but google groups
> came up blank :(

A good place to start research the subject:

	http://rest.blueoxen.net/cgi-bin/wiki.pl

which has links to other resources.


Ellie

Eleanor McHugh
Games With Brains
----
raise ArgumentError unless @reality.responds_to? :reason




More information about the Chat mailing list