views:

37

answers:

1

I have an Admin controller for a page of a Rails 3 app that lists all the User model accounts in a table, and part of each row should be a link (with confirmation) to toggle the is_admin attribute of my User model. I would like to do this by allowing an admin to click on text in the table cell that lists the current admin status (i.e. clicking on 'No' will set is_admin to true).

For now, I'm not interested in any fancy AJAX approach, and let's disregard the fact that from a UI perspective, clicking on text indicating the state of an object to toggle that state isn't necessarily the best choice.

What I'm wondering is, can this be done within a single link_to call, or do I need to construct a form around it? My admin controller is defined as 'resources :admin' in routes.rb and rake routes lists a PUT action with the named path of 'admin' that invokes the admin#update method.

Here's what I've got so far:

<%= link_to (user.is_admin ?  "Yes" : "No"), 
             admin_path(:user => {:id => user.id, :is_admin => !user.is_admin}), 
             {:confirm => "Are you sure?", :method => :put} %>

My thinking is I'm passing in enough data in a RESTful call to the update method so that I can modify just that attribute. I realize it would be possible to just define a method called toggle_admin, pass along just the ID and operate on that member of the model, but I thought this approach would be as RESTful as possible, and I'm more interested in learning the extent to wich I can use link_to here.

My error that I'm getting is:

No route matches {:action=>"destroy", :controller=>"admin", :user=>{:id=>1, :is_admin=>false}}

I'm not sure why it's trying to invoke the destroy method, it seems to be the last thing in my list of admin specific routes:

rake routes | grep admin
            site_admin        /admin(.:format)                {:controller=>"admin", :action=>"index"}
           admin_index GET    /admin(.:format)                {:action=>"index", :controller=>"admin"}
           admin_index POST   /admin(.:format)                {:action=>"create", :controller=>"admin"}
             new_admin GET    /admin/new(.:format)            {:action=>"new", :controller=>"admin"}
            edit_admin GET    /admin/:id/edit(.:format)       {:action=>"edit", :controller=>"admin"}
                 admin GET    /admin/:id(.:format)            {:action=>"show", :controller=>"admin"}
                 admin PUT    /admin/:id(.:format)            {:action=>"update", :controller=>"admin"}
                 admin DELETE /admin/:id(.:format)            {:action=>"destroy", :controller=>"admin"}

I thought perhaps I'm passing the method wrong and it's defaulting to the last one, but this page suggests that you can set :method => with your :confirm e.g. as an HTML option.

So fundamentally, my question is if I'm supposed to be able to make a RESTful non GET call via link_to like this? I assumed that Rails would just build a little mini form behind the scenes for me.

More source code in this gist.

A: 

Use this:

div_for @user do
  link_to (@user.is_admin? ? "Yes" : "No"), [@user, :toggle], :confirm => ...
end

and this route:

map.resources :users, :as => "admin", :member => { :toggle => :put }

and this controller method:

class UsersController < ...
  def toggle
    @user = User.find params[:id], :lock => true
    @user.is_admin = [email protected]_admin?
    @user.save!
    if request.xhr?
      render :update {|page| page[@user].replace_html @user }
    else
      redirect_to @user
    end
  end
end

Trying to handle this with a link to the update action gets just too complicated for just one case (as you already figured out there are some pitfalls). Using link_to_remote for this case is a better candidate (just in case I already added the ajax handling to show it's not too fancy).

hurikhan77
Thanks @hurikhan77. I'll be giving this one a shot in the next day or two.
Joost Schuur