mongo_followable 0.3.0 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +32 -13
- data/lib/mongo_followable.rb +2 -5
- data/lib/mongo_followable/features/authorization.rb +5 -0
- data/lib/mongo_followable/features/confirmation.rb +5 -0
- data/lib/mongo_followable/features/history.rb +76 -0
- data/lib/mongo_followable/followed.rb +193 -0
- data/lib/mongo_followable/follower.rb +212 -269
- data/lib/mongo_followable/version.rb +4 -2
- data/mongo_followable.gemspec +6 -6
- data/spec/mongo/followable_spec.rb +10 -0
- data/spec/mongo/performance_spec.rb +29 -0
- data/spec/mongo_mapper/childuser.rb +3 -2
- data/spec/mongo_mapper/group.rb +2 -1
- data/spec/mongo_mapper/user.rb +3 -2
- data/spec/mongoid/childuser.rb +3 -2
- data/spec/mongoid/group.rb +2 -1
- data/spec/mongoid/user.rb +3 -2
- metadata +18 -20
- data/.idea/encodings.xml +0 -5
- data/.idea/misc.xml +0 -5
- data/.idea/modules.xml +0 -9
- data/.idea/mongo_followable.iml +0 -17
- data/.idea/vcs.xml +0 -7
- data/lib/mongo_followable/followable.rb +0 -247
- data/lib/mongo_followable/railtie.rb +0 -9
data/README.rdoc
CHANGED
@@ -9,29 +9,33 @@ In console:
|
|
9
9
|
or in Gemfile:
|
10
10
|
gem 'mongo_followable'
|
11
11
|
|
12
|
+
== Notice
|
13
|
+
|
14
|
+
Please read following documentation first. Since 0.3.2, some apis have been changed. Sorry for the inconvenience.
|
15
|
+
|
16
|
+
If you want to remove `follow_history` and `followed_history` fields totally from your database after you decide not to use follow/followed history feature, do this:
|
17
|
+
|
18
|
+
# in the rails console, taking user as an example:
|
19
|
+
User.all.each { |u| u.unset(:follow_history) } # this will remove the follow_history field
|
20
|
+
|
12
21
|
== Usage
|
13
22
|
|
14
23
|
To make model followable you need to include Mongo::Followable into your model; You also need to include Mongo::Follower in your follower model:
|
15
24
|
class User
|
16
25
|
include Mongoid::Document #for Mongo_Mapper users, this line of code should be include MongoMapper::Document
|
17
|
-
include Mongo::Followable
|
18
|
-
include Mongo::Follower
|
26
|
+
include Mongo::Followable::Followed
|
27
|
+
include Mongo::Followable::Follower
|
28
|
+
include Mongo::Followable::History # you have to add this line to enable follow/followed history
|
19
29
|
end
|
20
30
|
|
21
31
|
class Group
|
22
32
|
include Mongoid::Document #for Mongo_Mapper users, this line of code should be include MongoMapper::Document
|
23
|
-
include Mongo::Followable
|
33
|
+
include Mongo::Followable::Followed
|
34
|
+
include Mongo::Followable::History # you have to add this line to enable follow/followed history
|
24
35
|
end
|
25
36
|
|
26
|
-
|
27
|
-
|
28
|
-
# Note: for current version, you can only set the config once(first time the application is created).
|
29
|
-
# This should be fixed in next version.
|
30
|
-
config.mongo_followable = { :authorization => false, :history => false } # this is default value
|
31
|
-
|
32
|
-
Now you can set authorization:
|
33
|
-
current_user.set_authorization('user', 'game') # now current_user cannot follow User and Game model
|
34
|
-
current_user.unset_authorization('User', 'Game')
|
37
|
+
I've decided to remove authorization because it is quite inefficient to keep this field for every record in the database.
|
38
|
+
However, it's possible that I'll add it back as a plugin in the future.
|
35
39
|
|
36
40
|
And then you can follow and unfollow:
|
37
41
|
|
@@ -104,6 +108,20 @@ You can also get a model's follow/followed history:
|
|
104
108
|
@user.ever_follow
|
105
109
|
@group.ever_followed
|
106
110
|
|
111
|
+
or to tell if ever follow/followed by someone:
|
112
|
+
|
113
|
+
@user.ever_follow? @some_group
|
114
|
+
@group.ever_followed? @some_user
|
115
|
+
|
116
|
+
Sure you can clear the histories:
|
117
|
+
|
118
|
+
@user.clear_history!
|
119
|
+
|
120
|
+
#or more specific:
|
121
|
+
|
122
|
+
@user.clear_follow_history!
|
123
|
+
@group.clear_followed_history!
|
124
|
+
|
107
125
|
Another feature is to get a list of models which has the most followers/followees:
|
108
126
|
|
109
127
|
User.with_max_followees
|
@@ -126,7 +144,7 @@ And see what the common followers/followees are:
|
|
126
144
|
@user.common_followers_with(@group)
|
127
145
|
|
128
146
|
* Any bug or issue, please send me an email: ustc.flyingfox@gmail.com
|
129
|
-
|
147
|
+
include Mongo::Followable::History # you have to add this line to enable follow/followed history
|
130
148
|
== TODO
|
131
149
|
|
132
150
|
* inter-models followable #FINISHED#
|
@@ -136,6 +154,7 @@ And see what the common followers/followees are:
|
|
136
154
|
* add authorization to followable models #FINISHED#
|
137
155
|
* common followers/followees #FINISHED#
|
138
156
|
* add support for mongo_mapper in next version #FINISHED#
|
157
|
+
* implement plugins: confirmation, authorization etc.
|
139
158
|
|
140
159
|
!!If you have any advice, plese do not hesitate to tell me!!
|
141
160
|
|
data/lib/mongo_followable.rb
CHANGED
@@ -1,10 +1,7 @@
|
|
1
|
-
if defined?(Rails)
|
2
|
-
require File.join(File.dirname(__FILE__), "mongo_followable/railtie")
|
3
|
-
end
|
4
|
-
|
5
1
|
if defined?(Mongoid) or defined?(MongoMapper)
|
6
2
|
require File.join(File.dirname(__FILE__), "mongo_followable/core_ext/string")
|
7
|
-
require File.join(File.dirname(__FILE__), "mongo_followable/
|
3
|
+
require File.join(File.dirname(__FILE__), "mongo_followable/followed")
|
8
4
|
require File.join(File.dirname(__FILE__), "mongo_followable/follower")
|
9
5
|
require File.join(File.dirname(__FILE__), "../app/models/follow")
|
6
|
+
require File.join(File.dirname(__FILE__), "mongo_followable/features/history")
|
10
7
|
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Mongo
|
2
|
+
module Followable
|
3
|
+
module History
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do |base|
|
7
|
+
if base.include?(Mongo::Followable::Follower)
|
8
|
+
if defined?(Mongoid)
|
9
|
+
base.field :follow_history, :type => Array, :default => []
|
10
|
+
elsif defined?(MongoMapper)
|
11
|
+
base.key :follow_history, :type => Array, :default => []
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
if base.include?(Mongo::Followable::Followed)
|
16
|
+
if defined?(Mongoid)
|
17
|
+
base.field :followed_history, :type => Array, :default => []
|
18
|
+
elsif defined?(MongoMapper)
|
19
|
+
base.key :followed_history, :type => Array, :default => []
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module ClassMethods
|
25
|
+
# def clear_history!
|
26
|
+
# self.all.each { |m| m.unset(:follow_history) }
|
27
|
+
# self.all.each { |m| m.unset(:followed_history) }
|
28
|
+
# end
|
29
|
+
end
|
30
|
+
|
31
|
+
def clear_history!
|
32
|
+
clear_follow_history!
|
33
|
+
clear_followed_histroy!
|
34
|
+
end
|
35
|
+
|
36
|
+
def clear_follow_history!
|
37
|
+
self.update_attribute(:follow_history, []) if has_follow_history?
|
38
|
+
end
|
39
|
+
|
40
|
+
def clear_followed_histroy!
|
41
|
+
self.update_attribute(:followed_history, []) if has_followed_history?
|
42
|
+
end
|
43
|
+
|
44
|
+
def ever_follow
|
45
|
+
rebuild(self.follow_history) if has_follow_history?
|
46
|
+
end
|
47
|
+
|
48
|
+
def ever_followed
|
49
|
+
rebuild(self.followed_history) if has_followed_history?
|
50
|
+
end
|
51
|
+
|
52
|
+
def ever_follow?(model)
|
53
|
+
self.follow_history.include?(model.class.name + "_" + model.id.to_s) if has_follow_history?
|
54
|
+
end
|
55
|
+
|
56
|
+
def ever_followed?(model)
|
57
|
+
self.followed_history.include?(model.class.name + "_" + model.id.to_s) if has_followed_history?
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
def has_follow_history?
|
62
|
+
self.respond_to? :follow_history
|
63
|
+
end
|
64
|
+
|
65
|
+
def has_followed_history?
|
66
|
+
self.respond_to? :followed_history
|
67
|
+
end
|
68
|
+
|
69
|
+
def rebuild(ary)
|
70
|
+
ary.group_by { |x| x.split("_").first }.
|
71
|
+
inject([]) { |n,(k,v)| n += k.constantize.
|
72
|
+
find(v.map { |x| x.split("_").last}) }
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,193 @@
|
|
1
|
+
module Mongo
|
2
|
+
module Followable
|
3
|
+
module Followed
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do |base|
|
7
|
+
if defined?(Mongoid)
|
8
|
+
base.has_many :followers, :class_name => "Follow", :as => :followable, :dependent => :destroy
|
9
|
+
elsif defined?(MongoMapper)
|
10
|
+
base.many :followers, :class_name => "Follow", :as => :followable, :dependent => :destroy
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
|
16
|
+
# get certain model's followees of this type
|
17
|
+
#
|
18
|
+
# Example:
|
19
|
+
# >> @jim = User.new
|
20
|
+
# >> @ruby = Group.new
|
21
|
+
# >> @jim.save
|
22
|
+
# >> @ruby.save
|
23
|
+
#
|
24
|
+
# >> @jim.follow(@ruby)
|
25
|
+
# >> User.followees_of(@jim)
|
26
|
+
# => [@ruby]
|
27
|
+
#
|
28
|
+
# Arguments:
|
29
|
+
# model: instance of some followable model
|
30
|
+
|
31
|
+
def followees_of(model)
|
32
|
+
model.followees_by_type(self.name)
|
33
|
+
end
|
34
|
+
|
35
|
+
# 4 methods in this function
|
36
|
+
#
|
37
|
+
# Example:
|
38
|
+
# >> Group.with_max_followers
|
39
|
+
# => [@ruby]
|
40
|
+
# >> Group.with_max_followers_by_type('user')
|
41
|
+
# => [@ruby]
|
42
|
+
|
43
|
+
["max", "min"].each do |s|
|
44
|
+
define_method(:"with_#{s}_followers") do
|
45
|
+
follow_array = self.all.to_a.sort! { |a, b| a.followers_count <=> b.followers_count }
|
46
|
+
num = follow_array[-1].followers_count
|
47
|
+
follow_array.select { |c| c.followers_count == num }
|
48
|
+
end
|
49
|
+
|
50
|
+
define_method(:"with_#{s}_followers_by_type") do |*args|
|
51
|
+
follow_array = self.all.to_a.sort! { |a, b| a.followers_count_by_type(args[0]) <=> b.followers_count_by_type(args[0]) }
|
52
|
+
num = follow_array[-1].followers_count_by_type(args[0])
|
53
|
+
follow_array.select { |c| c.followers_count_by_type(args[0]) == num }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
#def method_missing(name, *args)
|
58
|
+
# if name.to_s =~ /^with_(max|min)_followers$/i
|
59
|
+
# follow_array = self.all.to_a.sort! { |a, b| a.followers_count <=> b.followers_count }
|
60
|
+
# if $1 == "max"
|
61
|
+
# max = follow_array[-1].followers_count
|
62
|
+
# follow_array.select { |c| c.followers_count == max }
|
63
|
+
# elsif $1 == "min"
|
64
|
+
# min = follow_array[0].followers_count
|
65
|
+
# follow_array.select { |c| c.followers_count == min }
|
66
|
+
# end
|
67
|
+
# elsif name.to_s =~ /^with_(max|min)_followers_by_type$/i
|
68
|
+
# follow_array = self.all.to_a.sort! { |a, b| a.followers_count_by_type(args[0]) <=> b.followers_count_by_type(args[0]) }
|
69
|
+
# if $1 == "max"
|
70
|
+
# max = follow_array[-1].followers_count_by_type(args[0])
|
71
|
+
# follow_array.select { |c| c.followers_count_by_type(args[0]) == max }
|
72
|
+
# elsif $1 == "min"
|
73
|
+
# min = follow_array[0].followers_count
|
74
|
+
# follow_array.select { |c| c.followers_count_by_type(args[0]) == min }
|
75
|
+
# end
|
76
|
+
# else
|
77
|
+
# super
|
78
|
+
# end
|
79
|
+
#end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
# see if this model is followee of some model
|
84
|
+
#
|
85
|
+
# Example:
|
86
|
+
# >> @ruby.followee_of?(@jim)
|
87
|
+
# => true
|
88
|
+
|
89
|
+
def followee_of?(model)
|
90
|
+
0 < self.followers.by_model(model).limit(1).count * model.followees.by_model(self).limit(1).count
|
91
|
+
end
|
92
|
+
|
93
|
+
# return true if self is followed by some models
|
94
|
+
#
|
95
|
+
# Example:
|
96
|
+
# >> @ruby.followed?
|
97
|
+
# => true
|
98
|
+
|
99
|
+
def followed?
|
100
|
+
0 < self.followers.length
|
101
|
+
end
|
102
|
+
|
103
|
+
# get all the followers of this model, same with classmethod followers_of
|
104
|
+
#
|
105
|
+
# Example:
|
106
|
+
# >> @ruby.all_followers
|
107
|
+
# => [@jim]
|
108
|
+
|
109
|
+
def all_followers
|
110
|
+
rebuild_instances(self.followers)
|
111
|
+
end
|
112
|
+
|
113
|
+
def unfollowed(*models, &block)
|
114
|
+
if block_given?
|
115
|
+
models.delete_if { |model| !yield(model) }
|
116
|
+
end
|
117
|
+
|
118
|
+
models.each do |model|
|
119
|
+
unless model == self or !self.followee_of?(model) or !model.follower_of?(self)
|
120
|
+
model.followees.by_model(self).first.destroy
|
121
|
+
self.followers.by_model(model).first.destroy
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# unfollow all
|
127
|
+
|
128
|
+
def unfollowed_all
|
129
|
+
unfollowed(*self.all_followers)
|
130
|
+
end
|
131
|
+
|
132
|
+
# get all the followers of this model in certain type
|
133
|
+
#
|
134
|
+
# Example:
|
135
|
+
# >> @ruby.followers_by_type("user")
|
136
|
+
# => [@jim]
|
137
|
+
|
138
|
+
def followers_by_type(type)
|
139
|
+
rebuild_instances(self.followers.by_type(type))
|
140
|
+
end
|
141
|
+
|
142
|
+
# get the number of followers
|
143
|
+
#
|
144
|
+
# Example:
|
145
|
+
# >> @ruby.followers_count
|
146
|
+
# => 1
|
147
|
+
|
148
|
+
def followers_count
|
149
|
+
self.followers.count
|
150
|
+
end
|
151
|
+
|
152
|
+
# get the number of followers in certain type
|
153
|
+
#
|
154
|
+
# Example:
|
155
|
+
# >> @ruby.followers_count_by_type("user")
|
156
|
+
# => 1
|
157
|
+
|
158
|
+
def followers_count_by_type(type)
|
159
|
+
self.followers.by_type(type).count
|
160
|
+
end
|
161
|
+
|
162
|
+
# return if there is any common followers
|
163
|
+
#
|
164
|
+
# Example:
|
165
|
+
# >> @ruby.common_followees?(@python)
|
166
|
+
# => true
|
167
|
+
|
168
|
+
def common_followers?(model)
|
169
|
+
0 < (rebuild_instances(self.followers) & rebuild_instances(model.followers)).length
|
170
|
+
end
|
171
|
+
|
172
|
+
# get common followers with some model
|
173
|
+
#
|
174
|
+
# Example:
|
175
|
+
# >> @ruby.common_followers_with(@python)
|
176
|
+
# => [@jim]
|
177
|
+
|
178
|
+
def common_followers_with(model)
|
179
|
+
rebuild_instances(self.followers) & rebuild_instances(model.followers)
|
180
|
+
end
|
181
|
+
|
182
|
+
private
|
183
|
+
def rebuild_instances(follows) #:nodoc:
|
184
|
+
follows.group_by(&:f_type).inject([]) { |r, (k, v)| r += k.constantize.find(v.map(&:f_id)).to_a }
|
185
|
+
#follow_list = []
|
186
|
+
#follows.each do |follow|
|
187
|
+
# follow_list << follow.f_type.constantize.find(follow.f_id)
|
188
|
+
#end
|
189
|
+
#follow_list
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
@@ -1,273 +1,216 @@
|
|
1
1
|
module Mongo
|
2
|
-
module
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
2
|
+
module Followable
|
3
|
+
module Follower
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do |base|
|
7
|
+
if defined?(Mongoid)
|
8
|
+
base.has_many :followees, :class_name => "Follow", :as => :following, :dependent => :destroy
|
9
|
+
elsif defined?(MongoMapper)
|
10
|
+
base.many :followees, :class_name => "Follow", :as => :following, :dependent => :destroy
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
|
16
|
+
# get certain model's followers of this type
|
17
|
+
#
|
18
|
+
# Example:
|
19
|
+
# >> @jim = User.new
|
20
|
+
# >> @ruby = Group.new
|
21
|
+
# >> @jim.save
|
22
|
+
# >> @ruby.save
|
23
|
+
#
|
24
|
+
# >> @jim.follow(@ruby)
|
25
|
+
# >> User.followers_of(@ruby)
|
26
|
+
# => [@jim]
|
27
|
+
#
|
28
|
+
# Arguments:
|
29
|
+
# model: instance of some followable model
|
30
|
+
|
31
|
+
def followers_of(model)
|
32
|
+
model.followers_by_type(self.name)
|
33
|
+
end
|
34
|
+
|
35
|
+
# 4 methods in this function
|
36
|
+
#
|
37
|
+
# Example:
|
38
|
+
# >> User.with_max_followees
|
39
|
+
# => [@jim]
|
40
|
+
# >> User.with_max_followees_by_type('group')
|
41
|
+
# => [@jim]
|
42
|
+
|
43
|
+
["max", "min"].each do |s|
|
44
|
+
define_method(:"with_#{s}_followees") do
|
45
|
+
follow_array = self.all.to_a.sort! { |a, b| a.followees_count <=> b.followees_count }
|
46
|
+
num = follow_array[-1].followees_count
|
47
|
+
follow_array.select { |c| c.followees_count == num }
|
48
|
+
end
|
49
|
+
|
50
|
+
define_method(:"with_#{s}_followees_by_type") do |*args|
|
51
|
+
follow_array = self.all.to_a.sort! { |a, b| a.followees_count_by_type(args[0]) <=> b.followees_count_by_type(args[0]) }
|
52
|
+
num = follow_array[-1].followees_count_by_type(args[0])
|
53
|
+
follow_array.select { |c| c.followees_count_by_type(args[0]) == num }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
#def method_missing(name, *args)
|
58
|
+
# if name.to_s =~ /^with_(max|min)_followees$/i
|
59
|
+
# follow_array = self.all.to_a.sort! { |a, b| a.followees_count <=> b.followees_count }
|
60
|
+
# if $1 == "max"
|
61
|
+
# max = follow_array[-1].followees_count
|
62
|
+
# follow_array.select { |c| c.followees_count == max }
|
63
|
+
# elsif $1 == "min"
|
64
|
+
# min = follow_array[0].followees_count
|
65
|
+
# follow_array.select { |c| c.followees_count == min }
|
66
|
+
# end
|
67
|
+
# elsif name.to_s =~ /^with_(max|min)_followees_by_type$/i
|
68
|
+
# follow_array = self.all.to_a.sort! { |a, b| a.followees_count_by_type(args[0]) <=> b.followees_count_by_type(args[0]) }
|
69
|
+
# if $1 == "max"
|
70
|
+
# max = follow_array[-1].followees_count_by_type(args[0])
|
71
|
+
# follow_array.select { |c| c.followees_count_by_type(args[0]) == max }
|
72
|
+
# elsif $1 == "min"
|
73
|
+
# min = follow_array[0].followees_count
|
74
|
+
# follow_array.select { |c| c.followees_count_by_type(args[0]) == min }
|
75
|
+
# end
|
76
|
+
# else
|
77
|
+
# super
|
78
|
+
# end
|
79
|
+
#end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
# see if this model is follower of some model
|
84
|
+
#
|
85
|
+
# Example:
|
86
|
+
# >> @jim.follower_of?(@ruby)
|
87
|
+
# => true
|
88
|
+
|
89
|
+
def follower_of?(model)
|
90
|
+
0 < self.followees.by_model(model).limit(1).count * model.followers.by_model(self).limit(1).count
|
91
|
+
end
|
92
|
+
|
93
|
+
# return true if self is following some models
|
94
|
+
#
|
95
|
+
# Example:
|
96
|
+
# >> @jim.following?
|
97
|
+
# => true
|
98
|
+
|
99
|
+
def following?
|
100
|
+
0 < self.followees.length
|
101
|
+
end
|
102
|
+
|
103
|
+
# get all the followees of this model, same with classmethod followees_of
|
104
|
+
#
|
105
|
+
# Example:
|
106
|
+
# >> @jim.all_followees
|
107
|
+
# => [@ruby]
|
108
|
+
|
109
|
+
def all_followees
|
110
|
+
rebuild_instances(self.followees)
|
111
|
+
end
|
112
|
+
|
113
|
+
# get all the followees of this model in certain type
|
114
|
+
#
|
115
|
+
# Example:
|
116
|
+
# >> @ruby.followees_by_type("group")
|
117
|
+
# => [@ruby]
|
118
|
+
|
119
|
+
def followees_by_type(type)
|
120
|
+
rebuild_instances(self.followees.by_type(type))
|
121
|
+
end
|
122
|
+
|
123
|
+
# follow some model
|
124
|
+
|
125
|
+
def follow(*models, &block)
|
126
|
+
if block_given?
|
127
|
+
models.delete_if { |model| !yield(model) }
|
128
|
+
end
|
129
|
+
|
130
|
+
models.each do |model|
|
131
|
+
unless model == self or self.follower_of?(model) or model.followee_of?(self)
|
132
|
+
model.followers.create!(:f_type => self.class.name, :f_id => self.id.to_s)
|
133
|
+
self.followees.create!(:f_type => model.class.name, :f_id => model.id.to_s)
|
134
|
+
|
135
|
+
model.followed_history << self.class.name + '_' + self.id.to_s if model.respond_to? :followed_history
|
136
|
+
self.follow_history << model.class.name + '_' + model.id.to_s if self.respond_to? :follow_history
|
137
|
+
|
138
|
+
model.save
|
139
|
+
self.save
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# unfollow some model
|
145
|
+
|
146
|
+
def unfollow(*models, &block)
|
147
|
+
if block_given?
|
148
|
+
models.delete_if { |model| !yield(model) }
|
149
|
+
end
|
150
|
+
|
151
|
+
models.each do |model|
|
152
|
+
unless model == self or !self.follower_of?(model) or !model.followee_of?(self)
|
153
|
+
model.followers.by_model(self).first.destroy
|
154
|
+
self.followees.by_model(model).first.destroy
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# unfollow all
|
160
|
+
|
161
|
+
def unfollow_all
|
162
|
+
unfollow(*self.all_followees)
|
163
|
+
end
|
164
|
+
|
165
|
+
# get the number of followees
|
166
|
+
#
|
167
|
+
# Example:
|
168
|
+
# >> @jim.followers_count
|
169
|
+
# => 1
|
170
|
+
|
171
|
+
def followees_count
|
172
|
+
self.followees.count
|
173
|
+
end
|
174
|
+
|
175
|
+
# get the number of followers in certain type
|
176
|
+
#
|
177
|
+
# Example:
|
178
|
+
# >> @ruby.followers_count_by_type("user")
|
179
|
+
# => 1
|
180
|
+
|
181
|
+
def followees_count_by_type(type)
|
182
|
+
self.followees.by_type(type).count
|
183
|
+
end
|
184
|
+
|
185
|
+
# return if there is any common followees
|
186
|
+
#
|
187
|
+
# Example:
|
188
|
+
# >> @jim.common_followees?(@tom)
|
189
|
+
# => true
|
190
|
+
|
191
|
+
def common_followees?(model)
|
192
|
+
0 < (rebuild_instances(self.followees) & rebuild_instances(model.followees)).length
|
193
|
+
end
|
194
|
+
|
195
|
+
# get common followees with some model
|
196
|
+
#
|
197
|
+
# Example:
|
198
|
+
# >> @jim.common_followees_with(@tom)
|
199
|
+
# => [@ruby]
|
200
|
+
|
201
|
+
def common_followees_with(model)
|
202
|
+
rebuild_instances(self.followees) & rebuild_instances(model.followees)
|
203
|
+
end
|
204
|
+
|
205
|
+
private
|
206
|
+
def rebuild_instances(follows) #:nodoc:
|
207
|
+
follows.group_by(&:f_type).inject([]) { |r, (k, v)| r += k.constantize.find(v.map(&:f_id)).to_a }
|
208
|
+
#follow_list = []
|
209
|
+
#follows.each do |follow|
|
210
|
+
# follow_list << follow.f_type.constantize.find(follow.f_id)
|
211
|
+
#end
|
212
|
+
#follow_list
|
213
|
+
end
|
27
214
|
end
|
28
|
-
|
29
|
-
module ClassMethods
|
30
|
-
|
31
|
-
# get certain model's followers of this type
|
32
|
-
#
|
33
|
-
# Example:
|
34
|
-
# >> @jim = User.new
|
35
|
-
# >> @ruby = Group.new
|
36
|
-
# >> @jim.save
|
37
|
-
# >> @ruby.save
|
38
|
-
#
|
39
|
-
# >> @jim.follow(@ruby)
|
40
|
-
# >> User.followers_of(@ruby)
|
41
|
-
# => [@jim]
|
42
|
-
#
|
43
|
-
# Arguments:
|
44
|
-
# model: instance of some followable model
|
45
|
-
|
46
|
-
def followers_of(model)
|
47
|
-
model.followers_by_type(self.name)
|
48
|
-
end
|
49
|
-
|
50
|
-
# 4 methods in this function
|
51
|
-
#
|
52
|
-
# Example:
|
53
|
-
# >> User.with_max_followees
|
54
|
-
# => [@jim]
|
55
|
-
# >> User.with_max_followees_by_type('group')
|
56
|
-
# => [@jim]
|
57
|
-
|
58
|
-
["max", "min"].each do |s|
|
59
|
-
define_method(:"with_#{s}_followees") do
|
60
|
-
follow_array = self.all.to_a.sort! { |a, b| a.followees_count <=> b.followees_count }
|
61
|
-
num = follow_array[-1].followees_count
|
62
|
-
follow_array.select { |c| c.followees_count == num }
|
63
|
-
end
|
64
|
-
|
65
|
-
define_method(:"with_#{s}_followees_by_type") do |*args|
|
66
|
-
follow_array = self.all.to_a.sort! { |a, b| a.followees_count_by_type(args[0]) <=> b.followees_count_by_type(args[0]) }
|
67
|
-
num = follow_array[-1].followees_count_by_type(args[0])
|
68
|
-
follow_array.select { |c| c.followees_count_by_type(args[0]) == num }
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
#def method_missing(name, *args)
|
73
|
-
# if name.to_s =~ /^with_(max|min)_followees$/i
|
74
|
-
# follow_array = self.all.to_a.sort! { |a, b| a.followees_count <=> b.followees_count }
|
75
|
-
# if $1 == "max"
|
76
|
-
# max = follow_array[-1].followees_count
|
77
|
-
# follow_array.select { |c| c.followees_count == max }
|
78
|
-
# elsif $1 == "min"
|
79
|
-
# min = follow_array[0].followees_count
|
80
|
-
# follow_array.select { |c| c.followees_count == min }
|
81
|
-
# end
|
82
|
-
# elsif name.to_s =~ /^with_(max|min)_followees_by_type$/i
|
83
|
-
# follow_array = self.all.to_a.sort! { |a, b| a.followees_count_by_type(args[0]) <=> b.followees_count_by_type(args[0]) }
|
84
|
-
# if $1 == "max"
|
85
|
-
# max = follow_array[-1].followees_count_by_type(args[0])
|
86
|
-
# follow_array.select { |c| c.followees_count_by_type(args[0]) == max }
|
87
|
-
# elsif $1 == "min"
|
88
|
-
# min = follow_array[0].followees_count
|
89
|
-
# follow_array.select { |c| c.followees_count_by_type(args[0]) == min }
|
90
|
-
# end
|
91
|
-
# else
|
92
|
-
# super
|
93
|
-
# end
|
94
|
-
#end
|
95
|
-
|
96
|
-
end
|
97
|
-
|
98
|
-
# set which mongoid user cannot follow
|
99
|
-
#
|
100
|
-
# Example:
|
101
|
-
# >> @jim.set_authorization('group', 'user')
|
102
|
-
# => true
|
103
|
-
|
104
|
-
if CONFIG[:authorization]
|
105
|
-
define_method(:set_authorization) do |*models|
|
106
|
-
models.each do |model|
|
107
|
-
self.cannot_follow << model.safe_capitalize
|
108
|
-
end
|
109
|
-
self.save
|
110
|
-
end
|
111
|
-
|
112
|
-
#unset which mongoid user cannot follow
|
113
|
-
|
114
|
-
define_method(:unset_authorization) do |*models|
|
115
|
-
models.each do |model|
|
116
|
-
self.cannot_follow -= [model.safe_capitalize]
|
117
|
-
end
|
118
|
-
self.save
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
# see if this model is follower of some model
|
123
|
-
#
|
124
|
-
# Example:
|
125
|
-
# >> @jim.follower_of?(@ruby)
|
126
|
-
# => true
|
127
|
-
|
128
|
-
def follower_of?(model)
|
129
|
-
0 < self.followees.by_model(model).limit(1).count * model.followers.by_model(self).limit(1).count
|
130
|
-
end
|
131
|
-
|
132
|
-
# return true if self is following some models
|
133
|
-
#
|
134
|
-
# Example:
|
135
|
-
# >> @jim.following?
|
136
|
-
# => true
|
137
|
-
|
138
|
-
def following?
|
139
|
-
0 < self.followees.length
|
140
|
-
end
|
141
|
-
|
142
|
-
# get all the followees of this model, same with classmethod followees_of
|
143
|
-
#
|
144
|
-
# Example:
|
145
|
-
# >> @jim.all_followees
|
146
|
-
# => [@ruby]
|
147
|
-
|
148
|
-
def all_followees
|
149
|
-
rebuild_instances(self.followees)
|
150
|
-
end
|
151
|
-
|
152
|
-
# get all the followees of this model in certain type
|
153
|
-
#
|
154
|
-
# Example:
|
155
|
-
# >> @ruby.followees_by_type("group")
|
156
|
-
# => [@ruby]
|
157
|
-
|
158
|
-
def followees_by_type(type)
|
159
|
-
rebuild_instances(self.followees.by_type(type))
|
160
|
-
end
|
161
|
-
|
162
|
-
# follow some model
|
163
|
-
|
164
|
-
def follow(*models, &block)
|
165
|
-
if block_given?
|
166
|
-
models.delete_if { |model| !yield(model) }
|
167
|
-
end
|
168
|
-
|
169
|
-
models.each do |model|
|
170
|
-
term = CONFIG[:authorization] ? (self.cannot_follow.include?(model.class.name) or model.cannot_followed.include?(self.class.name)) : false
|
171
|
-
|
172
|
-
unless model == self or self.follower_of?(model) or model.followee_of?(self) or term
|
173
|
-
model.followers.create!(:f_type => self.class.name, :f_id => self.id.to_s)
|
174
|
-
self.followees.create!(:f_type => model.class.name, :f_id => model.id.to_s)
|
175
|
-
|
176
|
-
if CONFIG[:history]
|
177
|
-
model.followed_history << self.class.name + '_' + self.id.to_s
|
178
|
-
self.follow_history << model.class.name + '_' + model.id.to_s
|
179
|
-
end
|
180
|
-
|
181
|
-
model.save
|
182
|
-
self.save
|
183
|
-
end
|
184
|
-
end
|
185
|
-
end
|
186
|
-
|
187
|
-
# unfollow some model
|
188
|
-
|
189
|
-
def unfollow(*models, &block)
|
190
|
-
if block_given?
|
191
|
-
models.delete_if { |model| !yield(model) }
|
192
|
-
end
|
193
|
-
|
194
|
-
models.each do |model|
|
195
|
-
unless model == self or !self.follower_of?(model) or !model.followee_of?(self) or self.cannot_follow.include?(model.class.name) or model.cannot_followed.include?(self.class.name)
|
196
|
-
model.followers.by_model(self).first.destroy
|
197
|
-
self.followees.by_model(model).first.destroy
|
198
|
-
end
|
199
|
-
end
|
200
|
-
end
|
201
|
-
|
202
|
-
# unfollow all
|
203
|
-
|
204
|
-
def unfollow_all
|
205
|
-
unfollow(*self.all_followees)
|
206
|
-
end
|
207
|
-
|
208
|
-
# get the number of followees
|
209
|
-
#
|
210
|
-
# Example:
|
211
|
-
# >> @jim.followers_count
|
212
|
-
# => 1
|
213
|
-
|
214
|
-
def followees_count
|
215
|
-
self.followees.count
|
216
|
-
end
|
217
|
-
|
218
|
-
# get the number of followers in certain type
|
219
|
-
#
|
220
|
-
# Example:
|
221
|
-
# >> @ruby.followers_count_by_type("user")
|
222
|
-
# => 1
|
223
|
-
|
224
|
-
def followees_count_by_type(type)
|
225
|
-
self.followees.by_type(type).count
|
226
|
-
end
|
227
|
-
|
228
|
-
# see user's follow history
|
229
|
-
#
|
230
|
-
# Example:
|
231
|
-
# >> @jim.ever_follow
|
232
|
-
# => [@ruby]
|
233
|
-
|
234
|
-
if CONFIG[:history]
|
235
|
-
define_method(:ever_follow) do
|
236
|
-
follow = []
|
237
|
-
self.follow_history.each do |h|
|
238
|
-
follow << h.split('_')[0].constantize.find(h.split('_')[1])
|
239
|
-
end
|
240
|
-
follow
|
241
|
-
end
|
242
|
-
end
|
243
|
-
|
244
|
-
# return if there is any common followees
|
245
|
-
#
|
246
|
-
# Example:
|
247
|
-
# >> @jim.common_followees?(@tom)
|
248
|
-
# => true
|
249
|
-
|
250
|
-
def common_followees?(model)
|
251
|
-
0 < (rebuild_instances(self.followees) & rebuild_instances(model.followees)).length
|
252
|
-
end
|
253
|
-
|
254
|
-
# get common followees with some model
|
255
|
-
#
|
256
|
-
# Example:
|
257
|
-
# >> @jim.common_followees_with(@tom)
|
258
|
-
# => [@ruby]
|
259
|
-
|
260
|
-
def common_followees_with(model)
|
261
|
-
rebuild_instances(self.followees) & rebuild_instances(model.followees)
|
262
|
-
end
|
263
|
-
|
264
|
-
private
|
265
|
-
def rebuild_instances(follows)
|
266
|
-
follow_list = []
|
267
|
-
follows.each do |follow|
|
268
|
-
follow_list << follow.f_type.constantize.find(follow.f_id)
|
269
|
-
end
|
270
|
-
follow_list
|
271
|
-
end
|
272
215
|
end
|
273
216
|
end
|