Multi-tasking with RESTful controllers?

Guys,

I’m working to get my head around REST. Reading the chapter in AWDROR
is not helping unfortunately, but I think it may be a result of either
an error in my copy of the book, or my understanding in general.

REST in Rails for simple examples is easy to follow, but it’s where
associations come into the mix that I get a bit confused.

Take the example in the AWDROR book. You have ArticlesController and
CommentsController, and the appropriate nested resource mapping to
make comments available via articles urls…so that you can do:

/articles/1/comments

and get a list of comments for the article with id 1. So far, so good.

Now, extend this example hypothetically and say that I, for whatever
reason, want to get all comments, irregardless of article. One would
expect that, if #index is used for all listings of comments, then I’d
have to write CommentsController#index as:

def index
if params[:article_id]
@comments = Article.find(params[:article_id]).comments
else
@comments = Comment.find(:all)
end
respond_to do |format|
format.html # index.rhtml
format.xml { render :xml => @articles.to_xml }
end
end

This would allow the method to be used in two contexts…one where I’m
after
/articles/1/comments
and one where I’m seeking just
/comments

Now, this design could get pretty nasty, I’d presume, because let’s
say I have other resources in my application that could also have
comments. I’m suddenly forced to walk through the parameters for each
method that could be called in this way and change the behavior of the
resource accordingly.

Here’s where I think an error in the book is confusing me. Of course,
it may not be an error at all. In the table on page 418, it lists
actions that are called in response to various url mappings.

The first listed in CommentsController says that
Actions in CommentsController
GET /articles/1/comments index
comments_url(:article_id => 1)

This says that /articles/1/comments will call #index in the
CommentsController, but if you’ll note in the actual implementation of
the CommentsController, there is no index defined!?!

Which leads me to believe that this is wrong, and that #index will
only ever be called when accessed directly via a GET to /comments

So, two questions:

  1. Is this actually incorrect on the part of the book? Is #index
    unnecessary when called in the context of a nested resource?
  2. How does one manage a resource controller implemention of a
    resource that might be shared amongst different parent resources
    (i.e., I have three resources: Articles, BlogEntries, and
    ProductListings, all that have a has_many :comments?

Thanks for any help you can provide in cleaning up these mental cobwebs.

John

That’s confusing, isn’t it?

Its kinda up to you how you want it to work. Does it make sense to get a
comment outside of the article scope? To me, it does not, so I wouldn’t
even
worry about that case. In fact, I’d make sure that there was no route
that
could even pull that URL up.

Other cases, like /users/1/groups or /groups would make sense. In
that
case, you would definitely need to code for both.

The thing to remember is that it’s about resources and URLs. What does
your
application require you to do? There’s no “right way” or “wrong way” for
this particular case.

On 11/26/07, Brian H. [email protected] wrote:

Its kinda up to you how you want it to work. Does it make sense to get a
comment outside of the article scope? To me, it does not, so I wouldn’t even
worry about that case. In fact, I’d make sure that there was no route that
could even pull that URL up.

Yeah, bad example, but it’s in a book just about everyone on this list
has. So assume it does make sense to use comments outside of the
context of Articles.

Other cases, like /users/1/groups or /groups would make sense. In that
case, you would definitely need to code for both.

The thing to remember is that it’s about resources and URLs. What does your
application require you to do? There’s no “right way” or “wrong way” for
this particular case.

So in the case of my example of Articles, BlogEntries, and
ProductListings sharing comments, I’d have to do:

def index
if params[:article_id]
@comments = Article.find(params[:article_id]).comments
elseif params[:blog_entry_id]
@comments = BlogEntry.find(params[:blog_entry_id]).comments
elseif params[:product_listing_id]
@comments =
ProductListing.find(params[:product_listing_id]).comments
end
respond_to do |format|
format.html # index.rhtml
format.xml { render :xml => @comments.to_xml }
end
end

And the above assumes that CommentsController#index will indeed be
called in response to /articles/1/comments, which the book seems to
contradict itself on. For example, another confusing passage:

“We’ll create a CommentsController to manage the comments resource.
We’ll give it the same actions as the scaffold-generated articles
controller, except we’ll omit index and show, because comments are
displayed only from an article’s show action.” (p 419)

Thanks for your continued help.

John

On 11/26/07, Brian H. [email protected] wrote:

You’re on the right track. Keep going. Go with what your gut tells you with
this stuff. Does it make sense? Absolutely it does.

So you’re saying the CommentsController#index would indeed be called
in response to /articles/1/comments? If so, then why do they
specifically not implement this in the book example?

Thanks,
John

You’re on the right track. Keep going. Go with what your gut tells you
with
this stuff. Does it make sense? Absolutely it does.

You’re polymorphically showing the comments for an article, a
blog_entry,
etc. What you pass in the URL will determine what record it looks up.
The
way you’re doing it is nice and clean too because you access it via the
scoped entity as opposed to doing comment.find_by_id.

You’ll need logic similar to this for doing a show, and maybe even a new
/
create… Probably not for an edit, but then again, maybe. I suggest a
before_filter for that to reduce code duplication.

I don’t know what the book is suggesting you do.

The index action refers to the return of a collection. If you have a
nested
route, /articles/1/comments would look for comments#index.

@Nicholas:

I think you meant to direct this to the OP.

Brian, the issue with polymorphic resources has been experienced by
others (I have not had to deal with this yet). If you are just
starting out in RESTful design, then I’m not sure if this will be of
help, but since you are asking the right questions it might be. James
Golick has developed a plugin called ResourceController. Part of this
responsibility is to solve the issue you raised. See the section
“Polymorphic Resources”.

http://jamesgolick.com/2007/10/19/introducing-resource_controller-focus-on-what-makes-your-controller-special

HTH,
Nicholas

Quite right, Brian, sorry about that - yes my post was directed to
John.

On 11/27/07, Nicholas H. [email protected] wrote:

Quite right, Brian, sorry about that - yes my post was directed to
John.

Very interesting…thanks Nicholas!