mongo_followable 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|