activerecord-reputation-system 1.0.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/LICENSE +177 -0
- data/README.md +255 -0
- data/Rakefile +20 -0
- data/lib/generators/reputation_system/reputation_system_generator.rb +35 -0
- data/lib/generators/reputation_system/templates/create_reputation_system.rb +59 -0
- data/lib/models/rs_evaluation.rb +52 -0
- data/lib/models/rs_reputation.rb +181 -0
- data/lib/models/rs_reputation_message.rb +46 -0
- data/lib/reputation_system.rb +28 -0
- data/lib/reputation_system/base.rb +70 -0
- data/lib/reputation_system/evaluation.rb +88 -0
- data/lib/reputation_system/network.rb +222 -0
- data/lib/reputation_system/normalization.rb +50 -0
- data/lib/reputation_system/query.rb +102 -0
- data/lib/reputation_system/reputation.rb +39 -0
- data/lib/reputation_system/scope.rb +37 -0
- data/lib/reputation_system/version.rb +19 -0
- data/spec/models/rs_evaluation_spec.rb +44 -0
- data/spec/models/rs_reputation_message_spec.rb +47 -0
- data/spec/models/rs_reputation_spec.rb +101 -0
- data/spec/reputation_system/base_spec.rb +68 -0
- data/spec/reputation_system/evaluation_spec.rb +289 -0
- data/spec/reputation_system/network_spec.rb +27 -0
- data/spec/reputation_system/normalization_spec.rb +68 -0
- data/spec/reputation_system/query_spec.rb +183 -0
- data/spec/reputation_system/reputation_spec.rb +119 -0
- data/spec/reputation_system/scope_spec.rb +43 -0
- data/spec/spec_helper.rb +190 -0
- metadata +168 -0
@@ -0,0 +1,88 @@
|
|
1
|
+
##
|
2
|
+
# Copyright 2012 Twitter, Inc
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
##
|
16
|
+
|
17
|
+
module ReputationSystem
|
18
|
+
module Evaluation
|
19
|
+
def add_evaluation(reputation_name, value, source, *args)
|
20
|
+
raise ArgumentError, "#{reputation_name.to_s} is not defined for #{self.class.name}" unless ReputationSystem::Network.has_reputation_for?(self.class.name, reputation_name)
|
21
|
+
scope = args.first
|
22
|
+
srn = ReputationSystem::Network.get_scoped_reputation_name(self.class.name, reputation_name, scope)
|
23
|
+
process = ReputationSystem::Network.get_reputation_def(self.class.name, srn)[:aggregated_by]
|
24
|
+
evaluation = RSEvaluation.create_evaluation(srn, value, source, self)
|
25
|
+
rep = RSReputation.find_or_create_reputation(srn, self, process)
|
26
|
+
RSReputation.update_reputation_value_with_new_source(rep, evaluation, 1, process)
|
27
|
+
end
|
28
|
+
|
29
|
+
def update_evaluation(reputation_name, value, source, *args)
|
30
|
+
raise ArgumentError, "#{reputation_name.to_s} is not defined for #{self.class.name}" unless ReputationSystem::Network.has_reputation_for?(self.class.name, reputation_name)
|
31
|
+
scope = args.first
|
32
|
+
srn = ReputationSystem::Network.get_scoped_reputation_name(self.class.name, reputation_name, scope)
|
33
|
+
evaluation = RSEvaluation.find_by_reputation_name_and_source_and_target(srn, source, self)
|
34
|
+
if evaluation.nil?
|
35
|
+
raise ArgumentError, "Given instance of #{source.class.name} has not evaluated #{reputation_name} of the instance of #{self.class.name} yet."
|
36
|
+
else
|
37
|
+
oldValue = evaluation.value
|
38
|
+
evaluation.value = value
|
39
|
+
evaluation.save!
|
40
|
+
process = ReputationSystem::Network.get_reputation_def(self.class.name, srn)[:aggregated_by]
|
41
|
+
rep = RSReputation.find_by_reputation_name_and_target(srn, self)
|
42
|
+
RSReputation.update_reputation_value_with_updated_source(rep, evaluation, oldValue, 1, process)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def add_or_update_evaluation(reputation_name, value, source, *args)
|
47
|
+
scope = args.first
|
48
|
+
evaluation = RSEvaluation.find_by_reputation_name_and_source_and_target(reputation_name, source, self)
|
49
|
+
if evaluation.nil?
|
50
|
+
self.add_evaluation(reputation_name, value, source, scope)
|
51
|
+
else
|
52
|
+
self.update_evaluation(reputation_name, value, source, scope)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def delete_evaluation(reputation_name, source, *args)
|
57
|
+
raise ArgumentError, "#{reputation_name.to_s} is not defined for #{self.class.name}" unless ReputationSystem::Network.has_reputation_for?(self.class.name, reputation_name)
|
58
|
+
scope = args.first
|
59
|
+
srn = ReputationSystem::Network.get_scoped_reputation_name(self.class.name, reputation_name, scope)
|
60
|
+
evaluation = RSEvaluation.find_by_reputation_name_and_source_and_target(srn, source, self)
|
61
|
+
unless evaluation.nil?
|
62
|
+
process = ReputationSystem::Network.get_reputation_def(self.class.name, srn)[:aggregated_by]
|
63
|
+
oldValue = evaluation.value
|
64
|
+
evaluation.value = process == :product ? 1 : 0
|
65
|
+
rep = RSReputation.find_by_reputation_name_and_target(srn, self)
|
66
|
+
RSReputation.update_reputation_value_with_updated_source(rep, evaluation, oldValue, 1, process)
|
67
|
+
evaluation.destroy
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def delete_evaluation!(reputation_name, source, *args)
|
72
|
+
raise ArgumentError, "#{reputation_name.to_s} is not defined for #{self.class.name}" unless ReputationSystem::Network.has_reputation_for?(self.class.name, reputation_name)
|
73
|
+
scope = args.first
|
74
|
+
srn = ReputationSystem::Network.get_scoped_reputation_name(self.class.name, reputation_name, scope)
|
75
|
+
evaluation = RSEvaluation.find_by_reputation_name_and_source_and_target(srn, source, self)
|
76
|
+
if evaluation.nil?
|
77
|
+
raise ArgumentError, "Given instance of #{source.class.name} has not evaluated #{reputation_name} of the instance of #{self.class.name} yet."
|
78
|
+
else
|
79
|
+
process = ReputationSystem::Network.get_reputation_def(self.class.name, srn)[:aggregated_by]
|
80
|
+
oldValue = evaluation.value
|
81
|
+
evaluation.value = process == :product ? 1 : 0
|
82
|
+
rep = RSReputation.find_by_reputation_name_and_target(srn, self)
|
83
|
+
RSReputation.update_reputation_value_with_updated_source(rep, evaluation, oldValue, 1, process)
|
84
|
+
evaluation.destroy
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,222 @@
|
|
1
|
+
##
|
2
|
+
# Copyright 2012 Twitter, Inc
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
##
|
16
|
+
|
17
|
+
module ReputationSystem
|
18
|
+
class Network
|
19
|
+
class << self
|
20
|
+
def has_reputation_for?(class_name, reputation_name)
|
21
|
+
reputation_defs = get_reputation_defs(class_name)
|
22
|
+
reputation_defs[reputation_name.to_sym] && reputation_defs[reputation_name.to_sym][:source]
|
23
|
+
end
|
24
|
+
|
25
|
+
def get_reputation_defs(class_name)
|
26
|
+
network[class_name.to_sym] = {} unless network[class_name.to_sym]
|
27
|
+
network[class_name.to_sym]
|
28
|
+
end
|
29
|
+
|
30
|
+
def get_reputation_def(class_name, reputation_name)
|
31
|
+
reputation_defs = get_reputation_defs(class_name)
|
32
|
+
reputation_defs[reputation_name.to_sym] = {} unless reputation_defs[reputation_name.to_sym]
|
33
|
+
reputation_defs[reputation_name.to_sym]
|
34
|
+
end
|
35
|
+
|
36
|
+
def add_reputation_def(class_name, reputation_name, options)
|
37
|
+
reputation_defs = get_reputation_defs(class_name)
|
38
|
+
options[:source] = convert_to_array_if_hash(options[:source])
|
39
|
+
options[:source_of] = [] unless options[:source_of]
|
40
|
+
options[:source_of] = convert_to_array_if_hash(options[:source_of])
|
41
|
+
assign_self_as_default_value_for_of_attr(options[:source])
|
42
|
+
assign_self_as_default_value_for_of_attr(options[:source_of])
|
43
|
+
reputation_defs[reputation_name] = options
|
44
|
+
options[:source].each do |s|
|
45
|
+
src_class_name = derive_class_name_from_attribute(class_name, s[:of])
|
46
|
+
if has_reputation_for?(src_class_name, s[:reputation])
|
47
|
+
derive_source_of_from_source(class_name, reputation_name, s, src_class_name)
|
48
|
+
else
|
49
|
+
# Because the source class might not have been initialized at this time.
|
50
|
+
derive_source_of_from_source_later(class_name, reputation_name, s, src_class_name)
|
51
|
+
end
|
52
|
+
end unless is_primary_reputation?(class_name, reputation_name)
|
53
|
+
perform_derive_later(class_name, reputation_name)
|
54
|
+
construct_scoped_reputation_options(class_name, reputation_name, options)
|
55
|
+
end
|
56
|
+
|
57
|
+
def remove_reputation_def(class_name, reputation_name)
|
58
|
+
reputation_defs = get_reputation_defs(class_name)
|
59
|
+
reputation_defs.delete(reputation_name.to_sym)
|
60
|
+
end
|
61
|
+
|
62
|
+
def is_primary_reputation?(class_name, reputation_name)
|
63
|
+
options = get_reputation_def(class_name, reputation_name)
|
64
|
+
options[:source].is_a?(Symbol)
|
65
|
+
end
|
66
|
+
|
67
|
+
def add_scope_for(class_name, reputation_name, scope)
|
68
|
+
options = get_reputation_def(class_name, reputation_name)
|
69
|
+
if has_scope?(class_name, reputation_name, scope)
|
70
|
+
raise ArgumentError, "#{scope.to_s} is already defined for #{reputation_name.to_s}"
|
71
|
+
else
|
72
|
+
options[:scopes].push scope.to_sym if options[:scopes]
|
73
|
+
create_scoped_reputation_def(class_name, reputation_name, scope, options)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def has_scopes?(class_name, reputation_name)
|
78
|
+
!get_reputation_def(class_name, reputation_name)[:scopes].nil?
|
79
|
+
end
|
80
|
+
|
81
|
+
def has_scope?(class_name, reputation_name, scope)
|
82
|
+
scopes = get_reputation_def(class_name, reputation_name)[:scopes]
|
83
|
+
scopes && scopes.include?(scope.to_sym)
|
84
|
+
end
|
85
|
+
|
86
|
+
def get_scoped_reputation_name(class_name, reputation_name, scope)
|
87
|
+
scope = scope.to_sym if scope
|
88
|
+
validate_scope_necessity(class_name, reputation_name, scope)
|
89
|
+
validate_scope_existence(class_name, reputation_name, scope)
|
90
|
+
"#{reputation_name.to_s}#{"_#{scope.to_s}" if scope}"
|
91
|
+
end
|
92
|
+
|
93
|
+
def get_weight_of_source_from_reputation_name_of_target(target, source_name, reputation_name)
|
94
|
+
source = get_reputation_def(target.class.name, reputation_name)[:source]
|
95
|
+
if source.is_a?(Array)
|
96
|
+
source.each do |s|
|
97
|
+
scope = target.evaluate_reputation_scope(s[:scope]) if s[:scope]
|
98
|
+
of = target.get_attributes_of(s)
|
99
|
+
srn = get_scoped_reputation_name((of.is_a?(Array) ? of[0] : of ).class.name, s[:reputation], scope)
|
100
|
+
source = s if srn.to_sym == source_name.to_sym
|
101
|
+
end
|
102
|
+
end
|
103
|
+
source[:weight]
|
104
|
+
end
|
105
|
+
|
106
|
+
protected
|
107
|
+
|
108
|
+
def network
|
109
|
+
@network = {} unless @network
|
110
|
+
@network
|
111
|
+
end
|
112
|
+
|
113
|
+
def data_for_derive_later
|
114
|
+
@data_for_derive_later = {} unless @data_for_derive_later
|
115
|
+
@data_for_derive_later
|
116
|
+
end
|
117
|
+
|
118
|
+
def create_scoped_reputation_def(class_name, reputation_name, scope, options)
|
119
|
+
raise ArgumentError, "#{reputation_name.to_s} does not have scope." unless has_scopes?(class_name, reputation_name)
|
120
|
+
scope_options = {}
|
121
|
+
reputation_def = get_reputation_def(class_name, reputation_name)
|
122
|
+
if is_primary_reputation?(class_name, reputation_name)
|
123
|
+
scope_options[:source] = options[:source]
|
124
|
+
else
|
125
|
+
scope_options[:source] = []
|
126
|
+
reputation_def[:source].each do |s|
|
127
|
+
rep = {}
|
128
|
+
rep[:reputation] = s[:reputation]
|
129
|
+
# Passing "this" is not pretty but in some case "instance_exec" method
|
130
|
+
# does not give right context for some reason.
|
131
|
+
# This could be ruby bug. Needs further investigation.
|
132
|
+
rep[:of] = lambda { |this| instance_exec(this, scope.to_s, &s[:of]) } if s[:of].is_a? Proc
|
133
|
+
scope_options[:source].push rep
|
134
|
+
end
|
135
|
+
end
|
136
|
+
source_of = reputation_def[:source_of]
|
137
|
+
source_of.each do |so|
|
138
|
+
if so[:defined_for_scope].nil? || (so[:defined_for_scope] && so[:defined_for_scope].include?(scope.to_sym))
|
139
|
+
scope_options[:source_of] ||= []
|
140
|
+
scope_options[:source_of].push so
|
141
|
+
end
|
142
|
+
end if source_of
|
143
|
+
scope_options[:aggregated_by] = options[:aggregated_by]
|
144
|
+
srn = get_scoped_reputation_name(class_name, reputation_name, scope)
|
145
|
+
network[class_name.to_sym][srn.to_sym] = scope_options
|
146
|
+
end
|
147
|
+
|
148
|
+
def construct_scoped_reputation_options(class_name, reputation_name, options)
|
149
|
+
scopes = get_reputation_def(class_name, reputation_name)[:scopes]
|
150
|
+
scopes.each do |scope|
|
151
|
+
create_scoped_reputation_def(class_name, reputation_name, scope, options)
|
152
|
+
end if scopes
|
153
|
+
end
|
154
|
+
|
155
|
+
def derive_source_of_from_source(class_name, reputation_name, source, src_class_name)
|
156
|
+
if source[:of] && source[:of].is_a?(Symbol) && source[:of] != :self
|
157
|
+
klass = src_class_name.to_s.constantize
|
158
|
+
of_value = class_name.tableize
|
159
|
+
of_value = of_value.chomp('s') unless klass.instance_methods.include?(of_value.to_s) || klass.instance_methods.include?(of_value.to_sym)
|
160
|
+
else
|
161
|
+
of_value = "self"
|
162
|
+
end
|
163
|
+
reputation_def = get_reputation_def(src_class_name, source[:reputation])
|
164
|
+
reputation_def[:source_of] ||= []
|
165
|
+
unless reputation_def[:source_of].any? {|elem| elem[:reputation] == reputation_name.to_sym}
|
166
|
+
reputation_def[:source_of] << {:reputation => reputation_name.to_sym, :of => of_value.to_sym}
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def derive_source_of_from_source_later(class_name, reputation_name, source, src_class_name)
|
171
|
+
reputation = source[:reputation].to_sym
|
172
|
+
src_class_name = src_class_name.to_sym
|
173
|
+
data = data_for_derive_later
|
174
|
+
data[src_class_name] ||= {}
|
175
|
+
data[src_class_name][reputation] ||= {}
|
176
|
+
data[src_class_name][reputation].merge!(:source => source, :class_name => class_name, :reputation_name => reputation_name)
|
177
|
+
end
|
178
|
+
|
179
|
+
def perform_derive_later(src_class_name, reputation)
|
180
|
+
src_class_name = src_class_name.to_sym
|
181
|
+
reputation = reputation.to_sym
|
182
|
+
data = data_for_derive_later
|
183
|
+
if data[src_class_name] && data[src_class_name][reputation]
|
184
|
+
class_name = data[src_class_name][reputation][:class_name]
|
185
|
+
source = data[src_class_name][reputation][:source]
|
186
|
+
reputation_name = data[src_class_name][reputation][:reputation_name]
|
187
|
+
derive_source_of_from_source(class_name, reputation_name, source, src_class_name)
|
188
|
+
data[src_class_name].delete(reputation)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def derive_class_name_from_attribute(class_name, attribute)
|
193
|
+
if attribute && attribute != :self && attribute != "self"
|
194
|
+
attribute.to_s.camelize.chomp('s')
|
195
|
+
else
|
196
|
+
class_name
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def convert_to_array_if_hash(tar)
|
201
|
+
tar = [tar] if tar.is_a? Hash
|
202
|
+
tar
|
203
|
+
end
|
204
|
+
|
205
|
+
def assign_self_as_default_value_for_of_attr(tar)
|
206
|
+
tar = tar.each { |s| s[:of] = :self unless s[:of] } if tar.is_a? Array
|
207
|
+
end
|
208
|
+
|
209
|
+
def validate_scope_necessity(class_name, reputation_name, scope)
|
210
|
+
if scope.nil? && has_scopes?(class_name, reputation_name)
|
211
|
+
raise ArgumentError, "Evaluations of #{reputation_name} must have scope specified."
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def validate_scope_existence(class_name, reputation_name, scope)
|
216
|
+
if !scope.nil? && !has_scope?(class_name, reputation_name, scope)
|
217
|
+
raise ArgumentError, "#{reputation_name} does not have scope #{scope}"
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
##
|
2
|
+
# Copyright 2012 Twitter, Inc
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
##
|
16
|
+
|
17
|
+
module ReputationSystem
|
18
|
+
module Normalization
|
19
|
+
def normalized_reputation_value_for(reputation_name, *args)
|
20
|
+
scope = args.first
|
21
|
+
if !self.class.has_reputation_for?(reputation_name)
|
22
|
+
raise ArgumentError, "#{reputation_name} is not valid"
|
23
|
+
else
|
24
|
+
reputation_name = ReputationSystem::Network.get_scoped_reputation_name(self.class.name, reputation_name, scope)
|
25
|
+
process = ReputationSystem::Network.get_reputation_def(self.class.name, reputation_name)[:aggregated_by]
|
26
|
+
reputation = RSReputation.find_or_create_reputation(reputation_name, self, process)
|
27
|
+
reputation.normalized_value
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def activate_all_reputations
|
32
|
+
RSReputation.find(:all, :conditions => {:target_id => self.id, :target_type => self.class.name, :active => false}).each do |r|
|
33
|
+
r.active = true
|
34
|
+
r.save!
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def deactivate_all_reputations
|
39
|
+
RSReputation.find(:all, :conditions => {:target_id => self.id, :target_type => self.class.name, :active => true}).each do |r|
|
40
|
+
r.active = false
|
41
|
+
r.save!
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def reputations_activated?(reputation_name)
|
46
|
+
r = RSReputation.find(:first, :conditions => {:reputation_name => reputation_name.to_s, :target_id => self.id, :target_type => self.class.name})
|
47
|
+
r ? r.active : false
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
##
|
2
|
+
# Copyright 2012 Twitter, Inc
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
##
|
16
|
+
|
17
|
+
module ReputationSystem
|
18
|
+
module Query
|
19
|
+
def self.included(klass)
|
20
|
+
klass.extend ClassMethods
|
21
|
+
end
|
22
|
+
|
23
|
+
module ClassMethods
|
24
|
+
DELTA = 0.000001
|
25
|
+
|
26
|
+
def find_with_reputation(*args)
|
27
|
+
reputation_name, srn, find_scope, options = parse_query_args(*args)
|
28
|
+
options[:select] ||= sanitize_sql_array(["%s.*", self.table_name])
|
29
|
+
options[:select] = sanitize_sql_array(["%s, COALESCE(rs_reputations.value, 0) AS %s", options[:select], reputation_name])
|
30
|
+
find_options = get_find_options(srn, options)
|
31
|
+
find_options[:conditions][0].gsub!(reputation_name.to_s, "COALESCE(rs_reputations.value, 0)")
|
32
|
+
find(find_scope, find_options)
|
33
|
+
end
|
34
|
+
|
35
|
+
def count_with_reputation(*args)
|
36
|
+
reputation_name, srn, find_scope, options = parse_query_args(*args)
|
37
|
+
find_options = get_find_options(srn, options)
|
38
|
+
find_options[:conditions][0].gsub!(reputation_name.to_s, "COALESCE(rs_reputations.value, 0)")
|
39
|
+
count(find_scope, find_options)
|
40
|
+
end
|
41
|
+
|
42
|
+
def find_with_normalized_reputation(*args)
|
43
|
+
reputation_name, srn, find_scope, options = parse_query_args(*args)
|
44
|
+
max = RSReputation.max(srn, self.name)
|
45
|
+
min = RSReputation.min(srn, self.name)
|
46
|
+
range = max - min
|
47
|
+
options[:select] ||= sanitize_sql_array(["%s.*", self.table_name])
|
48
|
+
if range < DELTA
|
49
|
+
options[:select] = sanitize_sql_array(["%s, (0) AS normalized_%s", options[:select], reputation_name])
|
50
|
+
else
|
51
|
+
options[:select] = sanitize_sql_array(["%s, ((rs_reputations.value - %s) / %s) AS normalized_%s", options[:select], min, range, reputation_name])
|
52
|
+
end
|
53
|
+
find_options = get_find_options(srn, options)
|
54
|
+
find(find_scope, options)
|
55
|
+
end
|
56
|
+
|
57
|
+
def find_with_reputation_sql(*args)
|
58
|
+
reputation_name, srn, find_scope, options = parse_query_args(*args)
|
59
|
+
options[:select] ||= sanitize_sql_array(["%s.*", self.table_name])
|
60
|
+
options[:select] = sanitize_sql_array(["%s, COALESCE(rs_reputations.value, 0) AS %s", options[:select], reputation_name])
|
61
|
+
find_options = get_find_options(srn, options)
|
62
|
+
if respond_to?(:construct_finder_sql, true)
|
63
|
+
construct_finder_sql(find_options)
|
64
|
+
else
|
65
|
+
construct_finder_arel(find_options).to_sql
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
protected
|
70
|
+
def get_find_options(srn, options)
|
71
|
+
options[:joins] ||= []
|
72
|
+
options[:joins] = [options[:joins]] unless options[:joins].is_a? Array
|
73
|
+
temp_joins = sanitize_sql_array(["LEFT JOIN rs_reputations ON %s.id = rs_reputations.target_id AND rs_reputations.target_type = ? AND rs_reputations.reputation_name = ? AND rs_reputations.active = ?", self.name, srn.to_s, true])
|
74
|
+
temp_joins = sanitize_sql_array([temp_joins, self.table_name])
|
75
|
+
options[:joins] << temp_joins
|
76
|
+
options[:conditions] ||= [""]
|
77
|
+
options[:conditions] = [options[:conditions]] unless options[:conditions].is_a? Array
|
78
|
+
options
|
79
|
+
end
|
80
|
+
|
81
|
+
def parse_query_args(*args)
|
82
|
+
case args.length
|
83
|
+
when 2
|
84
|
+
find_scope = args[1]
|
85
|
+
options = {}
|
86
|
+
when 3
|
87
|
+
find_scope = args[1]
|
88
|
+
options = args[2]
|
89
|
+
when 4
|
90
|
+
scope = args[1]
|
91
|
+
find_scope = args[2]
|
92
|
+
options = args[3]
|
93
|
+
else
|
94
|
+
raise ArgumentError, "Expecting 2, 3 or 4 arguments but got #{args.length}"
|
95
|
+
end
|
96
|
+
reputation_name = args[0]
|
97
|
+
srn = ReputationSystem::Network.get_scoped_reputation_name(name, reputation_name, scope)
|
98
|
+
[reputation_name, srn, find_scope, options]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|