views:

152

answers:

2

I would like to create a mechanism for a User to keep track of other, favorite Users, similar to SO's favorite questions. I'm using the Rails 3.0 beta.

To do so, I have a User-Favorite HABTM relationship, which works as expected:

class User < ActiveRecord::Base
  has_and_belongs_to_many :favorites, :class_name => "User", :join_table => "favorites", :association_foreign_key => "favorite_id", :foreign_key => "user_id"
end

The Favorites Controller only needs 3 of the 7 RESTful methods to manage a User's favorites:

class FavoritesController < ApplicationController

  # GET /favorites
  # GET /favorites.xml
  def index

    @user = User.find(params[:user_id])
    @favorites =  @user.favorites.joins(:profile).order("last_name,first_name")

    ...

  end

  def create

    @favorite = User.find(params[:id])
    current_user.favorites << @favorite

    ...

  end

  def destroy

    @favorite = User.find(params[:id])
    current_user.favorites.delete(@favorite)

    ...

  end

end

The Routes.rb file contains the routing instruction:

resources :users, :except => :destroy do
  resources :favorites, :only => [:index,:create,:destroy]
end

that generates these user-favorite routes:

GET    /users/:user_id/favorites(.:format)            {:controller=>"favorites", :action=>"index"}
user_favorites POST   /users/:user_id/favorites(.:format)            {:controller=>"favorites", :action=>"create"}
user_favorite DELETE /users/:user_id/favorites/:id(.:format)        {:controller=>"favorites", :action=>"destroy"}

In the User's Show View, the User (@user) can be toggled as a favorite using image links, which works as expected:

<% if [test if user is a favorite] %>

    # http://localhost:3000/favorites/destroy/:id?post=true
    <%= link_to image_tag("favorite.png", :border => 0), :controller => :favorites, :action => :destroy, :post=>true, :id => @user %>

<% else %>

    # http://localhost:3000/favorites/create/:id?post=true
    <%= link_to image_tag("not-favorite.png", :border => 0), :controller => :favorites, :action => :create, :post=>true, :id => @user %>

<% end %>

However, in the current_user's favorite Index View, the link_to each favorite user:

# http://localhost:3010/users/4/favorites/3?post=true
<%= link_to image_tag("favorite.png", :border => 0), :controller => :favorites, :action => :destroy, :id => favorite, :post=>true %>

generates an error that reads:

No route matches "/users/4/favorites/3"

Questions:

  1. Have I correctly specified my routing? Seem like the create and destroy routes would only need the id of the favorite, as the 'owner' of the favorite is always current_user.
  2. If I'm simply referencing the Controller/Action in the Show view, do I even need the create/destroy routes?
  3. Why doesn't the link_to in the Index View work correctly?
  4. Are there any improvements that can be made to the over-all approach?
A: 

in your rake routes output, the paramater needed is :user_id not :id, so you need to send that in your link_to call.

Jed Schneider
Craig
+1  A: 

Your routing looks fine.

I think there is something wrong with your link_to, though. For one thing, the RESTful way is not to specify URLs with :controller and :action parameters. The correct way is using the generated URL methods, such as user_favorite_path. Also, you need to specify the :method parameter when targeting the destroy action. This is how I think the link_to should look like:

<%= link_to image_tag("favorite.png", :border => 0), user_favorite_path(@user, @favorite), :method => :delete %>

I believe the reason it says no route matches that URL is because you didn't specify the :method as :delete.

Audie
Needed to be:<%= link_to image_tag("favorite.png", :border => 0), user_favorite_path(current_user,@user), :method => :delete %>Thanks!
Craig