Ruby
Rails
rails3

Rails3.2でスター機能実装

More than 5 years have passed since last update.

モデルは post、user、star 。20130706021155.jpg

ルーティング

# toggle_star
match 'toggle_star', :to => 'toggle_star#toggle_star', :via => [:get, :post]

View

app/view/posts/show.html.erb
<%=  form_tag({:controller => 'star', :action => 'toggle_star'}, {:name => 'toggle_star', :remote => true}) do %>
    <input type="hidden" value="<%= @post.id %>" name="post_toggle_star">
    <span class="js-toggle_star" data-num="<%= @post.id %>">スターボタン</span>
<% end %>

Model

app/models/post.rb
belongs_to :user
has_many :stars, :dependent => :destroy
has_many :stared_users, :through => :stars, :source => :user

# user -> post の関係を複数作る場合、別の名前(:stared_users)を付け、sourceオプションで本当の名前を指定するらしい。
app/models/user.rb
has_many :posts, :dependent => :destroy
has_many :stars, :dependent => :destroy
has_many :stared_posts, :through => :stars, :source => :post

def starable_for?(post)
  post && post.user != self && !stars.exists?(:post_id => post.id)
end

# postの有無
# 投稿者がユーザー自身の場合はお気に入りさせない
# 重複させない
app/models/star.rb
belongs_to :user
belongs_to :post
attr_accessible :user_id, :post_id

validate do
  unless user && user.starable_for?(post)
    return errors.add(:post_id)
  end
end

Controller

app/controllers/star_controller.rb
def toggle_star
  # ログインチェック
  raise unless login_user

  # 既にお気に入りしているかどうかチェック
  @post = Post.find(params[:post_toggle_star])
  if current_user.stared_posts.exists?(@post)
    render remove_star(@post)
  else
    render add_star(@post)
  end

  rescue
  render :json => {:result => "error" }, :status => 400 
end

# スターを追加
def add_star(postobj)
  current_user.stared_posts << postobj
  return  { :json => { :type => "add", :result => "success" } }
end

#スターを削除
def remove_star(postobj)
  current_user.stared_posts.delete(postobj)
  return  { :json => { :type => "remove", :result => "success" } }
end

JavaScript

// コールバックなどを設定。それぞれの処理に合わせたメソッドを作っていく
jQuery(formObj)
    .bind("ajax:loading", function(xhr){})
    .bind("ajax:success", function(data, status, xhr){})
    .bind("ajax:complete", function(xhr){})
    .bind("ajax:failure", function(xhr){});

スター/スター解除 でURLを共通にしたかったので、振り分ける分岐をController側に書いてあります。
URLを分けると、DBへのアクセスがもっと減るのかも。
↓ブログに書いたものの転載です。
http://www.rokurofire.info/2013/07/08/rails_star/