mongo_followable 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +115 -0
- data/Rakefile +1 -0
- data/app/models/follow.rb +19 -0
- data/lib/mongo_followable/followable.rb +185 -0
- data/lib/mongo_followable/follower.rb +213 -0
- data/lib/mongo_followable/version.rb +3 -0
- data/lib/mongo_followable.rb +7 -0
- data/mongo_followable.gemspec +26 -0
- data/spec/mongo/followable_spec.rb +126 -0
- data/spec/mongo_mapper/group.rb +4 -0
- data/spec/mongo_mapper/user.rb +5 -0
- data/spec/mongoid/group.rb +4 -0
- data/spec/mongoid/user.rb +5 -0
- data/spec/spec_helper.rb +31 -0
- metadata +150 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Jie Fan
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
= mongo_followable
|
2
|
+
|
3
|
+
Now works for both Mongoid and Mongo_Mapper!
|
4
|
+
|
5
|
+
== Installation
|
6
|
+
|
7
|
+
In console:
|
8
|
+
gem install mongo_followable
|
9
|
+
or in Gemfile:
|
10
|
+
gem 'mongo_followable', "~> 0.2.0"
|
11
|
+
|
12
|
+
== Usage
|
13
|
+
|
14
|
+
To make model followable you need to include Mongo::Followable into your document;Meanwhile, you also need to include Mongo::Follower in your follower model:
|
15
|
+
class User
|
16
|
+
include Mongo::Document
|
17
|
+
include Mongo::Followable
|
18
|
+
include Mongo::Follower
|
19
|
+
end
|
20
|
+
|
21
|
+
class Group
|
22
|
+
include Mongo::Document
|
23
|
+
include Mongo::Followable
|
24
|
+
end
|
25
|
+
|
26
|
+
Now you can set authorization:
|
27
|
+
current_user.set_authorization('user', 'game') # now current_user cannot follow User and Game model
|
28
|
+
current_user.unset_authorization('User', 'Game')
|
29
|
+
|
30
|
+
And then you can follow a model:
|
31
|
+
|
32
|
+
@group = Group.new
|
33
|
+
@group.save
|
34
|
+
|
35
|
+
current_user.follow(@group)
|
36
|
+
current_user.unfollow(@group)
|
37
|
+
|
38
|
+
You can also judge whether a model is a follower of another model or a model is a followee of another model:
|
39
|
+
|
40
|
+
current_user.follower_of?(@group)
|
41
|
+
current_user.followee_of?(@group)
|
42
|
+
|
43
|
+
Moreover, it's easy to get a model's follower/followee count:
|
44
|
+
|
45
|
+
current_user.followers_count
|
46
|
+
current_user.followees_count
|
47
|
+
|
48
|
+
Of course, you can get a list of followers/followees:
|
49
|
+
|
50
|
+
User.followers_of(@group)
|
51
|
+
User.followees_of(@group)
|
52
|
+
|
53
|
+
@group.all_followers
|
54
|
+
@user.all_followees
|
55
|
+
|
56
|
+
Getting a model's followers/followees by type is also possible:
|
57
|
+
|
58
|
+
@group.followers_by_type("user")
|
59
|
+
@user.followees_by_type("group")
|
60
|
+
|
61
|
+
And their count:
|
62
|
+
|
63
|
+
@group.followers_by_type("user")
|
64
|
+
@group.followers_count_by_type("user")
|
65
|
+
@user.followees_by_type("group")
|
66
|
+
@user.followees_count_by_type("group")
|
67
|
+
|
68
|
+
You can also get a model's follow/followed history:
|
69
|
+
|
70
|
+
@user.ever_follow
|
71
|
+
@group.ever_followed
|
72
|
+
|
73
|
+
Another feature is to get a list of models which has the most followers/followees:
|
74
|
+
|
75
|
+
User.with_max_followees
|
76
|
+
User.with_min_followees
|
77
|
+
User.with_max_followees_by_type('group')
|
78
|
+
User.with_min_followees_by_type('group')
|
79
|
+
Group.with_max_followers
|
80
|
+
Group.with_min_followers
|
81
|
+
Group.with_max_followers_by_type('user')
|
82
|
+
Group.with_min_followers_by_type('user')
|
83
|
+
|
84
|
+
Now you can tell if two models have some common followers/followees by following methods:
|
85
|
+
|
86
|
+
@user.common_followees?(@another_user)
|
87
|
+
@user.common_followers?(@group)
|
88
|
+
|
89
|
+
And see what the common followers/followees are:
|
90
|
+
|
91
|
+
@user.common_followees_with(@another_user)
|
92
|
+
@user.common_followers_with(@group)
|
93
|
+
|
94
|
+
* Any bug or issue, please send me an email: ustc.flyingfox@gmail.com
|
95
|
+
|
96
|
+
== TODO
|
97
|
+
|
98
|
+
* inter-models followable #FINISHED#
|
99
|
+
* divide into two parts: followable(being followed) and follower(following others) #FINISHED#
|
100
|
+
* following history/followed history #FINISHED#
|
101
|
+
* most/least followed/following #FINISHED
|
102
|
+
* add authorization to followable models #FINISHED#
|
103
|
+
* common followers/followees #FINISHED#
|
104
|
+
* add support for mongo_mapper in next version #FINISHED#
|
105
|
+
|
106
|
+
!!If you have any advice, plese do not hesitate to tell me!!
|
107
|
+
|
108
|
+
== Thanks
|
109
|
+
|
110
|
+
Thanks the author(s) of acts_as_followable, you can find this gem here[https://github.com/xpepermint/acts_as_followable]
|
111
|
+
Thanks the author(s) of voteable_mongo, you can find this gem here[https://github.com/vinova/voteable_mongo]
|
112
|
+
|
113
|
+
== Copyright
|
114
|
+
|
115
|
+
Copyright (c) Jie Fan. See LICENSE.txt for further details.
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class Follow
|
2
|
+
if defined?(Mongoid)
|
3
|
+
include Mongoid::Document
|
4
|
+
|
5
|
+
field :f_type, :type => String
|
6
|
+
field :f_id, :type => String
|
7
|
+
elsif defined?(MongoMapper)
|
8
|
+
include MongoMapper::Document
|
9
|
+
|
10
|
+
key :f_type, :type => String
|
11
|
+
key :f_id, :type => String
|
12
|
+
end
|
13
|
+
|
14
|
+
belongs_to :followable, :polymorphic => true
|
15
|
+
belongs_to :following, :polymorphic => true
|
16
|
+
|
17
|
+
scope :by_type, lambda { |type| where(:f_type => type.capitalize) }
|
18
|
+
scope :by_model, lambda { |model| where(:f_id => model.id.to_s).by_type(model.class.name) }
|
19
|
+
end
|
@@ -0,0 +1,185 @@
|
|
1
|
+
module Mongo
|
2
|
+
module Followable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do |base|
|
6
|
+
if defined?(Mongoid)
|
7
|
+
base.field :cannot_followed, :type => Array, :default => []
|
8
|
+
base.field :followed_history, :type => Array, :default => []
|
9
|
+
base.has_many :followers, :class_name => "Follow", :as => :followable, :dependent => :destroy
|
10
|
+
elsif defined?(MongoMapper)
|
11
|
+
base.key :cannot_followed, :type => Array, :default => []
|
12
|
+
base.key :followed_history, :type => Array, :default => []
|
13
|
+
base.many :followers, :class_name => "Follow", :as => :followable, :dependent => :destroy
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
|
19
|
+
# get certain model's followees of this type
|
20
|
+
#
|
21
|
+
# Example:
|
22
|
+
# >> @jim = User.new
|
23
|
+
# >> @ruby = Group.new
|
24
|
+
# >> @jim.save
|
25
|
+
# >> @ruby.save
|
26
|
+
#
|
27
|
+
# >> @jim.follow(@ruby)
|
28
|
+
# >> User.followees_of(@jim)
|
29
|
+
# => [@ruby]
|
30
|
+
#
|
31
|
+
# Arguments:
|
32
|
+
# model: instance of some followable model
|
33
|
+
|
34
|
+
def followees_of(model)
|
35
|
+
model.followees_by_type(self.name)
|
36
|
+
end
|
37
|
+
|
38
|
+
# 4 methods in this function
|
39
|
+
#
|
40
|
+
# Example:
|
41
|
+
# >> Group.with_max_followers
|
42
|
+
# => [@ruby]
|
43
|
+
# >> Group.with_max_followers_by_type('user')
|
44
|
+
# => [@ruby]
|
45
|
+
|
46
|
+
def method_missing(name, *args)
|
47
|
+
if name.to_s =~ /^with_(max|min)_followers$/i
|
48
|
+
follow_array = self.all.to_a.sort! { |a, b| a.followers_count <=> b.followers_count }
|
49
|
+
if $1 == "max"
|
50
|
+
max = follow_array[-1].followers_count
|
51
|
+
follow_array.select { |c| c.followers_count == max }
|
52
|
+
elsif $1 == "min"
|
53
|
+
min = follow_array[0].followers_count
|
54
|
+
follow_array.select { |c| c.followers_count == min }
|
55
|
+
end
|
56
|
+
elsif name.to_s =~ /^with_(max|min)_followers_by_type$/i
|
57
|
+
follow_array = self.all.to_a.sort! { |a, b| a.followers_count_by_type(args[0]) <=> b.followers_count_by_type(args[0]) }
|
58
|
+
if $1 == "max"
|
59
|
+
max = follow_array[-1].followers_count_by_type(args[0])
|
60
|
+
follow_array.select { |c| c.followers_count_by_type(args[0]) == max }
|
61
|
+
elsif $1 == "min"
|
62
|
+
min = follow_array[0].followers_count
|
63
|
+
follow_array.select { |c| c.followers_count_by_type(args[0]) == min }
|
64
|
+
end
|
65
|
+
else
|
66
|
+
super
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
# set which mongoid cannot follow self
|
73
|
+
#
|
74
|
+
# Example:
|
75
|
+
# >> @ruby.set_authorization('user')
|
76
|
+
# => true
|
77
|
+
|
78
|
+
def set_authorization(*models)
|
79
|
+
models.each do |model|
|
80
|
+
self.cannot_followed << model.capitalize
|
81
|
+
end
|
82
|
+
self.save
|
83
|
+
end
|
84
|
+
|
85
|
+
def unset_authorization(*models)
|
86
|
+
models.each do |model|
|
87
|
+
self.cannot_followed -= [model.capitalize]
|
88
|
+
end
|
89
|
+
self.save
|
90
|
+
end
|
91
|
+
|
92
|
+
# see if this model is followee of some model
|
93
|
+
#
|
94
|
+
# Example:
|
95
|
+
# >> @ruby.followee_of?(@jim)
|
96
|
+
# => true
|
97
|
+
|
98
|
+
def followee_of?(model)
|
99
|
+
0 < self.followers.by_model(model).limit(1).count * model.followees.by_model(self).limit(1).count
|
100
|
+
end
|
101
|
+
|
102
|
+
# get all the followers of this model, same with classmethod followers_of
|
103
|
+
#
|
104
|
+
# Example:
|
105
|
+
# >> @ruby.all_followers
|
106
|
+
# => [@jim]
|
107
|
+
|
108
|
+
def all_followers
|
109
|
+
rebuild_instances(self.followers)
|
110
|
+
end
|
111
|
+
|
112
|
+
# get all the followers of this model in certain type
|
113
|
+
#
|
114
|
+
# Example:
|
115
|
+
# >> @ruby.followers_by_type("user")
|
116
|
+
# => [@jim]
|
117
|
+
|
118
|
+
def followers_by_type(type)
|
119
|
+
rebuild_instances(self.followers.by_type(type))
|
120
|
+
end
|
121
|
+
|
122
|
+
# get the number of followers
|
123
|
+
#
|
124
|
+
# Example:
|
125
|
+
# >> @ruby.followers_count
|
126
|
+
# => 1
|
127
|
+
|
128
|
+
def followers_count
|
129
|
+
self.followers.count
|
130
|
+
end
|
131
|
+
|
132
|
+
# get the number of followers in certain type
|
133
|
+
#
|
134
|
+
# Example:
|
135
|
+
# >> @ruby.followers_count_by_type("user")
|
136
|
+
# => 1
|
137
|
+
|
138
|
+
def followers_count_by_type(type)
|
139
|
+
self.followers.by_type(type).count
|
140
|
+
end
|
141
|
+
|
142
|
+
# see model's followed history
|
143
|
+
#
|
144
|
+
# Example:
|
145
|
+
# >> @ruby.ever_followed
|
146
|
+
# => [@jim]
|
147
|
+
|
148
|
+
def ever_followed
|
149
|
+
follow = []
|
150
|
+
self.followed_history.each do |h|
|
151
|
+
follow << h.split('_')[0].constantize.find(h.split('_')[1])
|
152
|
+
end
|
153
|
+
follow
|
154
|
+
end
|
155
|
+
|
156
|
+
# return if there is any common followers
|
157
|
+
#
|
158
|
+
# Example:
|
159
|
+
# >> @ruby.common_followees?(@python)
|
160
|
+
# => true
|
161
|
+
|
162
|
+
def common_followers?(model)
|
163
|
+
0 < (rebuild_instances(self.followers) & rebuild_instances(model.followers)).length
|
164
|
+
end
|
165
|
+
|
166
|
+
# get common followers with some model
|
167
|
+
#
|
168
|
+
# Example:
|
169
|
+
# >> @ruby.common_followers_with(@python)
|
170
|
+
# => [@jim]
|
171
|
+
|
172
|
+
def common_followers_with(model)
|
173
|
+
rebuild_instances(self.followers) & rebuild_instances(model.followers)
|
174
|
+
end
|
175
|
+
|
176
|
+
private
|
177
|
+
def rebuild_instances(follows)
|
178
|
+
follow_list = []
|
179
|
+
follows.each do |follow|
|
180
|
+
follow_list << follow.f_type.capitalize.constantize.find(follow.f_id)
|
181
|
+
end
|
182
|
+
follow_list
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
@@ -0,0 +1,213 @@
|
|
1
|
+
module Mongo
|
2
|
+
module Follower
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do |base|
|
6
|
+
if defined?(Mongoid)
|
7
|
+
base.field :cannot_follow, :type => Array, :default => []
|
8
|
+
base.field :follow_history, :type => Array, :default => []
|
9
|
+
base.has_many :followees, :class_name => "Follow", :as => :following, :dependent => :destroy
|
10
|
+
elsif defined?(MongoMapper)
|
11
|
+
base.key :cannot_follow, :type => Array, :default => []
|
12
|
+
base.key :follow_history, :type => Array, :default => []
|
13
|
+
base.many :followees, :class_name => "Follow", :as => :following, :dependent => :destroy
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
|
19
|
+
# get certain model's followers of this type
|
20
|
+
#
|
21
|
+
# Example:
|
22
|
+
# >> @jim = User.new
|
23
|
+
# >> @ruby = Group.new
|
24
|
+
# >> @jim.save
|
25
|
+
# >> @ruby.save
|
26
|
+
#
|
27
|
+
# >> @jim.follow(@ruby)
|
28
|
+
# >> User.followers_of(@ruby)
|
29
|
+
# => [@jim]
|
30
|
+
#
|
31
|
+
# Arguments:
|
32
|
+
# model: instance of some followable model
|
33
|
+
|
34
|
+
def followers_of(model)
|
35
|
+
model.followers_by_type(self.name)
|
36
|
+
end
|
37
|
+
|
38
|
+
# 4 methods in this function
|
39
|
+
#
|
40
|
+
# Example:
|
41
|
+
# >> User.with_max_followees
|
42
|
+
# => [@jim]
|
43
|
+
# >> User.with_max_followees_by_type('group')
|
44
|
+
# => [@jim]
|
45
|
+
|
46
|
+
def method_missing(name, *args)
|
47
|
+
if name.to_s =~ /^with_(max|min)_followees$/i
|
48
|
+
follow_array = self.all.to_a.sort! { |a, b| a.followees_count <=> b.followees_count }
|
49
|
+
if $1 == "max"
|
50
|
+
max = follow_array[-1].followees_count
|
51
|
+
follow_array.select { |c| c.followees_count == max }
|
52
|
+
elsif $1 == "min"
|
53
|
+
min = follow_array[0].followees_count
|
54
|
+
follow_array.select { |c| c.followees_count == min }
|
55
|
+
end
|
56
|
+
elsif name.to_s =~ /^with_(max|min)_followees_by_type$/i
|
57
|
+
follow_array = self.all.to_a.sort! { |a, b| a.followees_count_by_type(args[0]) <=> b.followees_count_by_type(args[0]) }
|
58
|
+
if $1 == "max"
|
59
|
+
max = follow_array[-1].followees_count_by_type(args[0])
|
60
|
+
follow_array.select { |c| c.followees_count_by_type(args[0]) == max }
|
61
|
+
elsif $1 == "min"
|
62
|
+
min = follow_array[0].followees_count
|
63
|
+
follow_array.select { |c| c.followees_count_by_type(args[0]) == min }
|
64
|
+
end
|
65
|
+
else
|
66
|
+
super
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
# set which mongoid user cannot follow
|
73
|
+
#
|
74
|
+
# Example:
|
75
|
+
# >> @jim.set_authorization('group', 'user')
|
76
|
+
# => true
|
77
|
+
|
78
|
+
def set_authorization(*models)
|
79
|
+
models.each do |model|
|
80
|
+
self.cannot_follow << model.capitalize
|
81
|
+
end
|
82
|
+
self.save
|
83
|
+
end
|
84
|
+
|
85
|
+
#unset which mongoid user cannot follow
|
86
|
+
|
87
|
+
def unset_authorization(*models)
|
88
|
+
models.each do |model|
|
89
|
+
self.cannot_follow -= [model.capitalize]
|
90
|
+
end
|
91
|
+
self.save
|
92
|
+
end
|
93
|
+
|
94
|
+
# see if this model is follower of some model
|
95
|
+
#
|
96
|
+
# Example:
|
97
|
+
# >> @jim.follower_of?(@ruby)
|
98
|
+
# => true
|
99
|
+
|
100
|
+
def follower_of?(model)
|
101
|
+
0 < self.followees.by_model(model).limit(1).count * model.followers.by_model(self).limit(1).count
|
102
|
+
end
|
103
|
+
|
104
|
+
# get all the followees of this model, same with classmethod followees_of
|
105
|
+
#
|
106
|
+
# Example:
|
107
|
+
# >> @jim.all_followees
|
108
|
+
# => [@ruby]
|
109
|
+
|
110
|
+
def all_followees
|
111
|
+
rebuild_instances(self.followees)
|
112
|
+
end
|
113
|
+
|
114
|
+
# get all the followees of this model in certain type
|
115
|
+
#
|
116
|
+
# Example:
|
117
|
+
# >> @ruby.followees_by_type("group")
|
118
|
+
# => [@ruby]
|
119
|
+
|
120
|
+
def followees_by_type(type)
|
121
|
+
rebuild_instances(self.followees.by_type(type))
|
122
|
+
end
|
123
|
+
|
124
|
+
# follow some model
|
125
|
+
|
126
|
+
def follow(*models)
|
127
|
+
models.each do |model|
|
128
|
+
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)
|
129
|
+
model.followers.create!(:f_type => self.class.name, :f_id => self.id.to_s)
|
130
|
+
model.followed_history << self.class.name + '_' + self.id.to_s
|
131
|
+
model.save
|
132
|
+
self.followees.create!(:f_type => model.class.name, :f_id => model.id.to_s)
|
133
|
+
self.follow_history << model.class.name + '_' + model.id.to_s
|
134
|
+
self.save
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# unfollow some model
|
140
|
+
|
141
|
+
def unfollow(*models)
|
142
|
+
models.each do |model|
|
143
|
+
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)
|
144
|
+
model.followers.by_model(self).first.destroy
|
145
|
+
self.followees.by_model(model).first.destroy
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# get the number of followees
|
151
|
+
#
|
152
|
+
# Example:
|
153
|
+
# >> @jim.followers_count
|
154
|
+
# => 1
|
155
|
+
|
156
|
+
def followees_count
|
157
|
+
self.followees.count
|
158
|
+
end
|
159
|
+
|
160
|
+
# get the number of followers in certain type
|
161
|
+
#
|
162
|
+
# Example:
|
163
|
+
# >> @ruby.followers_count_by_type("user")
|
164
|
+
# => 1
|
165
|
+
|
166
|
+
def followees_count_by_type(type)
|
167
|
+
self.followees.by_type(type).count
|
168
|
+
end
|
169
|
+
|
170
|
+
# see user's follow history
|
171
|
+
#
|
172
|
+
# Example:
|
173
|
+
# >> @jim.ever_follow
|
174
|
+
# => [@ruby]
|
175
|
+
|
176
|
+
def ever_follow
|
177
|
+
follow = []
|
178
|
+
self.follow_history.each do |h|
|
179
|
+
follow << h.split('_')[0].constantize.find(h.split('_')[1])
|
180
|
+
end
|
181
|
+
follow
|
182
|
+
end
|
183
|
+
|
184
|
+
# return if there is any common followees
|
185
|
+
#
|
186
|
+
# Example:
|
187
|
+
# >> @jim.common_followees?(@tom)
|
188
|
+
# => true
|
189
|
+
|
190
|
+
def common_followees?(model)
|
191
|
+
0 < (rebuild_instances(self.followees) & rebuild_instances(model.followees)).length
|
192
|
+
end
|
193
|
+
|
194
|
+
# get common followees with some model
|
195
|
+
#
|
196
|
+
# Example:
|
197
|
+
# >> @jim.common_followees_with(@tom)
|
198
|
+
# => [@ruby]
|
199
|
+
|
200
|
+
def common_followees_with(model)
|
201
|
+
rebuild_instances(self.followees) & rebuild_instances(model.followees)
|
202
|
+
end
|
203
|
+
|
204
|
+
private
|
205
|
+
def rebuild_instances(follows)
|
206
|
+
follow_list = []
|
207
|
+
follows.each do |follow|
|
208
|
+
follow_list << follow.f_type.capitalize.constantize.find(follow.f_id)
|
209
|
+
end
|
210
|
+
follow_list
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
#require "mongo_followable/followable"
|
2
|
+
#require "mongo_followable/follower"
|
3
|
+
#require "../app/models/follow"
|
4
|
+
|
5
|
+
require File.join(File.dirname(__FILE__), "mongo_followable/followable")
|
6
|
+
require File.join(File.dirname(__FILE__), "mongo_followable/follower")
|
7
|
+
require File.join(File.dirname(__FILE__), "../app/models/follow")
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "mongo_followable/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "mongo_followable"
|
7
|
+
s.version = MongoFollowable::VERSION
|
8
|
+
s.authors = ["Jie Fan"]
|
9
|
+
s.email = ["ustc.flyingfox@gmail.com"]
|
10
|
+
s.homepage = "https://github.com/lastomato/mongo_followable"
|
11
|
+
s.summary = %q{ adds following feature to mongoid }
|
12
|
+
s.description = %q{ Mongo Followable adds following feature to mongoid }
|
13
|
+
|
14
|
+
s.rubyforge_project = "mongo_followable"
|
15
|
+
|
16
|
+
s.add_development_dependency('rspec', '~> 2.6')
|
17
|
+
s.add_development_dependency('mongoid', '~> 2.0')
|
18
|
+
s.add_development_dependency('mongo_mapper', '~> 0.9')
|
19
|
+
s.add_development_dependency('bson_ext', '~> 1.4')
|
20
|
+
s.add_development_dependency('database_cleaner', '~>0.6')
|
21
|
+
|
22
|
+
s.files = `git ls-files`.split("\n")
|
23
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
24
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
25
|
+
s.require_paths = ["lib"]
|
26
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Mongo::Followable do
|
4
|
+
|
5
|
+
describe User do
|
6
|
+
|
7
|
+
let!(:u) { User.new }
|
8
|
+
|
9
|
+
context "begins" do
|
10
|
+
|
11
|
+
before do
|
12
|
+
u.save
|
13
|
+
|
14
|
+
@v = User.new
|
15
|
+
@v.save
|
16
|
+
|
17
|
+
@g = Group.new
|
18
|
+
@g.save
|
19
|
+
end
|
20
|
+
|
21
|
+
it "following a user" do
|
22
|
+
u.follow(@v)
|
23
|
+
|
24
|
+
u.follower_of?(@v).should be_true
|
25
|
+
@v.followee_of?(u).should be_true
|
26
|
+
|
27
|
+
u.all_followees.should == [@v]
|
28
|
+
@v.all_followers.should == [u]
|
29
|
+
|
30
|
+
u.followees_by_type("user").should == [@v]
|
31
|
+
@v.followers_by_type("user").should == [u]
|
32
|
+
|
33
|
+
u.followees_count.should == 1
|
34
|
+
@v.followers_count.should == 1
|
35
|
+
|
36
|
+
u.followees_count_by_type("user").should == 1
|
37
|
+
@v.followers_count_by_type("user").should == 1
|
38
|
+
|
39
|
+
u.ever_follow.should == [@v]
|
40
|
+
@v.ever_followed.should == [u]
|
41
|
+
|
42
|
+
u.common_followees?(@v).should be_false
|
43
|
+
@v.common_followers?(u).should be_false
|
44
|
+
u.common_followees_with(@v).should == []
|
45
|
+
@v.common_followers_with(u).should == []
|
46
|
+
|
47
|
+
User.with_max_followees.should == [u]
|
48
|
+
User.with_max_followers.should == [@v]
|
49
|
+
User.with_max_followees_by_type('user').should == [u]
|
50
|
+
User.with_max_followers_by_type('user').should == [@v]
|
51
|
+
end
|
52
|
+
|
53
|
+
it "unfollowing a user" do
|
54
|
+
u.unfollow(@v)
|
55
|
+
|
56
|
+
u.follower_of?(@v).should be_false
|
57
|
+
@v.followee_of?(u).should be_false
|
58
|
+
|
59
|
+
u.all_followees.should == []
|
60
|
+
@v.all_followers.should == []
|
61
|
+
|
62
|
+
u.followees_by_type("user").should == []
|
63
|
+
@v.followers_by_type("user").should == []
|
64
|
+
|
65
|
+
u.followees_count.should == 0
|
66
|
+
@v.followers_count.should == 0
|
67
|
+
|
68
|
+
u.followees_count_by_type("user").should == 0
|
69
|
+
@v.followers_count_by_type("user").should == 0
|
70
|
+
end
|
71
|
+
|
72
|
+
it "following a group" do
|
73
|
+
u.follow(@g)
|
74
|
+
|
75
|
+
u.follower_of?(@g).should be_true
|
76
|
+
@g.followee_of?(u).should be_true
|
77
|
+
|
78
|
+
u.all_followees.should == [@g]
|
79
|
+
@g.all_followers.should == [u]
|
80
|
+
|
81
|
+
u.followees_by_type("group").should == [@g]
|
82
|
+
@g.followers_by_type("user").should == [u]
|
83
|
+
|
84
|
+
u.followees_count.should == 1
|
85
|
+
@g.followers_count.should == 1
|
86
|
+
|
87
|
+
u.followees_count_by_type("group").should == 1
|
88
|
+
@g.followers_count_by_type("user").should == 1
|
89
|
+
|
90
|
+
u.follow(@v)
|
91
|
+
|
92
|
+
u.ever_follow.should == [@g, @v]
|
93
|
+
@g.ever_followed.should == [u]
|
94
|
+
|
95
|
+
u.common_followees?(@v).should be_false
|
96
|
+
@v.common_followers?(@g).should be_true
|
97
|
+
u.common_followees_with(@v).should == []
|
98
|
+
@v.common_followers_with(@g).should == [u]
|
99
|
+
|
100
|
+
User.with_max_followees.should == [u]
|
101
|
+
Group.with_max_followers.should == [@g]
|
102
|
+
User.with_max_followees_by_type('group').should == [u]
|
103
|
+
Group.with_max_followers_by_type('user').should == [@g]
|
104
|
+
end
|
105
|
+
|
106
|
+
it "unfollowing a group" do
|
107
|
+
u.unfollow(@g)
|
108
|
+
|
109
|
+
u.follower_of?(@g).should be_false
|
110
|
+
@g.followee_of?(u).should be_false
|
111
|
+
|
112
|
+
u.all_followees.should == []
|
113
|
+
@g.all_followers.should == []
|
114
|
+
|
115
|
+
u.followees_by_type("group").should == []
|
116
|
+
@g.followers_by_type("group").should == []
|
117
|
+
|
118
|
+
u.followees_count.should == 0
|
119
|
+
@g.followers_count.should == 0
|
120
|
+
|
121
|
+
u.followees_count_by_type("group").should == 0
|
122
|
+
@g.followers_count_by_type("group").should == 0
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "bundler/setup"
|
3
|
+
|
4
|
+
require "database_cleaner"
|
5
|
+
require "rspec"
|
6
|
+
|
7
|
+
if rand > 0.5
|
8
|
+
puts 'Mongoid'
|
9
|
+
require 'mongoid'
|
10
|
+
require File.expand_path("../../lib/mongo_followable", __FILE__)
|
11
|
+
require File.expand_path("../mongoid/user", __FILE__)
|
12
|
+
require File.expand_path("../mongoid/group", __FILE__)
|
13
|
+
Mongoid.configure do |config|
|
14
|
+
name = 'voteable_mongo_test'
|
15
|
+
host = 'localhost'
|
16
|
+
config.master = Mongo::Connection.new.db(name)
|
17
|
+
config.autocreate_indexes = true
|
18
|
+
end
|
19
|
+
else
|
20
|
+
puts 'MongoMapper'
|
21
|
+
require 'mongo_mapper'
|
22
|
+
require File.expand_path("../../lib/mongo_followable", __FILE__)
|
23
|
+
require File.expand_path("../mongo_mapper/user", __FILE__)
|
24
|
+
require File.expand_path("../mongo_mapper/group", __FILE__)
|
25
|
+
MongoMapper.database = 'voteable_mongo_test'
|
26
|
+
end
|
27
|
+
|
28
|
+
RSpec.configure do |c|
|
29
|
+
c.before(:all) { DatabaseCleaner.strategy = :truncation }
|
30
|
+
c.before(:each) { DatabaseCleaner.clean }
|
31
|
+
end
|
metadata
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mongo_followable
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 2
|
8
|
+
- 0
|
9
|
+
version: 0.2.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Jie Fan
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-11-06 01:00:00 -04:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rspec
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ~>
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 2
|
30
|
+
- 6
|
31
|
+
version: "2.6"
|
32
|
+
type: :development
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: mongoid
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ~>
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
segments:
|
43
|
+
- 2
|
44
|
+
- 0
|
45
|
+
version: "2.0"
|
46
|
+
type: :development
|
47
|
+
version_requirements: *id002
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: mongo_mapper
|
50
|
+
prerelease: false
|
51
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ~>
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
segments:
|
57
|
+
- 0
|
58
|
+
- 9
|
59
|
+
version: "0.9"
|
60
|
+
type: :development
|
61
|
+
version_requirements: *id003
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: bson_ext
|
64
|
+
prerelease: false
|
65
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ~>
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
segments:
|
71
|
+
- 1
|
72
|
+
- 4
|
73
|
+
version: "1.4"
|
74
|
+
type: :development
|
75
|
+
version_requirements: *id004
|
76
|
+
- !ruby/object:Gem::Dependency
|
77
|
+
name: database_cleaner
|
78
|
+
prerelease: false
|
79
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
80
|
+
none: false
|
81
|
+
requirements:
|
82
|
+
- - ~>
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
segments:
|
85
|
+
- 0
|
86
|
+
- 6
|
87
|
+
version: "0.6"
|
88
|
+
type: :development
|
89
|
+
version_requirements: *id005
|
90
|
+
description: " Mongo Followable adds following feature to mongoid "
|
91
|
+
email:
|
92
|
+
- ustc.flyingfox@gmail.com
|
93
|
+
executables: []
|
94
|
+
|
95
|
+
extensions: []
|
96
|
+
|
97
|
+
extra_rdoc_files: []
|
98
|
+
|
99
|
+
files:
|
100
|
+
- .gitignore
|
101
|
+
- Gemfile
|
102
|
+
- LICENSE.txt
|
103
|
+
- README.rdoc
|
104
|
+
- Rakefile
|
105
|
+
- app/models/follow.rb
|
106
|
+
- lib/mongo_followable.rb
|
107
|
+
- lib/mongo_followable/followable.rb
|
108
|
+
- lib/mongo_followable/follower.rb
|
109
|
+
- lib/mongo_followable/version.rb
|
110
|
+
- mongo_followable.gemspec
|
111
|
+
- spec/mongo/followable_spec.rb
|
112
|
+
- spec/mongo_mapper/group.rb
|
113
|
+
- spec/mongo_mapper/user.rb
|
114
|
+
- spec/mongoid/group.rb
|
115
|
+
- spec/mongoid/user.rb
|
116
|
+
- spec/spec_helper.rb
|
117
|
+
has_rdoc: true
|
118
|
+
homepage: https://github.com/lastomato/mongo_followable
|
119
|
+
licenses: []
|
120
|
+
|
121
|
+
post_install_message:
|
122
|
+
rdoc_options: []
|
123
|
+
|
124
|
+
require_paths:
|
125
|
+
- lib
|
126
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
127
|
+
none: false
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
segments:
|
132
|
+
- 0
|
133
|
+
version: "0"
|
134
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
135
|
+
none: false
|
136
|
+
requirements:
|
137
|
+
- - ">="
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
segments:
|
140
|
+
- 0
|
141
|
+
version: "0"
|
142
|
+
requirements: []
|
143
|
+
|
144
|
+
rubyforge_project: mongo_followable
|
145
|
+
rubygems_version: 1.3.7
|
146
|
+
signing_key:
|
147
|
+
specification_version: 3
|
148
|
+
summary: adds following feature to mongoid
|
149
|
+
test_files: []
|
150
|
+
|