reputation 0.0.2
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/.infinity_test +35 -0
- data/.rspec +2 -0
- data/app/models/reputation_behaviour.rb +38 -0
- data/app/models/reputation_intermediate_value.rb +22 -0
- data/app/models/reputation_rule.rb +107 -0
- data/generators/reputation/reputation_generator.rb +11 -0
- data/generators/reputation/templates/reputation_create_tables.rb +37 -0
- data/lib/reputation.rb +3 -0
- data/lib/reputation/engine.rb +8 -0
- data/lib/reputation/functions.rb +4 -0
- data/lib/reputation/functions/generalised_logistic_curve.rb +37 -0
- data/lib/reputation/functions/linear.rb +21 -0
- data/lib/reputation/functions/mixin.rb +44 -0
- data/lib/reputation/functions/step.rb +27 -0
- data/lib/reputation/user.rb +38 -0
- data/rails/init.rb +1 -0
- data/spec/functions/generalised_logistic_curve_spec.rb +50 -0
- data/spec/functions/linear_spec.rb +21 -0
- data/spec/functions/step_spec.rb +28 -0
- data/spec/helpers/functions.rb +13 -0
- data/spec/models/behaviour_spec.rb +31 -0
- data/spec/models/intermediate_value_spec.rb +29 -0
- data/spec/models/rule_spec.rb +233 -0
- data/spec/models/user_spec.rb +25 -0
- data/spec/rails_root/Rakefile +10 -0
- data/spec/rails_root/app/controllers/application_controller.rb +10 -0
- data/spec/rails_root/app/helpers/application_helper.rb +3 -0
- data/spec/rails_root/app/models/user.rb +3 -0
- data/spec/rails_root/config/boot.rb +114 -0
- data/spec/rails_root/config/database.yml +16 -0
- data/spec/rails_root/config/environment.rb +41 -0
- data/spec/rails_root/config/environments/development.rb +17 -0
- data/spec/rails_root/config/environments/test.rb +28 -0
- data/spec/rails_root/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/rails_root/config/initializers/cookie_verification_secret.rb +7 -0
- data/spec/rails_root/config/initializers/inflections.rb +10 -0
- data/spec/rails_root/config/initializers/mime_types.rb +5 -0
- data/spec/rails_root/config/initializers/new_rails_defaults.rb +21 -0
- data/spec/rails_root/config/initializers/session_store.rb +15 -0
- data/spec/rails_root/config/routes.rb +43 -0
- data/spec/rails_root/db/development.sqlite3 +0 -0
- data/spec/rails_root/db/migrate/20110414125319_create_users.rb +11 -0
- data/spec/rails_root/db/migrate/20110812160932_reputation_create_tables.rb +37 -0
- data/spec/rails_root/db/schema.rb +48 -0
- data/spec/rails_root/db/test.sqlite3 +0 -0
- data/spec/rails_root/log/development.log +3471 -0
- data/spec/rails_root/log/test.log +27696 -0
- data/spec/rails_root/script/about +4 -0
- data/spec/rails_root/script/console +3 -0
- data/spec/rails_root/script/dbconsole +3 -0
- data/spec/rails_root/script/destroy +3 -0
- data/spec/rails_root/script/generate +3 -0
- data/spec/rails_root/script/performance/benchmarker +3 -0
- data/spec/rails_root/script/performance/profiler +3 -0
- data/spec/rails_root/script/plugin +3 -0
- data/spec/rails_root/script/runner +3 -0
- data/spec/rails_root/script/server +3 -0
- data/spec/scenarios/collection_spec.rb +118 -0
- data/spec/scenarios/singular_spec.rb +84 -0
- data/spec/spec.opts +3 -0
- data/spec/spec_helper.rb +15 -0
- metadata +289 -0
data/.infinity_test
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
infinity_test do
|
2
|
+
|
3
|
+
notifications :growl do
|
4
|
+
show_images :mode => :mario_bros
|
5
|
+
end
|
6
|
+
|
7
|
+
use :rubies => %w(1.9.2 1.9.1 1.8.7 ree jruby macruby rbx), :test_framework => :rspec
|
8
|
+
|
9
|
+
# use :specific_options => {'jruby' => 'J-cp bar/whisky-in-the.jar:.'}
|
10
|
+
|
11
|
+
before(:each_ruby) do |environment|
|
12
|
+
# ...
|
13
|
+
end
|
14
|
+
|
15
|
+
after(:each_ruby) do |environment|
|
16
|
+
# ...
|
17
|
+
end
|
18
|
+
|
19
|
+
before_run do
|
20
|
+
clear :terminal
|
21
|
+
end
|
22
|
+
|
23
|
+
after_run do
|
24
|
+
# ...
|
25
|
+
end
|
26
|
+
|
27
|
+
#heuristics('my_pattern') do |file|
|
28
|
+
# ...
|
29
|
+
#end
|
30
|
+
|
31
|
+
replace_patterns do |application|
|
32
|
+
# ...
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
data/.rspec
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
class ReputationBehaviour < ActiveRecord::Base
|
2
|
+
|
3
|
+
belongs_to :user
|
4
|
+
belongs_to :rule, :class_name => 'ReputationRule'
|
5
|
+
|
6
|
+
before_save :recalculate_rule
|
7
|
+
|
8
|
+
attr_accessible :user, :rule, :metric
|
9
|
+
|
10
|
+
validates_presence_of :user
|
11
|
+
validates_presence_of :rule
|
12
|
+
validates_numericality_of :metric
|
13
|
+
|
14
|
+
validates_uniqueness_of :user_id, :scope => :rule_id
|
15
|
+
|
16
|
+
# Set the rule, can either be a ReputationRule or the name of the rule
|
17
|
+
#
|
18
|
+
# @example
|
19
|
+
# behaviour.rule = 'rule_1'
|
20
|
+
# # or
|
21
|
+
# behaviour.rule = ReputationRule.find(:first)
|
22
|
+
|
23
|
+
def rule=(rule)
|
24
|
+
write_attribute :rule_id, case rule
|
25
|
+
when ReputationRule
|
26
|
+
rule.id
|
27
|
+
else
|
28
|
+
ReputationRule.find_by_name( rule ).try(:id)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def recalculate_rule
|
35
|
+
rule.recalculate_intermediate_values_for user
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class ReputationIntermediateValue < ActiveRecord::Base
|
2
|
+
|
3
|
+
belongs_to :user
|
4
|
+
belongs_to :rule, :class_name => 'ReputationRule'
|
5
|
+
|
6
|
+
validates_presence_of :user
|
7
|
+
validates_presence_of :rule
|
8
|
+
validates_numericality_of :value
|
9
|
+
|
10
|
+
validates_uniqueness_of :name, :scope => [:user_id, :rule_id]
|
11
|
+
|
12
|
+
attr_accessible :user, :rule, :name, :value
|
13
|
+
|
14
|
+
def initialize(args = {})
|
15
|
+
options = {
|
16
|
+
:value => 0
|
17
|
+
}.merge(args)
|
18
|
+
|
19
|
+
super options
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
class ReputationRule < ActiveRecord::Base
|
2
|
+
|
3
|
+
has_many :intermediate_values, :class_name => 'ReputationIntermediateValue', :foreign_key => 'rule_id'
|
4
|
+
has_many :behaviours, :class_name => 'ReputationBehaviour', :foreign_key => 'rule_id'
|
5
|
+
|
6
|
+
serialize :constants, Hash
|
7
|
+
serialize :aggregate_constants, Hash
|
8
|
+
|
9
|
+
validates_presence_of :name
|
10
|
+
validates_uniqueness_of :name
|
11
|
+
validates_numericality_of :weight, :only_integer => true, :greater_than => 0
|
12
|
+
|
13
|
+
attr_accessible :name, :kind, :weight, :function, :constants, :aggregate_function, :aggregate_constants
|
14
|
+
|
15
|
+
attr_readonly :kind
|
16
|
+
|
17
|
+
def initialize(*args)
|
18
|
+
args[0] = {
|
19
|
+
:weight => 1,
|
20
|
+
:kind => 'singular',
|
21
|
+
:function => 'linear',
|
22
|
+
:constants => { :m => 1 },
|
23
|
+
:aggregate_function => 'linear',
|
24
|
+
:aggregate_constants => { :m => 1 }
|
25
|
+
}.merge(args.first||{})
|
26
|
+
|
27
|
+
super *args
|
28
|
+
end
|
29
|
+
|
30
|
+
# Return the total score for a certain user
|
31
|
+
#
|
32
|
+
# @param [User]
|
33
|
+
def self.value_for(user)
|
34
|
+
all.inject(0){|total,r| total + r.value_for( user ) }
|
35
|
+
end
|
36
|
+
|
37
|
+
# Lookup the weighting relative to all other rules
|
38
|
+
#
|
39
|
+
def normalized_weighting
|
40
|
+
BigDecimal(weight.to_s) / ReputationRule.sum('weight')
|
41
|
+
end
|
42
|
+
|
43
|
+
# Calculate the score for a certain user
|
44
|
+
#
|
45
|
+
# @param [User]
|
46
|
+
def value_for(user)
|
47
|
+
behaviour = user.behaviours.find_by_rule_id id
|
48
|
+
if behaviour
|
49
|
+
case kind
|
50
|
+
when 'singular'
|
51
|
+
f(behaviour.metric) * normalized_weighting
|
52
|
+
when 'collection'
|
53
|
+
ivo = intermediate_values.find_by_user_id_and_name(user.id,kind)
|
54
|
+
iv = ivo ? ivo.value : 0
|
55
|
+
aggregate_f(
|
56
|
+
f(behaviour.metric) + iv
|
57
|
+
) * normalized_weighting
|
58
|
+
end
|
59
|
+
else
|
60
|
+
0
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Return the function object defined by :function and :constants
|
65
|
+
#
|
66
|
+
# @return [Reputation::Functions::Linear, Reputation::Functions::Step, Reputation::Functions::GeneralisedLogisticCurve]
|
67
|
+
def function
|
68
|
+
build_function(super, constants)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Return the aggregate function object defined by :aggregate_function and :aggregate_constants
|
72
|
+
#
|
73
|
+
# @return [Reputation::Functions::Linear, Reputation::Functions::Step, Reputation::Functions::GeneralisedLogisticCurve]
|
74
|
+
def aggregate_function
|
75
|
+
build_function(super, aggregate_constants)
|
76
|
+
end
|
77
|
+
|
78
|
+
def recalculate_intermediate_values_for(user) # :nodoc:
|
79
|
+
behaviour = user.behaviours.find_by_rule_id(self.id)
|
80
|
+
if behaviour
|
81
|
+
case kind
|
82
|
+
when 'collection'
|
83
|
+
iv = intermediate_values.find_by_user_id_and_name(user.id,kind) || intermediate_values.build(:user => user, :name => kind)
|
84
|
+
iv.value += f(behaviour.metric)
|
85
|
+
iv.save!
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
# Delegate f to the function#f
|
93
|
+
def f(*args)
|
94
|
+
function.f(*args)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Delegate aggregate_f to the aggregate_function#f
|
98
|
+
def aggregate_f(*args)
|
99
|
+
aggregate_function.f(*args)
|
100
|
+
end
|
101
|
+
|
102
|
+
def build_function(name, constants)
|
103
|
+
klass = "Reputation::Functions::#{name.to_s.camelcase}".constantize
|
104
|
+
klass.new(constants)
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class ReputationCreateTables < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :reputation_rules do |t|
|
4
|
+
t.string :name
|
5
|
+
t.integer :weight
|
6
|
+
t.string :kind
|
7
|
+
t.string :function
|
8
|
+
t.string :constants
|
9
|
+
t.string :aggregate_function
|
10
|
+
t.string :aggregate_constants
|
11
|
+
end
|
12
|
+
|
13
|
+
create_table :reputation_intermediate_values do |t|
|
14
|
+
t.references :user
|
15
|
+
t.references :rule
|
16
|
+
t.string :name
|
17
|
+
t.decimal :value
|
18
|
+
end
|
19
|
+
add_index :reputation_intermediate_values, :user_id
|
20
|
+
add_index :reputation_intermediate_values, :rule_id
|
21
|
+
add_index :reputation_intermediate_values, :name
|
22
|
+
|
23
|
+
create_table :reputation_behaviours do |t|
|
24
|
+
t.references :user
|
25
|
+
t.references :rule
|
26
|
+
t.decimal :metric
|
27
|
+
end
|
28
|
+
add_index :reputation_behaviours, :user_id
|
29
|
+
add_index :reputation_behaviours, :rule_id
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.down
|
33
|
+
drop_table :reputation_rules
|
34
|
+
drop_table :reputation_user_rule
|
35
|
+
drop_table :reputation_behaviours
|
36
|
+
end
|
37
|
+
end
|
data/lib/reputation.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
class Reputation
|
2
|
+
class Functions
|
3
|
+
class GeneralisedLogisticCurve
|
4
|
+
|
5
|
+
include Mixin
|
6
|
+
|
7
|
+
attr_accessor :a, :k, :b, :v, :q, :m
|
8
|
+
|
9
|
+
def initialize(args = {})
|
10
|
+
constants = {
|
11
|
+
:a => -1, # lower asymptote
|
12
|
+
:k => 1, # upper asymptote
|
13
|
+
:b => 10, # growth rate
|
14
|
+
:v => 0.5, # affects near which asymptote maximum growth occurs
|
15
|
+
:q => 0.5, # depends on the value Y(0)
|
16
|
+
:m => 0, # the time of maximum growth if Q=v
|
17
|
+
}.merge( args )
|
18
|
+
@a = constants[:a]
|
19
|
+
@k = constants[:k]
|
20
|
+
@b = constants[:b]
|
21
|
+
@v = constants[:v]
|
22
|
+
@q = constants[:q]
|
23
|
+
@m = constants[:m]
|
24
|
+
end
|
25
|
+
|
26
|
+
def f(t)
|
27
|
+
limit(
|
28
|
+
a.to_f + (
|
29
|
+
(k.to_f - a.to_f) /
|
30
|
+
(1 + q.to_f * Math.exp(-1 * b.to_f*(t.to_f-m.to_f)) )**(1.to_f/v.to_f)
|
31
|
+
)
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class Reputation
|
2
|
+
class Functions
|
3
|
+
class Linear
|
4
|
+
|
5
|
+
include Mixin
|
6
|
+
|
7
|
+
attr_accessor :m
|
8
|
+
|
9
|
+
def initialize(args = {})
|
10
|
+
constants = { :m => 1 }.merge( args )
|
11
|
+
@m = constants[:m]
|
12
|
+
end
|
13
|
+
|
14
|
+
def f(x)
|
15
|
+
limit x * m
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require "googlecharts"
|
2
|
+
require "launchy"
|
3
|
+
|
4
|
+
class Reputation
|
5
|
+
class Functions
|
6
|
+
module Mixin
|
7
|
+
|
8
|
+
# Produces the URL for a google chart of the function
|
9
|
+
#
|
10
|
+
def google_chart_url
|
11
|
+
points = 1000
|
12
|
+
range = Array.new(points){|i| (i-points/2) * 0.01 }
|
13
|
+
y = range.map{|x| f(x) }
|
14
|
+
c = Googlecharts.line(
|
15
|
+
:size => '800x200',
|
16
|
+
:axis_with_labels => 'x,y',
|
17
|
+
:title => self.class.name,
|
18
|
+
:data => y,
|
19
|
+
:axis_range => [[range.first,range.last],[y.min,y.max]]
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Opens the google chart for the function in the browser
|
24
|
+
#
|
25
|
+
def google_chart
|
26
|
+
Launchy::Browser.run google_chart_url
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def limit(r)
|
32
|
+
if r > 1
|
33
|
+
1
|
34
|
+
elsif r < -1
|
35
|
+
-1
|
36
|
+
else
|
37
|
+
r
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class Reputation
|
2
|
+
class Functions
|
3
|
+
class Step
|
4
|
+
|
5
|
+
include Mixin
|
6
|
+
|
7
|
+
attr_accessor :a, :k, :c
|
8
|
+
|
9
|
+
def initialize(args = {})
|
10
|
+
constants = {
|
11
|
+
:a => -1, # lower asymptote
|
12
|
+
:k => 1, # upper asymptote
|
13
|
+
:c => 0.5 # point of switch
|
14
|
+
}.merge( args )
|
15
|
+
@c = constants[:c]
|
16
|
+
@k = constants[:k]
|
17
|
+
@a = constants[:a]
|
18
|
+
end
|
19
|
+
|
20
|
+
def f(x)
|
21
|
+
limit( x.to_f >= c.to_f ? k : a )
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
class Reputation
|
2
|
+
module User
|
3
|
+
|
4
|
+
def self.included(model)
|
5
|
+
model.send(:include, InstanceMethods)
|
6
|
+
model.send(:include, Relations)
|
7
|
+
end
|
8
|
+
|
9
|
+
module Relations
|
10
|
+
def self.included(model)
|
11
|
+
model.class_eval do
|
12
|
+
|
13
|
+
has_many :behaviours, :class_name => "ReputationBehaviour" do
|
14
|
+
def add(rule, metric)
|
15
|
+
rule = ReputationRule.find_by_name rule unless rule.is_a? ReputationRule
|
16
|
+
b = proxy_reflection.klass.find_by_user_id_and_rule_id proxy_owner.id, rule.id
|
17
|
+
b ||= proxy_reflection.klass.new :rule => rule, :user => proxy_owner
|
18
|
+
b.update_attribute :metric, metric
|
19
|
+
proxy_owner.behaviours(true)
|
20
|
+
b
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
module InstanceMethods
|
29
|
+
|
30
|
+
# Returns the reputation value for the user
|
31
|
+
#
|
32
|
+
def reputation
|
33
|
+
ReputationRule.value_for(self)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|