activerecord-reputation-system 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/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
|