6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
|
# File 'lib/recommendable/acts_as_recommendable.rb', line 6
def acts_as_recommendable
class_eval do
Recommendable.recommendable_classes << self
has_many :recommendable_likes, :as => :likeable, :dependent => :destroy,
:class_name => "Recommendable::Like"
has_many :recommendable_dislikes, :as => :dislikeable, :dependent => :destroy,
:class_name => "Recommendable::Dislike"
has_many :recommendable_ignores, :as => :ignorable, :dependent => :destroy,
:class_name => "Recommendable::Ignore"
has_many :recommendable_stashes, :as => :stashable, :dependent => :destroy,
:class_name => "Recommendable::Stash"
has_many :liked_by, :through => :recommendable_likes, :source => :user,
:foreign_key => :user_id, :class_name => Recommendable.user_class.to_s
has_many :disliked_by, :through => :recommendable_dislikes, :source => :user,
:foreign_key => :user_id, :class_name => Recommendable.user_class.to_s
include LikeableMethods
include DislikeableMethods
before_destroy :remove_from_scores, :remove_from_recommendations
def self.acts_as_recommendable?() true end
def been_rated?
recommendable_likes.count + recommendable_dislikes.count > 0
end
def rated_by
liked_by + disliked_by
end
def self.top count = 1
ids = Recommendable.redis.zrevrange(self.score_set, 0, count - 1).map(&:to_i)
items = self.find ids
return items.first if count == 1
return items.sort { |x, y| ids.index(x.id) <=> ids.index(y.id) }
end
private
def update_score
return 0 unless been_rated?
z = 1.96
n = recommendable_likes.count + recommendable_dislikes.count
phat = recommendable_likes.count / n.to_f
score = (phat + z*z/(2*n) - z * Math.sqrt((phat*(1-phat)+z*z/(4*n))/n))/(1+z*z/n)
Recommendable.redis.zadd self.class.score_set, score, self.id
true
end
def remove_from_scores
Recommendable.redis.zrem self.class.score_set, self.id
true
end
def remove_from_recommendations
Recommendable.user_class.find_each do |user|
user.send :completely_unrecommend, self
end
end
def create_recommendable_sets
[create_liked_by_set, create_disliked_by_set]
end
def destroy_recommendable_sets
Recommendable.redis.del "#{self.class.base_class}:#{id}:liked_by"
Recommendable.redis.del "#{self.class.base_class}:#{id}:disliked_by"
end
def rates_by
recommendable_likes.map(&:user_id) + recommendable_dislikes.map(&:user_id)
end
def self.score_set
"#{self}:sorted"
end
private :recommendable_likes, :recommendable_dislikes,
:recommendable_ignores, :recommendable_stashes
end
end
|