voteable_mongoid 0.7.0 → 0.7.1
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/CHANGELOG.rdoc +5 -0
- data/README.rdoc +33 -11
- data/TODO +1 -1
- data/lib/voteable_mongoid.rb +2 -3
- data/lib/voteable_mongoid/railties/database.rake +4 -4
- data/lib/voteable_mongoid/version.rb +1 -1
- data/lib/voteable_mongoid/voteable.rb +57 -198
- data/lib/voteable_mongoid/voteable/tasks.rb +156 -0
- data/lib/voteable_mongoid/voteable/votes.rb +3 -43
- data/lib/voteable_mongoid/voteable/voting.rb +202 -0
- data/lib/voteable_mongoid/voter.rb +5 -4
- data/spec/spec_helper.rb +3 -3
- data/spec/voteable_mongoid/voteable_spec.rb +14 -14
- data/spec/voteable_mongoid/voter_spec.rb +0 -2
- data/voteable_mongoid.gemspec +0 -2
- metadata +7 -36
- data/lib/voteable_mongoid/voteable/stats.rb +0 -115
data/CHANGELOG.rdoc
CHANGED
data/README.rdoc
CHANGED
@@ -60,12 +60,38 @@ user.rb
|
|
60
60
|
|
61
61
|
@user.vote(@post, :up)
|
62
62
|
# is equivalent to
|
63
|
-
@
|
63
|
+
@user.vote(:votee => @post, :value => :up)
|
64
|
+
@post.vote(:voter => @user, :value => :up)
|
64
65
|
|
65
|
-
#
|
66
|
-
@user.vote(
|
66
|
+
# In case you don't need to init voter and / or votee objects you can
|
67
|
+
@user.vote(:votee_type => 'Post', :votee_id => post_id, :value => :down)
|
68
|
+
@post.vote(:voter_id => user_id, :value => :up)
|
69
|
+
Post.vote(:voter_id => user_id, :votee_id => post_id, :value => :up)
|
67
70
|
|
68
|
-
===
|
71
|
+
=== Undo a vote
|
72
|
+
|
73
|
+
@user.unvote(@comment)
|
74
|
+
|
75
|
+
=== If have full information you do not need to init voter and votee object
|
76
|
+
|
77
|
+
Post.vote(:voter_id => user_id, :votee_id => post_id, :value => :up, :revote => true)
|
78
|
+
Post.vote(:voter_id => user_id, :votee_id => post_id, :value => :up, :unvote => true)
|
79
|
+
|
80
|
+
=== Getting vote_value
|
81
|
+
|
82
|
+
@user.vote_value(@post)
|
83
|
+
@user.vote_value(:class_type => 'Post', :votee_id => post_id)
|
84
|
+
@post.vote_value(@user)
|
85
|
+
@post.vote_value(user_id)
|
86
|
+
|
87
|
+
=== Check if voted?
|
88
|
+
|
89
|
+
@user.voted?(@post)
|
90
|
+
@user.voted?(:class_type => 'Post', :votee_id => post_id)
|
91
|
+
@post.voted_by?(@user)
|
92
|
+
@post.voted_by?(user_id)
|
93
|
+
|
94
|
+
=== Getting votes counts and points
|
69
95
|
|
70
96
|
puts @post.votes_point
|
71
97
|
puts @post.votes_count
|
@@ -78,29 +104,25 @@ user.rb
|
|
78
104
|
Post.up_voted_by(@user)
|
79
105
|
Post.down_voted_by(@user)
|
80
106
|
|
81
|
-
=== Undo a vote
|
82
|
-
|
83
|
-
@user.unvote(@comment)
|
84
|
-
|
85
107
|
== Utilities
|
86
108
|
|
87
109
|
=== Re-generate counters and vote points in case you change :up / :down vote points
|
88
110
|
Rails
|
89
111
|
rake db:mongoid:voteable:remake_stats
|
90
112
|
Ruby
|
91
|
-
Mongoid::Voteable::
|
113
|
+
Mongoid::Voteable::Tasks.remake_stats
|
92
114
|
|
93
115
|
=== Set counters and point to 0 for uninitialized voteable objects in order sort and query
|
94
116
|
Rails
|
95
117
|
rake db:mongoid:voteable:init_stats
|
96
118
|
Ruby
|
97
|
-
Mongoid::Voteable::
|
119
|
+
Mongoid::Voteable::Tasks::init_stats
|
98
120
|
|
99
121
|
=== Migrate from version < 0.7.0
|
100
122
|
Rails
|
101
123
|
rake db:mongoid:voteable:migrate_old_votes
|
102
124
|
Ruby
|
103
|
-
Mongoid::Voteable.migrate_old_votes
|
125
|
+
Mongoid::Voteable::Tasks.migrate_old_votes
|
104
126
|
|
105
127
|
== Credits
|
106
128
|
|
data/TODO
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
* Option hash validations
|
2
|
-
|
2
|
+
|
data/lib/voteable_mongoid.rb
CHANGED
@@ -1,10 +1,9 @@
|
|
1
1
|
require 'mongoid'
|
2
|
-
require 'voteable_mongoid/voteable/stats'
|
3
|
-
require 'voteable_mongoid/voteable/votes'
|
4
2
|
require 'voteable_mongoid/voteable'
|
5
3
|
require 'voteable_mongoid/voter'
|
6
4
|
|
7
|
-
|
5
|
+
require 'voteable_mongoid/voteable/tasks'
|
6
|
+
# Add railtie
|
8
7
|
if defined?(Rails)
|
9
8
|
require 'voteable_mongoid/railtie'
|
10
9
|
end
|
@@ -3,17 +3,17 @@ namespace :db do
|
|
3
3
|
namespace :voteable do
|
4
4
|
desc 'Update up_votes_count, down_votes_count, votes_count and votes_point'
|
5
5
|
task :remake_stats => :environment do
|
6
|
-
Mongoid::Voteable::
|
6
|
+
Mongoid::Voteable::Tasks.remake_stats(:log)
|
7
7
|
end
|
8
8
|
|
9
9
|
desc 'Set counters and point to 0 for uninitizized voteable objects'
|
10
10
|
task :init_stats => :environment do
|
11
|
-
Mongoid::Voteable::
|
11
|
+
Mongoid::Voteable::Tasks.init_stats(:log)
|
12
12
|
end
|
13
13
|
|
14
|
-
desc 'Migrate vote data created by version < 0.
|
14
|
+
desc 'Migrate vote data created by version < 0.7.0 to new vote data storage'
|
15
15
|
task :migrate_old_votes => :environment do
|
16
|
-
Mongoid::Voteable.migrate_old_votes(:log)
|
16
|
+
Mongoid::Voteable::Tasks.migrate_old_votes(:log)
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
@@ -1,236 +1,70 @@
|
|
1
|
+
require 'voteable_mongoid/voteable/votes'
|
2
|
+
require 'voteable_mongoid/voteable/voting'
|
3
|
+
|
1
4
|
module Mongoid
|
2
5
|
module Voteable
|
3
6
|
extend ActiveSupport::Concern
|
4
7
|
|
5
|
-
# How many points should be assigned for each up or down vote.
|
6
|
-
# This hash should manipulated using voteable method
|
7
|
-
VOTEABLE = {}
|
8
|
-
|
9
8
|
included do
|
10
|
-
include
|
11
|
-
include
|
12
|
-
|
9
|
+
include Document
|
10
|
+
include Voting
|
11
|
+
|
12
|
+
field :votes, :type => Votes
|
13
|
+
|
14
|
+
before_create do
|
15
|
+
# Init votes so that counters and point have numeric values (0)
|
16
|
+
self.votes = Votes::DEFAULT_ATTRIBUTES
|
17
|
+
end
|
13
18
|
|
14
19
|
scope :voted_by, lambda { |voter|
|
15
|
-
voter_id = voter.is_a?(BSON::ObjectId) ? voter : voter.
|
20
|
+
voter_id = voter.is_a?(BSON::ObjectId) ? voter : voter.id
|
16
21
|
any_of({ 'votes.up' => voter_id }, { 'votes.down' => voter_id })
|
17
22
|
}
|
18
23
|
|
19
24
|
scope :up_voted_by, lambda { |voter|
|
20
|
-
voter_id = voter.is_a?(BSON::ObjectId) ? voter : voter.
|
25
|
+
voter_id = voter.is_a?(BSON::ObjectId) ? voter : voter.id
|
21
26
|
where( 'votes.up' => voter_id )
|
22
27
|
}
|
23
28
|
|
24
29
|
scope :down_voted_by, lambda { |voter|
|
25
|
-
voter_id = voter.is_a?(BSON::ObjectId) ? voter : voter.
|
30
|
+
voter_id = voter.is_a?(BSON::ObjectId) ? voter : voter.id
|
26
31
|
where( 'votes.down' => voter_id )
|
27
32
|
}
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
33
|
+
end # include
|
34
|
+
|
35
|
+
# How many points should be assigned for each up or down vote and other options
|
36
|
+
# This hash should manipulated using voteable method
|
37
|
+
VOTEABLE = {}
|
38
|
+
|
39
|
+
module ClassMethods
|
34
40
|
# Set vote point for each up (down) vote on an object of this class
|
35
41
|
#
|
36
42
|
# @param [Hash] options a hash containings:
|
37
43
|
#
|
38
44
|
# voteable self, :up => +1, :down => -3
|
39
45
|
# voteable Post, :up => +2, :down => -1, :update_counters => false # skip counter update
|
40
|
-
def
|
46
|
+
def voteable(klass = self, options = nil)
|
41
47
|
VOTEABLE[self.name] ||= {}
|
42
48
|
VOTEABLE[self.name][klass.name] ||= options
|
43
49
|
end
|
44
|
-
|
45
|
-
# We usually need to show current_user his voting value on voteable object
|
46
|
-
# voting value can be nil (not voted yet), :up or :down
|
47
|
-
# from voting value, we can decide it should be new vote or revote with :up or :down
|
48
|
-
# In this case, validation can be skip to maximize performance
|
49
|
-
|
50
|
-
# Make a vote on an object of this class
|
51
|
-
#
|
52
|
-
# @param [Hash] options a hash containings:
|
53
|
-
# - :votee_id: the votee document id
|
54
|
-
# - :voter_id: the voter document id
|
55
|
-
# - :value: :up or :down
|
56
|
-
# - :revote: change from vote up to vote down
|
57
|
-
# - :unvote: unvote the vote value (:up or :down)
|
58
|
-
def self.vote(options)
|
59
|
-
options.symbolize_keys!
|
60
|
-
value = options[:value].to_sym
|
61
|
-
|
62
|
-
votee_id = options[:votee_id]
|
63
|
-
voter_id = options[:voter_id]
|
64
|
-
|
65
|
-
votee_id = BSON::ObjectId(votee_id) if votee_id.is_a?(String)
|
66
|
-
voter_id = BSON::ObjectId(voter_id) if voter_id.is_a?(String)
|
67
|
-
|
68
|
-
klass = options[:class]
|
69
|
-
klass ||= VOTEABLE.keys.include?(name) ? name : collection.name.classify
|
70
|
-
voteable = VOTEABLE[klass][klass]
|
71
|
-
|
72
|
-
if options[:revote]
|
73
|
-
if value == :up
|
74
|
-
positive_voter_ids = 'votes.up'
|
75
|
-
negative_voter_ids = 'votes.down'
|
76
|
-
positive_votes_count = 'votes.up_count'
|
77
|
-
negative_votes_count = 'votes.down_count'
|
78
|
-
point_delta = voteable[:up] - voteable[:down]
|
79
|
-
else
|
80
|
-
positive_voter_ids = 'votes.down'
|
81
|
-
negative_voter_ids = 'votes.up'
|
82
|
-
positive_votes_count = 'votes.down_count'
|
83
|
-
negative_votes_count = 'votes.up_count'
|
84
|
-
point_delta = -voteable[:up] + voteable[:down]
|
85
|
-
end
|
86
|
-
|
87
|
-
update_result = collection.update({
|
88
|
-
# Validate voter_id did a vote with value for votee_id
|
89
|
-
:_id => votee_id,
|
90
|
-
positive_voter_ids => { '$ne' => voter_id },
|
91
|
-
negative_voter_ids => voter_id
|
92
|
-
}, {
|
93
|
-
# then update
|
94
|
-
'$pull' => { negative_voter_ids => voter_id },
|
95
|
-
'$push' => { positive_voter_ids => voter_id },
|
96
|
-
'$inc' => {
|
97
|
-
positive_votes_count => +1,
|
98
|
-
negative_votes_count => -1,
|
99
|
-
'votes.point' => point_delta
|
100
|
-
}
|
101
|
-
}, {
|
102
|
-
:safe => true
|
103
|
-
})
|
104
|
-
|
105
|
-
elsif options[:unvote]
|
106
|
-
if value == :up
|
107
|
-
positive_voter_ids = 'votes.up'
|
108
|
-
negative_voter_ids = 'votes.down'
|
109
|
-
positive_votes_count = 'votes.up_count'
|
110
|
-
else
|
111
|
-
positive_voter_ids = 'votes.down'
|
112
|
-
negative_voter_ids = 'votes.up'
|
113
|
-
positive_votes_count = 'votes.down_count'
|
114
|
-
end
|
115
|
-
|
116
|
-
# Check if voter_id did a vote with value for votee_id
|
117
|
-
update_result = collection.update({
|
118
|
-
# Validate voter_id did a vote with value for votee_id
|
119
|
-
:_id => votee_id,
|
120
|
-
negative_voter_ids => { '$ne' => voter_id },
|
121
|
-
positive_voter_ids => voter_id
|
122
|
-
}, {
|
123
|
-
# then update
|
124
|
-
'$pull' => { positive_voter_ids => voter_id },
|
125
|
-
'$inc' => {
|
126
|
-
positive_votes_count => -1,
|
127
|
-
'votes.count' => -1,
|
128
|
-
'votes.point' => -voteable[value]
|
129
|
-
}
|
130
|
-
}, {
|
131
|
-
:safe => true
|
132
|
-
})
|
133
|
-
|
134
|
-
else # new vote
|
135
|
-
if value.to_sym == :up
|
136
|
-
positive_voter_ids = 'votes.up'
|
137
|
-
positive_votes_count = 'votes.up_count'
|
138
|
-
else
|
139
|
-
positive_voter_ids = 'votes.down'
|
140
|
-
positive_votes_count = 'votes.down_count'
|
141
|
-
end
|
142
|
-
|
143
|
-
update_result = collection.update({
|
144
|
-
# Validate voter_id did not vote for votee_id yet
|
145
|
-
:_id => votee_id,
|
146
|
-
'votes.up' => { '$ne' => voter_id },
|
147
|
-
'votes.down' => { '$ne' => voter_id }
|
148
|
-
}, {
|
149
|
-
# then update
|
150
|
-
'$push' => { positive_voter_ids => voter_id },
|
151
|
-
'$inc' => {
|
152
|
-
'votes.count' => +1,
|
153
|
-
positive_votes_count => +1,
|
154
|
-
'votes.point' => voteable[value] }
|
155
|
-
}, {
|
156
|
-
:safe => true
|
157
|
-
})
|
158
|
-
end
|
159
|
-
|
160
|
-
# Only update parent class if votee is updated successfully
|
161
|
-
successed = ( update_result['err'] == nil and
|
162
|
-
update_result['updatedExisting'] == true and
|
163
|
-
update_result['n'] == 1 )
|
164
|
-
|
165
|
-
if successed
|
166
|
-
VOTEABLE[klass].each do |class_name, voteable|
|
167
|
-
# For other class in VOTEABLE options, if is parent of current class
|
168
|
-
next unless relation_metadata = relations[class_name.underscore]
|
169
|
-
next unless votee ||= options[:votee] || find(options[:votee_id])
|
170
|
-
# If can find current votee foreign_key value for that class
|
171
|
-
next unless foreign_key_value = votee.read_attribute(relation_metadata.foreign_key)
|
172
|
-
|
173
|
-
inc_options = {}
|
174
|
-
|
175
|
-
if options[:revote]
|
176
|
-
if value == :up
|
177
|
-
inc_options['votes.point'] = voteable[:up] - voteable[:down]
|
178
|
-
unless voteable[:update_counters] == false
|
179
|
-
inc_options['votes.up_count'] = +1
|
180
|
-
inc_options['votes.down_count'] = -1
|
181
|
-
end
|
182
|
-
else
|
183
|
-
inc_options['votes.point'] = -voteable[:up] + voteable[:down]
|
184
|
-
unless voteable[:update_counters] == false
|
185
|
-
inc_options['votes.up_count'] = -1
|
186
|
-
inc_options['votes.down_count'] = +1
|
187
|
-
end
|
188
|
-
end
|
189
|
-
elsif options[:unvote]
|
190
|
-
inc_options['votes.point'] = -voteable[value]
|
191
|
-
unless voteable[:update_counters] == false
|
192
|
-
inc_options['votes.count'] = -1
|
193
|
-
if value == :up
|
194
|
-
inc_options['votes.up_count'] = -1
|
195
|
-
else
|
196
|
-
inc_options['votes.down_count'] = -1
|
197
|
-
end
|
198
|
-
end
|
199
|
-
else # new vote
|
200
|
-
inc_options['votes.point'] = voteable[value]
|
201
|
-
unless voteable[:update_counters] == false
|
202
|
-
inc_options['votes.count'] = +1
|
203
|
-
if value == :up
|
204
|
-
inc_options['votes.up_count'] = +1
|
205
|
-
else
|
206
|
-
inc_options['votes.down_count'] = +1
|
207
|
-
end
|
208
|
-
end
|
209
|
-
end
|
210
|
-
|
211
|
-
class_name.constantize.collection.update(
|
212
|
-
{ :_id => foreign_key_value },
|
213
|
-
{ '$inc' => inc_options }
|
214
|
-
)
|
215
|
-
end
|
216
|
-
end
|
217
|
-
true
|
218
|
-
end
|
219
50
|
end
|
220
|
-
|
51
|
+
|
221
52
|
# Make a vote on this votee
|
222
53
|
#
|
223
54
|
# @param [Hash] options a hash containings:
|
224
55
|
# - :voter_id: the voter document id
|
225
56
|
# - :value: vote :up or vote :down
|
57
|
+
# - :revote: change from vote up to vote down
|
58
|
+
# - :unvote: unvote the vote value (:up or :down)
|
226
59
|
def vote(options)
|
227
|
-
options[:votee_id]
|
228
|
-
options[:votee]
|
60
|
+
options[:votee_id] = id
|
61
|
+
options[:votee] = self
|
62
|
+
options[:voter_id] ||= options[:voter].id
|
229
63
|
|
230
64
|
if options[:unvote]
|
231
65
|
options[:value] ||= vote_value(options[:voter_id])
|
232
66
|
else
|
233
|
-
options[:revote] ||=
|
67
|
+
options[:revote] ||= vote_value(options[:voter_id]).present?
|
234
68
|
end
|
235
69
|
|
236
70
|
self.class.vote(options)
|
@@ -240,19 +74,44 @@ module Mongoid
|
|
240
74
|
#
|
241
75
|
# @param [Mongoid Object, BSON::ObjectId] voter is Mongoid object or the id of the voter who made the vote
|
242
76
|
def vote_value(voter)
|
243
|
-
voter_id = voter.is_a?(BSON::ObjectId) ? voter : voter.
|
77
|
+
voter_id = voter.is_a?(BSON::ObjectId) ? voter : voter.id
|
244
78
|
return :up if up_voter_ids.include?(voter_id)
|
245
79
|
return :down if down_voter_ids.include?(voter_id)
|
246
80
|
end
|
81
|
+
|
82
|
+
def voted_by?(voter)
|
83
|
+
!!vote_value(voter)
|
84
|
+
end
|
247
85
|
|
248
86
|
# Array of up voter ids
|
249
87
|
def up_voter_ids
|
250
88
|
votes.try(:[], 'up') || []
|
251
89
|
end
|
252
|
-
|
90
|
+
|
253
91
|
# Array of down voter ids
|
254
92
|
def down_voter_ids
|
255
93
|
votes.try(:[], 'down') || []
|
256
94
|
end
|
95
|
+
|
96
|
+
# Get the number of up votes
|
97
|
+
def up_votes_count
|
98
|
+
votes.try(:[], 'up_count') || 0
|
99
|
+
end
|
100
|
+
|
101
|
+
# Get the number of down votes
|
102
|
+
def down_votes_count
|
103
|
+
votes.try(:[], 'down_count') || 0
|
104
|
+
end
|
105
|
+
|
106
|
+
# Get the number of votes
|
107
|
+
def votes_count
|
108
|
+
votes.try(:[], 'count') || 0
|
109
|
+
end
|
110
|
+
|
111
|
+
# Get the votes point
|
112
|
+
def votes_point
|
113
|
+
votes.try(:[], 'point') || 0
|
114
|
+
end
|
115
|
+
|
257
116
|
end
|
258
117
|
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
module Mongoid
|
2
|
+
module Voteable
|
3
|
+
module Tasks
|
4
|
+
|
5
|
+
# Set counters and point to 0 for uninitialized voteable objects
|
6
|
+
# in order sort and query
|
7
|
+
def self.init_stats(log = false)
|
8
|
+
VOTEABLE.each do |class_name, voteable|
|
9
|
+
klass = class_name.constantize
|
10
|
+
klass_voteable = voteable[class_name]
|
11
|
+
puts "Init stats for #{class_name}" if log
|
12
|
+
klass.collection.update({:votes => nil}, {
|
13
|
+
'$set' => { :votes => Votes::DEFAULT_ATTRIBUTES }
|
14
|
+
}, {
|
15
|
+
:safe => true,
|
16
|
+
:multi => true
|
17
|
+
})
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Re-generate vote counters and vote points
|
22
|
+
def self.remake_stats(log = false)
|
23
|
+
remake_stats_for_all_voteable_classes(log)
|
24
|
+
update_parent_stats(log)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Convert votes from from version < 0.7.0 to new data store
|
28
|
+
def self.migrate_old_votes(log = false)
|
29
|
+
VOTEABLE.each do |class_name, voteable|
|
30
|
+
klass = class_name.constantize
|
31
|
+
klass_voteable = voteable[class_name]
|
32
|
+
puts "* Migrating old vote data for #{class_name} ..." if log
|
33
|
+
migrate_old_votes_for(klass, klass_voteable)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.migrate_old_votes_for(klass, voteable)
|
38
|
+
klass.all.each do |doc|
|
39
|
+
# Version 0.6.x use very short field names (u, d, uc, dc, c, p) to minimize
|
40
|
+
# votes storage but it's not human friendly
|
41
|
+
# Version >= 0.7.0 use readable field names (up, down, up_count, down_count,
|
42
|
+
# count, point)
|
43
|
+
votes = doc['votes'] || doc['voteable'] || {}
|
44
|
+
|
45
|
+
up_voter_ids = votes['u'] || votes['up'] ||
|
46
|
+
votes['up_voter_ids'] || doc['up_voter_ids'] || []
|
47
|
+
|
48
|
+
down_voter_ids = votes['d'] || votes['down'] ||
|
49
|
+
votes['down_voter_ids'] || doc['down_voter_ids'] || []
|
50
|
+
|
51
|
+
up_count = up_voter_ids.size
|
52
|
+
down_count = down_voter_ids.size
|
53
|
+
|
54
|
+
klass.collection.update({ :_id => doc.id }, {
|
55
|
+
'$set' => {
|
56
|
+
'votes' => {
|
57
|
+
'up' => up_voter_ids,
|
58
|
+
'down' => down_voter_ids,
|
59
|
+
'up_count' => up_count,
|
60
|
+
'down_count' => down_count,
|
61
|
+
'count' => up_count + down_count,
|
62
|
+
'point' => voteable[:up]*up_count + voteable[:down]*down_count
|
63
|
+
}
|
64
|
+
},
|
65
|
+
'$unset' => {
|
66
|
+
'up_voter_ids' => true,
|
67
|
+
'down_voter_ids' => true,
|
68
|
+
'votes_count' => true,
|
69
|
+
'votes_point' => true,
|
70
|
+
'voteable' => true
|
71
|
+
}
|
72
|
+
}, { :safe => true })
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
def self.remake_stats_for_all_voteable_classes(log)
|
78
|
+
VOTEABLE.each do |class_name, voteable|
|
79
|
+
klass = class_name.constantize
|
80
|
+
klass_voteable = voteable[class_name]
|
81
|
+
puts "Generating stats for #{class_name}" if log
|
82
|
+
klass.all.each{ |doc|
|
83
|
+
remake_stats_for(doc, klass_voteable)
|
84
|
+
}
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
def self.remake_stats_for(doc, voteable)
|
90
|
+
up_count = doc.up_voter_ids.length
|
91
|
+
down_count = doc.down_voter_ids.length
|
92
|
+
|
93
|
+
doc.update_attributes(
|
94
|
+
'votes.up_count' => up_count,
|
95
|
+
'votes.down_count' => down_count,
|
96
|
+
'votes.count' => up_count + down_count,
|
97
|
+
'votes.point' => voteable[:up]*up_count + voteable[:down]*down_count
|
98
|
+
)
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
def self.update_parent_stats(log)
|
103
|
+
VOTEABLE.each do |class_name, voteable|
|
104
|
+
klass = class_name.constantize
|
105
|
+
voteable.each do |parent_class_name, parent_voteable|
|
106
|
+
relation_metadata = klass.relations[parent_class_name.underscore]
|
107
|
+
if relation_metadata
|
108
|
+
parent_class = parent_class_name.constantize
|
109
|
+
foreign_key = relation_metadata.foreign_key
|
110
|
+
puts "Updating stats for #{class_name} > #{parent_class_name}" if log
|
111
|
+
klass.all.each{ |doc|
|
112
|
+
update_parent_stats_for(doc, parent_class, foreign_key, parent_voteable)
|
113
|
+
}
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
def self.update_parent_stats_for(doc, parent_class, foreign_key, voteable)
|
121
|
+
parent_id = doc.read_attribute(foreign_key.to_sym)
|
122
|
+
if parent_id
|
123
|
+
up_count = doc.up_voter_ids.length
|
124
|
+
down_count = doc.down_voter_ids.length
|
125
|
+
|
126
|
+
return if up_count == 0 && down_count == 0
|
127
|
+
|
128
|
+
inc_options = {
|
129
|
+
'votes.point' => voteable[:up]*up_count + voteable[:down]*down_count
|
130
|
+
}
|
131
|
+
|
132
|
+
unless voteable[:update_counters] == false
|
133
|
+
inc_options.merge!(
|
134
|
+
'votes.count' => up_count + down_count,
|
135
|
+
'votes.up_count' => up_count,
|
136
|
+
'votes.down_count' => down_count
|
137
|
+
)
|
138
|
+
end
|
139
|
+
|
140
|
+
parent_class.collection.update(
|
141
|
+
{ '_id' => parent_id },
|
142
|
+
{ '$inc' => inc_options },
|
143
|
+
{ :safe => true }
|
144
|
+
)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
private_class_method :migrate_old_votes_for,
|
149
|
+
:remake_stats_for,
|
150
|
+
:remake_stats_for_all_voteable_classes,
|
151
|
+
:update_parent_stats,
|
152
|
+
:update_parent_stats_for
|
153
|
+
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
@@ -3,56 +3,16 @@ module Mongoid
|
|
3
3
|
|
4
4
|
class Votes
|
5
5
|
include Mongoid::Document
|
6
|
+
|
6
7
|
field :up, :type => Array, :default => []
|
7
8
|
field :down, :type => Array, :default => []
|
8
9
|
field :up_count, :type => Integer, :default => 0
|
9
10
|
field :down_count, :type => Integer, :default => 0
|
10
11
|
field :count, :type => Integer, :default => 0
|
11
12
|
field :point, :type => Integer, :default => 0
|
12
|
-
end
|
13
|
-
|
14
|
-
VOTES_DEFAULT_ATTRIBUTES = Votes.new.attributes
|
15
|
-
VOTES_DEFAULT_ATTRIBUTES.delete('_id')
|
16
|
-
|
17
|
-
def self.migrate_old_votes(log = false)
|
18
|
-
VOTEABLE.each do |class_name, voteable|
|
19
|
-
klass = class_name.constantize
|
20
|
-
klass_voteable = voteable[class_name]
|
21
|
-
puts "* Migrating old vote data for #{class_name} ..." if log
|
22
13
|
|
23
|
-
|
24
|
-
|
25
|
-
# votes storage but it's not human friendly
|
26
|
-
# Version >= 0.7.0 use readable field names (up, down, up_count, down_count,
|
27
|
-
# count, point)
|
28
|
-
votes = doc['votes'] || doc['voteable'] || {}
|
29
|
-
up_voter_ids = votes['u'] || votes['up'] || votes['up_voter_ids'] || doc['up_voter_ids'] || []
|
30
|
-
down_voter_ids = votes['d'] || votes['down'] || votes['down_voter_ids'] || doc['down_voter_ids'] || []
|
31
|
-
|
32
|
-
up_count = up_voter_ids.size
|
33
|
-
down_count = down_voter_ids.size
|
34
|
-
|
35
|
-
klass.collection.update({ :_id => doc.id }, {
|
36
|
-
'$set' => {
|
37
|
-
'votes' => {
|
38
|
-
'up' => up_voter_ids,
|
39
|
-
'down' => down_voter_ids,
|
40
|
-
'up_count' => up_count,
|
41
|
-
'down_count' => down_count,
|
42
|
-
'count' => up_count + down_count,
|
43
|
-
'point' => klass_voteable[:up]*up_count + klass_voteable[:down]*down_count
|
44
|
-
}
|
45
|
-
},
|
46
|
-
'$unset' => {
|
47
|
-
'up_voter_ids' => true,
|
48
|
-
'down_voter_ids' => true,
|
49
|
-
'votes_count' => true,
|
50
|
-
'votes_point' => true,
|
51
|
-
'voteable' => true
|
52
|
-
}
|
53
|
-
})
|
54
|
-
end
|
55
|
-
end
|
14
|
+
DEFAULT_ATTRIBUTES = new.attributes
|
15
|
+
DEFAULT_ATTRIBUTES.delete('_id')
|
56
16
|
end
|
57
17
|
|
58
18
|
end
|
@@ -0,0 +1,202 @@
|
|
1
|
+
module Mongoid
|
2
|
+
module Voteable
|
3
|
+
module Voting
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
# Make a vote on an object of this class
|
8
|
+
#
|
9
|
+
# @param [Hash] options a hash containings:
|
10
|
+
# - :votee_id: the votee document id
|
11
|
+
# - :voter_id: the voter document id
|
12
|
+
# - :value: :up or :down
|
13
|
+
# - :revote: change from vote up to vote down
|
14
|
+
# - :unvote: unvote the vote value (:up or :down)
|
15
|
+
def vote(options)
|
16
|
+
options.symbolize_keys!
|
17
|
+
options[:votee_id] = BSON::ObjectId(options[:votee_id]) if options[:votee_id].is_a?(String)
|
18
|
+
options[:voter_id] = BSON::ObjectId(options[:voter_id]) if options[:voter_id].is_a?(String)
|
19
|
+
options[:value] = options[:value].to_sym
|
20
|
+
options[:voteable] = VOTEABLE[name][name]
|
21
|
+
|
22
|
+
update_parents = true
|
23
|
+
|
24
|
+
if options[:voteable]
|
25
|
+
update_result = if options[:revote]
|
26
|
+
revote(options)
|
27
|
+
elsif options[:unvote]
|
28
|
+
unvote(options)
|
29
|
+
else
|
30
|
+
new_vote(options)
|
31
|
+
end
|
32
|
+
# Only update parent votes if votee is updated successfully
|
33
|
+
update_parents = ( update_result['err'] == nil and
|
34
|
+
update_result['updatedExisting'] == true and
|
35
|
+
update_result['n'] == 1 )
|
36
|
+
end
|
37
|
+
|
38
|
+
update_parent_votes(options) if update_parents
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
private
|
43
|
+
def new_vote(options)
|
44
|
+
if options[:value] == :up
|
45
|
+
positive_voter_ids = 'votes.up'
|
46
|
+
positive_votes_count = 'votes.up_count'
|
47
|
+
else
|
48
|
+
positive_voter_ids = 'votes.down'
|
49
|
+
positive_votes_count = 'votes.down_count'
|
50
|
+
end
|
51
|
+
|
52
|
+
collection.update({
|
53
|
+
# Validate voter_id did not vote for votee_id yet
|
54
|
+
:_id => options[:votee_id],
|
55
|
+
'votes.up' => { '$ne' => options[:voter_id] },
|
56
|
+
'votes.down' => { '$ne' => options[:voter_id] }
|
57
|
+
}, {
|
58
|
+
# then update
|
59
|
+
'$push' => { positive_voter_ids => options[:voter_id] },
|
60
|
+
'$inc' => {
|
61
|
+
'votes.count' => +1,
|
62
|
+
positive_votes_count => +1,
|
63
|
+
'votes.point' => options[:voteable][options[:value]] }
|
64
|
+
}, {
|
65
|
+
:safe => true
|
66
|
+
})
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
def revote(options)
|
71
|
+
if options[:value] == :up
|
72
|
+
positive_voter_ids = 'votes.up'
|
73
|
+
negative_voter_ids = 'votes.down'
|
74
|
+
positive_votes_count = 'votes.up_count'
|
75
|
+
negative_votes_count = 'votes.down_count'
|
76
|
+
point_delta = options[:voteable][:up] - options[:voteable][:down]
|
77
|
+
else
|
78
|
+
positive_voter_ids = 'votes.down'
|
79
|
+
negative_voter_ids = 'votes.up'
|
80
|
+
positive_votes_count = 'votes.down_count'
|
81
|
+
negative_votes_count = 'votes.up_count'
|
82
|
+
point_delta = -options[:voteable][:up] + options[:voteable][:down]
|
83
|
+
end
|
84
|
+
|
85
|
+
collection.update({
|
86
|
+
# Validate voter_id did a vote with value for votee_id
|
87
|
+
:_id => options[:votee_id],
|
88
|
+
positive_voter_ids => { '$ne' => options[:voter_id] },
|
89
|
+
negative_voter_ids => options[:voter_id]
|
90
|
+
}, {
|
91
|
+
# then update
|
92
|
+
'$pull' => { negative_voter_ids => options[:voter_id] },
|
93
|
+
'$push' => { positive_voter_ids => options[:voter_id] },
|
94
|
+
'$inc' => {
|
95
|
+
positive_votes_count => +1,
|
96
|
+
negative_votes_count => -1,
|
97
|
+
'votes.point' => point_delta
|
98
|
+
}
|
99
|
+
}, {
|
100
|
+
:safe => true
|
101
|
+
})
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
def unvote(options)
|
106
|
+
if options[:value] == :up
|
107
|
+
positive_voter_ids = 'votes.up'
|
108
|
+
negative_voter_ids = 'votes.down'
|
109
|
+
positive_votes_count = 'votes.up_count'
|
110
|
+
else
|
111
|
+
positive_voter_ids = 'votes.down'
|
112
|
+
negative_voter_ids = 'votes.up'
|
113
|
+
positive_votes_count = 'votes.down_count'
|
114
|
+
end
|
115
|
+
|
116
|
+
# Check if voter_id did a vote with value for votee_id
|
117
|
+
update_result = collection.update({
|
118
|
+
# Validate voter_id did a vote with value for votee_id
|
119
|
+
:_id => options[:votee_id],
|
120
|
+
negative_voter_ids => { '$ne' => options[:voter_id] },
|
121
|
+
positive_voter_ids => options[:voter_id]
|
122
|
+
}, {
|
123
|
+
# then update
|
124
|
+
'$pull' => { positive_voter_ids => options[:voter_id] },
|
125
|
+
'$inc' => {
|
126
|
+
positive_votes_count => -1,
|
127
|
+
'votes.count' => -1,
|
128
|
+
'votes.point' => -options[:voteable][options[:value]]
|
129
|
+
}
|
130
|
+
}, {
|
131
|
+
:safe => true
|
132
|
+
})
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
def update_parent_votes(options)
|
137
|
+
value = options[:value]
|
138
|
+
votee ||= options[:votee]
|
139
|
+
|
140
|
+
VOTEABLE[name].each do |class_name, voteable|
|
141
|
+
# For other class in VOTEABLE options, if is parent of current class
|
142
|
+
next unless relation_metadata = relations[class_name.underscore]
|
143
|
+
votee ||= find(options[:votee_id])
|
144
|
+
# If can find current votee foreign_key value for that class
|
145
|
+
next unless foreign_key_value = votee.read_attribute(relation_metadata.foreign_key)
|
146
|
+
|
147
|
+
class_name.constantize.collection.update(
|
148
|
+
{ '_id' => foreign_key_value },
|
149
|
+
{ '$inc' => parent_inc_options(value, voteable, options) }
|
150
|
+
)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
|
155
|
+
def parent_inc_options(value, voteable, options)
|
156
|
+
inc_options = {}
|
157
|
+
|
158
|
+
if options[:revote]
|
159
|
+
if value == :up
|
160
|
+
inc_options['votes.point'] = voteable[:up] - voteable[:down]
|
161
|
+
unless voteable[:update_counters] == false
|
162
|
+
inc_options['votes.up_count'] = +1
|
163
|
+
inc_options['votes.down_count'] = -1
|
164
|
+
end
|
165
|
+
else
|
166
|
+
inc_options['votes.point'] = -voteable[:up] + voteable[:down]
|
167
|
+
unless voteable[:update_counters] == false
|
168
|
+
inc_options['votes.up_count'] = -1
|
169
|
+
inc_options['votes.down_count'] = +1
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
elsif options[:unvote]
|
174
|
+
inc_options['votes.point'] = -voteable[value]
|
175
|
+
unless voteable[:update_counters] == false
|
176
|
+
inc_options['votes.count'] = -1
|
177
|
+
if value == :up
|
178
|
+
inc_options['votes.up_count'] = -1
|
179
|
+
else
|
180
|
+
inc_options['votes.down_count'] = -1
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
else # new vote
|
185
|
+
inc_options['votes.point'] = voteable[value]
|
186
|
+
unless voteable[:update_counters] == false
|
187
|
+
inc_options['votes.count'] = +1
|
188
|
+
if value == :up
|
189
|
+
inc_options['votes.up_count'] = +1
|
190
|
+
else
|
191
|
+
inc_options['votes.down_count'] = +1
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
inc_options
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
@@ -9,12 +9,12 @@ module Mongoid
|
|
9
9
|
def voted?(options)
|
10
10
|
unless options.is_a?(Hash)
|
11
11
|
votee_class = options.class
|
12
|
-
votee_id = options.
|
12
|
+
votee_id = options.id
|
13
13
|
else
|
14
14
|
votee = options[:votee]
|
15
15
|
if votee
|
16
16
|
votee_class = votee.class
|
17
|
-
votee_id = votee.
|
17
|
+
votee_id = votee.id
|
18
18
|
else
|
19
19
|
votee_class = options[:votee_type].classify.constantize
|
20
20
|
votee_id = options[:votee_id]
|
@@ -64,7 +64,7 @@ module Mongoid
|
|
64
64
|
end
|
65
65
|
|
66
66
|
if votee
|
67
|
-
options[:votee_id] = votee.
|
67
|
+
options[:votee_id] = votee.id
|
68
68
|
votee_class = votee.class
|
69
69
|
else
|
70
70
|
votee_class = options[:votee_type].classify.constantize
|
@@ -77,7 +77,8 @@ module Mongoid
|
|
77
77
|
options[:revote] = options.has_key?(:revote) ? !options[:revote].blank? : voted?(options)
|
78
78
|
end
|
79
79
|
|
80
|
-
options[:
|
80
|
+
options[:voter] = self
|
81
|
+
options[:voter_id] = id
|
81
82
|
|
82
83
|
( votee || votee_class ).vote(options)
|
83
84
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -2,8 +2,6 @@ require "spec_helper"
|
|
2
2
|
|
3
3
|
describe Mongoid::Voteable do
|
4
4
|
before :all do
|
5
|
-
Mongoid::database.connection.drop_database(Mongoid::database.name)
|
6
|
-
|
7
5
|
@post1 = Post.create!
|
8
6
|
@post2 = Post.create!
|
9
7
|
|
@@ -38,9 +36,9 @@ describe Mongoid::Voteable do
|
|
38
36
|
Post.voted_by(@user2).should be_empty
|
39
37
|
end
|
40
38
|
|
41
|
-
it 'test
|
42
|
-
@post1.votes.should == Mongoid::Voteable::
|
43
|
-
@post2.votes.should == Mongoid::Voteable::
|
39
|
+
it 'test init stats' do
|
40
|
+
@post1.votes.should == Mongoid::Voteable::Votes::DEFAULT_ATTRIBUTES
|
41
|
+
@post2.votes.should == Mongoid::Voteable::Votes::DEFAULT_ATTRIBUTES
|
44
42
|
|
45
43
|
@post1.votes = nil
|
46
44
|
@post1.save
|
@@ -48,17 +46,17 @@ describe Mongoid::Voteable do
|
|
48
46
|
@post2.votes = nil
|
49
47
|
@post2.save
|
50
48
|
|
51
|
-
Mongoid::Voteable::
|
49
|
+
Mongoid::Voteable::Tasks.init_stats
|
52
50
|
|
53
51
|
@post1.reload
|
54
52
|
@post2.reload
|
55
53
|
|
56
|
-
@post1.votes.should == Mongoid::Voteable::
|
57
|
-
@post2.votes.should == Mongoid::Voteable::
|
54
|
+
@post1.votes.should == Mongoid::Voteable::Votes::DEFAULT_ATTRIBUTES
|
55
|
+
@post2.votes.should == Mongoid::Voteable::Votes::DEFAULT_ATTRIBUTES
|
58
56
|
end
|
59
57
|
|
60
58
|
it 'revote has no effect' do
|
61
|
-
|
59
|
+
@post1.vote(:revote => true, :voter => @user1, :value => 'up')
|
62
60
|
@post1.reload
|
63
61
|
|
64
62
|
@post1.up_votes_count.should == 0
|
@@ -89,7 +87,9 @@ describe Mongoid::Voteable do
|
|
89
87
|
@post1.votes_point.should == 1
|
90
88
|
|
91
89
|
@post1.vote_value(@user1).should == :up
|
90
|
+
@post1.should be_voted_by(@user1)
|
92
91
|
@post1.vote_value(@user2.id).should be_nil
|
92
|
+
@post1.should_not be_voted_by(@user2.id)
|
93
93
|
|
94
94
|
Post.voted_by(@user1).to_a.should == [ @post1 ]
|
95
95
|
Post.voted_by(@user2).to_a.should be_empty
|
@@ -131,7 +131,7 @@ describe Mongoid::Voteable do
|
|
131
131
|
context 'user1 change vote on post1 from up to down' do
|
132
132
|
before :all do
|
133
133
|
Post.vote(:revote => true, :votee_id => @post1.id, :voter_id => @user1.id, :value => :down)
|
134
|
-
Mongoid::Voteable::
|
134
|
+
Mongoid::Voteable::Tasks.remake_stats
|
135
135
|
@post1.reload
|
136
136
|
end
|
137
137
|
|
@@ -171,7 +171,7 @@ describe Mongoid::Voteable do
|
|
171
171
|
context 'user1 change vote on post2 from down to up' do
|
172
172
|
before :all do
|
173
173
|
Post.vote(:revote => true, :votee_id => @post2.id.to_s, :voter_id => @user1.id.to_s, :value => :up)
|
174
|
-
Mongoid::Voteable::
|
174
|
+
Mongoid::Voteable::Tasks.remake_stats
|
175
175
|
@post2.reload
|
176
176
|
end
|
177
177
|
|
@@ -247,7 +247,7 @@ describe Mongoid::Voteable do
|
|
247
247
|
context "user1 unvote on post1" do
|
248
248
|
before(:all) do
|
249
249
|
@post1.vote(:voter_id => @user1.id, :votee_id => @post1.id, :unvote => true)
|
250
|
-
Mongoid::Voteable::
|
250
|
+
Mongoid::Voteable::Tasks.remake_stats
|
251
251
|
@post1.reload
|
252
252
|
end
|
253
253
|
|
@@ -283,7 +283,7 @@ describe Mongoid::Voteable do
|
|
283
283
|
context "user1 unvote on comment" do
|
284
284
|
before(:all) do
|
285
285
|
@user1.unvote(@comment)
|
286
|
-
Mongoid::Voteable::
|
286
|
+
Mongoid::Voteable::Tasks.remake_stats
|
287
287
|
@comment.reload
|
288
288
|
@post2.reload
|
289
289
|
end
|
@@ -303,7 +303,7 @@ describe Mongoid::Voteable do
|
|
303
303
|
|
304
304
|
context 'final' do
|
305
305
|
it "test remake stats" do
|
306
|
-
Mongoid::Voteable::
|
306
|
+
Mongoid::Voteable::Tasks.remake_stats
|
307
307
|
|
308
308
|
@post1.up_votes_count.should == 0
|
309
309
|
@post1.down_votes_count.should == 1
|
data/voteable_mongoid.gemspec
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: voteable_mongoid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 1
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 7
|
9
|
-
-
|
10
|
-
version: 0.7.
|
9
|
+
- 1
|
10
|
+
version: 0.7.1
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Alex Nguyen
|
@@ -15,8 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-04-
|
19
|
-
default_executable:
|
18
|
+
date: 2011-04-03 00:00:00 Z
|
20
19
|
dependencies:
|
21
20
|
- !ruby/object:Gem::Dependency
|
22
21
|
name: mongoid
|
@@ -48,34 +47,6 @@ dependencies:
|
|
48
47
|
version: "0"
|
49
48
|
type: :development
|
50
49
|
version_requirements: *id002
|
51
|
-
- !ruby/object:Gem::Dependency
|
52
|
-
name: bson_ext
|
53
|
-
prerelease: false
|
54
|
-
requirement: &id003 !ruby/object:Gem::Requirement
|
55
|
-
none: false
|
56
|
-
requirements:
|
57
|
-
- - ">="
|
58
|
-
- !ruby/object:Gem::Version
|
59
|
-
hash: 3
|
60
|
-
segments:
|
61
|
-
- 0
|
62
|
-
version: "0"
|
63
|
-
type: :development
|
64
|
-
version_requirements: *id003
|
65
|
-
- !ruby/object:Gem::Dependency
|
66
|
-
name: watchr
|
67
|
-
prerelease: false
|
68
|
-
requirement: &id004 !ruby/object:Gem::Requirement
|
69
|
-
none: false
|
70
|
-
requirements:
|
71
|
-
- - ">="
|
72
|
-
- !ruby/object:Gem::Version
|
73
|
-
hash: 3
|
74
|
-
segments:
|
75
|
-
- 0
|
76
|
-
version: "0"
|
77
|
-
type: :development
|
78
|
-
version_requirements: *id004
|
79
50
|
description: Add Up / Down Voting for Mongoid (built for speed by using one Mongodb update-in-place for each collection when provided enough information)
|
80
51
|
email:
|
81
52
|
- alex@vinova.sg
|
@@ -98,8 +69,9 @@ files:
|
|
98
69
|
- lib/voteable_mongoid/railties/database.rake
|
99
70
|
- lib/voteable_mongoid/version.rb
|
100
71
|
- lib/voteable_mongoid/voteable.rb
|
101
|
-
- lib/voteable_mongoid/voteable/
|
72
|
+
- lib/voteable_mongoid/voteable/tasks.rb
|
102
73
|
- lib/voteable_mongoid/voteable/votes.rb
|
74
|
+
- lib/voteable_mongoid/voteable/voting.rb
|
103
75
|
- lib/voteable_mongoid/voter.rb
|
104
76
|
- spec/.rspec
|
105
77
|
- spec/models/comment.rb
|
@@ -109,7 +81,6 @@ files:
|
|
109
81
|
- spec/voteable_mongoid/voteable_spec.rb
|
110
82
|
- spec/voteable_mongoid/voter_spec.rb
|
111
83
|
- voteable_mongoid.gemspec
|
112
|
-
has_rdoc: true
|
113
84
|
homepage: https://github.com/vinova/voteable_mongoid
|
114
85
|
licenses: []
|
115
86
|
|
@@ -139,7 +110,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
139
110
|
requirements: []
|
140
111
|
|
141
112
|
rubyforge_project: voteable_mongoid
|
142
|
-
rubygems_version: 1.
|
113
|
+
rubygems_version: 1.7.1
|
143
114
|
signing_key:
|
144
115
|
specification_version: 3
|
145
116
|
summary: Add Up / Down Voting for Mongoid
|
@@ -1,115 +0,0 @@
|
|
1
|
-
module Mongoid
|
2
|
-
module Voteable
|
3
|
-
module Stats
|
4
|
-
extend ActiveSupport::Concern
|
5
|
-
|
6
|
-
# Get the number of up votes
|
7
|
-
def up_votes_count
|
8
|
-
votes.try(:[], 'up_count') || 0
|
9
|
-
end
|
10
|
-
|
11
|
-
# Get the number of down votes
|
12
|
-
def down_votes_count
|
13
|
-
votes.try(:[], 'down_count') || 0
|
14
|
-
end
|
15
|
-
|
16
|
-
# Get the number of votes
|
17
|
-
def votes_count
|
18
|
-
votes.try(:[], 'count') || 0
|
19
|
-
end
|
20
|
-
|
21
|
-
# Get the votes point
|
22
|
-
def votes_point
|
23
|
-
votes.try(:[], 'point') || 0
|
24
|
-
end
|
25
|
-
|
26
|
-
def self.init(log = false)
|
27
|
-
VOTEABLE.each do |class_name, voteable|
|
28
|
-
klass = class_name.constantize
|
29
|
-
klass_voteable = voteable[class_name]
|
30
|
-
puts "Init stats for #{class_name}" if log
|
31
|
-
klass.collection.update({:votes => nil}, {
|
32
|
-
'$set' => { :votes => VOTES_DEFAULT_ATTRIBUTES }
|
33
|
-
}, {
|
34
|
-
:safe => true,
|
35
|
-
:multi => true
|
36
|
-
})
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
# Re-generate vote counters and vote points
|
41
|
-
def self.remake(log = false)
|
42
|
-
remake_stats(log)
|
43
|
-
update_parent_stats(log)
|
44
|
-
end
|
45
|
-
|
46
|
-
def self.remake_stats(log)
|
47
|
-
VOTEABLE.each do |class_name, voteable|
|
48
|
-
klass = class_name.constantize
|
49
|
-
klass_voteable = voteable[class_name]
|
50
|
-
puts "Generating stats for #{class_name}" if log
|
51
|
-
klass.all.each{ |doc|
|
52
|
-
doc.remake_stats(klass_voteable)
|
53
|
-
}
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def remake_stats(voteable)
|
58
|
-
up_count = up_voter_ids.length
|
59
|
-
down_count = down_voter_ids.length
|
60
|
-
|
61
|
-
update_attributes(
|
62
|
-
'votes.up_count' => up_count,
|
63
|
-
'votes.down_count' => down_count,
|
64
|
-
'votes.count' => up_count + down_count,
|
65
|
-
'votes.point' => voteable[:up]*up_count + voteable[:down]*down_count
|
66
|
-
)
|
67
|
-
end
|
68
|
-
|
69
|
-
def self.update_parent_stats(log)
|
70
|
-
VOTEABLE.each do |class_name, voteable|
|
71
|
-
klass = class_name.constantize
|
72
|
-
voteable.each do |parent_class_name, parent_voteable|
|
73
|
-
relation_metadata = klass.relations[parent_class_name.underscore]
|
74
|
-
if relation_metadata
|
75
|
-
parent_class = parent_class_name.constantize
|
76
|
-
foreign_key = relation_metadata.foreign_key
|
77
|
-
puts "Updating stats for #{class_name} > #{parent_class_name}" if log
|
78
|
-
klass.all.each{ |doc|
|
79
|
-
doc.update_parent_stats(parent_class, foreign_key, parent_voteable)
|
80
|
-
}
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def update_parent_stats(parent_class, foreign_key, voteable)
|
87
|
-
parent_id = read_attribute(foreign_key.to_sym)
|
88
|
-
if parent_id
|
89
|
-
up_count = up_voter_ids.length
|
90
|
-
down_count = down_voter_ids.length
|
91
|
-
|
92
|
-
return if up_count == 0 && down_count == 0
|
93
|
-
|
94
|
-
inc_options = {
|
95
|
-
'votes.point' => voteable[:up]*up_count + voteable[:down]*down_count
|
96
|
-
}
|
97
|
-
|
98
|
-
unless voteable[:update_counters] == false
|
99
|
-
inc_options.merge!(
|
100
|
-
'votes.count' => up_count + down_count,
|
101
|
-
'votes.up_count' => up_count,
|
102
|
-
'votes.down_count' => down_count
|
103
|
-
)
|
104
|
-
end
|
105
|
-
|
106
|
-
parent_class.collection.update(
|
107
|
-
{ '_id' => parent_id },
|
108
|
-
{ '$inc' => inc_options }
|
109
|
-
)
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
end
|
114
|
-
end
|
115
|
-
end
|