qalam_merit 4.0.35
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +21 -0
- data/README.md +358 -0
- data/RELEASING.md +16 -0
- data/Rakefile +44 -0
- data/TESTING.txt +12 -0
- data/config/locales/en.yml +5 -0
- data/lib/merit.rb +95 -0
- data/lib/merit/badge_rules_methods.rb +30 -0
- data/lib/merit/base_target_finder.rb +31 -0
- data/lib/merit/class_methods.rb +41 -0
- data/lib/merit/controller_extensions.rb +71 -0
- data/lib/merit/generators/active_record/install_generator.rb +40 -0
- data/lib/merit/generators/active_record/merit_generator.rb +25 -0
- data/lib/merit/generators/active_record/remove_generator.rb +30 -0
- data/lib/merit/generators/active_record/templates/add_merit_fields_to_model.erb +7 -0
- data/lib/merit/generators/active_record/templates/create_badges_sashes.erb +14 -0
- data/lib/merit/generators/active_record/templates/create_merit_actions.erb +16 -0
- data/lib/merit/generators/active_record/templates/create_merit_activity_logs.erb +12 -0
- data/lib/merit/generators/active_record/templates/create_merit_badges.erb +17 -0
- data/lib/merit/generators/active_record/templates/create_sashes.erb +8 -0
- data/lib/merit/generators/active_record/templates/create_scores_and_points.erb +16 -0
- data/lib/merit/generators/active_record/templates/remove_merit_fields_from_model.erb +7 -0
- data/lib/merit/generators/active_record/templates/remove_merit_tables.erb +12 -0
- data/lib/merit/generators/install_generator.rb +30 -0
- data/lib/merit/generators/merit_generator.rb +33 -0
- data/lib/merit/generators/remove_generator.rb +23 -0
- data/lib/merit/generators/templates/badge.erb +49 -0
- data/lib/merit/generators/templates/badge_sash.erb +21 -0
- data/lib/merit/generators/templates/merit.erb +18 -0
- data/lib/merit/generators/templates/merit_badge_rules.erb +50 -0
- data/lib/merit/generators/templates/merit_point_rules.erb +31 -0
- data/lib/merit/generators/templates/merit_rank_rules.erb +32 -0
- data/lib/merit/generators/templates/sash.erb +15 -0
- data/lib/merit/judge.rb +102 -0
- data/lib/merit/models/action_concern.rb +50 -0
- data/lib/merit/models/active_record/action.rb +11 -0
- data/lib/merit/models/active_record/activity_log.rb +11 -0
- data/lib/merit/models/active_record/badges_sash.rb +13 -0
- data/lib/merit/models/active_record/sash.rb +32 -0
- data/lib/merit/models/active_record/score.rb +25 -0
- data/lib/merit/models/badges_sash_concern.rb +13 -0
- data/lib/merit/models/base/badges_sash.rb +15 -0
- data/lib/merit/models/base/sash.rb +55 -0
- data/lib/merit/models/sash_concern.rb +53 -0
- data/lib/merit/point_rules_methods.rb +33 -0
- data/lib/merit/rank_rules_methods.rb +58 -0
- data/lib/merit/reputation_change_observer.rb +19 -0
- data/lib/merit/rule.rb +35 -0
- data/lib/merit/rules_matcher.rb +24 -0
- data/lib/merit/sash_finder.rb +15 -0
- data/lib/merit/target_finder.rb +43 -0
- data/qalam_merit.gemspec +23 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/controllers/admin/users_controller.rb +9 -0
- data/test/dummy/app/controllers/api/comments_controller.rb +5 -0
- data/test/dummy/app/controllers/api/users_controller.rb +5 -0
- data/test/dummy/app/controllers/application_controller.rb +7 -0
- data/test/dummy/app/controllers/comments_controller.rb +56 -0
- data/test/dummy/app/controllers/registrations_controller.rb +21 -0
- data/test/dummy/app/controllers/users_controller.rb +38 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/models/address.rb +3 -0
- data/test/dummy/app/models/comment.rb +13 -0
- data/test/dummy/app/models/dummy_observer.rb +3 -0
- data/test/dummy/app/models/merit/badge_rules.rb +66 -0
- data/test/dummy/app/models/merit/point_rules.rb +44 -0
- data/test/dummy/app/models/merit/rank_rules.rb +24 -0
- data/test/dummy/app/models/user.rb +23 -0
- data/test/dummy/app/views/admin/users/index.html.erb +26 -0
- data/test/dummy/app/views/comments/_form.html.erb +29 -0
- data/test/dummy/app/views/comments/edit.html.erb +6 -0
- data/test/dummy/app/views/comments/index.html.erb +35 -0
- data/test/dummy/app/views/comments/new.html.erb +5 -0
- data/test/dummy/app/views/comments/show.html.erb +23 -0
- data/test/dummy/app/views/layouts/application.html.erb +24 -0
- data/test/dummy/app/views/users/_form.html.erb +22 -0
- data/test/dummy/app/views/users/edit.html.erb +6 -0
- data/test/dummy/app/views/users/index.html.erb +27 -0
- data/test/dummy/app/views/users/new.html.erb +5 -0
- data/test/dummy/app/views/users/show.html.erb +18 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +26 -0
- data/test/dummy/config/application_api_only.rb +28 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +22 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environment_api_only.rb +7 -0
- data/test/dummy/config/environments/development.rb +24 -0
- data/test/dummy/config/environments/production.rb +51 -0
- data/test/dummy/config/environments/test.rb +36 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/inflections.rb +10 -0
- data/test/dummy/config/initializers/merit.rb +47 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/new_framework_defaults.rb +3 -0
- data/test/dummy/config/initializers/secret_token.rb +12 -0
- data/test/dummy/config/initializers/session_store.rb +8 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/mongoid.yml +13 -0
- data/test/dummy/config/routes.rb +16 -0
- data/test/dummy/db/migrate/20110421191249_create_users.rb +12 -0
- data/test/dummy/db/migrate/20110421191250_create_comments.rb +16 -0
- data/test/dummy/db/migrate/20120318022220_add_fields_to_users.rb +11 -0
- data/test/dummy/db/migrate/20130321082817_add_fields_to_comments.rb +11 -0
- data/test/dummy/db/migrate/20130329224406_create_merit_actions.rb +18 -0
- data/test/dummy/db/migrate/20130329224407_create_merit_activity_logs.rb +15 -0
- data/test/dummy/db/migrate/20130329224408_create_sashes.rb +11 -0
- data/test/dummy/db/migrate/20130329224409_create_badges_sashes.rb +16 -0
- data/test/dummy/db/migrate/20130329224410_create_scores_and_points.rb +20 -0
- data/test/dummy/db/migrate/20140211144001_create_addresses.rb +11 -0
- data/test/dummy/db/migrate/20140819133931_add_target_data_to_merit_actions.rb +5 -0
- data/test/dummy/db/schema.rb +89 -0
- data/test/dummy/db/seeds.rb +19 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +26 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/public/rails.js +404 -0
- data/test/dummy/public/stylesheets/.gitkeep +0 -0
- data/test/dummy/public/stylesheets/scaffold.css +56 -0
- data/test/dummy/script/rails +6 -0
- data/test/integration/navigation_test.rb +332 -0
- data/test/support/integration_case.rb +5 -0
- data/test/test_helper.rb +40 -0
- data/test/unit/action_test.rb +12 -0
- data/test/unit/base_target_finder_test.rb +64 -0
- data/test/unit/merit_unit_test.rb +33 -0
- data/test/unit/rule_unit_test.rb +57 -0
- data/test/unit/rules_matcher_test.rb +37 -0
- data/test/unit/sash_finder_test.rb +27 -0
- data/test/unit/sash_test.rb +104 -0
- data/test/unit/score_test.rb +13 -0
- data/test/unit/target_finder_test.rb +98 -0
- metadata +360 -0
data/lib/merit/judge.rb
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'observer'
|
2
|
+
|
3
|
+
module Merit
|
4
|
+
class Judge
|
5
|
+
include Observable
|
6
|
+
|
7
|
+
def initialize(rule, options = {})
|
8
|
+
@rule = rule
|
9
|
+
@action = options[:action]
|
10
|
+
Merit.observers.each do |class_name|
|
11
|
+
add_observer class_name.constantize.new
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Grant badge if rule applies. If it doesn't, and the badge is temporary,
|
16
|
+
# then remove it.
|
17
|
+
def apply_badges
|
18
|
+
if rule_applies?
|
19
|
+
grant_badges
|
20
|
+
else
|
21
|
+
remove_badges if @rule.temporary
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def apply_points
|
26
|
+
return unless rule_applies?
|
27
|
+
sashes.each do |sash|
|
28
|
+
point = sash.add_points points, category: category
|
29
|
+
notify_observers(
|
30
|
+
description: I18n.t("merit.granted_points", points: points),
|
31
|
+
merit_object: point,
|
32
|
+
sash_id: point.sash_id
|
33
|
+
)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def grant_badges
|
40
|
+
sashes.each do |sash|
|
41
|
+
next unless new_or_multiple?(sash)
|
42
|
+
badge_sash = sash.add_badge badge.id
|
43
|
+
notify_observers(
|
44
|
+
description: I18n.t("merit.granted_badge", badge_name: badge.name),
|
45
|
+
merit_object: badge_sash,
|
46
|
+
sash_id: badge_sash.sash_id
|
47
|
+
)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def remove_badges
|
52
|
+
sashes.each do |sash|
|
53
|
+
sash.rm_badge badge.id
|
54
|
+
notify_observers(
|
55
|
+
description: I18n.t("merit.removed_badge", badge_name: badge.name),
|
56
|
+
sash_id: sash.id
|
57
|
+
)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def new_or_multiple?(sash)
|
62
|
+
!sash.badge_ids.include?(badge.id) || @rule.multiple
|
63
|
+
end
|
64
|
+
|
65
|
+
def rule_object
|
66
|
+
BaseTargetFinder.find(@rule, @action)
|
67
|
+
end
|
68
|
+
|
69
|
+
def rule_applies?
|
70
|
+
@rule.applies? rule_object
|
71
|
+
end
|
72
|
+
|
73
|
+
def points
|
74
|
+
if @rule.score.respond_to?(:call)
|
75
|
+
@rule.score.call(rule_object)
|
76
|
+
else
|
77
|
+
@rule.score
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def category
|
82
|
+
@rule.category
|
83
|
+
end
|
84
|
+
|
85
|
+
def sashes
|
86
|
+
@sashes ||= SashFinder.find @rule, @action
|
87
|
+
end
|
88
|
+
|
89
|
+
def badge
|
90
|
+
@rule.badge
|
91
|
+
end
|
92
|
+
|
93
|
+
def notify_observers(changed_data = {})
|
94
|
+
changed
|
95
|
+
hash = {
|
96
|
+
granted_at: @action.created_at,
|
97
|
+
merit_action_id: @action.id
|
98
|
+
}.merge(changed_data)
|
99
|
+
super(hash)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# Merit::Action general schema
|
2
|
+
# ______________________________________________________________
|
3
|
+
# source | action | target
|
4
|
+
# user_id | method,value | model,id | processed
|
5
|
+
# ______________________________________________________________
|
6
|
+
# 1 | comment nil | List 8 | true
|
7
|
+
# 1 | vote 3 | List 12 | true
|
8
|
+
# 3 | follow nil | User 1 | false
|
9
|
+
# X | create nil | User #{generated_id} | false
|
10
|
+
# ______________________________________________________________
|
11
|
+
#
|
12
|
+
# Rules relate to merit_actions by action name ('controller#action' string)
|
13
|
+
module Merit::Models
|
14
|
+
module ActionConcern
|
15
|
+
extend ActiveSupport::Concern
|
16
|
+
|
17
|
+
class_methods do
|
18
|
+
def check_unprocessed
|
19
|
+
where(processed: false).find_each(&:check_all_rules)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Check rules defined for a merit_action
|
24
|
+
def check_all_rules
|
25
|
+
mark_as_processed!
|
26
|
+
return if had_errors
|
27
|
+
|
28
|
+
check_rules rules_matcher.select_from(Merit::AppBadgeRules), :badges
|
29
|
+
check_rules rules_matcher.select_from(Merit::AppPointRules), :points
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def check_rules(rules_array, badges_or_points)
|
35
|
+
rules_array.each do |rule|
|
36
|
+
judge = Merit::Judge.new(rule, action: self)
|
37
|
+
judge.public_send(:"apply_#{badges_or_points}")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def mark_as_processed!
|
42
|
+
self.processed = true
|
43
|
+
save
|
44
|
+
end
|
45
|
+
|
46
|
+
def rules_matcher
|
47
|
+
@rules_matcher ||= ::Merit::RulesMatcher.new(target_model, action_method)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Merit::Models::ActiveRecord
|
2
|
+
class Action < ActiveRecord::Base
|
3
|
+
include Merit::Models::ActionConcern
|
4
|
+
|
5
|
+
self.table_name = :merit_actions
|
6
|
+
|
7
|
+
has_many :activity_logs, class_name: 'Merit::ActivityLog'
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class Merit::Action < Merit::Models::ActiveRecord::Action; end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Merit::Models::ActiveRecord
|
2
|
+
class ActivityLog < ActiveRecord::Base
|
3
|
+
self.table_name = :merit_activity_logs
|
4
|
+
|
5
|
+
belongs_to :action, class_name: 'Merit::Action'
|
6
|
+
belongs_to :related_change, polymorphic: true, optional: true
|
7
|
+
has_one :sash, through: :related_change
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class Merit::ActivityLog < Merit::Models::ActiveRecord::ActivityLog; end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Merit::Models::ActiveRecord
|
2
|
+
class BadgesSash < ActiveRecord::Base
|
3
|
+
include Merit::Models::BadgesSashConcern
|
4
|
+
|
5
|
+
has_many :activity_logs,
|
6
|
+
class_name: 'Merit::ActivityLog',
|
7
|
+
as: :related_change
|
8
|
+
|
9
|
+
validates_presence_of :badge_id, :sash
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class Merit::BadgesSash < Merit::Models::ActiveRecord::BadgesSash; end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Merit::Models::ActiveRecord
|
2
|
+
# Sash is a container for reputation data for meritable models. It's an
|
3
|
+
# indirection between meritable models and badges and scores (one to one
|
4
|
+
# relationship).
|
5
|
+
#
|
6
|
+
# It's existence make join models like badges_users and scores_users
|
7
|
+
# unnecessary. It should be transparent at the application.
|
8
|
+
class Sash < ActiveRecord::Base
|
9
|
+
include Merit::Models::SashConcern
|
10
|
+
|
11
|
+
has_many :badges_sashes, dependent: :destroy
|
12
|
+
has_many :scores, dependent: :destroy, class_name: 'Merit::Score'
|
13
|
+
|
14
|
+
after_create :create_scores
|
15
|
+
|
16
|
+
# Retrieve all points from a category or none if category doesn't exist
|
17
|
+
# By default retrieve all Points
|
18
|
+
# @param category [String] The category
|
19
|
+
# @return [ActiveRecord::Relation] containing the points
|
20
|
+
def score_points(options = {})
|
21
|
+
scope = Merit::Score::Point
|
22
|
+
.joins(:score)
|
23
|
+
.where('merit_scores.sash_id = ?', id)
|
24
|
+
if (category = options[:category])
|
25
|
+
scope = scope.where('merit_scores.category = ?', category)
|
26
|
+
end
|
27
|
+
scope
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Merit::Sash < Merit::Models::ActiveRecord::Sash; end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Merit::Models::ActiveRecord
|
2
|
+
class Score < ActiveRecord::Base
|
3
|
+
self.table_name = :merit_scores
|
4
|
+
belongs_to :sash
|
5
|
+
has_many :score_points,
|
6
|
+
dependent: :destroy,
|
7
|
+
class_name: 'Merit::Score::Point'
|
8
|
+
|
9
|
+
def points
|
10
|
+
score_points.group(:score_id).sum(:num_points).values.first || 0
|
11
|
+
end
|
12
|
+
|
13
|
+
class Point < ActiveRecord::Base
|
14
|
+
belongs_to :score, class_name: 'Merit::Score'
|
15
|
+
has_one :sash, through: :score
|
16
|
+
has_many :activity_logs,
|
17
|
+
class_name: 'Merit::ActivityLog',
|
18
|
+
as: :related_change
|
19
|
+
delegate :sash_id, to: :score
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Merit::Score < Merit::Models::ActiveRecord::Score; end
|
25
|
+
class Merit::Score::Point < Merit::Models::ActiveRecord::Score::Point; end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Merit
|
2
|
+
module Models::Base
|
3
|
+
module Sash
|
4
|
+
def badges
|
5
|
+
badge_ids.map { |id| Merit::Badge.find id }
|
6
|
+
end
|
7
|
+
|
8
|
+
def badge_ids
|
9
|
+
badges_sashes.map(&:badge_id)
|
10
|
+
end
|
11
|
+
|
12
|
+
def add_badge(badge_id)
|
13
|
+
bs = Merit::BadgesSash.new(badge_id: badge_id.to_i)
|
14
|
+
badges_sashes << bs
|
15
|
+
bs
|
16
|
+
end
|
17
|
+
|
18
|
+
def rm_badge(badge_id)
|
19
|
+
badges_sashes.where(badge_id: badge_id.to_i).first.try(:destroy)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Retrieve the number of points from a category
|
23
|
+
# By default all points are summed up
|
24
|
+
# @param category [String] The category
|
25
|
+
# @return [Integer] The number of points
|
26
|
+
def points(options = {})
|
27
|
+
if (category = options[:category])
|
28
|
+
scores.where(category: category).first.try(:points) || 0
|
29
|
+
else
|
30
|
+
scores.reduce(0) { |sum, score| sum + score.points }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def add_points(num_points, options = {})
|
35
|
+
point = Merit::Score::Point.new
|
36
|
+
point.num_points = num_points
|
37
|
+
scores
|
38
|
+
.where(category: options[:category] || 'default')
|
39
|
+
.first_or_create
|
40
|
+
.score_points << point
|
41
|
+
point
|
42
|
+
end
|
43
|
+
|
44
|
+
def subtract_points(num_points, options = {})
|
45
|
+
add_points(-num_points, options)
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def create_scores
|
51
|
+
scores << Merit::Score.create
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Merit::Models
|
2
|
+
module SashConcern
|
3
|
+
def badges
|
4
|
+
badge_ids.map { |id| Merit::Badge.find id }
|
5
|
+
end
|
6
|
+
|
7
|
+
def badge_ids
|
8
|
+
badges_sashes.map(&:badge_id)
|
9
|
+
end
|
10
|
+
|
11
|
+
def add_badge(badge_id)
|
12
|
+
bs = Merit::BadgesSash.new(badge_id: badge_id.to_i)
|
13
|
+
badges_sashes << bs
|
14
|
+
bs
|
15
|
+
end
|
16
|
+
|
17
|
+
def rm_badge(badge_id)
|
18
|
+
badges_sashes.where(badge_id: badge_id.to_i).first.try(:destroy)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Retrieve the number of points from a category
|
22
|
+
# By default all points are summed up
|
23
|
+
# @param category [String] The category
|
24
|
+
# @return [Integer] The number of points
|
25
|
+
def points(options = {})
|
26
|
+
if (category = options[:category])
|
27
|
+
scores.where(category: category).first.try(:points) || 0
|
28
|
+
else
|
29
|
+
scores.reduce(0) { |sum, score| sum + score.points }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def add_points(num_points, options = {})
|
34
|
+
point = Merit::Score::Point.new
|
35
|
+
point.num_points = num_points
|
36
|
+
scores
|
37
|
+
.where(category: options[:category] || 'default')
|
38
|
+
.first_or_create
|
39
|
+
.score_points << point
|
40
|
+
point
|
41
|
+
end
|
42
|
+
|
43
|
+
def subtract_points(num_points, options = {})
|
44
|
+
add_points(-num_points, options)
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def create_scores
|
50
|
+
scores << Merit::Score.create
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Merit
|
2
|
+
# Points are a simple integer value which are given to "meritable" resources
|
3
|
+
# according to rules in +app/models/merit/point_rules.rb+. They are given on
|
4
|
+
# actions-triggered.
|
5
|
+
module PointRulesMethods
|
6
|
+
# Define rules on certaing actions for giving points
|
7
|
+
def score(points, *args, &block)
|
8
|
+
options = args.extract_options!
|
9
|
+
options_to = options.fetch(:to) { :action_user }
|
10
|
+
|
11
|
+
actions = Array.wrap(options[:on])
|
12
|
+
|
13
|
+
Array.wrap(options_to).each do |to|
|
14
|
+
rule = Rule.new
|
15
|
+
rule.score = points
|
16
|
+
rule.to = to
|
17
|
+
rule.block = block
|
18
|
+
rule.category = options.fetch(:category) { :default }
|
19
|
+
rule.model_name = options[:model_name] if options[:model_name]
|
20
|
+
|
21
|
+
actions.each do |action|
|
22
|
+
defined_rules[action] ||= []
|
23
|
+
defined_rules[action] << rule
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Currently defined rules
|
29
|
+
def defined_rules
|
30
|
+
@defined_rules ||= {}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Merit
|
2
|
+
# 5 stars is a common ranking use case. They are not given at specified
|
3
|
+
# actions like badges, you should define a cron job to test if ranks are to
|
4
|
+
# be granted.
|
5
|
+
#
|
6
|
+
# +set_rank+ accepts:
|
7
|
+
# * :+level+ ranking level (greater is better)
|
8
|
+
# * :+to+ model or scope to check if new rankings apply
|
9
|
+
# * :+level_name+ attribute name (default is empty and results in 'level'
|
10
|
+
# attribute, if set it's appended like 'level_#{level_name}')
|
11
|
+
module RankRulesMethods
|
12
|
+
# Populates +defined_rules+ hash with following hierarchy:
|
13
|
+
# defined_rules[ModelToRank] = { levels => blocks }
|
14
|
+
def set_rank(*args, &block)
|
15
|
+
options = args.extract_options!
|
16
|
+
|
17
|
+
rule = Rule.new
|
18
|
+
rule.block = block
|
19
|
+
if options[:level_name].present?
|
20
|
+
rule.level_name = "level_#{options[:level_name]}"
|
21
|
+
else
|
22
|
+
rule.level_name = 'level'
|
23
|
+
end
|
24
|
+
|
25
|
+
defined_rules[options[:to]] ||= {}
|
26
|
+
defined_rules[options[:to]].merge!(options[:level] => rule)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Not part of merit after_filter. To be called asynchronously:
|
30
|
+
# Merit::RankRules.new.check_rank_rules
|
31
|
+
def check_rank_rules
|
32
|
+
defined_rules.each do |scoped_model, level_and_rules|
|
33
|
+
level_and_rules.sort.each do |level, rule|
|
34
|
+
grant_when_applies(scoped_model, rule, level)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Currently defined rules
|
40
|
+
def defined_rules
|
41
|
+
@defined_rules ||= {}
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def grant_when_applies(scoped_model, rule, level)
|
47
|
+
scoped_model.where("#{rule.level_name} < #{level}").each do |object|
|
48
|
+
next unless rule.applies?(object)
|
49
|
+
|
50
|
+
object.update_attribute rule.level_name, level
|
51
|
+
end
|
52
|
+
rescue ActiveRecord::StatementInvalid
|
53
|
+
str = "Error while granting rankings. Probably you need to add
|
54
|
+
#{rule.level_name} column to #{scoped_model.class.name}."
|
55
|
+
raise RankAttributeNotDefined, str
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|