mongo_followable 0.2.0
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/.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
|
+
|