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.).

2 Answers 2

2

As noted in this question:

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.

Sign up to request clarification or add additional context in comments.

3 Comments

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!
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 github.com/slippyd/default_routing however.
Thanks for the link, Slippy, your version worked perfectly for me!
0

See my answer in this related question

Basically, you can pass :path => '' as an option to negate the identifying segment (Tested in Rails 3 only) Just be aware that this may clash with other routing patterns, so be careful where and how you place it.

1 Comment

This does seem like the kind of issue (and fix) that Rails 3.x has eliminated. I'll have to check out your solution when I update the app I need this for from Rails 2.3.x to 3.x.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.