This project is archived and is in readonly mode.

add support for shallow nesting of resource routes
Reported by S. Brent Faulkner | August 26th, 2008 @ 07:20 AM | in 2.x
UPDATED: I've edited this post to reflect the current patch... the original post is still included after this for the record.
Attached is a patch for resource mapping (and appropriate tests) that implements (optional) shallow nesting for resources. This allows you to specify :shallow => true for a resource. The option is inherited by any nested resources and causes any member routes (those with an id parameter) to not require the parent path prefix.
So, you can now...
map.resources :projects, :shallow => true do |project|
project.resources :milestones do |milestone|
  milestone.resources :features do |feature|
    feature.resources :tasks
  end
end
end
In addition, the patch enhances the has_many shorthand notation to allow use of a hash to specify multiple levels of nested resources, which can be combined with the :shallow option as follows...
map.resources :projects, :has_many => { :milestones => { :features => :tasks } }, :shallow => true
vvvvv original post vvvv
The attached patch adds support for specifying a subset of named routes to create when mapping verb-oriented controllers for collections of resources.
You can specify which routes to add using :only and/or :except options with array values containing symbols of :collection, :new, :member and :associations.
This is especially useful for creating nested routes, so that the redundant parent ids are not required for routes containing explicit ids.
For example...
  map.resources :posts do |post|
    post.resources :comments, :only => [ :collection, :new ]
  end
  map.resources :comments, :except => [ :collection, :new ]
The generated routes would nest the collection and new record routes for comment resources within posts...
e.g. GET /posts/1/comments or POST /posts/1/comments
However, the routes with explicit ids would not require the post_id...
e.g. GET /comments/2 instead of GET /posts/1/comments/2
This really helps to clean up urls when nesting resources more than once.
For example, instead of:
  map.resources :projects do |project|
    project.resources :milestones do |milestone|
      milestone.resources :features do |feature|
        feature.resources :tasks
      end
    end
  end
You could use:
  map.resources :projects do |project|
    project.resources :milestones, :only => [ :collection, :new ]
  end
  map.resources :milestones, :except => [ :collection, :new ] do |milestone|
    milestone.resources :features, :only => [ :collection, :new ]
  end
  map.resources :features, :except => [ :collection, :new ] do |feature|
    feature.resources :tasks, :only =>[ :collection, :new ]
  end
  map.resouces :tasks, :except => [ :collection, :new ]
This would result in the ability to simply access:
/features/72/tasks
Instead of:
/projects/13/milestones/16/features/72/tasks
Comments and changes to this ticket
- 
            
         RSL August 15th, 2008 @ 01:39 AMOr [and I'm going on a limb here] you could just not make that resource nested so deep. http://weblog.jamisbuck.org/2007... 
- 
            
         S. Brent Faulkner August 15th, 2008 @ 01:45 AMActually that is the purpose of the selective routes. Jamis's proposed solution to the problem winds up doubly mapping the routes... so there is one route for /projects/1/milestones/2 and a second for /milestones/2 By selectively mapping them, the show_milestones route exists... the show_project_milestones routes does not. (and my example was an exageration on purpose ;-) 
- 
            
         RSL August 15th, 2008 @ 02:15 AMI admit I was blinded by that last URL. If the goal of this is to DRY up the routing there while encouraging a more direct routing where possible, I'd like to see something more akin to map.resources :users do |user| # Yes, this is rather hideous. user.resources :comments, :nest => [:collection, :new], :flat => [:member] endor # A little better. ;) map.resources :users, :nest => {:comments => [:collection, :new]Apologies for my completely kneejerk reaction to seeing "nested resource" and that last URL. ;) 
- 
            
         S. Brent Faulkner August 15th, 2008 @ 03:22 AMHow about using a notation similar to that used for ActiveRecord#find :includes We could then use... map.resources :projects => { :milestones => { :features => :tasks } }Instead of the deeply nested blocks of my previous example. Then, to get the flattening, we could just add a boolean option... map.resources :projects => { :milestones => { :features => :tasks } }, :flatten => truePersonally, I wouldn't mind the :flatten option defaulting to true and requiring :flatten => false to get the "legacy" style of routes. Thoughts? 
- 
            
         S. Brent Faulkner August 15th, 2008 @ 03:32 AMHmmm... just remembered the addition of has_many and has_one... So, I guess I'm suggesting the following... map.resources :projects, :has_many => { :milestones => { :features => :tasks } }, :flatten => trueAgain, I guess the default could be to flatten, but this would mean that we're changing an existing behaviour for the simple case... map.resources :posts, :has_many => :comments(which I don't think is a good idea -- ie. changing the existing behaviour -- so, I can live with needing to explicitly specify :flatten => true :-) I think I'll try and build up a patch for this version instead... 
- 
            
         S. Brent Faulkner August 16th, 2008 @ 12:52 PMOK... how about this? I've attached a new patch for resource mapping (and appropriate tests) that implements (optional) shallow nesting for resources. This allows you to specify :shallow => true for a resource. The option is inherited by any nested resources and causes any member routes (those with an id parameter) to not require the parent path prefix. So, you can now... map.resources :projects, :shallow => true do |project| project.resources :milestones do |milestone| milestone.resources :features do |feature| feature.resources :tasks end end endor... map.resources :projects, :has_many => { :milestones => { :features => :tasks } }, :shallow => trueMuch nicer than my original proposal. In either case, I'd be able to access resources with paths like the following... /projects /projects/1 /projects/1/milestones /milestones/2 /milestones/2/features /features/56/tasks/new (no redundant parent_id parameters... just what's required) 
- 
            
         S. Brent Faulkner August 16th, 2008 @ 01:13 PM- Title changed from add support for selective resource routes to add support for shallow nesting of resource routes
 
- 
         DHH August 16th, 2008 @ 08:45 PMReally nice. Shallow is a great term for it too. It needs documentation, though. Then I'd say it would be ready to go. Nice work. 
- 
            
         S. Brent Faulkner August 17th, 2008 @ 05:14 AMThanks again, David. I've attached a new patch with: a) documentation b) the support for passing deep :has_many information using a hash/array for example: map.resources :projects, :has_many => { :milestones => { :features => :tasks } }, :shallow => true
- 
            
         S. Brent Faulkner August 17th, 2008 @ 05:50 AMregarding (b) ... I had actually thought that this was already supported, but must have been thinking of the ActiveRecord finder :include option, so added it in since that was was part of the beauty of this solution 
- 
            
         S. Brent Faulkner August 19th, 2008 @ 03:54 PMtests pass, documentation included, please consider for commit (hey, dhh thought it was ok :-) 
- 
            
         
- 
            
         S. Brent Faulkner August 22nd, 2008 @ 04:06 AMpatch would not apply... fixed... uploaded new patch. 
- 
         Pratik August 28th, 2008 @ 05:04 PM- Assigned user set to Pratik
 
- 
            
         S. Brent Faulkner August 30th, 2008 @ 05:29 AMI saw a recent commit to resources.rb and tested the patch. It wouldn't apply again, so I've rebased and attached the new one... 
- 
         Repository August 30th, 2008 @ 03:22 PM- State changed from new to resolved
 (from [83c6ba18899a9f797d79726ca0078bdf618ec3d4]) Add support for shallow nesting of routes. [#838 state:resolved] Adds :shallow option to resource route definition. If true, paths for nested resources which reference a specific member (ie. those with an :id parameter) will not use the parent path prefix or name prefix. Example : map.resources :users, :shallow => true do |user| user.resources :posts end - 
GET /users/1/posts (maps to PostsController#index action as usual) named route "user_posts" is added as usual. 
- 
GET /posts/2 (maps to PostsController#show action as if it were not nested) Additionally, named route "post" is added too. http://github.com/rails/rails/co... 
 
Create your profile
Help contribute to this project by taking a few moments to create your personal profile. Create your profile »
<h2 style="font-size: 14px">Tickets have moved to Github</h2>
The new ticket tracker is available at <a href="https://github.com/rails/rails/issues">https://github.com/rails/rails/issues</a>
People watching this ticket
Attachments
Tags
Referenced by
- 
         838 
          add support for shallow nesting of resource routes
        (from [83c6ba18899a9f797d79726ca0078bdf618ec3d4]) Add sup... 838 
          add support for shallow nesting of resource routes
        (from [83c6ba18899a9f797d79726ca0078bdf618ec3d4]) Add sup...
 Chris Mear
      Chris Mear
 DHH
      DHH
 Pratik
      Pratik
 RSL
      RSL
 S. Brent Faulkner
      S. Brent Faulkner