RubyChina有一个喜欢功能,具体的表现可以查看每一个帖子的页面
如:
http://ruby-china.org/topics/5272
实现的代码解析如下:
在topics/show.html.erb页面中, 有如下代码
<%= likeable_tag(@topic) %>
likeable_tag 是定义在/app/helpers/likes_helper.rb中的helper方法
具体代码如下
def likeable_tag(likeable)
return "" if likeable.blank?
label = "#{likeable.likes_count}人喜欢"
if likeable.likes_count == 0
label = "喜欢"
end
if current_user && likeable.liked_by_user?(current_user)
title = "取消喜欢"
state = "liked"
icon = content_tag("i", "", :class => "icon small_liked")
else
title = "喜欢"
state = ""
icon = content_tag("i", "", :class => "icon small_like")
end
like_label = raw "#{icon} <span>#{label}</span>"
link_to(like_label,"#",:title => title, :rel => "twipsy", 'data-count' => likeable.likes_count,
'data-state' => state,'data-type' => likeable.class,'data-id' => likeable.id,
:class => 'likeable', :onclick => "return App.likeable(this);")
end
此处在于构造一个link_to tag,并且bind了一个click方法
return App.likeable(this);
App.likeable的方法定义在
app/assets/javascripts/app.coffee
中
具体实现如下
likeable : (el) ->
$el = $(el)
likeable_type = $el.data("type")
likeable_id = $el.data("id")
likes_count = parseInt($el.data("count"))
if $el.data("state") != "liked"
$.ajax
url : "/likes"
type : "POST"
data :
type : likeable_type
id : likeable_id
likes_count += 1
$el.data("state","liked").data('count', likes_count).attr("title", "取消喜欢")
$('span',el).text("#{likes_count}人喜欢")
$("i.icon",el).attr("class","icon small_liked")
else
$.ajax
url : "/likes/#{likeable_id}"
type : "DELETE"
data :
type : likeable_type
if likes_count > 0
likes_count -= 1
$el.data("state","").data('count', likes_count).attr("title", "喜欢")
if likes_count == 0
$('span',el).text("喜欢")
else
$('span',el).text("#{likes_count}人喜欢")
$("i.icon",el).attr("class","icon small_like")
false
看看服务器端的action吧。
# coding: utf-8
class LikesController < ApplicationController
before_filter :require_user
before_filter :find_likeable
def create
current_user.like(@item)
render :text => @item.reload.likes_count
end
def destroy
current_user.unlike(@item)
render :text => @item.reload.likes_count
end
private
def find_likeable
@success = false
@element_id = "likeable_#{params[:type]}_#{params[:id]}"
if not params[:type].in?(['Topic','Reply'])
render :text => "-1"
return false
end
klass = params[:type].constantize
@item = klass.find_by_id(params[:id])
if @item.blank?
render :text => "-2"
return false
end
end
end
定义了两个action: create 和 destroy , 分别对应 喜欢 和 取消喜欢 , 对来自与客户端的 type 参数,做了过滤
if not params[:type].in?(['Topic','Reply'])
并将字符串 转换成 类
klass = params[:type].constantize
在models/topic.rb中,include的一个module
include Mongoid::Likeable
likeable的定义
# coding: utf-8
module Mongoid
module Likeable
extend ActiveSupport::Concern
included do
field :liked_user_ids, :type => Array, :default => []
field :likes_count, :type => Integer, :default => 0
end
def liked_by_user?(user)
return false if user.blank?
self.liked_user_ids.include?(user.id)
end
end
end
定义了 两个字段
liked_user_ids 和 likes_count
并定义了一个方法
liked_by_user?
controller中使用了 user.like 方法
# 收藏东西
def like(likeable)
return false if likeable.blank?
return false if likeable.liked_by_user?(self)
likeable.push(:liked_user_ids, self.id)
likeable.inc(:likes_count, 1)
end
like方法就是想数组中push 数据
以上功能的实现,使用了 Ruby的 module 引入和 duck type, 有很多值得学习的地方。