2

I'd love to know how Rails infers the parent of a nested resource automatically. I have done some homework down in RouteSet but in all that module_eval and dynamically generated methods I haven't figured out how it infers the parent.

This is what I am talking about specifically

Assume the evergreen Posts and Comments data hierarchy with the appropriate models and controllers.

A nested routing of these would looks like this

resources :posts do
  resources :comments
end

When I am in the context of the controller or views of the comments rails is able to infer the post like so:

post_comments_path

Which would map to the comments index under the current Post.

When I am in the context of the posts controller and views I rails can no longer infer the post for this route, requiring me to supply it.

post_comments_path # error
post_comments_path(@post) # ok

It has been suggested in other threads that the second row is the correct way to go.

I may be crazy, but I'd love to find out how Rails is able to infer the Post. I have an edge case where one or two actions are on the root controller for a menu listing routes to mostly nested stuff. I would prefer to tell Rails what the current parent is for these few actions rather than clutter my view code with explicit parent references all over the place.

Update:

I did try the basic thing of setting a post_id parameter. Verifying that it is indeed set on both request-params and params without success. I have vague memories of Rails making copies of the params so perhaps the routing helpers have a separate copy without my added parameter? (Setting a @post instance variable has no effect either. I just had to check.)

Community
  • 1
  • 1
Martin Westin
  • 1,419
  • 1
  • 14
  • 25
  • I am not sure but I think it has to do with the fact that in the comments controller `:post_id` is set in the url but in the posts controller it is set as `:id` but this is a very interesting question. Although as you stated implicitly defining the parent is considered proper convention. – engineersmnky Oct 31 '13 at 16:18
  • As I added above, I did try setting a post_id in params without any change in behavior. Pragmatic-me should just add the explicit parent to ..._path calls and get on with my coding :) – Martin Westin Nov 01 '13 at 08:19

1 Answers1

1

Okay so it seems to have to do with the defaults parameter of a path.

ie.

get '/posts/:post_id/comments', to: "comments#index", as: :show_default_comments, defaults:{post_id: Post.all.first.id}

this will show you the comments index action based on the very first post so I think that when you are in the comments controller the defaults post_id is set to the current value of post_id. I would have to do more testing to confirm this.

(Copied from my comment) Found it. default_url_options Here. Confirmed that doing this does work although it broke my index view but this is as far as I am going seems default_url_options gets passed in at some point and this is where the inference occurs and you can set it using the method in the link.

defining a url_options method in the controller to set the default_url_options for a path will allow you to specify a parent dynamically for routing purposes.

Community
  • 1
  • 1
engineersmnky
  • 25,495
  • 2
  • 36
  • 52
  • That's interesting but would unfortunately not fit my current scenario. I would be on the show or edit action of a particular post and wanting post_comments_path to point to that current post's comments. I couldn't get your line to do that. Adding a defaults key to the resources declaration invalidated all routes for that entire controller too unfortunately. – Martin Westin Nov 01 '13 at 08:30
  • Yes I have been unable to determine a way to implement a solution that will dynamically set the defaults value based on the current path or variables – engineersmnky Nov 01 '13 at 13:31
  • Okay so I am still trying to track down where it happens but from what I have read it seems that the methods in the route are being called on the current routed object(s) so in the comments controller it is calling `:post_id` to determine the route. When it attempts to do this on the post it fails because `:post_id` is not an attribute of `Post`. I am not 100% sure on this answer and routing is a deeply nested Object so it is becoming difficult to track but I think you are stuck with implying the parent. – engineersmnky Nov 01 '13 at 16:26
  • Yeah. Looks that way. I, too, ended up trying to keep all the dynamically generated methods straight in my head. Even pry didn't help enough. Thanks so much for digging into this. I have 15 hours or air travel on Monday... maybe a good time to bundle open actionpack again. – Martin Westin Nov 01 '13 at 17:45
  • looks like this is some how handled in `ActionDispatch::Routing::PolymorphicRoutes.polymorphic_url` I am still trying to figure out how it is handling the fact that you are in essence passing nil. This seems like it should fail but you are saying it doesn't – engineersmnky Nov 01 '13 at 20:01
  • Found it. default_url_options [Here](http://stackoverflow.com/questions/6100954/default-url-options-and-rails-3). Confirmed that doing this does work although it broke my index view but this is as far as I am going seems default_url_options gets passed in at some point and this is where the inference occurs and you can set it using the method in the link. – engineersmnky Nov 01 '13 at 20:31
  • Works as described in that thread. Def:ing url_options in the parent controller and copying :id to :post_id does the trick. Please add it to the answer and I'll happily promote the H**l out of it! – Martin Westin Nov 04 '13 at 08:28