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!
@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
@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.
@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.
@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.
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.
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.
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.
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).
the problem that @Frank & Javier #31 mention could be perfectly fit with the <a href="http://railscasts.com/episodes/43-ajax-with-rjs">episode 43</a>, wich by the way, i am really interesting on how to solve.
@ryan (or anybody), can you please post some link/solution?...thanks.
Is this now outdated? I am using Rails 2.3.5 and if I use the @article.comments.build I get an error message noting that there is a "undefined method 'build' for nil:NilClass .....
I'm running into a problem with my form, which is shared across new and edit actions. In the case of new, I need the parent and child objects passed into the form_for to generate the right route. In the case of edit, I need only the child object. I found a work-around for this problem googling around, but I'm surprised it is an issue that requires a work-around? Maybe I'm missing something?
My question is though, now that I'm accessing the nested tags resource under posts, should I be hitting the Tags controller still? Or should I setup some other controller to handle the nested nature of tags at this point? Otherwise I have to build additional logic into the Tags controller. This can be done of course, but is this the common way of handling nested routes and resources? The code I have in the index action for the Tags controller is as follows:
def index
if params[:post_id] && @post = Post.find_by_id(params[:post_id])
@tags = Post.find_by_id(params[:post_id]).tags
else
@tags = Tag.order(:name)
end
respond_to do |format|
format.html
format.json {render json: @tags.tokens(params[:q]) }
end
end
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!
@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.
@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
If you remove the index route for comments the delete links will break. Is there any work around for this?
Great Work Ryan! I just started using Rails 6 months ago and watched all 170+ screen casts as I learned.
Ryan, Wanted to use nested routes with custom RESTful actions. For some reason it's not working.
Learned a lot from your screencasts.
Wish something like this exists for jquery.
the problem that @Frank & Javier #31 mention could be perfectly fit with the <a href="http://railscasts.com/episodes/43-ajax-with-rjs">episode 43</a>, wich by the way, i am really interesting on how to solve.
@ryan (or anybody), can you please post some link/solution?...thanks.
pd: link to pastie on #32 is broken.
found the answer on the comments:
http://apidock.com/rails/ActionView/Helpers/FormHelper/form_for
Is this now outdated? I am using Rails 2.3.5 and if I use the @article.comments.build I get an error message noting that there is a "undefined method 'build' for nil:NilClass .....
its 2013 and this screencast is still actual
I'm running into a problem with my form, which is shared across new and edit actions. In the case of new, I need the parent and child objects passed into the form_for to generate the right route. In the case of edit, I need only the child object. I found a work-around for this problem googling around, but I'm surprised it is an issue that requires a work-around? Maybe I'm missing something?
hi
I get this error while using nested resorces in my new action .
undefined method `fetch_value' for nil:NilClass
can you help me ?
Thanks Ryan!
My question is though, now that I'm accessing the nested tags resource under posts, should I be hitting the Tags controller still? Or should I setup some other controller to handle the nested nature of tags at this point? Otherwise I have to build additional logic into the Tags controller. This can be done of course, but is this the common way of handling nested routes and resources? The code I have in the index action for the Tags controller is as follows:
def index
if params[:post_id] && @post = Post.find_by_id(params[:post_id])
@tags = Post.find_by_id(params[:post_id]).tags
else
@tags = Tag.order(:name)
end
respond_to do |format|
format.html
format.json {render json: @tags.tokens(params[:q]) }
end
end