This project is archived and is in readonly mode.

#838 ✓resolved
S. Brent Faulkner

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

    RSL August 15th, 2008 @ 01:39 AM

    Or [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

    S. Brent Faulkner August 15th, 2008 @ 01:45 AM

    Actually 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

    RSL August 15th, 2008 @ 02:15 AM

    I 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]
    end
    

    or

    
    # 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

    S. Brent Faulkner August 15th, 2008 @ 03:22 AM

    How 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 => true
    

    Personally, 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

    S. Brent Faulkner August 15th, 2008 @ 03:32 AM

    Hmmm... 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 => true
    

    Again, 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

    S. Brent Faulkner August 16th, 2008 @ 12:52 PM

    OK... 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
      end
    

    or...

    
      map.resources :projects, :has_many => { :milestones => { :features => :tasks } }, :shallow => true
    

    Much 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

    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

    DHH August 16th, 2008 @ 08:45 PM

    Really 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

    S. Brent Faulkner August 17th, 2008 @ 05:14 AM

    Thanks 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

    S. Brent Faulkner August 17th, 2008 @ 05:50 AM

    regarding (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

    S. Brent Faulkner August 19th, 2008 @ 03:54 PM

    tests pass, documentation included, please consider for commit

    (hey, dhh thought it was ok :-)

  • S. Brent Faulkner
  • S. Brent Faulkner

    S. Brent Faulkner August 22nd, 2008 @ 04:06 AM

    patch would not apply... fixed... uploaded new patch.

  • Pratik

    Pratik August 28th, 2008 @ 05:04 PM

    • Assigned user set to “Pratik”
  • S. Brent Faulkner

    S. Brent Faulkner August 30th, 2008 @ 05:29 AM

    I 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

    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>

Attachments

Referenced by

Pages