views:

446

answers:

1

I'm trying to create a nested resource with a url scheme along the lines of: "http://example.com/username/...".

What I currently have is this:

ActionController::Routing::Routes.draw do |map|
  map.home '/', :controller => 'home'

  map.resource :session

  map.resources :users, :has_many => :nodes
  #map.user '/:id',                :controller => 'users',    :action => 'show',      :has_many => :nodes

  map.resources :nodes, :belongs_to => :user
end

This results in URLs like:

http://example.local/users/username
http://example.local/users/username/nodes

How to avoid the "users" prefix is beyond me. Passing a "as: => ''" option to map.resources doesn't work and it seems named routes don't support the ":has_many" or ":belongs_to" options.

Commenting out the "map.resources :users" and uncommmenting the "map.user" line after it seems to work… until you reach a nested resource. Then it spits out the following error:

undefined method `user_nodes_path' for #<ActionView::Base:0x1052c8d18>

I know this problem has come up plenty of times before and is always met with "Why would you want to do that?" responses. Frankly, Twitter does it, Facebook does it, and I want to do it too! ;-D

As for the common criticism of how to avoid usernames from conflicting built-in paths, I've set my minimum username length to 6 characters and plan to make all built-in root-level paths segments paths 5 characters or shorter (i.e. "/opt/..." for options, "/in/..." for session log-in, etc.).

Thanks a ton in advance and hopefully there's a solution out there that everyone can use!

+1  A: 

As noted in this question:

http://stackoverflow.com/questions/182040/default-segment-name-in-rails-resources-routing

You should be able to use this plugin:

http://github.com/caring/default_routing

Alternatively, you could specify them manually with something like

map.users     '/users',     :controller => 'users', :action => 'index',
  :conditions => { :method => :get }
map.connect   '/users',     :controller => 'users', :action => 'create',
  :conditions => { :method => :post }
map.user      '/:id',       :controller => 'users', :action => 'show',
  :conditions => { :method => :get }
map.edit_user '/:id/edit',  :controller => 'users', :action => 'edit',
  :conditions => { :method => :get }
map.new_user  '/users/new', :controller => 'users', :action => 'new',
  :conditions => { :method => :get }
map.connect   '/:id', :controller => 'users', :action => 'update',
  :conditions => { :method => :put }
map.connect   '/:id', :controller => 'users', :action => 'destroy',
  :conditions => { :method => :delete }

map.resources :nodes, :path_prefix => '/:user_id', :name_prefix => 'user_'
# to generate user_nodes_path and user_node_path(@node) routes

That or something like it should give you what you want from map.resources.

map.resources doesn't work and it seems named routes don't support the ":has_many" or ":belongs_to" options.

Named routes supports has_many, which is used to add another resource level to the URL path. For example

map.resources :users, :has_many => :nodes

generates all the users_path and user_nodes_path routes like /users, /users/:id, /users/:id/nodes and /users/:id/nodes/:id. It's identical to

map.resources :users do |user|
  user.resources :nodes
end

Commenting out the "map.resources :users" and uncommmenting the "map.user" line after it seems to work… until you reach a nested resource. Then it spits out the following error:

undefined method `user_nodes_path' for #<ActionView::Base:0x1052c8d18>

That's because map.resources :users, :has_many => :nodes generates these routes. Only one named route is generated by map.user '/:id', :controller => 'users', :action => 'show' and that's user_path.

autodata
Brilliant! I've quite surprised I didn't find the default_routing plug-in earlier; it looks like a must-have for any project! Thanks for your help!
Slippy Douglas
I should mention that while the default_routing plugin is awesome, it is also awesomely broken in Rails 2.3.4 (and possibly earlier versions as well).There is a fixed version at http://github.com/slippyd/default_routing however.
Slippy Douglas
Thanks for the link, Slippy, your version worked perfectly for me!
Eduardo Scoz