Routing Problem????

I started out with a normal scaffold for the USERS table. I changed the index view to be list.html.erb but the url: '/users/list' returns an 'unknown action' error. '/users/list/:id' also returns an 'unknown action' error. I've also merged both the edit and new views into the manage.html.erb which duplicates the list view except with a form on the right side. the url '/users/manage' returns an 'unknown action' error but '/users/manage/:id' brings me to the manage page with the edit form.

in my routes.rb, it reads: map.resources :users

The update an delete actions work but they don't route me to the right url. the new action does not work. can anyone help? I've pasted my controller and views below.

USERS CONTOLLER class UsersController < ApplicationController

  layout 'staff'

  def index     list     render :action => 'list'   end

  # GET /users   # GET /users.xml   def list     @users = User.find(:all)

# respond_to do |format| # format.html # index.html.erb # format.xml { render :xml => @users } # end   end

  # GET /users/new   # GET /users/new.xml   def manage     list     @user = User.find(params[:id]) if params[:id]     @user = User.new if @user.nil?

# respond_to do |format| # format.html # new.html.erb # format.xml { render :xml => @user } # end   end

  # POST /users   # POST /users.xml   def create     @user = User.new(params[:user])

    respond_to do |format|       if @user.save         flash[:notice] = 'User was successfully created.'         format.html { redirect_to(users_url) }         format.xml { render :xml => @user, :status => :created, :location => @user }       else         format.html { render(:action => "manage") }         format.xml { render :xml => @user.errors, :status => :unprocessable_entity }       end     end   end

  # PUT /users/1   # PUT /users/1.xml   def update     @user = User.find(params[:id])

    respond_to do |format|       if @user.update_attributes(params[:user])         flash[:notice] = 'User was successfully updated.'         format.html { redirect_to(users_url) }         format.xml { head :ok }       else         format.html { render(:action => 'manage') }         format.xml { render :xml => @user.errors, :status => :unprocessable_entity }       end     end   end

  # DELETE /users/1   # DELETE /users/1.xml   def destroy     @user = User.find(params[:id])     @user.destroy

    respond_to do |format|       flash[:notice] = 'User was successfully deleted.'       format.html { redirect_to(:action => 'list') }       format.xml { head :ok }     end   end

end

LIST VIEW <% @page_title = 'Current Users' -%>

<%= content_tag('p', link_to('&laquo; Back to Menu', :controller => 'staff', :action => 'menu')) %>

<div class='userlist_left'>   <%= render(:partial => 'listing') -%> </div> <div class= 'userlist_right'>   Please select a user on the left or create a new user.   <%= link_to('New user', :action => 'manage') -%> </div>

MANAGE VIEW <% @page_title = 'Current Users' -%>

<%= content_tag('p', link_to('&laquo; Back to Menu', :controller => 'staff', :action => 'menu')) %>

<div class='userlist_left'>   <%= render(:partial => 'listing') -%> </div> <div class= 'userlist_right'>   <% if params[:id].blank? %>     <h2>New user</h2>

    <% form_for(@user) do |f| -%>       <%= render(:partial => 'form') -%>       <p class= 'user_submit'><%= submit_tag "Create" -%></p>     <% end -%>   <% else -%>     <h2>Editing user</h2>

    <% form_for(@user) do |f| -%>       <%= render(:partial => 'form')%>       <p class= 'user_submit'><%= submit_tag "Update" -%></p>     <% end -%>

    <p class= 'user_submit'><%= link_to('Delete User', { :action => 'destroy',     :id => @user }, :confirm => 'Are you sure you want to permanently delete this user?',     :method => :delete) -%>

  <% end -%>

  <%= link_to('Cancel', :action => 'list') %></p>

</div>

_LISTING PARTIAL <div class='userlist_listing'>   <table>     <tr>       <th>First Name</th>       <th>Last Name</th>       <th>Username</th>       <th>User Level</th>     </tr>   <% for user in @users -%>     <tr class='<%= cycle('row1', 'row2')%>'>       <td><%= h(user.first_name) -%></td>       <td><%= h(user.last_name) -%></td>       <td><%= h(user.username) -%></td>       <td><%= h(user.user_level) -%></td>         <td><%= link_to('Edit', :action => 'manage', :id => user) -%></

    </tr>   <% end %>   </table> </div><br/>

_FORM PARTIAL <%= error_messages_for(:user) -%>

<table>   <tr>     <th>Username</td>     <td><%= text_field(:user, :username) -%></td>   </tr>   <tr>     <th>Hashed Password</th>     <td><%= text_field(:user, :hashed_password) -%></td>   </tr>   <tr>     <th>First Name</th>     <td><%= text_field(:user, :first_name) -%></td>   </tr>   <tr>     <th>Last Name</th>     <td><%= text_field(:user, :last_name) -%></td>   </tr>   <tr>     <th>Email</th>     <td><%= text_field(:user, :email) -%></td>   </tr>

  <tr>     <th>Display Name</th>     <td><%= text_field(:user, :display_name) -%></td>   </tr>

  <tr>     <th>User Level</th>     <td><%= select(:user, :user_level, [0,1,2,3,4,9]) -%></td>   </tr> </table>

Check the output of "rake routes" to see which routes are recognized. When you are sure your URLs follow the expected URLs, check that your actions correspond. Keep an eye on the log file to see what's going on.

Christian

Please read frozenplague.net   to gain a better understanding of restful routing.

I also don't recommend merging the new action with the edit action
because you may run into problems later on

rename the action in your controller

chris wrote:

I started out with a normal scaffold for the USERS table. I changed the index view to be list.html.erb but the url: '/users/list' returns an 'unknown action' error. '/users/list/:id' also returns an 'unknown action' error. I've also merged both the edit and new views into the manage.html.erb which duplicates the list view except with a form on the right side. the url '/users/manage' returns an 'unknown action' error but '/users/manage/:id' brings me to the manage page with the edit form.

Simply adding the method in the controller doesn't give you the route, nor does changing the name of the method in the controller. The reason '/users/manage/:id' works is because it gets scooped up by your default route '/:controller/:action/:id'.

Take a look at the :member and :collection options to map.resources. Then can be used as such:

# to add a method to an object of class User # this will give you /users/:id/change_name map.resources :users, :member => {:change_name => [:get, :post]}

# to add a method to the class itself # this will give you /users/active map.resources :users, :collection => {:active => :get}

You may also be interested in the :path_names option, to change the names of the typical routes.

# to change "edit" to "manage" map.resources :users, :path_names => { :new => 'manage' }

I'm not sure what would happen if you mapped two methods to the same name:

# may or may not work as you'd expect map.resources :users, :path_names => { :new => 'manage', :edit => 'manage' }

Reference: http://api.rubyonrails.org/classes/ActionController/Resources.html

Hope that helps.

Cheers, Darrik

Thanks for the replies guys, especially you Darrik. I actually did have a look at the options to map.resources but was a little overwhelmed. The tutorial I've been trying to complete merges the NEW and EDIT actions to show how its possible to put all those actions into a single method but in doing so, it just royally screws with the default routes. I'm going to play around with the options as you suggest and I'll let you know how I do in the next few days. thanks.

We'll i've read into RESTful routes and your other suggestions yet still can't seem to fix the problem. here's the output from RAKE ROUTES:

users GET /users {:controller=>"users", :action=>"index"} formatted_users GET /users.:format {:controller=>"users", :action=>"index"}   POST /users {:controller=>"users", :action=>"create"}   POST /users.:format {:controller=>"users", :action=>"create"} new_user GET /users/manage {:controller=>"users", :action=>"new"} formatted_new_user GET /users/manage.:format {:controller=>"users", :action=>"new"} edit_user GET /users/:id/manage {:controller=>"users", :action=>"edit"} formatted_edit_user GET /users/:id/manage.:format {:controller=>"users", :action=>"edit"} user GET /users/:id {:controller=>"users", :action=>"show"} formatted_user GET /users/:id.:format {:controller=>"users", :action=>"show"}   PUT /users/:id {:controller=>"users", :action=>"update"}   PUT /users/:id.:format {:controller=>"users", :action=>"update"}   DELETE /users/:id {:controller=>"users", :action=>"destroy"}   DELETE /users/:id.:format {:controller=>"users", :action=>"destroy"} categories GET /categories {:controller=>"categories", :action=>"index"} formatted_categories GET /categories.:format {:controller=>"categories", :action=>"index"}   POST /categories {:controller=>"categories", :action=>"create"}   POST /categories.:format {:controller=>"categories", :action=>"create"} new_category GET /categories/new {:controller=>"categories", :action=>"new"} formatted_new_category GET /categories/new.:format {:controller=>"categories", :action=>"new"} edit_category GET /categories/:id/edit {:controller=>"categories", :action=>"edit"} formatted_edit_category GET /categories/:id/edit.:format {:controller=>"categories", :action=>"edit"} category GET /categories/:id {:controller=>"categories", :action=>"show"} formatted_category GET /categories/:id.:format {:controller=>"categories", :action=>"show"}   PUT /categories/:id {:controller=>"categories", :action=>"update"}   PUT /categories/:id.:format {:controller=>"categories", :action=>"update"}   DELETE /categories/:id {:controller=>"categories", :action=>"destroy"}   DELETE /categories/:id.:format {:controller=>"categories", :action=>"destroy"} posts GET /posts {:controller=>"posts", :action=>"index"} formatted_posts GET /posts.:format {:controller=>"posts", :action=>"index"}   POST /posts {:controller=>"posts", :action=>"create"}   POST /posts.:format {:controller=>"posts", :action=>"create"} new_post GET /posts/new {:controller=>"posts", :action=>"new"} formatted_new_post GET /posts/new.:format {:controller=>"posts", :action=>"new"} edit_post GET /posts/:id/edit {:controller=>"posts", :action=>"edit"} formatted_edit_post GET /posts/:id/edit.:format {:controller=>"posts", :action=>"edit"} post GET /posts/:id {:controller=>"posts", :action=>"show"} formatted_post GET /posts/:id.:format {:controller=>"posts", :action=>"show"}   PUT /posts/:id {:controller=>"posts", :action=>"update"}   PUT /posts/:id.:format {:controller=>"posts", :action=>"update"}   DELETE /posts/:id {:controller=>"posts", :action=>"destroy"}   DELETE /posts/:id.:format {:controller=>"posts", :action=>"destroy"}     /:controller/:action/:id.:format     /:controller/:action/:id

Sorry that its difficult to read, google's text editor doesn't really offer much. I also tried adding the following into my ROUTES.RB after

map.resources :users| which was already in there:

:path_names => { :new => 'manage', :edit => 'manage' }, :collection => {:list => :get, :manage => [:post, :put, :delete]}

This adding the following 4 lines to my RAKE ROUTES:

manage_users POSTPUTDELETE /users/manage {:controller=>"users", :action=>"manage"} formatted_manage_users POSTPUTDELETE /users/manage.:format {:controller=>"users", :action=>"manage"} list_users GET /users/list {:controller=>"users", :action=>"list"} formatted_list_users GET /users/list.:format {:controller=>"users", :action=>"list"}

It looks right to me but when i redirect to the url => 'users/list' or 'users/manage', it sends 'list' or 'manage' as the :id and it gives me an unknown action error. This means my new, create, update, destroy methods all don't work.

please look at the first post for my CONTROLLER and VIEW code.

chris wrote:

We'll i've read into RESTful routes and your other suggestions yet still can't seem to fix the problem. here's the output from RAKE ROUTES:

users GET /users {:controller=>"users", :action=>"index"} formatted_users GET /users.:format {:controller=>"users", :action=>"index"}   POST /users {:controller=>"users", :action=>"create"}   POST /users.:format {:controller=>"users", :action=>"create"} new_user GET /users/manage {:controller=>"users", :action=>"new"} formatted_new_user GET /users/manage.:format {:controller=>"users", :action=>"new"} edit_user GET /users/:id/manage {:controller=>"users", :action=>"edit"} formatted_edit_user GET /users/:id/manage.:format {:controller=>"users", :action=>"edit"} user GET /users/:id {:controller=>"users", :action=>"show"} formatted_user GET /users/:id.:format {:controller=>"users", :action=>"show"}   PUT /users/:id {:controller=>"users", :action=>"update"}   PUT /users/:id.:format {:controller=>"users", :action=>"update"}   DELETE /users/:id {:controller=>"users", :action=>"destroy"}   DELETE /users/:id.:format {:controller=>"users", :action=>"destroy"} categories GET /categories {:controller=>"categories", :action=>"index"} formatted_categories GET /categories.:format {:controller=>"categories", :action=>"index"}   POST /categories {:controller=>"categories", :action=>"create"}   POST /categories.:format {:controller=>"categories", :action=>"create"} new_category GET /categories/new {:controller=>"categories", :action=>"new"} formatted_new_category GET /categories/new.:format {:controller=>"categories", :action=>"new"} edit_category GET /categories/:id/edit {:controller=>"categories", :action=>"edit"} formatted_edit_category GET /categories/:id/edit.:format {:controller=>"categories", :action=>"edit"} category GET /categories/:id {:controller=>"categories", :action=>"show"} formatted_category GET /categories/:id.:format {:controller=>"categories", :action=>"show"}   PUT /categories/:id {:controller=>"categories", :action=>"update"}   PUT /categories/:id.:format {:controller=>"categories", :action=>"update"}   DELETE /categories/:id {:controller=>"categories", :action=>"destroy"}   DELETE /categories/:id.:format {:controller=>"categories", :action=>"destroy"} posts GET /posts {:controller=>"posts", :action=>"index"} formatted_posts GET /posts.:format {:controller=>"posts", :action=>"index"}   POST /posts {:controller=>"posts", :action=>"create"}   POST /posts.:format {:controller=>"posts", :action=>"create"} new_post GET /posts/new {:controller=>"posts", :action=>"new"} formatted_new_post GET /posts/new.:format {:controller=>"posts", :action=>"new"} edit_post GET /posts/:id/edit {:controller=>"posts", :action=>"edit"} formatted_edit_post GET /posts/:id/edit.:format {:controller=>"posts", :action=>"edit"} post GET /posts/:id {:controller=>"posts", :action=>"show"} formatted_post GET /posts/:id.:format {:controller=>"posts", :action=>"show"}   PUT /posts/:id {:controller=>"posts", :action=>"update"}   PUT /posts/:id.:format {:controller=>"posts", :action=>"update"}   DELETE /posts/:id {:controller=>"posts", :action=>"destroy"}   DELETE /posts/:id.:format {:controller=>"posts", :action=>"destroy"}     /:controller/:action/:id.:format     /:controller/:action/:id

Sorry that its difficult to read, google's text editor doesn't really offer much. I also tried adding the following into my ROUTES.RB after >map.resources :users| which was already in there:

:path_names => { :new => 'manage', :edit => 'manage' }, :collection => {:list => :get, :manage => [:post, :put, :delete]}

This adding the following 4 lines to my RAKE ROUTES:

manage_users POSTPUTDELETE /users/manage {:controller=>"users", :action=>"manage"} formatted_manage_users POSTPUTDELETE /users/manage.:format {:controller=>"users", :action=>"manage"} list_users GET /users/list {:controller=>"users", :action=>"list"} formatted_list_users GET /users/list.:format {:controller=>"users", :action=>"list"}

It looks right to me but when i redirect to the url => 'users/list' or 'users/manage', it sends 'list' or 'manage' as the :id and it gives me an unknown action error. This means my new, create, update, destroy methods all don't work.

please look at the first post for my CONTROLLER and VIEW code.

Thanks for the replies guys, especially you Darrik. I actually did have a look at the options to map.resources but was a little overwhelmed. The tutorial I've been trying to complete merges the NEW and EDIT actions to show how its possible to put all those actions into a single method but in doing so, it just royally screws with the default routes. I'm going to play around with the options as you suggest and I'll let you know how I do in the next few days. thanks.

chriswrote:

I started out with a normal scaffold for the USERS table. I changed the index view to be list.html.erb but the url: '/users/list' returns an 'unknown action' error. '/users/list/:id' also returns an 'unknown action' error. I've also merged both the edit and new views into the manage.html.erb which duplicates the list view except with a form on the right side. the url '/users/manage' returns an 'unknown action' error but '/users/manage/:id' brings me to the manage page with the edit form.

Simply adding the method in the controller doesn't give you the route, nor does changing the name of the method in the controller. The reason '/users/manage/:id' works is because it gets scooped up by your default route '/:controller/:action/:id'. Take a look at the :member and :collection options to map.resources. Then can be used as such: # to add a method to an object of class User # this will give you /users/:id/change_name map.resources :users, :member => {:change_name => [:get, :post]} # to add a method to the class itself # this will give you /users/active map.resources :users, :collection => {:active => :get} You may also be interested in the :path_names option, to change the names of the typical routes. # to change "edit" to "manage" map.resources :users, :path_names => { :new => 'manage' } I'm not sure what would happen if you mapped two methods to the same name: # may or may not work as you'd expect map.resources :users, :path_names => { :new => 'manage', :edit => 'manage' } Reference:http://api.rubyonrails.org/classes/ActionController/Resources.html Hope that helps. Cheers, Darrik

>

Your controller doesn't have a method called 'list' or 'manage'.

Also of note, if you expect the manage method to manipulate an individual object of the User class, you should add it as :member, not :collection. Collection (as the name implies) defines actions that operate on the entire collection (list, index, etc), whereas member defines actions that operate on a single member (also as the name implies). The key distinction is that the :collection option to mapping a User resource will give you a route like /users/:action, whereas the :member option will give you a route like /users/:id/:action, in both cases where :action is whatever you specify.

Hope that helps. Darrik

I actually do have a method called 'list' and 'manage' in my controller so i'm not sure why you say I don't.

I've actually updated my Users Controller since the first post. it now looks like this (I've CAPITALIZED the LIST and MANAGE methods:

class UsersController < ApplicationController

  layout 'staff'

  def index     list     render :action => 'list'   end

  # GET /users   # GET /users.xml def LIST     @users = User.find(:all)

# respond_to do |format| # format.html # index.html.erb # format.xml { render :xml => @users } # end   end

  # GET /users/new   # GET /users/new.xml   def MANAGE     list     if request.get? && params[:id].blank? #new       @user = User.new     elsif request.post? && params[:id].blank? #create       @user = User.new(params[:user])       respond_to do |format|         if @user.save           flash[:notice] = 'The user was successfully created.'           format.html { redirect_to(:action => 'list') }           format.xml { render :xml => @user, :status => :created, :location => @user }         else           format.html { render(:action => "manage") }           format.xml { render :xml => @user.errors, :status => :unprocessable_entity }         end       end

    elsif request.get? && !params[:id].blank? #edit       @user = User.find(params[:id])     else request.post? && !params[:id].blank? #update or delete       @user = User.find(params[:id])       if params[:commit] == 'Update'         respond_to do |format|           if @user.update_attributes(params[:user])             flash[:notice] = 'The user was successfully updated.'             format.html { redirect_to(:action => 'list') }             format.xml { head :ok }           else             format.html { render(:action => 'manage') }             format.xml { render :xml => @user.errors, :status => :unprocessable_entity }           end         end       else #action should delete         @user = User.find(params[:id])         @user.destroy         respond_to do |format|           flash[:notice] = 'The user was successfully deleted.'           format.html { redirect_to(:action => 'list') }           format.xml { head :ok }         end       end     end   end

end

I've made some very slight changes to my view to deal with the fact that I no longer have a 'create' 'update' or 'destroy' method anymore. As you can see, I've rolled all those methods into a single method, 'manage'.

LIST VIEW <% @page_title = 'Current Users' -%>

<%= content_tag('p', link_to('&laquo; Back to Menu', :controller => 'staff', :action => 'menu')) %>

<div class='userlist_left'>   <%= render(:partial => 'listing') -%> </div> <div class= 'userlist_right'>   Please select a user on the left or create a new user.   <%= link_to('New user', :action => 'manage') -%> </div>

MANAGE VIEW <% @page_title = 'Current Users' -%>

<%= content_tag('p', link_to('&laquo; Back to Menu', :controller => 'staff', :action => 'menu')) %>

<div class='userlist_left'>   <%= render(:partial => 'listing') -%> </div> <div class= 'userlist_right'>   <% if params[:id].blank? %>     <h2>New user</h2>

    <%# form_tag(:action => 'manage') do -%>     <% form_for(@user) do -%>       <%= render(:partial => 'form') -%>       <p class= 'user_submit'><%= submit_tag "Create" -%></p>     <% end -%>   <% else -%>     <h2>Editing user</h2>

    <%# form_tag(:action => 'manage', :id => @user) do -%>     <% form_for(@user) do -%>       <%= render(:partial => 'form')%>       <p class= 'user_submit'><%= submit_tag "Update" -%></p>     <% end -%>

    <p class= 'user_submit'><%= link_to('Delete User', { :action => 'destroy',     :id => @user }, :confirm => 'Are you sure you want to permanently delete this user?',     :method => :delete) -%>

  <% end -%>

  <%= link_to('Cancel', :action => 'list') %></p>

</div>

_LISTING PARTIAL <div class='userlist_listing'>   <table>     <tr>       <th>First Name</th>       <th>Last Name</th>       <th>Username</th>       <th>User Level</th>     </tr>   <% for user in @users -%>     <tr class='<%= cycle('row1', 'row2')%>'>       <td><%= h(user.first_name) -%></td>       <td><%= h(user.last_name) -%></td>       <td><%= h(user.username) -%></td>       <td><%= h(user.user_level) -%></td>         <td><%= link_to('Edit', :action => 'manage', :id => user) -%></

    </tr>   <% end %>   </table> </div><br/>

_FORM PARTIAL

<%= error_messages_for(:user) -%>

<table>   <tr>     <th>Username</td>     <td><%= text_field(:user, :username) -%></td>   </tr>   <tr>     <th>Hashed Password</th>     <td><%= text_field(:user, :hashed_password) -%></td>   </tr>   <tr>     <th>First Name</th>     <td><%= text_field(:user, :first_name) -%></td>   </tr>   <tr>     <th>Last Name</th>     <td><%= text_field(:user, :last_name) -%></td>   </tr>   <tr>     <th>Email</th>     <td><%= text_field(:user, :email) -%></td>   </tr>

  <tr>     <th>Display Name</th>     <td><%= text_field(:user, :display_name) -%></td>   </tr>

  <tr>     <th>User Level</th>     <td><%= select(:user, :user_level, (0..9).to_a.reverse) -%></td>   </tr> </table>

I also tried to configure my ROUTES.RB as you've suggested, using the :member option instead of the :collection for the 'manage' action as such:

ActionController::Routing::Routes.draw do |map|   map.resources :users, :path_names => { :new => 'manage', :edit => 'manage' },   :collection => {:list => :get}, :member => {:manage => [:post, :put, :delete]}

  map.resources :categories

  map.resources :posts

  map.connect ':controller/:action/:id.:format'   map.connect ':controller/:action/:id' end

Now, when I goto the url: '/users/manage' (the url for creating a new user object) and '/users/:id/manage' (the url for editing an existing user object) it still gives me the unknown action error. Instead of sending 'manage' as the :id (which is what it did before I added the :path_names, :collection, :member options in my routes.rb code), it sends 'new' and 'edit' as the :action where instead it should be sending 'manage' as the action or so I think.

Thanks again.