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.
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