rs_voteable_mongo 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.watchr +23 -0
- data/CHANGELOG.rdoc +87 -0
- data/Gemfile +4 -0
- data/README.rdoc +212 -0
- data/Rakefile +10 -0
- data/TODO +17 -0
- data/lib/rs_votable_mongo.rb +1 -0
- data/lib/voteable_mongo/helpers.rb +18 -0
- data/lib/voteable_mongo/integrations/mongoid.rb +31 -0
- data/lib/voteable_mongo/railtie.rb +19 -0
- data/lib/voteable_mongo/railties/database.rake +18 -0
- data/lib/voteable_mongo/tasks.rb +152 -0
- data/lib/voteable_mongo/version.rb +3 -0
- data/lib/voteable_mongo/voteable.rb +203 -0
- data/lib/voteable_mongo/voter.rb +91 -0
- data/lib/voteable_mongo/voting.rb +214 -0
- data/lib/voteable_mongo.rb +8 -0
- data/spec/.rspec +1 -0
- data/spec/mongoid/models/category.rb +10 -0
- data/spec/mongoid/models/comment.rb +13 -0
- data/spec/mongoid/models/post.rb +17 -0
- data/spec/mongoid/models/user.rb +4 -0
- data/spec/spec_helper.rb +30 -0
- data/spec/voteable_mongo/tasks_spec.rb +34 -0
- data/spec/voteable_mongo/voteable_spec.rb +436 -0
- data/spec/voteable_mongo/voter_spec.rb +150 -0
- data/voteable_mongo.gemspec +24 -0
- metadata +118 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 53b6e38a882ecb70db25219bd78159c08e226bbf
|
4
|
+
data.tar.gz: 10a70421818708ecc107023550022bb31d1db72d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 167fc70a55bfc367fd4a441a8a7123267b0fc62c5aa58f0a58a20e5824e2847a47f45b1ee01cb5e95c93c2026e187fa1a634c26fe8516f5c09b5694578ad9dad
|
7
|
+
data.tar.gz: 6ca2f3486223cfc1554a2587b42d5884b830f49b50b94917822bf811e1d7ec6bf2edebe082b9198362b6339f8cb70bd57baaa82b42869a20814df60fe530ab6f
|
data/.gitignore
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
votable_mongo
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-2.0.0-p247
|
data/.watchr
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# vim:set filetype=ruby:
|
2
|
+
def run(cmd)
|
3
|
+
puts cmd
|
4
|
+
system cmd
|
5
|
+
end
|
6
|
+
|
7
|
+
def spec(file)
|
8
|
+
if File.exists?(file)
|
9
|
+
run("rspec #{file}")
|
10
|
+
else
|
11
|
+
puts("Spec: #{file} does not exist.")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
watch("spec/.*/*_spec\.rb") do |match|
|
16
|
+
puts(match[0])
|
17
|
+
spec(match[0])
|
18
|
+
end
|
19
|
+
|
20
|
+
watch("lib/(.*/.*)\.rb") do |match|
|
21
|
+
puts(match[1])
|
22
|
+
spec("spec/#{match[1]}_spec.rb")
|
23
|
+
end
|
data/CHANGELOG.rdoc
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
== 0.9.3
|
2
|
+
* Support mongoid ~> 2.0, mongo_mapper ~> 0.9
|
3
|
+
|
4
|
+
== 0.9.2
|
5
|
+
* Replace votee_type to votee_class
|
6
|
+
* Bug fixes
|
7
|
+
|
8
|
+
== 0.9.1
|
9
|
+
* Update gem description
|
10
|
+
|
11
|
+
== 0.9.0
|
12
|
+
* Add MongoMapper support
|
13
|
+
* Simplify voting algorithm
|
14
|
+
* vote / revote / unvote always return voteable object (votee)
|
15
|
+
* Tasks module bug fixes
|
16
|
+
|
17
|
+
== 0.8.1
|
18
|
+
* Fix gem release bug
|
19
|
+
|
20
|
+
== 0.8.0
|
21
|
+
* Rename to voteable_mongo to support other MongoDB Object-Document Mappers like MongoMapper
|
22
|
+
* Minor fixes and refactoring
|
23
|
+
|
24
|
+
== 0.7.4
|
25
|
+
* Add Votee#up_voters(VoterClass), Votee#down_voters(VoterClass), Votee#voters(VoterClass)
|
26
|
+
* Add Voter scopes: Voter.up_voted_for(votee), Voter.down_voted_for(votee), Voter.voted_for(votee)
|
27
|
+
* Add voteable ..., :index => true options
|
28
|
+
* Optimization on unvote and revote validations
|
29
|
+
* Fix for :up & :down points are nil in rake tasks
|
30
|
+
|
31
|
+
== 0.7.3
|
32
|
+
* Add :return_votee => true option to vote function to warranty always return voteable object
|
33
|
+
* Add Votee.voted?, Votee.up_voted?, Votee.down_voted?
|
34
|
+
* Update parent for ManyToMany relationship
|
35
|
+
* Refactor
|
36
|
+
|
37
|
+
== 0.7.2
|
38
|
+
* Use Collection#find_and_modify to retrieve updated votes data and parent_ids (don't need an extra query to get parent_ids)
|
39
|
+
|
40
|
+
== 0.7.1
|
41
|
+
* Add votee#voted_by?(voter or voter_id)
|
42
|
+
* Better doc
|
43
|
+
* Refactor & cleanup source code
|
44
|
+
|
45
|
+
== 0.7.0
|
46
|
+
* Use readable field names (up, down, up_count, down_count, count, point) instead of very short field names (u, d, uc, dc, c, p)
|
47
|
+
|
48
|
+
== 0.6.4
|
49
|
+
* Drop Voter#votees, Voter#up_votees, Voter#down_votees in favor of Votee#voted_by(voter), Votee#up_voted_by(voter), Votee#down_voted_by(voter) scopes
|
50
|
+
|
51
|
+
== 0.6.3
|
52
|
+
* Add rake db:mongoid:voteable:migrate_old_votes to migrate vote data created by version < 0.6.0 to new vote data storage
|
53
|
+
|
54
|
+
== 0.6.2
|
55
|
+
* Fix bug: use before_create instead of after_after_initialize
|
56
|
+
|
57
|
+
== 0.6.1
|
58
|
+
* Set counters and point to 0 for uninitialized voteable objects in order sort and query
|
59
|
+
|
60
|
+
== 0.6.0
|
61
|
+
* Minimize vote data store (using short field names votes.u, votes.d, votes.c ...)
|
62
|
+
* Add Voter#up_votees, Voter#down_votees
|
63
|
+
* Remove index and scope from statistic module. User have to add indexes and scopes manually (see https://github.com/vinova/simple_qa/blob/master/app/models/question.rb)
|
64
|
+
* Bug fixes
|
65
|
+
|
66
|
+
== 0.5.0
|
67
|
+
* Rename vote_point to voteable
|
68
|
+
|
69
|
+
== 0.4.5
|
70
|
+
* Can use rake db:mongoid:voteable:remake_stats in Rails apps
|
71
|
+
* Use mongoid 2.0.0
|
72
|
+
|
73
|
+
== 0.4.4
|
74
|
+
* Add up_votes_count, down_votes_count
|
75
|
+
* Re-generate vote statistic data (counters and point)
|
76
|
+
|
77
|
+
== 0.4.3
|
78
|
+
* Wrap vote data in voteable namespace (voteable.up_voters_id, voteable.down_voters_ids, voteable.votes_count ...)
|
79
|
+
|
80
|
+
== 0.4.2
|
81
|
+
* Bug fixes
|
82
|
+
|
83
|
+
== 0.4.0
|
84
|
+
* Can unvote
|
85
|
+
|
86
|
+
== 0.3.5
|
87
|
+
* Use mongoid 2.0.0.rc
|
data/Gemfile
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,212 @@
|
|
1
|
+
= Voteable Mongo (for Mongoid 3 / 4)
|
2
|
+
|
3
|
+
voteable_mongo allows you to make your Mongoid::Document or MongoMapper::Document objects voteable and tabulate votes count and votes point for you. For instance, in a forum, a user can vote up (or down) on a post or a comment. It's optimized for speed by using only ONE database request per collection to validate, update, and retrieve updated data.
|
4
|
+
|
5
|
+
Initial idea based on http://cookbook.mongodb.org/patterns/votes.
|
6
|
+
|
7
|
+
Sample app at https://github.com/vinova/simple_qa.
|
8
|
+
|
9
|
+
Wonder how fast voteable_mongo is compare to other SQL & MongoDB solutions?
|
10
|
+
Visit benchmarks at https://github.com/vinova/voteable_benchmarks
|
11
|
+
|
12
|
+
== Why voteable_mongo?
|
13
|
+
|
14
|
+
There are various solutions for up / down voting problem (1[https://github.com/medihack/make_voteable], 2[https://github.com/brady8/thumbs_up], 3[https://github.com/icaruswings/mm-voteable], 4[https://github.com/jcoene/mongoid_voteable], ...). Most of them using additional votes table (SQL) or votes collection (MongoDB) to store votes and do data tabulation on that votes table or votes collection.
|
15
|
+
|
16
|
+
voteable_mongo is different. It takes advantage of document-oriented database to store all related votes data inside voteable document. That has following benefits:
|
17
|
+
|
18
|
+
* Don't have to maintain additional votes table or votes collection.
|
19
|
+
|
20
|
+
* When voteable document is loaded, all votes data related to it also be loaded, no more additional database requests to see how many votes this document got, who give up votes who give down vote, total vote points, votes count ...
|
21
|
+
|
22
|
+
* When vote up, vote down, revote, unvote, voteable_mongo validates vote data, updates voteable document and retrieves updated data using only ONE database request thanks to atomic findAndModify operation.
|
23
|
+
|
24
|
+
* Atomic operations on single document warranty data integrity that makes sure if votes created / changed / deleted their associated counters and points will be updated.
|
25
|
+
|
26
|
+
So use voteable_mongo for less maintain cost, data integrity and save database requests for other tasks.
|
27
|
+
|
28
|
+
== Sites using voteable_mongo
|
29
|
+
* http://www.naiku.net
|
30
|
+
* http://www.amorveneris.com
|
31
|
+
* http://zheye.org
|
32
|
+
|
33
|
+
== Installation
|
34
|
+
|
35
|
+
=== Rails 3.x
|
36
|
+
|
37
|
+
To install the gem, add this to your Gemfile
|
38
|
+
|
39
|
+
gem 'rs_voteable_mongo'
|
40
|
+
|
41
|
+
After that, remember to run "bundle install"
|
42
|
+
|
43
|
+
== Usage
|
44
|
+
|
45
|
+
=== Make Post and Comment voteable, User become the voter
|
46
|
+
|
47
|
+
== Mongoid
|
48
|
+
post.rb
|
49
|
+
|
50
|
+
class Post
|
51
|
+
include Mongoid::Document
|
52
|
+
include Mongo::Voteable
|
53
|
+
|
54
|
+
# set points for each vote
|
55
|
+
voteable self, :up => +1, :down => -1
|
56
|
+
|
57
|
+
has_many :comments
|
58
|
+
end
|
59
|
+
|
60
|
+
comment.rb
|
61
|
+
|
62
|
+
require 'post'
|
63
|
+
|
64
|
+
class Comment
|
65
|
+
include Mongoid::Document
|
66
|
+
include Mongo::Voteable
|
67
|
+
|
68
|
+
belongs_to :post
|
69
|
+
|
70
|
+
voteable self, :up => +1, :down => -3
|
71
|
+
|
72
|
+
# each vote on a comment can affect votes count and point of the related post as well
|
73
|
+
voteable Post, :up => +2, :down => -1
|
74
|
+
end
|
75
|
+
|
76
|
+
user.rb
|
77
|
+
|
78
|
+
class User
|
79
|
+
include Mongoid::Document
|
80
|
+
include Mongo::Voter
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
== MongoMapper
|
85
|
+
post.rb
|
86
|
+
|
87
|
+
class Post
|
88
|
+
include MongoMapper::Document
|
89
|
+
include Mongo::Voteable
|
90
|
+
|
91
|
+
# set points for each vote
|
92
|
+
voteable self, :up => +1, :down => -1
|
93
|
+
|
94
|
+
many :comments
|
95
|
+
end
|
96
|
+
|
97
|
+
comment.rb
|
98
|
+
|
99
|
+
require 'post'
|
100
|
+
|
101
|
+
class Comment
|
102
|
+
include MongoMapper::Document
|
103
|
+
include Mongo::Voteable
|
104
|
+
|
105
|
+
belongs_to :post
|
106
|
+
|
107
|
+
voteable self, :up => +1, :down => -3
|
108
|
+
voteable Post, :up => +2, :down => -1
|
109
|
+
end
|
110
|
+
|
111
|
+
user.rb
|
112
|
+
|
113
|
+
class User
|
114
|
+
include MongoMapper::Document
|
115
|
+
include Mongo::Voter
|
116
|
+
end
|
117
|
+
|
118
|
+
|
119
|
+
=== Make a vote
|
120
|
+
|
121
|
+
@user.vote(@post, :up)
|
122
|
+
|
123
|
+
Is equivalent to
|
124
|
+
@user.vote(:votee => @post, :value => :up)
|
125
|
+
@post.vote(:voter => @user, :value => :up)
|
126
|
+
|
127
|
+
In case you don't need to init voter and / or votee objects you can
|
128
|
+
@user.vote(:votee_class => Post, :votee_id => post_id, :value => :down)
|
129
|
+
@post.vote(:voter_id => user_id, :value => :up)
|
130
|
+
Post.vote(:voter_id => user_id, :votee_id => post_id, :value => :up)
|
131
|
+
|
132
|
+
=== Undo a vote
|
133
|
+
|
134
|
+
@user.unvote(@comment)
|
135
|
+
|
136
|
+
=== If have voter_id, votee_id and vote value you don't need to init voter and votee objects (suitable for API calls)
|
137
|
+
|
138
|
+
New vote
|
139
|
+
Post.vote(:voter_id => user_id, :votee_id => post_id, :value => :up)
|
140
|
+
|
141
|
+
Re-vote
|
142
|
+
Post.vote(:voter_id => user_id, :votee_id => post_id, :value => :up, :revote => true)
|
143
|
+
|
144
|
+
Un-vote
|
145
|
+
Post.vote(:voter_id => user_id, :votee_id => post_id, :value => :up, :unvote => true)
|
146
|
+
|
147
|
+
Note: vote function always return updated votee object
|
148
|
+
|
149
|
+
=== Get vote_value
|
150
|
+
|
151
|
+
@user.vote_value(@post)
|
152
|
+
@user.vote_value(:votee_class => Post, :votee_id => post_id)
|
153
|
+
@post.vote_value(@user)
|
154
|
+
@post.vote_value(user_id)
|
155
|
+
|
156
|
+
=== Check if voted?
|
157
|
+
|
158
|
+
@user.voted?(@post)
|
159
|
+
@user.voted?(:votee_class => Post, :votee_id => post_id)
|
160
|
+
@post.voted_by?(@user)
|
161
|
+
@post.voted_by?(user_id)
|
162
|
+
|
163
|
+
=== Get votes counts and points
|
164
|
+
|
165
|
+
puts @post.votes_point
|
166
|
+
puts @post.votes_count
|
167
|
+
puts @post.up_votes_count
|
168
|
+
puts @post.down_votes_count
|
169
|
+
|
170
|
+
=== Get voters given voted object and voter class
|
171
|
+
|
172
|
+
@post.up_voters(User)
|
173
|
+
@post.down_voters(User)
|
174
|
+
@post.voters(User)
|
175
|
+
- or -
|
176
|
+
User.up_voted_for(@post)
|
177
|
+
User.down_voted_for(@post)
|
178
|
+
User.voted_for(@post)
|
179
|
+
|
180
|
+
=== Get the list of voted objects of a class
|
181
|
+
|
182
|
+
Post.voted_by(@user)
|
183
|
+
Post.up_voted_by(@user)
|
184
|
+
Post.down_voted_by(@user)
|
185
|
+
|
186
|
+
== Utilities
|
187
|
+
|
188
|
+
=== Set counters and point to 0 for uninitialized voteable objects in order sort and query
|
189
|
+
Rails
|
190
|
+
rake mongo:voteable:init_stats
|
191
|
+
Ruby
|
192
|
+
Mongo::Voteable::Tasks::init_stats
|
193
|
+
|
194
|
+
=== Re-generate counters and vote points in case you change :up / :down vote points
|
195
|
+
Rails
|
196
|
+
rake mongo:voteable:remake_stats
|
197
|
+
Ruby
|
198
|
+
Mongo::Voteable::Tasks.remake_stats
|
199
|
+
|
200
|
+
=== Migrate from voteable_mongoid version < 0.7.0
|
201
|
+
Rails
|
202
|
+
rake mongo:voteable:migrate_old_votes
|
203
|
+
Ruby
|
204
|
+
Mongo::Voteable::Tasks.migrate_old_votes
|
205
|
+
|
206
|
+
== Credits
|
207
|
+
* Alex Nguyen - Author
|
208
|
+
* Contributors[https://github.com/vinova/voteable_mongo/contributors]
|
209
|
+
|
210
|
+
Copyright (c) 2010-2011 Vinova Pte Ltd
|
211
|
+
|
212
|
+
Licensed under the MIT license.
|
data/Rakefile
ADDED
data/TODO
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
* Support Mongoid / MongoMapper embedded documents
|
2
|
+
|
3
|
+
* Add :foreign_key => ... option to update parents' votes
|
4
|
+
- Reduce time to check relationships and foreign keys
|
5
|
+
- More flexible
|
6
|
+
- Need to change votable interface?
|
7
|
+
|
8
|
+
* Support https://github.com/benmyles/mongomatic &
|
9
|
+
https://github.com/carlosparamio/mongo_odm
|
10
|
+
- Don't have scope
|
11
|
+
- Don't have relationships
|
12
|
+
|
13
|
+
* Add options hash validations
|
14
|
+
|
15
|
+
* Refactor specs
|
16
|
+
|
17
|
+
* More test cases for Tasks module
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'votable_mongo'
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Mongo
|
2
|
+
module Voteable
|
3
|
+
module Helpers
|
4
|
+
|
5
|
+
def self.try_to_convert_string_to_object_id(x)
|
6
|
+
if defined?(Moped::BSON)
|
7
|
+
x.is_a?(String) && Moped::BSON::ObjectId.legal?(x) ? Moped::BSON::ObjectId.from_string(x) : x
|
8
|
+
else
|
9
|
+
x.is_a?(String) && BSON::ObjectId.legal?(x) ? BSON::ObjectId.from_string(x) : x
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.get_mongo_id(x)
|
14
|
+
x.respond_to?(:id) ? x.id : x
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Mongo
|
2
|
+
module Voteable
|
3
|
+
module Integrations
|
4
|
+
module Mongoid
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
field :votes, :type => Hash, :default => DEFAULT_VOTES
|
9
|
+
|
10
|
+
class << self
|
11
|
+
alias_method :voteable_index, :index
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
def voteable_relation(class_name)
|
17
|
+
relations.find{ |x, r| r.class_name == class_name }.try(:last)
|
18
|
+
end
|
19
|
+
|
20
|
+
def voteable_collection
|
21
|
+
collection
|
22
|
+
end
|
23
|
+
|
24
|
+
def voteable_foreign_key(metadata)
|
25
|
+
metadata.foreign_key.to_s
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Rails #:nodoc:
|
2
|
+
module VoteableMongo #:nodoc:
|
3
|
+
class Railtie < Rails::Railtie #:nodoc:
|
4
|
+
|
5
|
+
initializer "preload all application models" do |app|
|
6
|
+
config.to_prepare do
|
7
|
+
if defined?(Mongoid)
|
8
|
+
::Rails::Mongoid.load_models(app)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
rake_tasks do
|
14
|
+
load 'voteable_mongo/railties/database.rake'
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
namespace :mongo do
|
2
|
+
namespace :voteable do
|
3
|
+
desc 'Update up_votes_count, down_votes_count, votes_count and votes_point'
|
4
|
+
task :remake_stats => :environment do
|
5
|
+
Mongo::Voteable::Tasks.remake_stats(:log)
|
6
|
+
end
|
7
|
+
|
8
|
+
desc 'Set counters and point to 0 for uninitizized voteable objects'
|
9
|
+
task :init_stats => :environment do
|
10
|
+
Mongo::Voteable::Tasks.init_stats(:log)
|
11
|
+
end
|
12
|
+
|
13
|
+
desc 'Migrate vote data created by version < 0.7.0 to new vote data storage'
|
14
|
+
task :migrate_old_votes => :environment do
|
15
|
+
Mongo::Voteable::Tasks.migrate_old_votes(:log)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
module Mongo
|
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.with(safe: true).where(votes: nil).update_all({ '$set' => {votes: DEFAULT_VOTES} })
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Re-generate vote counters and vote points
|
17
|
+
def self.remake_stats(log = false)
|
18
|
+
remake_stats_for_all_voteable_classes(log)
|
19
|
+
update_parent_stats(log)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Convert votes from from version < 0.7.0 to new data store
|
23
|
+
def self.migrate_old_votes(log = false)
|
24
|
+
VOTEABLE.each do |class_name, voteable|
|
25
|
+
klass = class_name.constantize
|
26
|
+
klass_voteable = voteable[class_name]
|
27
|
+
puts "* Migrating old vote data for #{class_name} ..." if log
|
28
|
+
migrate_old_votes_for(klass, klass_voteable)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.migrate_old_votes_for(klass, voteable)
|
33
|
+
klass.all.each do |doc|
|
34
|
+
# Version 0.6.x use very short field names (u, d, uc, dc, c, p) to minimize
|
35
|
+
# votes storage but it's not human friendly
|
36
|
+
# Version >= 0.7.0 use readable field names (up, down, up_count, down_count,
|
37
|
+
# count, point)
|
38
|
+
votes = doc['votes'] || doc['voteable'] || {}
|
39
|
+
|
40
|
+
up_voter_ids = votes['up'] || votes['u'] ||
|
41
|
+
votes['up_voter_ids'] || doc['up_voter_ids'] || []
|
42
|
+
|
43
|
+
down_voter_ids = votes['down'] || votes['d'] ||
|
44
|
+
votes['down_voter_ids'] || doc['down_voter_ids'] || []
|
45
|
+
|
46
|
+
up_count = up_voter_ids.size
|
47
|
+
down_count = down_voter_ids.size
|
48
|
+
|
49
|
+
klass.with(safe: true).where(_id: doc.id).update_all(
|
50
|
+
'$set' => {
|
51
|
+
'votes' => {
|
52
|
+
'up' => up_voter_ids,
|
53
|
+
'down' => down_voter_ids,
|
54
|
+
'up_count' => up_count,
|
55
|
+
'down_count' => down_count,
|
56
|
+
'count' => up_count + down_count,
|
57
|
+
'point' => voteable[:up].to_i*up_count + voteable[:down].to_i*down_count
|
58
|
+
}
|
59
|
+
},
|
60
|
+
'$unset' => {
|
61
|
+
'up_voter_ids' => true,
|
62
|
+
'down_voter_ids' => true,
|
63
|
+
'votes_count' => true,
|
64
|
+
'votes_point' => true,
|
65
|
+
'voteable' => true
|
66
|
+
}
|
67
|
+
)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
def self.remake_stats_for_all_voteable_classes(log)
|
73
|
+
VOTEABLE.each do |class_name, voteable|
|
74
|
+
klass = class_name.constantize
|
75
|
+
klass_voteable = voteable[class_name]
|
76
|
+
puts "Generating stats for #{class_name}" if log
|
77
|
+
klass.all.each{ |doc|
|
78
|
+
remake_stats_for(doc, klass_voteable)
|
79
|
+
}
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
def self.remake_stats_for(doc, voteable)
|
85
|
+
up_count = doc.up_voter_ids.length
|
86
|
+
down_count = doc.down_voter_ids.length
|
87
|
+
doc.update_attributes(
|
88
|
+
'votes' => {
|
89
|
+
'up' => doc.up_voter_ids,
|
90
|
+
'down' => doc.down_voter_ids,
|
91
|
+
'up_count' => up_count,
|
92
|
+
'down_count' => down_count,
|
93
|
+
'count' => up_count + down_count,
|
94
|
+
'point' => voteable[:up].to_i*up_count + voteable[:down].to_i*down_count
|
95
|
+
}
|
96
|
+
)
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
def self.update_parent_stats(log)
|
101
|
+
VOTEABLE.each do |class_name, voteable|
|
102
|
+
klass = class_name.constantize
|
103
|
+
voteable.each do |parent_class_name, parent_voteable|
|
104
|
+
metadata = klass.voteable_relation(parent_class_name)
|
105
|
+
if metadata
|
106
|
+
parent_class = parent_class_name.constantize
|
107
|
+
foreign_key = klass.voteable_foreign_key(metadata)
|
108
|
+
puts "Updating stats for #{class_name} > #{parent_class_name}" if log
|
109
|
+
klass.all.each{ |doc|
|
110
|
+
update_parent_stats_for(doc, parent_class, foreign_key, parent_voteable)
|
111
|
+
}
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
def self.update_parent_stats_for(doc, parent_class, foreign_key, voteable)
|
119
|
+
parent_id = doc.read_attribute(foreign_key.to_sym)
|
120
|
+
if parent_id
|
121
|
+
up_count = doc.up_voter_ids.length
|
122
|
+
down_count = doc.down_voter_ids.length
|
123
|
+
|
124
|
+
return if up_count == 0 && down_count == 0
|
125
|
+
|
126
|
+
inc_options = {
|
127
|
+
'votes.point' => voteable[:up].to_i*up_count + voteable[:down].to_i*down_count
|
128
|
+
}
|
129
|
+
|
130
|
+
unless voteable[:update_counters] == false
|
131
|
+
inc_options.merge!(
|
132
|
+
'votes.count' => up_count + down_count,
|
133
|
+
'votes.up_count' => up_count,
|
134
|
+
'votes.down_count' => down_count
|
135
|
+
)
|
136
|
+
end
|
137
|
+
|
138
|
+
parent_ids = parent_id.is_a?(Array) ? parent_id : [ parent_id ]
|
139
|
+
|
140
|
+
parent_class.with(safe: true).where(:_id.in => parent_ids).update_all({ '$inc' => inc_options })
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
private_class_method :migrate_old_votes_for,
|
145
|
+
:remake_stats_for,
|
146
|
+
:remake_stats_for_all_voteable_classes,
|
147
|
+
:update_parent_stats,
|
148
|
+
:update_parent_stats_for
|
149
|
+
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|