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
@@ -0,0 +1,203 @@
|
|
1
|
+
require 'voteable_mongo/voting'
|
2
|
+
require 'voteable_mongo/integrations/mongoid'
|
3
|
+
|
4
|
+
module Mongo
|
5
|
+
module Voteable
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
DEFAULT_VOTES = {
|
9
|
+
'up' => [],
|
10
|
+
'down' => [],
|
11
|
+
'up_count' => 0,
|
12
|
+
'down_count' => 0,
|
13
|
+
'count' => 0,
|
14
|
+
'point' => 0
|
15
|
+
}
|
16
|
+
|
17
|
+
included do
|
18
|
+
include Mongo::Voteable::Voting
|
19
|
+
|
20
|
+
if defined?(Mongoid) && defined?(field)
|
21
|
+
include Mongo::Voteable::Integrations::Mongoid
|
22
|
+
elsif defined?(MongoMapper)
|
23
|
+
include Mongo::Voteable::Integrations::MongoMapper
|
24
|
+
end
|
25
|
+
|
26
|
+
scope :voted_by, lambda { |voter|
|
27
|
+
voter_id = Helpers.get_mongo_id(voter)
|
28
|
+
where('$or' => [{ 'votes.up' => voter_id }, { 'votes.down' => voter_id }])
|
29
|
+
}
|
30
|
+
|
31
|
+
scope :up_voted_by, lambda { |voter|
|
32
|
+
voter_id = Helpers.get_mongo_id(voter)
|
33
|
+
where('votes.up' => voter_id)
|
34
|
+
}
|
35
|
+
|
36
|
+
scope :down_voted_by, lambda { |voter|
|
37
|
+
voter_id = Helpers.get_mongo_id(voter)
|
38
|
+
where('votes.down' => voter_id)
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
# How many points should be assigned for each up or down vote and other options
|
43
|
+
# This hash should manipulated using voteable method
|
44
|
+
VOTEABLE = {}
|
45
|
+
|
46
|
+
module ClassMethods
|
47
|
+
# Set vote point for each up (down) vote on an object of this class
|
48
|
+
#
|
49
|
+
# @param [Hash] options a hash containings:
|
50
|
+
#
|
51
|
+
# voteable self, :up => +1, :down => -3
|
52
|
+
# voteable Post, :up => +2, :down => -1, :update_counters => false # skip counter update
|
53
|
+
def voteable(klass = self, options = nil)
|
54
|
+
VOTEABLE[name] ||= {}
|
55
|
+
VOTEABLE[name][klass.name] ||= options
|
56
|
+
if klass == self
|
57
|
+
if options[:index] == true
|
58
|
+
create_voteable_indexes
|
59
|
+
end
|
60
|
+
else
|
61
|
+
VOTEABLE[name][name][:update_parents] ||= true
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Check if voter_id do a vote on votee_id
|
66
|
+
#
|
67
|
+
# @param [Hash] options a hash containings:
|
68
|
+
# - :votee_id: the votee document id
|
69
|
+
# - :voter_id: the voter document id
|
70
|
+
#
|
71
|
+
# @return [true, false]
|
72
|
+
def voted?(options)
|
73
|
+
validate_and_normalize_vote_options(options)
|
74
|
+
up_voted?(options) || down_voted?(options)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Check if voter_id do an up vote on votee_id
|
78
|
+
#
|
79
|
+
# @param [Hash] options a hash containings:
|
80
|
+
# - :votee_id: the votee document id
|
81
|
+
# - :voter_id: the voter document id
|
82
|
+
#
|
83
|
+
# @return [true, false]
|
84
|
+
def up_voted?(options)
|
85
|
+
validate_and_normalize_vote_options(options)
|
86
|
+
up_voted_by(options[:voter_id]).where(:_id => options[:votee_id]).count == 1
|
87
|
+
end
|
88
|
+
|
89
|
+
# Check if voter_id do a down vote on votee_id
|
90
|
+
#
|
91
|
+
# @param [Hash] options a hash containings:
|
92
|
+
# - :votee_id: the votee document id
|
93
|
+
# - :voter_id: the voter document id
|
94
|
+
#
|
95
|
+
# @return [true, false]
|
96
|
+
def down_voted?(options)
|
97
|
+
validate_and_normalize_vote_options(options)
|
98
|
+
down_voted_by(options[:voter_id]).where(:_id => options[:votee_id]).count == 1
|
99
|
+
end
|
100
|
+
|
101
|
+
def create_voteable_indexes
|
102
|
+
# Compound index _id and voters.up, _id and voters.down
|
103
|
+
# to make up_voted_by, down_voted_by, voted_by scopes and voting faster
|
104
|
+
# Should run in background since it introduce new index value and
|
105
|
+
# while waiting to build, the system can use _id for voting
|
106
|
+
# http://www.mongodb.org/display/DOCS/Indexing+as+a+Background+Operation
|
107
|
+
voteable_index({'votes.up' => 1, '_id' => 1}, {unique: true})
|
108
|
+
voteable_index({'votes.down' => 1, '_id' => 1}, {unique: true})
|
109
|
+
|
110
|
+
# Index counters and point for desc ordering
|
111
|
+
voteable_index({'votes.up_count' => -1})
|
112
|
+
voteable_index({'votes.down_count' => -1})
|
113
|
+
voteable_index({'votes.count' => -1})
|
114
|
+
voteable_index({'votes.point' => -1})
|
115
|
+
create_indexes
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# Make a vote on this votee
|
120
|
+
#
|
121
|
+
# @param [Hash] options a hash containings:
|
122
|
+
# - :voter_id: the voter document id
|
123
|
+
# - :value: vote :up or vote :down
|
124
|
+
# - :revote: change from vote up to vote down
|
125
|
+
# - :unvote: unvote the vote value (:up or :down)
|
126
|
+
def vote(options)
|
127
|
+
options[:votee_id] = id
|
128
|
+
options[:votee] = self
|
129
|
+
options[:voter_id] ||= options[:voter].id
|
130
|
+
|
131
|
+
if options[:unvote]
|
132
|
+
options[:value] ||= vote_value(options[:voter_id])
|
133
|
+
else
|
134
|
+
options[:revote] ||= vote_value(options[:voter_id]).present?
|
135
|
+
end
|
136
|
+
|
137
|
+
self.class.vote(options)
|
138
|
+
end
|
139
|
+
|
140
|
+
# Get a voted value on this votee
|
141
|
+
#
|
142
|
+
# @param voter is object or the id of the voter who made the vote
|
143
|
+
def vote_value(voter)
|
144
|
+
voter_id = Helpers.get_mongo_id(voter)
|
145
|
+
return :up if up_voter_ids.include?(voter_id)
|
146
|
+
return :down if down_voter_ids.include?(voter_id)
|
147
|
+
end
|
148
|
+
|
149
|
+
def voted_by?(voter)
|
150
|
+
!!vote_value(voter)
|
151
|
+
end
|
152
|
+
|
153
|
+
# Array of up voter ids
|
154
|
+
def up_voter_ids
|
155
|
+
votes.try(:[], 'up') || []
|
156
|
+
end
|
157
|
+
|
158
|
+
# Array of down voter ids
|
159
|
+
def down_voter_ids
|
160
|
+
votes.try(:[], 'down') || []
|
161
|
+
end
|
162
|
+
|
163
|
+
# Array of voter ids
|
164
|
+
def voter_ids
|
165
|
+
up_voter_ids + down_voter_ids
|
166
|
+
end
|
167
|
+
|
168
|
+
# Get the number of up votes
|
169
|
+
def up_votes_count
|
170
|
+
votes.try(:[], 'up_count') || 0
|
171
|
+
end
|
172
|
+
|
173
|
+
# Get the number of down votes
|
174
|
+
def down_votes_count
|
175
|
+
votes.try(:[], 'down_count') || 0
|
176
|
+
end
|
177
|
+
|
178
|
+
# Get the number of votes
|
179
|
+
def votes_count
|
180
|
+
votes.try(:[], 'count') || 0
|
181
|
+
end
|
182
|
+
|
183
|
+
# Get the votes point
|
184
|
+
def votes_point
|
185
|
+
votes.try(:[], 'point') || 0
|
186
|
+
end
|
187
|
+
|
188
|
+
# Get up voters
|
189
|
+
def up_voters(klass)
|
190
|
+
klass.where(:_id => { '$in' => up_voter_ids })
|
191
|
+
end
|
192
|
+
|
193
|
+
# Get down voters
|
194
|
+
def down_voters(klass)
|
195
|
+
klass.where(:_id => { '$in' => down_voter_ids })
|
196
|
+
end
|
197
|
+
|
198
|
+
# Get voters
|
199
|
+
def voters(klass)
|
200
|
+
klass.where(:_id => { '$in' => voter_ids })
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module Mongo
|
2
|
+
module Voter
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
scope :up_voted_for, lambda { |votee| where(:_id => { '$in' => votee.up_voter_ids }) }
|
7
|
+
scope :down_voted_for, lambda { |votee| where(:_id => { '$in' => votee.down_voter_ids }) }
|
8
|
+
scope :voted_for, lambda { |votee| where(:_id => { '$in' => votee.voter_ids }) }
|
9
|
+
end
|
10
|
+
|
11
|
+
# Check to see if this voter voted on the votee or not
|
12
|
+
#
|
13
|
+
# @param [Hash, Object] options the hash containing the votee, or the votee itself
|
14
|
+
# @return [true, false] true if voted, false otherwise
|
15
|
+
def voted?(options)
|
16
|
+
unless options.is_a?(Hash)
|
17
|
+
votee_class = options.class
|
18
|
+
votee_id = options.id
|
19
|
+
else
|
20
|
+
votee = options[:votee]
|
21
|
+
if votee
|
22
|
+
votee_class = votee.class
|
23
|
+
votee_id = votee.id
|
24
|
+
else
|
25
|
+
votee_class = options[:votee_class]
|
26
|
+
votee_id = options[:votee_id]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
votee_class.voted?(:voter_id => id, :votee_id => votee_id)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Get the voted value on a votee
|
34
|
+
#
|
35
|
+
# @param (see #voted?)
|
36
|
+
# @return [Symbol, nil] :up or :down or nil if not voted
|
37
|
+
def vote_value(options)
|
38
|
+
votee = unless options.is_a?(Hash)
|
39
|
+
options
|
40
|
+
else
|
41
|
+
options[:votee] || options[:votee_class].find(options[:votee_id])
|
42
|
+
end
|
43
|
+
votee.vote_value(_id)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Cancel the vote on a votee
|
47
|
+
#
|
48
|
+
# @param [Object] votee the votee to be unvoted
|
49
|
+
def unvote(options)
|
50
|
+
unless options.is_a?(Hash)
|
51
|
+
options = { :votee => options }
|
52
|
+
end
|
53
|
+
options[:unvote] = true
|
54
|
+
options[:revote] = false
|
55
|
+
vote(options)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Vote on a votee
|
59
|
+
#
|
60
|
+
# @param (see #voted?)
|
61
|
+
# @param [:up, :down] vote_value vote up or vote down, nil to unvote
|
62
|
+
def vote(options, value = nil)
|
63
|
+
if options.is_a?(Hash)
|
64
|
+
votee = options[:votee]
|
65
|
+
else
|
66
|
+
votee = options
|
67
|
+
options = { :votee => votee, :value => value }
|
68
|
+
end
|
69
|
+
|
70
|
+
if votee
|
71
|
+
options[:votee_id] = votee.id
|
72
|
+
votee_class = votee.class
|
73
|
+
else
|
74
|
+
votee_class = options[:votee_class]
|
75
|
+
end
|
76
|
+
|
77
|
+
if options[:value].nil?
|
78
|
+
options[:unvote] = true
|
79
|
+
options[:value] = vote_value(options)
|
80
|
+
else
|
81
|
+
options[:revote] = options.has_key?(:revote) ? !options[:revote].blank? : voted?(options)
|
82
|
+
end
|
83
|
+
|
84
|
+
options[:voter] = self
|
85
|
+
options[:voter_id] = id
|
86
|
+
|
87
|
+
(votee || votee_class).vote(options)
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,214 @@
|
|
1
|
+
module Mongo
|
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: if true change vote vote from :up to :down and vise versa
|
14
|
+
# - :unvote: if true undo the voting
|
15
|
+
#
|
16
|
+
# @return [votee, false]
|
17
|
+
def vote(options)
|
18
|
+
validate_and_normalize_vote_options(options)
|
19
|
+
options[:voteable] = VOTEABLE[name][name]
|
20
|
+
|
21
|
+
if options[:voteable]
|
22
|
+
query, update = if options[:revote]
|
23
|
+
revote_query_and_update(options)
|
24
|
+
elsif options[:unvote]
|
25
|
+
unvote_query_and_update(options)
|
26
|
+
else
|
27
|
+
new_vote_query_and_update(options)
|
28
|
+
end
|
29
|
+
|
30
|
+
# http://www.mongodb.org/display/DOCS/findAndModify+Command
|
31
|
+
begin
|
32
|
+
doc = where(query).find_and_modify(
|
33
|
+
update,
|
34
|
+
:new => true
|
35
|
+
)
|
36
|
+
rescue Moped::Errors::OperationFailure
|
37
|
+
doc = nil
|
38
|
+
end
|
39
|
+
|
40
|
+
if doc
|
41
|
+
update_parent_votes(doc, options) if options[:voteable][:update_parents]
|
42
|
+
# Update new votes data
|
43
|
+
options[:votee].write_attribute('votes', doc['votes']) if options[:votee]
|
44
|
+
options[:votee] || new(doc.as_document)
|
45
|
+
else
|
46
|
+
false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
private
|
53
|
+
def validate_and_normalize_vote_options(options)
|
54
|
+
options.symbolize_keys!
|
55
|
+
options[:votee_id] = Helpers.try_to_convert_string_to_object_id(options[:votee_id])
|
56
|
+
options[:voter_id] = Helpers.try_to_convert_string_to_object_id(options[:voter_id])
|
57
|
+
options[:value] &&= options[:value].to_sym
|
58
|
+
end
|
59
|
+
|
60
|
+
def new_vote_query_and_update(options)
|
61
|
+
if options[:value] == :up
|
62
|
+
positive_voter_ids = 'votes.up'
|
63
|
+
positive_votes_count = 'votes.up_count'
|
64
|
+
else
|
65
|
+
positive_voter_ids = 'votes.down'
|
66
|
+
positive_votes_count = 'votes.down_count'
|
67
|
+
end
|
68
|
+
|
69
|
+
return {
|
70
|
+
# Validate voter_id did not vote for votee_id yet
|
71
|
+
:_id => options[:votee_id],
|
72
|
+
'votes.up' => { '$ne' => options[:voter_id] },
|
73
|
+
'votes.down' => { '$ne' => options[:voter_id] }
|
74
|
+
}, {
|
75
|
+
# then update
|
76
|
+
'$push' => { positive_voter_ids => options[:voter_id] },
|
77
|
+
'$inc' => {
|
78
|
+
'votes.count' => +1,
|
79
|
+
positive_votes_count => +1,
|
80
|
+
'votes.point' => options[:voteable][options[:value]]
|
81
|
+
}
|
82
|
+
}
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
def revote_query_and_update(options)
|
87
|
+
if options[:value] == :up
|
88
|
+
positive_voter_ids = 'votes.up'
|
89
|
+
negative_voter_ids = 'votes.down'
|
90
|
+
positive_votes_count = 'votes.up_count'
|
91
|
+
negative_votes_count = 'votes.down_count'
|
92
|
+
point_delta = options[:voteable][:up] - options[:voteable][:down]
|
93
|
+
else
|
94
|
+
positive_voter_ids = 'votes.down'
|
95
|
+
negative_voter_ids = 'votes.up'
|
96
|
+
positive_votes_count = 'votes.down_count'
|
97
|
+
negative_votes_count = 'votes.up_count'
|
98
|
+
point_delta = -options[:voteable][:up] + options[:voteable][:down]
|
99
|
+
end
|
100
|
+
|
101
|
+
return {
|
102
|
+
# Validate voter_id did a vote with value for votee_id
|
103
|
+
:_id => options[:votee_id],
|
104
|
+
# Can skip $ne validation since creating a new vote
|
105
|
+
# already warranty that a voter can vote one only
|
106
|
+
# positive_voter_ids => { '$ne' => options[:voter_id] },
|
107
|
+
negative_voter_ids => options[:voter_id]
|
108
|
+
}, {
|
109
|
+
# then update
|
110
|
+
'$pull' => { negative_voter_ids => options[:voter_id] },
|
111
|
+
'$push' => { positive_voter_ids => options[:voter_id] },
|
112
|
+
'$inc' => {
|
113
|
+
positive_votes_count => +1,
|
114
|
+
negative_votes_count => -1,
|
115
|
+
'votes.point' => point_delta
|
116
|
+
}
|
117
|
+
}
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
def unvote_query_and_update(options)
|
122
|
+
if options[:value] == :up
|
123
|
+
positive_voter_ids = 'votes.up'
|
124
|
+
negative_voter_ids = 'votes.down'
|
125
|
+
positive_votes_count = 'votes.up_count'
|
126
|
+
else
|
127
|
+
positive_voter_ids = 'votes.down'
|
128
|
+
negative_voter_ids = 'votes.up'
|
129
|
+
positive_votes_count = 'votes.down_count'
|
130
|
+
end
|
131
|
+
|
132
|
+
return {
|
133
|
+
:_id => options[:votee_id],
|
134
|
+
# Validate if voter_id did a vote with value for votee_id
|
135
|
+
# Can skip $ne validation since creating a new vote
|
136
|
+
# already warranty that a voter can vote one only
|
137
|
+
# negative_voter_ids => { '$ne' => options[:voter_id] },
|
138
|
+
positive_voter_ids => options[:voter_id]
|
139
|
+
}, {
|
140
|
+
# then update
|
141
|
+
'$pull' => { positive_voter_ids => options[:voter_id] },
|
142
|
+
'$inc' => {
|
143
|
+
positive_votes_count => -1,
|
144
|
+
'votes.count' => -1,
|
145
|
+
'votes.point' => - options[:voteable][options[:value]]
|
146
|
+
}
|
147
|
+
}
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
def update_parent_votes(doc, options)
|
152
|
+
VOTEABLE[name].each do |class_name, voteable|
|
153
|
+
|
154
|
+
if metadata = voteable_relation(class_name)
|
155
|
+
|
156
|
+
if (parent_id = doc[voteable_foreign_key(metadata)]).present?
|
157
|
+
parent_ids = parent_id.is_a?(Array) ? parent_id : [ parent_id ]
|
158
|
+
class_name.constantize.collection.find({'_id' => {'$in' => parent_ids}}).update_all(
|
159
|
+
{ '$inc' => parent_inc_options(voteable, options) },
|
160
|
+
)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
|
167
|
+
def parent_inc_options(voteable, options)
|
168
|
+
inc_options = {}
|
169
|
+
|
170
|
+
if options[:revote]
|
171
|
+
if options[:value] == :up
|
172
|
+
inc_options['votes.point'] = voteable[:up] - voteable[:down]
|
173
|
+
unless voteable[:update_counters] == false
|
174
|
+
inc_options['votes.up_count'] = +1
|
175
|
+
inc_options['votes.down_count'] = -1
|
176
|
+
end
|
177
|
+
else
|
178
|
+
inc_options['votes.point'] = -voteable[:up] + voteable[:down]
|
179
|
+
unless voteable[:update_counters] == false
|
180
|
+
inc_options['votes.up_count'] = -1
|
181
|
+
inc_options['votes.down_count'] = +1
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
elsif options[:unvote]
|
186
|
+
inc_options['votes.point'] = -voteable[options[:value]]
|
187
|
+
unless voteable[:update_counters] == false
|
188
|
+
inc_options['votes.count'] = -1
|
189
|
+
if options[:value] == :up
|
190
|
+
inc_options['votes.up_count'] = -1
|
191
|
+
else
|
192
|
+
inc_options['votes.down_count'] = -1
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
else # new vote
|
197
|
+
inc_options['votes.point'] = voteable[options[:value]]
|
198
|
+
unless voteable[:update_counters] == false
|
199
|
+
inc_options['votes.count'] = +1
|
200
|
+
if options[:value] == :up
|
201
|
+
inc_options['votes.up_count'] = +1
|
202
|
+
else
|
203
|
+
inc_options['votes.down_count'] = +1
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
inc_options
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
data/spec/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color --debugger
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'category')
|
2
|
+
|
3
|
+
class Post
|
4
|
+
include Mongoid::Document
|
5
|
+
include Mongo::Voteable
|
6
|
+
|
7
|
+
field :title
|
8
|
+
field :content
|
9
|
+
|
10
|
+
has_and_belongs_to_many :categories
|
11
|
+
has_many :comments
|
12
|
+
|
13
|
+
field :_id, type: String, default: -> { title }
|
14
|
+
|
15
|
+
voteable self, :up => +1, :down => -1, :index => true
|
16
|
+
voteable Category, :up => +3, :down => -5, :update_counters => false
|
17
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
Bundler.setup
|
4
|
+
|
5
|
+
require 'mongoid'
|
6
|
+
models_folder = File.join(File.dirname(__FILE__), 'mongoid/models')
|
7
|
+
Mongoid.configure do |config|
|
8
|
+
name = 'voteable_mongo_test'
|
9
|
+
host = 'localhost'
|
10
|
+
config.connect_to(name)
|
11
|
+
end
|
12
|
+
|
13
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
14
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
15
|
+
|
16
|
+
|
17
|
+
require 'voteable_mongo'
|
18
|
+
require 'rspec'
|
19
|
+
require 'rspec/autorun'
|
20
|
+
|
21
|
+
Dir[ File.join(models_folder, '*.rb') ].each { |file|
|
22
|
+
require file
|
23
|
+
file_name = File.basename(file).sub('.rb', '')
|
24
|
+
klass = file_name.classify.constantize
|
25
|
+
begin
|
26
|
+
klass.collection.drop
|
27
|
+
rescue Exception => e
|
28
|
+
print e.message
|
29
|
+
end
|
30
|
+
}
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Mongo::Voteable::Tasks do
|
4
|
+
describe 'Mongo::Voteable::Tasks.init_stats' do
|
5
|
+
before :all do
|
6
|
+
@post1 = Post.create!(:title => 'post1')
|
7
|
+
@post2 = Post.create!(:title => 'post2')
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'after create votes has default value' do
|
11
|
+
@post1.votes.should == ::Mongo::Voteable::DEFAULT_VOTES
|
12
|
+
@post2.votes.should == ::Mongo::Voteable::DEFAULT_VOTES
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'reset votes data' do
|
16
|
+
@post1.votes = nil
|
17
|
+
@post1.save
|
18
|
+
|
19
|
+
@post2.votes = nil
|
20
|
+
@post2.save
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'init_stats recover votes default value' do
|
24
|
+
::Mongo::Voteable::Tasks.init_stats
|
25
|
+
::Mongo::Voteable::Tasks.migrate_old_votes
|
26
|
+
|
27
|
+
@post1.reload
|
28
|
+
@post2.reload
|
29
|
+
|
30
|
+
@post1.votes.should == ::Mongo::Voteable::DEFAULT_VOTES
|
31
|
+
@post2.votes.should == ::Mongo::Voteable::DEFAULT_VOTES
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|