activerecord-reputation-system 1.4.0 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +6 -6
- data/lib/models/rs_evaluation.rb +19 -1
- data/lib/models/rs_reputation.rb +18 -1
- data/lib/reputation_system/network.rb +17 -4
- data/lib/reputation_system/reputation.rb +11 -1
- data/lib/reputation_system/version.rb +1 -1
- data/spec/models/rs_evaluation_spec.rb +18 -0
- data/spec/models/rs_reputation_spec.rb +18 -0
- data/spec/reputation_system/base_spec.rb +8 -8
- data/spec/reputation_system/evaluation_spec.rb +49 -49
- data/spec/reputation_system/reputation_spec.rb +24 -24
- data/spec/reputation_system/scope_spec.rb +2 -2
- data/spec/spec_helper.rb +30 -12
- metadata +4 -4
data/README.md
CHANGED
@@ -70,9 +70,9 @@ Once reputation system is defined, evaluations for answers and questions can be
|
|
70
70
|
|
71
71
|
Reputation value can be accessed as follow:
|
72
72
|
```ruby
|
73
|
-
@answer.
|
74
|
-
@question.
|
75
|
-
@user.
|
73
|
+
@answer.reptuation_for(:avg_rating)
|
74
|
+
@question.reptuation_for(:votes)
|
75
|
+
@user.reptuation_for(:karma)
|
76
76
|
```
|
77
77
|
|
78
78
|
## Defining Reputation System
|
@@ -134,7 +134,7 @@ decrease_evaluation(reputation_name, value, source)
|
|
134
134
|
## Reputation
|
135
135
|
```ruby
|
136
136
|
# Returns the reputation value of the reputation with the given name.
|
137
|
-
|
137
|
+
reptuation_for(reputation_name)
|
138
138
|
|
139
139
|
# Returns the reputation rank of the reputation with the given name.
|
140
140
|
rank_for(reputation_name)
|
@@ -142,7 +142,7 @@ rank_for(reputation_name)
|
|
142
142
|
# Returns the normalized reputation value of the reputation with the given name. The normalization is computed using the following equation (assuming linear distribution):
|
143
143
|
# normalized_value = (x - min) / (max - min) if max - min is not 0
|
144
144
|
# normalized_value = 1 if max - min is 0
|
145
|
-
|
145
|
+
normalized_reptuation_for(reputation_name)
|
146
146
|
|
147
147
|
# Activates all reputations in the record. Active reputations are used when computing ranks or normalized reputation values.
|
148
148
|
activate_all_reputations
|
@@ -213,7 +213,7 @@ add_evaluation(:reputation_name, evaluation_value, source, :scope)
|
|
213
213
|
```
|
214
214
|
Also, reputations can be accessed in the context of scopes:
|
215
215
|
```ruby
|
216
|
-
|
216
|
+
reptuation_for(:reputation_name, :scope)
|
217
217
|
```
|
218
218
|
To use a scoped reputation as a source in another reputation, try this:
|
219
219
|
```ruby
|
data/lib/models/rs_evaluation.rb
CHANGED
@@ -21,15 +21,19 @@ class RSEvaluation < ActiveRecord::Base
|
|
21
21
|
|
22
22
|
attr_accessible :reputation_name, :value, :source, :source_id, :source_type, :target, :target_id, :target_type
|
23
23
|
|
24
|
+
# Sets an appropriate source type in case of Single Table Inheritance.
|
25
|
+
before_validation :set_source_type_for_sti
|
26
|
+
|
24
27
|
# the same source cannot evaluate the same target more than once.
|
25
28
|
validates_uniqueness_of :source_id, :scope => [:reputation_name, :source_type, :target_id, :target_type]
|
26
29
|
validate :source_must_be_defined_for_reputation_in_network
|
27
30
|
|
28
31
|
def self.find_by_reputation_name_and_source_and_target(reputation_name, source, target)
|
32
|
+
source_type = get_source_type_for_sti(source, target.class.name, reputation_name)
|
29
33
|
RSEvaluation.find(:first,
|
30
34
|
:conditions => {:reputation_name => reputation_name.to_s,
|
31
35
|
:source_id => source.id,
|
32
|
-
:source_type =>
|
36
|
+
:source_type => source_type,
|
33
37
|
:target_id => target.id,
|
34
38
|
:target_type => target.class.name
|
35
39
|
})
|
@@ -43,6 +47,20 @@ class RSEvaluation < ActiveRecord::Base
|
|
43
47
|
|
44
48
|
protected
|
45
49
|
|
50
|
+
def self.get_source_type_for_sti(source, target_type, reputation_name)
|
51
|
+
valid_source_type = ReputationSystem::Network.get_reputation_def(target_type, reputation_name)[:source].to_s.camelize
|
52
|
+
temp = source.class
|
53
|
+
while temp && valid_source_type != temp.name && temp.name != "ActiveRecord::Base"
|
54
|
+
temp = temp.superclass
|
55
|
+
end
|
56
|
+
temp ? temp.name : nil
|
57
|
+
end
|
58
|
+
|
59
|
+
def set_source_type_for_sti
|
60
|
+
temp = self.class.get_source_type_for_sti(source, target_type, reputation_name)
|
61
|
+
self.source_type = temp if temp
|
62
|
+
end
|
63
|
+
|
46
64
|
def source_must_be_defined_for_reputation_in_network
|
47
65
|
unless source_type == ReputationSystem::Network.get_reputation_def(target_type, reputation_name)[:source].to_s.camelize
|
48
66
|
errors.add(:source_type, "#{source_type} is not source of #{reputation_name} reputation")
|
data/lib/models/rs_reputation.rb
CHANGED
@@ -25,6 +25,7 @@ class RSReputation < ActiveRecord::Base
|
|
25
25
|
|
26
26
|
attr_accessible :reputation_name, :value, :aggregated_by, :active, :target, :target_id, :target_type, :received_messages
|
27
27
|
|
28
|
+
before_validation :set_target_type_for_sti
|
28
29
|
before_save :change_zero_value_in_case_of_product_process
|
29
30
|
|
30
31
|
VALID_PROCESSES = ['sum', 'average', 'product']
|
@@ -32,7 +33,8 @@ class RSReputation < ActiveRecord::Base
|
|
32
33
|
validates_uniqueness_of :reputation_name, :scope => [:target_id, :target_type]
|
33
34
|
|
34
35
|
def self.find_by_reputation_name_and_target(reputation_name, target)
|
35
|
-
|
36
|
+
target_type = get_target_type_for_sti(target, reputation_name)
|
37
|
+
RSReputation.find_by_reputation_name_and_target_id_and_target_type(reputation_name.to_s, target.id, target_type)
|
36
38
|
end
|
37
39
|
|
38
40
|
# All external access to reputation should use this since they are created lazily.
|
@@ -176,6 +178,21 @@ class RSReputation < ActiveRecord::Base
|
|
176
178
|
:conditions => {:reputation_name => reputation_name.to_s, :target_type => target_type, :active => true})
|
177
179
|
end
|
178
180
|
|
181
|
+
def self.get_target_type_for_sti(target, reputation_name)
|
182
|
+
temp = target.class
|
183
|
+
defs = ReputationSystem::Network.get_reputation_defs(temp.name)[reputation_name.to_sym]
|
184
|
+
while temp && temp.name != "ActiveRecord::Base" && defs && defs.empty?
|
185
|
+
temp = temp.superclass
|
186
|
+
defs = ReputationSystem::Network.get_reputation_defs(temp.name)[reputation_name.to_sym]
|
187
|
+
end
|
188
|
+
temp ? temp.name : nil
|
189
|
+
end
|
190
|
+
|
191
|
+
def set_target_type_for_sti
|
192
|
+
temp = self.class.get_target_type_for_sti(target, reputation_name)
|
193
|
+
self.target_type = temp if temp
|
194
|
+
end
|
195
|
+
|
179
196
|
def change_zero_value_in_case_of_product_process
|
180
197
|
self.value = 1 if self.value == 0 && self.aggregated_by == "product"
|
181
198
|
end
|
@@ -18,8 +18,8 @@ module ReputationSystem
|
|
18
18
|
class Network
|
19
19
|
class << self
|
20
20
|
def has_reputation_for?(class_name, reputation_name)
|
21
|
-
|
22
|
-
|
21
|
+
reputation_def = get_reputation_def(class_name, reputation_name)
|
22
|
+
!!reputation_def[:source]
|
23
23
|
end
|
24
24
|
|
25
25
|
def get_reputation_defs(class_name)
|
@@ -27,8 +27,21 @@ module ReputationSystem
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def get_reputation_def(class_name, reputation_name)
|
30
|
-
|
31
|
-
|
30
|
+
reputation_def = {}
|
31
|
+
unless class_name == "ActiveRecord::Base"
|
32
|
+
reputation_defs = get_reputation_defs(class_name)
|
33
|
+
reputation_defs[reputation_name.to_sym] ||= {}
|
34
|
+
reputation_def = reputation_defs[reputation_name.to_sym]
|
35
|
+
if reputation_def == {}
|
36
|
+
begin
|
37
|
+
klass = class_name.constantize.superclass
|
38
|
+
reputation_def = get_reputation_def(klass.name, reputation_name) if klass
|
39
|
+
rescue NameError
|
40
|
+
# Class might have not been initialized yet at this point.
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
reputation_def
|
32
45
|
end
|
33
46
|
|
34
47
|
def add_reputation_def(class_name, reputation_name, options)
|
@@ -17,10 +17,20 @@
|
|
17
17
|
module ReputationSystem
|
18
18
|
module Reputation
|
19
19
|
def reputation_value_for(reputation_name, *args)
|
20
|
+
warn "[DEPRECATION] `reputation_value_for` will be deprecated in version 2.0.0. Please use `reputation_for` instead."
|
21
|
+
reputation_for(reputation_name, *args)
|
22
|
+
end
|
23
|
+
|
24
|
+
def reputation_for(reputation_name, *args)
|
20
25
|
find_reputation(reputation_name, args.first).value
|
21
26
|
end
|
22
27
|
|
23
28
|
def normalized_reputation_value_for(reputation_name, *args)
|
29
|
+
warn "[DEPRECATION] `normalized_reputation_value_for` will be deprecated in version 2.0.0. Please use `normalized_reputation_for` instead."
|
30
|
+
normalized_reputation_for(reputation_name, *args)
|
31
|
+
end
|
32
|
+
|
33
|
+
def normalized_reputation_for(reputation_name, *args)
|
24
34
|
find_reputation(reputation_name, args.first).normalized_value
|
25
35
|
end
|
26
36
|
|
@@ -45,7 +55,7 @@ module ReputationSystem
|
|
45
55
|
|
46
56
|
def rank_for(reputation_name, *args)
|
47
57
|
scope = args.first
|
48
|
-
my_value = self.
|
58
|
+
my_value = self.reputation_for(reputation_name, scope)
|
49
59
|
self.class.count_with_reputation(reputation_name, scope, :all,
|
50
60
|
:conditions => ["rs_reputations.value > ?", my_value]
|
51
61
|
) + 1
|
@@ -32,6 +32,24 @@ describe RSEvaluation do
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
+
context "Callback" do
|
36
|
+
describe "#set_source_type_for_sti" do
|
37
|
+
it "should assign source class name as source type if not STI" do
|
38
|
+
question = Question.create!(:text => 'Does this work?', :author_id => @user.id)
|
39
|
+
question.add_evaluation(:total_votes, 5, @user)
|
40
|
+
evaluation = RSEvaluation.find_by_reputation_name_and_source_and_target(:total_votes, @user, question)
|
41
|
+
evaluation.source_type.should == @user.class.name
|
42
|
+
end
|
43
|
+
it "should assign source's ancestors class name where reputation is declared if STI" do
|
44
|
+
designer = Designer.create! :name => 'hiro'
|
45
|
+
programmer = Programmer.create! :name => 'katsuya'
|
46
|
+
programmer.add_evaluation(:leadership, 1, designer)
|
47
|
+
evaluation = RSEvaluation.find_by_reputation_name_and_source_and_target(:leadership, designer, programmer)
|
48
|
+
evaluation.source_type.should == Person.name
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
35
53
|
context "Association" do
|
36
54
|
it "should delete associated reputation message" do
|
37
55
|
@question.add_evaluation(:total_votes, 5, @user)
|
@@ -56,6 +56,24 @@ describe RSReputation do
|
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
59
|
+
context "Callback" do
|
60
|
+
describe "#set_target_type_for_sti" do
|
61
|
+
it "should assign target class name as target type if not STI" do
|
62
|
+
question = Question.create!(:text => 'Does this work?', :author_id => @user.id)
|
63
|
+
question.add_evaluation(:total_votes, 5, @user)
|
64
|
+
rep = RSReputation.find_by_reputation_name_and_target(:total_votes, question)
|
65
|
+
rep.target_type.should == question.class.name
|
66
|
+
end
|
67
|
+
it "should assign target's ancestors class name where reputation is declared if STI" do
|
68
|
+
designer = Designer.create! :name => 'hiro'
|
69
|
+
programmer = Programmer.create! :name => 'katsuya'
|
70
|
+
programmer.add_evaluation(:leadership, 1, designer)
|
71
|
+
rep = RSReputation.find_by_reputation_name_and_target(:leadership, programmer)
|
72
|
+
rep.target_type.should == Person.name
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
59
77
|
context "Association" do
|
60
78
|
before :each do
|
61
79
|
@question = Question.create!(:text => 'What is Twitter?', :author_id => @user.id)
|
@@ -36,14 +36,14 @@ describe ActiveRecord::Base do
|
|
36
36
|
@user.respond_to?(:add_evaluation).should == false
|
37
37
|
end
|
38
38
|
|
39
|
-
it "should add '
|
40
|
-
@user.respond_to?(:
|
41
|
-
@question.respond_to?(:
|
39
|
+
it "should add 'reputation_for' method to a model with reputation" do
|
40
|
+
@user.respond_to?(:reputation_for).should == true
|
41
|
+
@question.respond_to?(:reputation_for).should == true
|
42
42
|
end
|
43
43
|
|
44
|
-
it "should add '
|
45
|
-
@user.respond_to?(:
|
46
|
-
@question.respond_to?(:
|
44
|
+
it "should add 'normalized_reputation_for' method to a model with reputation" do
|
45
|
+
@user.respond_to?(:normalized_reputation_for).should == true
|
46
|
+
@question.respond_to?(:normalized_reputation_for).should == true
|
47
47
|
end
|
48
48
|
|
49
49
|
it "should delete reputations if target is deleted" do
|
@@ -56,12 +56,12 @@ describe ActiveRecord::Base do
|
|
56
56
|
end
|
57
57
|
|
58
58
|
it "should have declared default value if any" do
|
59
|
-
@answer.
|
59
|
+
@answer.reputation_for(:avg_rating).should == 1
|
60
60
|
end
|
61
61
|
|
62
62
|
it "should overwrite reputation definitions if the same reputation name is declared" do
|
63
63
|
Answer.has_reputation(:avg_rating, :source => :user, :aggregated_by => :average, :init_value => 2)
|
64
|
-
Answer.new.
|
64
|
+
Answer.new.reputation_for(:avg_rating).should == 2
|
65
65
|
end
|
66
66
|
end
|
67
67
|
end
|
@@ -75,7 +75,7 @@ describe ActiveRecord::Base do
|
|
75
75
|
describe "#add_evaluation" do
|
76
76
|
it "should create evaluation in case of valid input" do
|
77
77
|
@question.add_evaluation(:total_votes, 1, @user).should be_true
|
78
|
-
@question.
|
78
|
+
@question.reputation_for(:total_votes).should == 1
|
79
79
|
end
|
80
80
|
|
81
81
|
it "should raise exception if invalid reputation name is given" do
|
@@ -96,9 +96,9 @@ describe ActiveRecord::Base do
|
|
96
96
|
it "should add evaluation on appropriate scope" do
|
97
97
|
@phrase.add_evaluation(:difficulty_with_scope, 1, @user, :s1).should be_true
|
98
98
|
@phrase.add_evaluation(:difficulty_with_scope, 2, @user, :s2).should be_true
|
99
|
-
@phrase.
|
100
|
-
@phrase.
|
101
|
-
@phrase.
|
99
|
+
@phrase.reputation_for(:difficulty_with_scope, :s1).should == 1
|
100
|
+
@phrase.reputation_for(:difficulty_with_scope, :s2).should == 2
|
101
|
+
@phrase.reputation_for(:difficulty_with_scope, :s3).should == 0
|
102
102
|
end
|
103
103
|
|
104
104
|
it "should raise exception if invalid scope is given" do
|
@@ -114,22 +114,22 @@ describe ActiveRecord::Base do
|
|
114
114
|
describe "#add_or_update_evaluation" do
|
115
115
|
it "should create evaluation if it does not exist" do
|
116
116
|
@question.add_or_update_evaluation(:total_votes, 1, @user).should be_true
|
117
|
-
@question.
|
117
|
+
@question.reputation_for(:total_votes).should == 1
|
118
118
|
end
|
119
119
|
|
120
120
|
it "should update evaluation if it exists already" do
|
121
121
|
@question.add_evaluation(:total_votes, 1, @user)
|
122
122
|
@question.add_or_update_evaluation(:total_votes, 2, @user).should be_true
|
123
|
-
@question.
|
123
|
+
@question.reputation_for(:total_votes).should == 2
|
124
124
|
end
|
125
125
|
|
126
126
|
context "With Scopes" do
|
127
127
|
it "should add evaluation on appropriate scope if it does not exist" do
|
128
128
|
@phrase.add_or_update_evaluation(:difficulty_with_scope, 1, @user, :s1).should be_true
|
129
129
|
@phrase.add_or_update_evaluation(:difficulty_with_scope, 2, @user, :s2).should be_true
|
130
|
-
@phrase.
|
131
|
-
@phrase.
|
132
|
-
@phrase.
|
130
|
+
@phrase.reputation_for(:difficulty_with_scope, :s1).should == 1
|
131
|
+
@phrase.reputation_for(:difficulty_with_scope, :s2).should == 2
|
132
|
+
@phrase.reputation_for(:difficulty_with_scope, :s3).should == 0
|
133
133
|
end
|
134
134
|
|
135
135
|
it "should update evaluation on appropriate scope if it exists already" do
|
@@ -137,9 +137,9 @@ describe ActiveRecord::Base do
|
|
137
137
|
@phrase.add_evaluation(:difficulty_with_scope, 2, @user, :s2).should be_true
|
138
138
|
@phrase.add_or_update_evaluation(:difficulty_with_scope, 3, @user, :s1).should be_true
|
139
139
|
@phrase.add_or_update_evaluation(:difficulty_with_scope, 5, @user, :s2).should be_true
|
140
|
-
@phrase.
|
141
|
-
@phrase.
|
142
|
-
@phrase.
|
140
|
+
@phrase.reputation_for(:difficulty_with_scope, :s1).should == 3
|
141
|
+
@phrase.reputation_for(:difficulty_with_scope, :s2).should == 5
|
142
|
+
@phrase.reputation_for(:difficulty_with_scope, :s3).should == 0
|
143
143
|
end
|
144
144
|
end
|
145
145
|
end
|
@@ -151,7 +151,7 @@ describe ActiveRecord::Base do
|
|
151
151
|
|
152
152
|
it "should update evaluation in case of valid input" do
|
153
153
|
@question.update_evaluation(:total_votes, 2, @user).should be_true
|
154
|
-
@question.
|
154
|
+
@question.reputation_for(:total_votes).should == 2
|
155
155
|
end
|
156
156
|
|
157
157
|
it "should raise exception if invalid reputation name is given" do
|
@@ -173,9 +173,9 @@ describe ActiveRecord::Base do
|
|
173
173
|
|
174
174
|
it "should update evaluation on appropriate scope" do
|
175
175
|
@phrase.update_evaluation(:difficulty_with_scope, 5, @user, :s2).should be_true
|
176
|
-
@phrase.
|
177
|
-
@phrase.
|
178
|
-
@phrase.
|
176
|
+
@phrase.reputation_for(:difficulty_with_scope, :s1).should == 0
|
177
|
+
@phrase.reputation_for(:difficulty_with_scope, :s2).should == 5
|
178
|
+
@phrase.reputation_for(:difficulty_with_scope, :s3).should == 0
|
179
179
|
end
|
180
180
|
|
181
181
|
it "should raise exception if invalid scope is given" do
|
@@ -195,7 +195,7 @@ describe ActiveRecord::Base do
|
|
195
195
|
|
196
196
|
it "should delete evaluation in case of valid input" do
|
197
197
|
@question.delete_evaluation!(:total_votes, @user)
|
198
|
-
@question.
|
198
|
+
@question.reputation_for(:total_votes).should == 0
|
199
199
|
end
|
200
200
|
|
201
201
|
it "should raise exception if invalid reputation name is given" do
|
@@ -217,9 +217,9 @@ describe ActiveRecord::Base do
|
|
217
217
|
|
218
218
|
it "should delete evaluation on appropriate scope" do
|
219
219
|
@phrase.delete_evaluation!(:difficulty_with_scope, @user, :s2)
|
220
|
-
@phrase.
|
221
|
-
@phrase.
|
222
|
-
@phrase.
|
220
|
+
@phrase.reputation_for(:difficulty_with_scope, :s1).should == 0
|
221
|
+
@phrase.reputation_for(:difficulty_with_scope, :s2).should == 0
|
222
|
+
@phrase.reputation_for(:difficulty_with_scope, :s3).should == 0
|
223
223
|
end
|
224
224
|
|
225
225
|
it "should raise exception if invalid scope is given" do
|
@@ -239,7 +239,7 @@ describe ActiveRecord::Base do
|
|
239
239
|
|
240
240
|
it "should delete evaluation in case of valid input" do
|
241
241
|
@question.delete_evaluation(:total_votes, @user).should be_true
|
242
|
-
@question.
|
242
|
+
@question.reputation_for(:total_votes).should == 0
|
243
243
|
end
|
244
244
|
|
245
245
|
it "should raise exception if invalid reputation name is given" do
|
@@ -257,9 +257,9 @@ describe ActiveRecord::Base do
|
|
257
257
|
|
258
258
|
it "should delete evaluation on appropriate scope" do
|
259
259
|
@phrase.delete_evaluation(:difficulty_with_scope, @user, :s2).should be_true
|
260
|
-
@phrase.
|
261
|
-
@phrase.
|
262
|
-
@phrase.
|
260
|
+
@phrase.reputation_for(:difficulty_with_scope, :s1).should == 0
|
261
|
+
@phrase.reputation_for(:difficulty_with_scope, :s2).should == 0
|
262
|
+
@phrase.reputation_for(:difficulty_with_scope, :s3).should == 0
|
263
263
|
end
|
264
264
|
|
265
265
|
it "should raise exception if invalid scope is given" do
|
@@ -275,13 +275,13 @@ describe ActiveRecord::Base do
|
|
275
275
|
describe "#increase_evaluation" do
|
276
276
|
it "should add evaluation if it does not exist" do
|
277
277
|
@question.increase_evaluation(:total_votes, 2, @user).should be_true
|
278
|
-
@question.
|
278
|
+
@question.reputation_for(:total_votes).should == 2
|
279
279
|
end
|
280
280
|
|
281
281
|
it "should increase evaluation if it exists already" do
|
282
282
|
@question.add_evaluation(:total_votes, 1, @user)
|
283
283
|
@question.increase_evaluation(:total_votes, 2, @user).should be_true
|
284
|
-
@question.
|
284
|
+
@question.reputation_for(:total_votes).should == 3
|
285
285
|
end
|
286
286
|
|
287
287
|
context "With Scopes" do
|
@@ -291,9 +291,9 @@ describe ActiveRecord::Base do
|
|
291
291
|
|
292
292
|
it "should increase evaluation on appropriate scope" do
|
293
293
|
@phrase.increase_evaluation(:difficulty_with_scope, 5, @user, :s2).should be_true
|
294
|
-
@phrase.
|
295
|
-
@phrase.
|
296
|
-
@phrase.
|
294
|
+
@phrase.reputation_for(:difficulty_with_scope, :s1).should == 0
|
295
|
+
@phrase.reputation_for(:difficulty_with_scope, :s2).should == 7
|
296
|
+
@phrase.reputation_for(:difficulty_with_scope, :s3).should == 0
|
297
297
|
end
|
298
298
|
end
|
299
299
|
end
|
@@ -301,13 +301,13 @@ describe ActiveRecord::Base do
|
|
301
301
|
describe "#decrease_evaluation" do
|
302
302
|
it "should add evaluation if it does not exist" do
|
303
303
|
@question.decrease_evaluation(:total_votes, 2, @user).should be_true
|
304
|
-
@question.
|
304
|
+
@question.reputation_for(:total_votes).should == -2
|
305
305
|
end
|
306
306
|
|
307
307
|
it "should increase evaluation if it exists already" do
|
308
308
|
@question.add_evaluation(:total_votes, 1, @user)
|
309
309
|
@question.decrease_evaluation(:total_votes, 2, @user).should be_true
|
310
|
-
@question.
|
310
|
+
@question.reputation_for(:total_votes).should == -1
|
311
311
|
end
|
312
312
|
|
313
313
|
context "With Scopes" do
|
@@ -317,9 +317,9 @@ describe ActiveRecord::Base do
|
|
317
317
|
|
318
318
|
it "should decrease evaluation on appropriate scope" do
|
319
319
|
@phrase.decrease_evaluation(:difficulty_with_scope, 5, @user, :s2).should be_true
|
320
|
-
@phrase.
|
321
|
-
@phrase.
|
322
|
-
@phrase.
|
320
|
+
@phrase.reputation_for(:difficulty_with_scope, :s1).should == 0
|
321
|
+
@phrase.reputation_for(:difficulty_with_scope, :s2).should == -3
|
322
|
+
@phrase.reputation_for(:difficulty_with_scope, :s3).should == 0
|
323
323
|
end
|
324
324
|
end
|
325
325
|
end
|
@@ -336,8 +336,8 @@ describe ActiveRecord::Base do
|
|
336
336
|
it "should affect only reputations with relevant scope" do
|
337
337
|
@trans_ja.add_evaluation(:votes, 1, @user)
|
338
338
|
@trans_fr.add_evaluation(:votes, 2, @user)
|
339
|
-
@phrase.
|
340
|
-
@phrase.
|
339
|
+
@phrase.reputation_for(:maturity, :ja).should == 1
|
340
|
+
@phrase.reputation_for(:maturity, :fr).should == 2
|
341
341
|
end
|
342
342
|
end
|
343
343
|
|
@@ -348,8 +348,8 @@ describe ActiveRecord::Base do
|
|
348
348
|
|
349
349
|
it "should affect only reputations with relevant scope" do
|
350
350
|
@trans_ja.update_evaluation(:votes, 3, @user)
|
351
|
-
@phrase.
|
352
|
-
@phrase.
|
351
|
+
@phrase.reputation_for(:maturity, :ja).should == 3
|
352
|
+
@phrase.reputation_for(:maturity, :fr).should == 0
|
353
353
|
end
|
354
354
|
end
|
355
355
|
|
@@ -360,8 +360,8 @@ describe ActiveRecord::Base do
|
|
360
360
|
|
361
361
|
it "should affect only reputations with relevant scope" do
|
362
362
|
@trans_ja.delete_evaluation!(:votes, @user)
|
363
|
-
@phrase.
|
364
|
-
@phrase.
|
363
|
+
@phrase.reputation_for(:maturity, :ja).should == 0
|
364
|
+
@phrase.reputation_for(:maturity, :fr).should == 0
|
365
365
|
end
|
366
366
|
end
|
367
367
|
end
|
@@ -378,14 +378,14 @@ describe ActiveRecord::Base do
|
|
378
378
|
describe "#add_evaluation" do
|
379
379
|
it "should affect only reputations with relevant scope" do
|
380
380
|
@trans_ja.add_evaluation(:votes, 1, @user)
|
381
|
-
@phrase.
|
381
|
+
@phrase.reputation_for(:maturity_all).should == 1
|
382
382
|
@trans_fr.add_evaluation(:votes, 2, @user)
|
383
|
-
@phrase.
|
383
|
+
@phrase.reputation_for(:maturity_all).should == 3
|
384
384
|
@trans_de.add_evaluation(:votes, 3, @user)
|
385
|
-
@phrase.
|
386
|
-
@phrase.
|
387
|
-
@phrase.
|
388
|
-
@phrase.
|
385
|
+
@phrase.reputation_for(:maturity_all).should == 3
|
386
|
+
@phrase.reputation_for(:maturity, :ja).should == 1
|
387
|
+
@phrase.reputation_for(:maturity, :fr).should == 2
|
388
|
+
@phrase.reputation_for(:maturity, :de).should == 3
|
389
389
|
end
|
390
390
|
end
|
391
391
|
|
@@ -398,7 +398,7 @@ describe ActiveRecord::Base do
|
|
398
398
|
it "should affect only reputations with relevant scope" do
|
399
399
|
@trans_ja.update_evaluation(:votes, 3, @user)
|
400
400
|
@trans_de.update_evaluation(:votes, 2, @user)
|
401
|
-
@phrase.
|
401
|
+
@phrase.reputation_for(:maturity_all).should == 3
|
402
402
|
end
|
403
403
|
end
|
404
404
|
|
@@ -410,9 +410,9 @@ describe ActiveRecord::Base do
|
|
410
410
|
|
411
411
|
it "should affect only reputations with relevant scope" do
|
412
412
|
@trans_de.delete_evaluation!(:votes, @user)
|
413
|
-
@phrase.
|
413
|
+
@phrase.reputation_for(:maturity_all).should == 1
|
414
414
|
@trans_ja.delete_evaluation!(:votes, @user)
|
415
|
-
@phrase.
|
415
|
+
@phrase.reputation_for(:maturity_all).should == 0
|
416
416
|
end
|
417
417
|
end
|
418
418
|
end
|
@@ -26,28 +26,28 @@ describe ActiveRecord::Base do
|
|
26
26
|
end
|
27
27
|
|
28
28
|
context "Primary Reputation" do
|
29
|
-
describe "#
|
29
|
+
describe "#reputation_for" do
|
30
30
|
it "should return 0 as a default" do
|
31
|
-
@question.
|
31
|
+
@question.reputation_for(:total_votes).should == 0
|
32
32
|
end
|
33
33
|
|
34
34
|
it "should return appropriate value in case of valid input" do
|
35
35
|
user2 = User.new(:name => 'dick')
|
36
36
|
@question.add_evaluation(:total_votes, 1, @user)
|
37
37
|
@question.add_evaluation(:total_votes, 1, user2)
|
38
|
-
@question.
|
38
|
+
@question.reputation_for(:total_votes).should == 2
|
39
39
|
end
|
40
40
|
|
41
41
|
it "should raise exception if invalid reputation name is given" do
|
42
|
-
lambda {@question.
|
42
|
+
lambda {@question.reputation_for(:invalid)}.should raise_error(ArgumentError)
|
43
43
|
end
|
44
44
|
|
45
45
|
it "should raise exception if scope is given for reputation with no scopes" do
|
46
|
-
lambda {@question.
|
46
|
+
lambda {@question.reputation_for(:difficulty, :s1)}.should raise_error(ArgumentError)
|
47
47
|
end
|
48
48
|
|
49
49
|
it "should raise exception if scope is not given for reputation with scopes" do
|
50
|
-
lambda {@phrase.
|
50
|
+
lambda {@phrase.reputation_for(:difficulty_with_scope)}.should raise_error(ArgumentError)
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
@@ -83,20 +83,20 @@ describe ActiveRecord::Base do
|
|
83
83
|
end
|
84
84
|
|
85
85
|
context "Non-Primary Reputation with Gathering Aggregation" do
|
86
|
-
describe "#
|
86
|
+
describe "#reputation_for" do
|
87
87
|
it "should always have correct updated value" do
|
88
88
|
question2 = Question.create!(:text => 'Does this work?', :author_id => @user.id)
|
89
|
-
@user.
|
89
|
+
@user.reputation_for(:question_karma).should == 0
|
90
90
|
@question.add_evaluation(:total_votes, 1, @user)
|
91
|
-
@user.
|
91
|
+
@user.reputation_for(:question_karma).should == 1
|
92
92
|
question2.add_evaluation(:total_votes, 1, @user)
|
93
|
-
@user.
|
93
|
+
@user.reputation_for(:question_karma).should == 2
|
94
94
|
end
|
95
95
|
end
|
96
96
|
end
|
97
97
|
|
98
98
|
context "Non-Primary Reputation with Mixing Aggregation" do
|
99
|
-
describe "#
|
99
|
+
describe "#reputation_for" do
|
100
100
|
it "should always have correct updated value" do
|
101
101
|
question = Question.create!(:text => 'Does this work?', :author_id => @user.id)
|
102
102
|
question2 = Question.create!(:text => 'Does this work?', :author_id => @user.id)
|
@@ -108,19 +108,19 @@ describe ActiveRecord::Base do
|
|
108
108
|
answer2 = Answer.create!(:text => 'Yes!', :author_id => @user.id, :question_id => question2.id)
|
109
109
|
answer.add_evaluation(:avg_rating, 3, @user)
|
110
110
|
answer2.add_evaluation(:avg_rating, 2, @user)
|
111
|
-
answer.
|
112
|
-
answer2.
|
113
|
-
@user.
|
114
|
-
@user.
|
115
|
-
@user.
|
111
|
+
answer.reputation_for(:weighted_avg_rating).should == 3
|
112
|
+
answer2.reputation_for(:weighted_avg_rating).should == 4
|
113
|
+
@user.reputation_for(:answer_karma).should be_within(DELTA).of(3.5)
|
114
|
+
@user.reputation_for(:question_karma).should be_within(DELTA).of(2)
|
115
|
+
@user.reputation_for(:karma).should be_within(DELTA).of(1.4)
|
116
116
|
end
|
117
117
|
end
|
118
118
|
end
|
119
119
|
|
120
120
|
context "Normalization" do
|
121
|
-
describe "#
|
121
|
+
describe "#normalized_reputation_for" do
|
122
122
|
it "should return 0 as if there is no data" do
|
123
|
-
@question.
|
123
|
+
@question.normalized_reputation_for(:total_votes).should == 0
|
124
124
|
end
|
125
125
|
|
126
126
|
it "should return appropriate value in case of valid input" do
|
@@ -129,21 +129,21 @@ describe ActiveRecord::Base do
|
|
129
129
|
@question.add_evaluation(:total_votes, 1, @user)
|
130
130
|
question2.add_evaluation(:total_votes, 2, @user)
|
131
131
|
question3.add_evaluation(:total_votes, 3, @user)
|
132
|
-
@question.
|
133
|
-
question2.
|
134
|
-
question3.
|
132
|
+
@question.normalized_reputation_for(:total_votes).should == 0
|
133
|
+
question2.normalized_reputation_for(:total_votes).should == 0.5
|
134
|
+
question3.normalized_reputation_for(:total_votes).should == 1
|
135
135
|
end
|
136
136
|
|
137
137
|
it "should raise exception if invalid reputation name is given" do
|
138
|
-
lambda {@question.
|
138
|
+
lambda {@question.normalized_reputation_for(:invalid)}.should raise_error(ArgumentError)
|
139
139
|
end
|
140
140
|
|
141
141
|
it "should raise exception if scope is given for reputation with no scopes" do
|
142
|
-
lambda {@question.
|
142
|
+
lambda {@question.normalized_reputation_for(:difficulty, :s1)}.should raise_error(ArgumentError)
|
143
143
|
end
|
144
144
|
|
145
145
|
it "should raise exception if scope is not given for reputation with scopes" do
|
146
|
-
lambda {@phrase.
|
146
|
+
lambda {@phrase.normalized_reputation_for(:difficulty_with_scope)}.should raise_error(ArgumentError)
|
147
147
|
end
|
148
148
|
end
|
149
149
|
|
@@ -29,7 +29,7 @@ describe ActiveRecord::Base do
|
|
29
29
|
it "should add scope if the reputation has scopes defined" do
|
30
30
|
Phrase.add_scope_for(:difficulty_with_scope, :s4)
|
31
31
|
@phrase.add_evaluation(:difficulty_with_scope, 2, @user, :s4)
|
32
|
-
@phrase.
|
32
|
+
@phrase.reputation_for(:difficulty_with_scope, :s4).should == 2
|
33
33
|
end
|
34
34
|
|
35
35
|
it "should raise exception if the scope already exist" do
|
@@ -40,4 +40,4 @@ describe ActiveRecord::Base do
|
|
40
40
|
lambda{Question.add_scope_for(:difficulty, :s1)}.should raise_error(ArgumentError)
|
41
41
|
end
|
42
42
|
end
|
43
|
-
end
|
43
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -102,6 +102,12 @@ ActiveRecord::Schema.define do
|
|
102
102
|
t.string :locale
|
103
103
|
t.timestamps
|
104
104
|
end
|
105
|
+
|
106
|
+
create_table :people do |t|
|
107
|
+
t.string :name
|
108
|
+
t.string :type
|
109
|
+
t.timestamps
|
110
|
+
end
|
105
111
|
end
|
106
112
|
|
107
113
|
class User < ActiveRecord::Base
|
@@ -110,16 +116,16 @@ class User < ActiveRecord::Base
|
|
110
116
|
|
111
117
|
has_reputation :karma,
|
112
118
|
:source => [
|
113
|
-
{:reputation => :question_karma},
|
114
|
-
{:reputation => :answer_karma, :weight => 0.2}],
|
119
|
+
{ :reputation => :question_karma },
|
120
|
+
{ :reputation => :answer_karma, :weight => 0.2 }],
|
115
121
|
:aggregated_by => :product
|
116
122
|
|
117
123
|
has_reputation :question_karma,
|
118
|
-
:source => {:reputation => :total_votes, :of => :questions},
|
124
|
+
:source => { :reputation => :total_votes, :of => :questions },
|
119
125
|
:aggregated_by => :sum
|
120
126
|
|
121
127
|
has_reputation :answer_karma,
|
122
|
-
:source => {:reputation => :weighted_avg_rating, :of => :answers},
|
128
|
+
:source => { :reputation => :weighted_avg_rating, :of => :answers },
|
123
129
|
:aggregated_by => :average
|
124
130
|
end
|
125
131
|
|
@@ -130,7 +136,7 @@ class Question < ActiveRecord::Base
|
|
130
136
|
has_reputation :total_votes,
|
131
137
|
:source => :user,
|
132
138
|
:aggregated_by => :sum,
|
133
|
-
:source_of => {:reputation => :question_karma, :of => :author}
|
139
|
+
:source_of => { :reputation => :question_karma, :of => :author }
|
134
140
|
|
135
141
|
has_reputation :difficulty,
|
136
142
|
:source => :user,
|
@@ -143,10 +149,10 @@ class Answer < ActiveRecord::Base
|
|
143
149
|
|
144
150
|
has_reputation :weighted_avg_rating,
|
145
151
|
:source => [
|
146
|
-
{:reputation => :avg_rating},
|
147
|
-
{:reputation => :difficulty, :of => :question}],
|
152
|
+
{ :reputation => :avg_rating },
|
153
|
+
{ :reputation => :difficulty, :of => :question }],
|
148
154
|
:aggregated_by => :product,
|
149
|
-
:source_of => {:reputation => :answer_karma, :of => :author}
|
155
|
+
:source_of => { :reputation => :answer_karma, :of => :author }
|
150
156
|
|
151
157
|
has_reputation :avg_rating,
|
152
158
|
:source => :user,
|
@@ -163,15 +169,15 @@ class Phrase < ActiveRecord::Base
|
|
163
169
|
|
164
170
|
has_reputation :maturity_all,
|
165
171
|
:source => [
|
166
|
-
{:reputation => :maturity, :of => :self, :scope => :ja},
|
167
|
-
{:reputation => :maturity, :of => :self, :scope => :fr}],
|
172
|
+
{ :reputation => :maturity, :of => :self, :scope => :ja },
|
173
|
+
{ :reputation => :maturity, :of => :self, :scope => :fr }],
|
168
174
|
:aggregated_by => :sum
|
169
175
|
|
170
176
|
has_reputation :maturity,
|
171
177
|
:source => { :reputation => :votes, :of => lambda {|this, s| this.translations.for(s)} },
|
172
178
|
:aggregated_by => :sum,
|
173
179
|
:scopes => [:ja, :fr, :de],
|
174
|
-
:source_of => {:reputation => :maturity_all, :of => :self, :defined_for_scope => [:ja, :fr]}
|
180
|
+
:source_of => { :reputation => :maturity_all, :of => :self, :defined_for_scope => [:ja, :fr] }
|
175
181
|
|
176
182
|
has_reputation :difficulty_with_scope,
|
177
183
|
:source => :user,
|
@@ -186,5 +192,17 @@ class Translation < ActiveRecord::Base
|
|
186
192
|
has_reputation :votes,
|
187
193
|
:source => :user,
|
188
194
|
:aggregated_by => :sum,
|
189
|
-
:source_of => {:reputation => :maturity, :of => :phrase, :scope => :locale}
|
195
|
+
:source_of => { :reputation => :maturity, :of => :phrase, :scope => :locale}
|
196
|
+
end
|
197
|
+
|
198
|
+
class Person < ActiveRecord::Base
|
199
|
+
has_reputation :leadership,
|
200
|
+
:source => :person,
|
201
|
+
:aggregated_by => :sum
|
202
|
+
end
|
203
|
+
|
204
|
+
class Programmer < Person
|
205
|
+
end
|
206
|
+
|
207
|
+
class Designer < Person
|
190
208
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-reputation-system
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 3
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
|
-
-
|
8
|
+
- 5
|
9
9
|
- 0
|
10
|
-
version: 1.
|
10
|
+
version: 1.5.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Katsuya Noguchi
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-09-
|
18
|
+
date: 2012-09-15 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
prerelease: false
|