reputation 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/.infinity_test +35 -0
  2. data/.rspec +2 -0
  3. data/app/models/reputation_behaviour.rb +38 -0
  4. data/app/models/reputation_intermediate_value.rb +22 -0
  5. data/app/models/reputation_rule.rb +107 -0
  6. data/generators/reputation/reputation_generator.rb +11 -0
  7. data/generators/reputation/templates/reputation_create_tables.rb +37 -0
  8. data/lib/reputation.rb +3 -0
  9. data/lib/reputation/engine.rb +8 -0
  10. data/lib/reputation/functions.rb +4 -0
  11. data/lib/reputation/functions/generalised_logistic_curve.rb +37 -0
  12. data/lib/reputation/functions/linear.rb +21 -0
  13. data/lib/reputation/functions/mixin.rb +44 -0
  14. data/lib/reputation/functions/step.rb +27 -0
  15. data/lib/reputation/user.rb +38 -0
  16. data/rails/init.rb +1 -0
  17. data/spec/functions/generalised_logistic_curve_spec.rb +50 -0
  18. data/spec/functions/linear_spec.rb +21 -0
  19. data/spec/functions/step_spec.rb +28 -0
  20. data/spec/helpers/functions.rb +13 -0
  21. data/spec/models/behaviour_spec.rb +31 -0
  22. data/spec/models/intermediate_value_spec.rb +29 -0
  23. data/spec/models/rule_spec.rb +233 -0
  24. data/spec/models/user_spec.rb +25 -0
  25. data/spec/rails_root/Rakefile +10 -0
  26. data/spec/rails_root/app/controllers/application_controller.rb +10 -0
  27. data/spec/rails_root/app/helpers/application_helper.rb +3 -0
  28. data/spec/rails_root/app/models/user.rb +3 -0
  29. data/spec/rails_root/config/boot.rb +114 -0
  30. data/spec/rails_root/config/database.yml +16 -0
  31. data/spec/rails_root/config/environment.rb +41 -0
  32. data/spec/rails_root/config/environments/development.rb +17 -0
  33. data/spec/rails_root/config/environments/test.rb +28 -0
  34. data/spec/rails_root/config/initializers/backtrace_silencers.rb +7 -0
  35. data/spec/rails_root/config/initializers/cookie_verification_secret.rb +7 -0
  36. data/spec/rails_root/config/initializers/inflections.rb +10 -0
  37. data/spec/rails_root/config/initializers/mime_types.rb +5 -0
  38. data/spec/rails_root/config/initializers/new_rails_defaults.rb +21 -0
  39. data/spec/rails_root/config/initializers/session_store.rb +15 -0
  40. data/spec/rails_root/config/routes.rb +43 -0
  41. data/spec/rails_root/db/development.sqlite3 +0 -0
  42. data/spec/rails_root/db/migrate/20110414125319_create_users.rb +11 -0
  43. data/spec/rails_root/db/migrate/20110812160932_reputation_create_tables.rb +37 -0
  44. data/spec/rails_root/db/schema.rb +48 -0
  45. data/spec/rails_root/db/test.sqlite3 +0 -0
  46. data/spec/rails_root/log/development.log +3471 -0
  47. data/spec/rails_root/log/test.log +27696 -0
  48. data/spec/rails_root/script/about +4 -0
  49. data/spec/rails_root/script/console +3 -0
  50. data/spec/rails_root/script/dbconsole +3 -0
  51. data/spec/rails_root/script/destroy +3 -0
  52. data/spec/rails_root/script/generate +3 -0
  53. data/spec/rails_root/script/performance/benchmarker +3 -0
  54. data/spec/rails_root/script/performance/profiler +3 -0
  55. data/spec/rails_root/script/plugin +3 -0
  56. data/spec/rails_root/script/runner +3 -0
  57. data/spec/rails_root/script/server +3 -0
  58. data/spec/scenarios/collection_spec.rb +118 -0
  59. data/spec/scenarios/singular_spec.rb +84 -0
  60. data/spec/spec.opts +3 -0
  61. data/spec/spec_helper.rb +15 -0
  62. 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,2 @@
1
+ --color
2
+ --tty
@@ -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,11 @@
1
+ class ReputationGenerator < Rails::Generator::Base
2
+
3
+ def manifest
4
+
5
+ record do |m|
6
+ m.migration_template "reputation_create_tables.rb", "db/migrate", :migration_file_name => "reputation_create_tables"
7
+ end
8
+
9
+ end
10
+
11
+ 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,3 @@
1
+ require 'reputation/user'
2
+ require 'reputation/functions'
3
+ require 'reputation/engine' if defined?(Rails) && Rails::VERSION::MAJOR == 3
@@ -0,0 +1,8 @@
1
+ require "reputation"
2
+ require "rails"
3
+
4
+ class Reputation
5
+ class Engine < Rails::Engine
6
+
7
+ end
8
+ end
@@ -0,0 +1,4 @@
1
+ require "reputation/functions/mixin"
2
+ require "reputation/functions/linear"
3
+ require "reputation/functions/step"
4
+ require "reputation/functions/generalised_logistic_curve"
@@ -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