I was first! The new shallow routes is a great addition to Rails. Thanks, Ryan.
I don't see why shallow routes is a good idea. Nested resources define and detail a lot scoped resources. That describes dependencies between parents and children resources.
When a comment (of an article) is edited, anybody changing the ID of the comment in the URL can access to a children of another parent resource. You can of course handle this by an authorization mechanism but the error would be wrong ! It would raise non-authorized error although it is a non-existing error (no comment for this article match).
So I disagree that using Comment.find instead of @article.articles.find is a better idea...
So, what would be the best way to change CommentsController#index having on the one hand comments nested to a specific Article, and all the comments alone on the other?
You keep telling us to avoid nested resources, without giving any reasons other, than "it can get messy".
If that's the sole reason, then I'd rather suggest using the resource_fu plugin along with a little model query abstraction layer.
I added an entity_set method to my controllers, which I use instead of directly calling Model.find. It parses the current route to see what instance variables need to be set - so in case of /articles/1/comments/2 it would set @article = Article.find(1) and @comment = Comment.find(2). And all this I get from one call (e.g. entity_set(:find_one, params[:id])). This deals with the model queries. And resource_fu deals with the urls.
This is part of a larger DRYing layer, that generates the whole controller, model and view for me (allowing for changes where necessary), based on the database layout.
And here's why I think nested routes are better, than what you describe in this episode - they're easy and fast to abstract away. And that's what we should be doing.
Either way, it's been informative, so thanks again for your screencasts!
@AC I think what Ryan means is that the URLs and helper method names gets unnecessaryly long: /articles/1/comments/1 and edit_article_comment_path(@article, comment) and so on.
With shallow routes the URLs and method names is as short as possible, still enabling to uniquely identify a resource, e.g. a comment: /comments/1 and edit_comment_path(comment)
@David: but when do short/long urls really matter ? Only when pasting urls in irc, forums, emails, etc. In that case shallow routes don't help anyway, because people rarely paste links to edit, or destroy actions. It's mainly index and show in these situations.
On the other hand a full nested routing is descriptive - both for the developer and for the user. It presents a clear hierarchy. I personally, as a user, very often look at the link/form destination url to determine what it really does and how it relates to where I am. It's very helpful.
Maybe "map.root :articles" isn't such a good idea if some wants to make a seo optimised website. Thats because the content of articles is reachable under / and under /articles .
I did avoid this with a 301 redirect method which is called "map.root :controller => 'articles',:action => 'redirect'"
Thanks for this cast :)
Olli
@Martin: Something along the lines of:
@article = nil
unless params[:article_id].blank?
@article = Article.find(params[:article_id])
@comments = @article.comments
else
@comments = Comment.find(:all)
end
I aggree with AC. And instead of writing this:
map.resources :articles, :has_many => :comments, :shallow => true
map.resources :comments, :only => [:index]
yout could also write this:
map.resources :articles, :has_many => :comments
map.resources :comments, :except => [:create, :new]
don't you?
@EppO and AC, this may be more of a personal opinion, but from a user standpoint I much prefer concise URLs. However, this is not the sole reason I avoid nested routes.
I find most web sites difficult to fit in a pure hierarchical structure. Certainly we have one-to-many associations in our implementation, but these often collide with one another and can become deep. A URL is often not capable of handling complex associations, so attempting to mirror them blindly can get messy.
Another reason is due to the point of entry. Nested resources seem to make sense when we're only editing comments through the article. But what if User has_many comments as well, and on the user profile page we list his comments? Well this is another point of entry, and nesting comments under user also makes sense. A comment in itself is just a resource, and the URL should not be dependent on the point of entry.
That said, it ultimately comes down to your app requirements. I think nested routes definitely have their place if there's a strong one-to-many association which will always apply. Look at how github.com nests user/project in their URL. That is a great example of when to use nested resources.
@Martin, the controller could have an if condition like this: http://pastie.org/333938
@Olli, good point!
@Laszlo, you could do that. But then you'd have multiple URLs leading to the same resource (/comments/2 and /articles/1/comments/2). That can cause confusion and lead to poor search ranking.
@AC: In my oppinion short URLs always matter! Remember that the URL of the show action will also get shorter because it's handling a single resource e.g. a comment.
With shallow routes: /comments/1
Without shallow routes: /articles/1/comments/1
I understand your oppinion on nested routing, but I really don't think your navigation should depend on it. Breadcrumbs, tabs, headlines, <title>'s and so on should indicate "where you are", the URL should only be just long enough to identify a resource.
But again, it's a preference thing, and I don't think we're ever going to agree on this. :)
@Ryan: I see your point. I guess it comes down to personal taste and your line of thinking definitely makes sense.
That said, I see no reason why nesting comments in both articles and users would be a bad thing. It's very descriptive actually - you look at the url and immidiately understand what you're looking at. It makes sense for the app, when *it* looks at the url as well.
Also, I try not to think about SEO too much on that level. SEO should be a tweak on an overall great setup, not a fundamental issue when creating the design in the first place. That's IMHO anyway.
Just my two cents..
@David: yeah. As with Ryan - I get your point and it makes sense. It's just a difference in philosophy.
As for seeing where you are by looking at other hints - works fine for a human. But if your app does a lot of self-reflecting, the full nested urls come very handy. At least that's been my experience.
Cheers.
Hey Ryan, how do you nest self referencing routes such as a model that uses acts_as_tree ?
I for one have been waiting for this. My very first project had complex associations with resources quite comfortably nesting under a variety of models.
I was stuck with only one set of path names I could use in my controller when I really wanted to be far more dynamic.
I am looking forward to seeing if this truly brings the flexibility I hoped for.
I think from a newbies perspective it makes more sense going the long way and seeing articles_comment_edit_path just because that's the standard way you would accomplish it or recognize it within a rails application, unless of course they are using the shallow method.
I'm still wrapping my head around these routes in general and learning though so I have yet to fully grasp it all or when it's best to use one method or the other.
Wonderful episode, wasn't aware of this shallow routes addition until now!
Thanks Ryan!
Great show!
I already have implemented nested resources and shallow resources will be a great addition.
Thx a lot!
Question to the community:
On top of nested resources I uses activeresource to exchange data (XML) between 2 web applications. Everyhing is working, but it's super slow as I loop trough all the singe URL's. As far as I see it, it should be possible to transfer just one XML containing all the nested information. What is the best practice here. Is there a good documentation out?
The resources_controller plugin simplifies nested controllers A LOT. In many cases I just need one collection and member finder in my app controller. individual resource controllers are remain almost empty.
def find_resources
resource_service.paginate(:page => params[:page])
end
in the posts / comments example, resource_service is either Comment or @post.comments.
def find_resources
resource_service.paginate(:page => params[:page])
end
in the posts / comments example, resource_service is either Comment or @post.comments.
Ryan,
I have been using Nested Routes for a while now in dev and they ARE quite long and painful. Shallow routes DO help in that sense.
I am also trying to implement user based subdomains into the app with subdomain_fu. Right now, my routes are of the format http://localhost:3000/accounts/1/etc.
Meaning most of the resources are nested under the accounts resource. Instead, id like to just use the 'subdomain' attribute of the Account model as a subdomain and get rid of the '/accounts/1' part of the URL for good, like http://subdomain.localhost:3000/etc.
Ive actually been looking for write-ups on nested resources, routing and subdomain_fu but havent found any of real use. Could you give me a few pointers on how to get started on this?
Ryan: What would you put in the index action of the comments controller to handle whether or not an article ID has been passed? I've seen a number of ways of handling this and nothing really seems all that elegant. It'd be nice to see your take on it.
Thanks!
I too am interested in how you can elegantly implement the 2 different types of index action (/comments and /articles/1/comments).
The method outlined in http://pastie.org/333938 is fairly neat, but requires you to either have lots of logic in your view template, or override the template selection with a render call (see http://pastie.org/341264)
Because this is such a common problem, it'd be nice if there was a standard pattern for achieving this.
Replying to myself, an alternative method might be to move the scoped index action to the articles controller, like so: http://pastie.org/341271 (haven't checked to see if this works).
Good episode. Is there a way to include extra parameters in the URL while still keeping with REST? For example, if you have a resource Place but would like in include the country ID in the URL (ie, /places/us/123-new-york or /places/us/page/:page_number).
Ryan,
as usual a great screencast!
Now, let's assume we'd like to be able enter new comments directly on a form in the article-page (.../articles/1/show).
Since the form will post to the comments-controller create-action, we are not able to redisplay articles-show in case of a validation error.
In your example this problem doesn't exist, because if validations fail, you can just re-render the new-action of the comments controller.
Thanks a lot for your kind help!
The following pastie illustrates the problem mentioned above:
http://pastie.textmate.org/private/5abvempgeb8ytkuqni0pq





