merit 1.1.1 → 1.1.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/Gemfile.lock +1 -1
- data/README.md +5 -9
- data/UPGRADING.md +7 -0
- data/app/models/badge.rb +0 -36
- data/app/models/merit/action.rb +98 -0
- data/lib/generators/merit/templates/merit_badge_rules.rb +2 -0
- data/lib/generators/merit/templates/merit_point_rules.rb +2 -0
- data/lib/generators/merit/templates/merit_rank_rules.rb +2 -0
- data/lib/merit.rb +1 -0
- data/lib/merit/controller_extensions.rb +2 -2
- data/lib/merit/judge.rb +53 -0
- data/lib/merit/model_additions.rb +1 -1
- data/lib/merit/models/active_record/merit/action.rb +5 -0
- data/lib/merit/models/active_record/sash.rb +2 -0
- data/lib/merit/models/mongo_mapper/merit/action.rb +15 -0
- data/lib/merit/models/mongoid/merit/action.rb +15 -0
- data/lib/merit/rule.rb +1 -49
- data/lib/merit/rules_points_methods.rb +1 -1
- data/merit.gemspec +1 -1
- data/test/dummy/app/models/comment.rb +4 -0
- data/test/dummy/app/models/merit/badge_rules.rb +4 -0
- data/test/dummy/config/initializers/merit.rb +5 -1
- data/test/dummy/db/seeds.rb +3 -1
- data/test/integration/navigation_test.rb +26 -2
- data/test/merit_unit_test.rb +6 -18
- metadata +7 -6
- data/app/models/merit_action.rb +0 -87
- data/lib/merit/models/active_record/merit_action.rb +0 -2
- data/lib/merit/models/mongo_mapper/merit_action.rb +0 -13
- data/lib/merit/models/mongoid/merit_action.rb +0 -13
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -53,16 +53,16 @@ end
|
|
|
53
53
|
|
|
54
54
|
## Grant manually
|
|
55
55
|
|
|
56
|
-
You may also grant badges "by hand"
|
|
56
|
+
You may also grant badges "by hand":
|
|
57
57
|
|
|
58
58
|
```ruby
|
|
59
|
-
|
|
59
|
+
current_user.add_badge(badge.id)
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
Similarly you can
|
|
62
|
+
Similarly you can remove badges that have been granted:
|
|
63
63
|
|
|
64
64
|
```ruby
|
|
65
|
-
|
|
65
|
+
current_user.rm_badge(badge.id)
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
---
|
|
@@ -144,11 +144,7 @@ set_rank :level => 3, :to => Commiter.active do |commiter|
|
|
|
144
144
|
end
|
|
145
145
|
```
|
|
146
146
|
|
|
147
|
-
---
|
|
148
147
|
|
|
149
148
|
# To-do list
|
|
150
149
|
|
|
151
|
-
*
|
|
152
|
-
* Should namespace app/models into Merit module.
|
|
153
|
-
* :value parameter (for star voting for example) should be configurable
|
|
154
|
-
(depends on params[:value] on the controller).
|
|
150
|
+
* Should namespace Badge, BadgesSash and Sash into Merit module.
|
data/UPGRADING.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Upgrading
|
|
2
2
|
|
|
3
|
+
## to 1.2.0 (unreleased)
|
|
4
|
+
|
|
5
|
+
* `Badge#grant_to(meritable_object)` no longer exists. Use
|
|
6
|
+
`meritable_object.add_badge(badge_id)` (may add badges more than once).
|
|
7
|
+
* `Badge#delete_from(meritable_object)` no longer exists. Use
|
|
8
|
+
`meritable_object.rm_badge(badge_id)`.
|
|
9
|
+
|
|
3
10
|
## to 1.1.0
|
|
4
11
|
|
|
5
12
|
Code refactorings. Support for Ruby 1.8.7 has been dropped.
|
data/app/models/badge.rb
CHANGED
|
@@ -31,40 +31,4 @@ class Badge
|
|
|
31
31
|
end
|
|
32
32
|
badge
|
|
33
33
|
end
|
|
34
|
-
|
|
35
|
-
# Grant badge to sash
|
|
36
|
-
# Accepts :allow_multiple boolean option, defaults to false
|
|
37
|
-
def grant_to(object_or_sash, *args)
|
|
38
|
-
options = args.extract_options!
|
|
39
|
-
options[:allow_multiple] ||= false
|
|
40
|
-
sash = sash_from(object_or_sash)
|
|
41
|
-
|
|
42
|
-
if !sash.badge_ids.include?(id) || options[:allow_multiple]
|
|
43
|
-
sash.add_badge(id)
|
|
44
|
-
true
|
|
45
|
-
else
|
|
46
|
-
false
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
# Take out badge from sash
|
|
51
|
-
def delete_from(object_or_sash)
|
|
52
|
-
sash = sash_from(object_or_sash)
|
|
53
|
-
if sash.badge_ids.include?(id)
|
|
54
|
-
sash.rm_badge(id)
|
|
55
|
-
true
|
|
56
|
-
else
|
|
57
|
-
false
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
private
|
|
62
|
-
|
|
63
|
-
def sash_from(object_or_sash)
|
|
64
|
-
if object_or_sash.kind_of?(Sash)
|
|
65
|
-
object_or_sash
|
|
66
|
-
else
|
|
67
|
-
object_or_sash._sash
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
34
|
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
require "merit/models/#{Merit.orm}/merit/action"
|
|
2
|
+
|
|
3
|
+
# Merit::Action general schema
|
|
4
|
+
# ______________________________________________________________
|
|
5
|
+
# source | action | target
|
|
6
|
+
# user_id | method,value | model,id | processed
|
|
7
|
+
# ______________________________________________________________
|
|
8
|
+
# 1 | comment nil | List 8 | true
|
|
9
|
+
# 1 | vote 3 | List 12 | true
|
|
10
|
+
# 3 | follow nil | User 1 | false
|
|
11
|
+
# X | create nil | User #{generated_id} | false
|
|
12
|
+
# ______________________________________________________________
|
|
13
|
+
#
|
|
14
|
+
# Rules relate to merit_actions by action name ('controller#action' string)
|
|
15
|
+
module Merit
|
|
16
|
+
class Action
|
|
17
|
+
attr_accessible :user_id, :action_method, :action_value, :had_errors,
|
|
18
|
+
:target_model, :target_id, :processed, :log
|
|
19
|
+
|
|
20
|
+
def self.check_unprocessed_rules
|
|
21
|
+
where(:processed => false).map &:check_all_rules
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Check rules defined for a merit_action
|
|
25
|
+
def check_all_rules
|
|
26
|
+
processed!
|
|
27
|
+
return if had_errors
|
|
28
|
+
|
|
29
|
+
badge_rules = ::Merit::AppBadgeRules[action_str] || []
|
|
30
|
+
point_rules = ::Merit::AppPointRules[action_str] || []
|
|
31
|
+
check_rules badge_rules, :badges
|
|
32
|
+
check_rules point_rules, :points
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def log_activity(str)
|
|
36
|
+
self.update_attribute :log, "#{self.log}#{str}|"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def target_object(model_name = nil)
|
|
40
|
+
# Grab custom model_name from Rule, or target_model from Merit::Action triggered
|
|
41
|
+
klass = model_name || target_model
|
|
42
|
+
klass.singularize.camelize.constantize.find_by_id(target_id)
|
|
43
|
+
rescue => e
|
|
44
|
+
Rails.logger.warn "[merit] no target_obj found: #{e}"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def check_rules(rules_array, badges_or_points)
|
|
50
|
+
rules_array.each do |rule|
|
|
51
|
+
judge = Judge.new sash_to_badge(rule), rule, :action => self
|
|
52
|
+
judge.send :"apply_#{badges_or_points}"
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Subject to badge: source_user or target.user?
|
|
57
|
+
def sash_to_badge(rule)
|
|
58
|
+
if rule.to == :itself
|
|
59
|
+
target = target_object(rule.model_name)
|
|
60
|
+
else
|
|
61
|
+
target = target(rule.to)
|
|
62
|
+
end
|
|
63
|
+
target.try(:_sash)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def target(to)
|
|
67
|
+
(to == :action_user) ? action_user : other_target(to)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def action_str
|
|
71
|
+
"#{target_model}\##{action_method}"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def action_user
|
|
75
|
+
begin
|
|
76
|
+
Merit.user_model.find(user_id)
|
|
77
|
+
rescue ActiveRecord::RecordNotFound
|
|
78
|
+
Rails.logger.warn "[merit] no #{Merit.user_model} found with id #{user_id}"
|
|
79
|
+
return
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def other_target(to)
|
|
84
|
+
begin
|
|
85
|
+
target_object.send(to)
|
|
86
|
+
rescue NoMethodError
|
|
87
|
+
Rails.logger.warn "[merit] NoMethodError on '#{target_object.inspect}.#{to}' (called from Merit::Action#other_target)"
|
|
88
|
+
return
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Mark merit_action as processed
|
|
93
|
+
def processed!
|
|
94
|
+
self.processed = true
|
|
95
|
+
self.save
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# Be sure to restart your server when you modify this file.
|
|
2
|
+
#
|
|
1
3
|
# Points are a simple integer value which are given to "meritable" resources
|
|
2
4
|
# according to rules in +app/models/merit/point_rules.rb+. They are given on
|
|
3
5
|
# actions-triggered, either to the action user or to the method (or array of
|
data/lib/merit.rb
CHANGED
|
@@ -7,7 +7,7 @@ module Merit
|
|
|
7
7
|
base.after_filter do |controller|
|
|
8
8
|
return unless rules_defined?
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
Merit::Action.create(
|
|
11
11
|
:user_id => send(Merit.current_user_method).try(:id),
|
|
12
12
|
:action_method => action_name,
|
|
13
13
|
:action_value => params[:value],
|
|
@@ -17,7 +17,7 @@ module Merit
|
|
|
17
17
|
).id
|
|
18
18
|
|
|
19
19
|
if Merit.checks_on_each_request
|
|
20
|
-
|
|
20
|
+
Merit::Action.check_unprocessed_rules
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
23
|
end
|
data/lib/merit/judge.rb
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
module Merit
|
|
2
|
+
class Judge
|
|
3
|
+
def initialize(sash, rule, options = {})
|
|
4
|
+
@sash = sash
|
|
5
|
+
@rule = rule
|
|
6
|
+
# FIXME: Too much context:
|
|
7
|
+
# A Judge should apply reputation independently of the action
|
|
8
|
+
@action = options[:action]
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Grant badge if rule applies. If it doesn't, and the badge is temporary,
|
|
12
|
+
# then remove it.
|
|
13
|
+
def apply_badges
|
|
14
|
+
if rule_applies?
|
|
15
|
+
grant_badge if new_or_multiple?
|
|
16
|
+
else
|
|
17
|
+
remove_badge if @rule.temporary
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def apply_points
|
|
22
|
+
return unless rule_applies?
|
|
23
|
+
@sash.add_points @rule.score, @action.inspect[0..240]
|
|
24
|
+
@action.log_activity "points_granted:#{@rule.score}"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def grant_badge
|
|
30
|
+
@sash.add_badge(badge.id)
|
|
31
|
+
to_action_user = (@rule.to.to_sym == :action_user ? '_to_action_user' : '')
|
|
32
|
+
@action.log_activity "badge_granted#{to_action_user}:#{badge.id}"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def remove_badge
|
|
36
|
+
@sash.rm_badge(badge.id)
|
|
37
|
+
@action.log_activity "badge_removed:#{badge.id}"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def new_or_multiple?
|
|
41
|
+
!@sash.badge_ids.include?(badge.id) || @rule.multiple
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# FIXME: Too tightly coupled three objects
|
|
45
|
+
def rule_applies?
|
|
46
|
+
@rule.applies? @action.target_object(@rule.model_name)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def badge
|
|
50
|
+
@rule.badge
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -28,7 +28,7 @@ module Merit
|
|
|
28
28
|
# From Rails 3.2 we can override association methods to do so
|
|
29
29
|
# transparently, but merit supports Rails ~> 3.0.0. See:
|
|
30
30
|
# http://blog.hasmanythrough.com/2012/1/20/modularized-association-methods-in-rails-3-2
|
|
31
|
-
%w(badge_ids badges points add_points substract_points).each do |method|
|
|
31
|
+
%w(badge_ids badges points add_badge rm_badge add_points substract_points).each do |method|
|
|
32
32
|
delegate method, to: :_sash
|
|
33
33
|
end
|
|
34
34
|
define_method(:_sash) do
|
|
@@ -28,6 +28,7 @@ class Sash < ActiveRecord::Base
|
|
|
28
28
|
badges_sashes.find_by_badge_id(badge_id).try(:destroy)
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
+
|
|
31
32
|
def points(category = 'default')
|
|
32
33
|
scores.where(:category => category).first.points
|
|
33
34
|
end
|
|
@@ -38,6 +39,7 @@ class Sash < ActiveRecord::Base
|
|
|
38
39
|
point.num_points = num_points
|
|
39
40
|
self.scores.where(:category => category).first.score_points << point
|
|
40
41
|
end
|
|
42
|
+
|
|
41
43
|
def substract_points(num_points, log = 'Manually granted through `add_points`', category = 'default')
|
|
42
44
|
add_points -num_points, log, category
|
|
43
45
|
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Merit
|
|
2
|
+
class Action
|
|
3
|
+
include MongoMapper::Document
|
|
4
|
+
|
|
5
|
+
key :user_id, String
|
|
6
|
+
key :action_method, String
|
|
7
|
+
key :action_value, Integer
|
|
8
|
+
key :had_errors, Boolean
|
|
9
|
+
key :target_model, String
|
|
10
|
+
key :target_id, String
|
|
11
|
+
key :processed, Boolean, :default => false
|
|
12
|
+
key :log, String
|
|
13
|
+
timestamps!
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Merit
|
|
2
|
+
class Action
|
|
3
|
+
include Mongoid::Document
|
|
4
|
+
include Mongoid::Timestamps
|
|
5
|
+
|
|
6
|
+
belongs_to :user
|
|
7
|
+
field :action_method
|
|
8
|
+
field :action_value, :type => Integer
|
|
9
|
+
field :had_errors, :type => Boolean
|
|
10
|
+
|
|
11
|
+
belongs_to :target, :polymorphic => true
|
|
12
|
+
field :processed, :type => Boolean, :default => false
|
|
13
|
+
field :log
|
|
14
|
+
end
|
|
15
|
+
end
|
data/lib/merit/rule.rb
CHANGED
|
@@ -15,62 +15,14 @@ module Merit
|
|
|
15
15
|
if target_obj.present?
|
|
16
16
|
block.call(target_obj)
|
|
17
17
|
else
|
|
18
|
-
# TODO RAISE ERROR
|
|
19
18
|
Rails.logger.warn "[merit] no target_obj found on Rule#applies?"
|
|
20
19
|
false
|
|
21
20
|
end
|
|
22
|
-
|
|
21
|
+
when 0
|
|
23
22
|
block.call
|
|
24
23
|
end
|
|
25
24
|
end
|
|
26
25
|
|
|
27
|
-
def temporary?; self.temporary; end
|
|
28
|
-
|
|
29
|
-
# Grant badge if rule applies. If it doesn't, and the badge is temporary,
|
|
30
|
-
# then remove it.
|
|
31
|
-
def apply_badges(action)
|
|
32
|
-
unless (sash = sash_to_badge(action))
|
|
33
|
-
Rails.logger.warn "[merit] no sash found on Rule#apply_badges for action #{action.inspect}"
|
|
34
|
-
return
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
if applies? action.target_object(model_name)
|
|
38
|
-
if badge.grant_to(sash, :allow_multiple => self.multiple)
|
|
39
|
-
to_action_user = (to.to_sym == :action_user ? '_to_action_user' : '')
|
|
40
|
-
action.log_activity "badge_granted#{to_action_user}:#{badge.id}"
|
|
41
|
-
end
|
|
42
|
-
elsif temporary?
|
|
43
|
-
if badge.delete_from(sash)
|
|
44
|
-
action.log_activity "badge_removed:#{badge.id}"
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def apply_points(action)
|
|
50
|
-
unless (sash = sash_to_badge(action))
|
|
51
|
-
Rails.logger.warn "[merit] no sash found on Rule#grant_points"
|
|
52
|
-
return
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
if applies? action.target_object(model_name)
|
|
56
|
-
sash.add_points self.score, action.inspect[0..240]
|
|
57
|
-
action.log_activity "points_granted:#{self.score}"
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
# Subject to badge: source_user or target.user?
|
|
62
|
-
# Knows (law of demeter):
|
|
63
|
-
# * Rule#model_name & Rule#to
|
|
64
|
-
# * MeritAction#object & MeritAction#target_object
|
|
65
|
-
def sash_to_badge(action)
|
|
66
|
-
if to == :itself
|
|
67
|
-
target = action.target_object(model_name)
|
|
68
|
-
else
|
|
69
|
-
target = action.target(to)
|
|
70
|
-
end
|
|
71
|
-
target._sash if target
|
|
72
|
-
end
|
|
73
|
-
|
|
74
26
|
# Get rule's related Badge.
|
|
75
27
|
def badge
|
|
76
28
|
@badge ||= Badge.find_by_name_and_level(badge_name, level)
|
data/merit.gemspec
CHANGED
|
@@ -4,7 +4,7 @@ Gem::Specification.new do |s|
|
|
|
4
4
|
s.description = "Manage badges, points and rankings (reputation) of resources in a Rails application."
|
|
5
5
|
s.homepage = "http://github.com/tute/merit"
|
|
6
6
|
s.files = `git ls-files`.split("\n").reject{|f| f =~ /^\./ }
|
|
7
|
-
s.version = '1.1.
|
|
7
|
+
s.version = '1.1.2'
|
|
8
8
|
s.authors = ["Tute Costa"]
|
|
9
9
|
s.email = 'tutecosta@gmail.com'
|
|
10
10
|
s.add_dependency 'ambry', '~> 0.3.0'
|
|
@@ -33,6 +33,10 @@ module Merit
|
|
|
33
33
|
grant_on 'comments#create', :badge => 'commenter', :level => 10 do |comment|
|
|
34
34
|
comment.user.comments.count >= 10
|
|
35
35
|
end
|
|
36
|
+
# Testing badge granting in more than one rule per action with different targets
|
|
37
|
+
grant_on 'comments#create', :badge => 'has_commenter_friend', :to => :friend do |comment|
|
|
38
|
+
comment.user.comments.count >= 10
|
|
39
|
+
end
|
|
36
40
|
|
|
37
41
|
# If it has at least 10 votes, grant relevant-commenter badge
|
|
38
42
|
grant_on 'comments#vote', :badge => 'relevant-commenter', :to => :user do |comment|
|
|
@@ -12,12 +12,16 @@ badge_id = 0
|
|
|
12
12
|
[{
|
|
13
13
|
:id => (badge_id = badge_id+1),
|
|
14
14
|
:name => 'commenter',
|
|
15
|
-
:description => 'You\'ve participated good in our boards!',
|
|
15
|
+
:description => 'You\'ve participated good in our boards! (level 10)',
|
|
16
16
|
:level => 10
|
|
17
17
|
}, {
|
|
18
18
|
:id => (badge_id = badge_id+1),
|
|
19
19
|
:name => 'commenter',
|
|
20
20
|
:description => 'You\'ve participated great in our boards!'
|
|
21
|
+
}, {
|
|
22
|
+
:id => (badge_id = badge_id+1),
|
|
23
|
+
:name => 'has_commenter_friend',
|
|
24
|
+
:description => 'Testing badge granting in more than one rule per action, with different targets'
|
|
21
25
|
}, {
|
|
22
26
|
:id => (badge_id = badge_id+1),
|
|
23
27
|
:name => 'relevant-commenter',
|
data/test/dummy/db/seeds.rb
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
commenter = User.create(:name => 'the-commenter-guy')
|
|
2
2
|
social = User.create(:name => 'social-skilled-man')
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
User.create(:name => 'bored-or-speechless')
|
|
5
|
+
User.create(:name => 'friend')
|
|
4
6
|
|
|
5
7
|
(1..9).each do |i|
|
|
6
8
|
Comment.create(
|
|
@@ -10,6 +10,26 @@ class NavigationTest < ActiveSupport::IntegrationCase
|
|
|
10
10
|
assert_equal [Badge.by_name('just-registered').first], user.badges.to_a
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
+
test 'User#add_badge should add one badge, #rm_badge should delete one' do
|
|
14
|
+
user = User.create(:name => 'test-user')
|
|
15
|
+
assert_equal [], user.badges.to_a
|
|
16
|
+
|
|
17
|
+
badge = Badge.first
|
|
18
|
+
user.add_badge badge.id
|
|
19
|
+
user.add_badge badge.id
|
|
20
|
+
assert_equal [badge, badge], user.badges.to_a
|
|
21
|
+
|
|
22
|
+
user.rm_badge badge.id
|
|
23
|
+
assert_equal [badge], user.reload.badges.to_a
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
test 'Remove inexistent badge should do nothing' do
|
|
27
|
+
user = User.create(:name => 'test-user')
|
|
28
|
+
assert_equal [], user.badges.to_a
|
|
29
|
+
user.rm_badge 1
|
|
30
|
+
assert_equal [], user.badges.to_a
|
|
31
|
+
end
|
|
32
|
+
|
|
13
33
|
test 'users#index should grant badge multiple times' do
|
|
14
34
|
user = User.create(:name => 'test-user')
|
|
15
35
|
visit '/users'
|
|
@@ -22,6 +42,9 @@ class NavigationTest < ActiveSupport::IntegrationCase
|
|
|
22
42
|
test 'user workflow should grant some badges at some times' do
|
|
23
43
|
# Commented 9 times, no badges yet
|
|
24
44
|
user = User.create(:name => 'test-user')
|
|
45
|
+
# Create needed friend user object
|
|
46
|
+
friend = User.create(:name => 'friend')
|
|
47
|
+
|
|
25
48
|
(1..9).each do |i|
|
|
26
49
|
Comment.create(
|
|
27
50
|
:name => "Title #{i}",
|
|
@@ -47,8 +70,8 @@ class NavigationTest < ActiveSupport::IntegrationCase
|
|
|
47
70
|
fill_in 'User', :with => user.id
|
|
48
71
|
click_button('Create Comment')
|
|
49
72
|
|
|
50
|
-
|
|
51
|
-
assert_equal [Badge.by_name('
|
|
73
|
+
assert_equal [Badge.by_name('commenter').by_level(10).first], user.reload.badges.to_a
|
|
74
|
+
assert_equal [Badge.by_name('has_commenter_friend').first], friend.reload.badges.to_a
|
|
52
75
|
|
|
53
76
|
# Vote (to 5) a user's comment, assert relevant-commenter badge granted
|
|
54
77
|
relevant_comment = user.comments.where(:votes => 8).first
|
|
@@ -85,6 +108,7 @@ class NavigationTest < ActiveSupport::IntegrationCase
|
|
|
85
108
|
end
|
|
86
109
|
|
|
87
110
|
test 'user workflow should add up points at some times' do
|
|
111
|
+
User.delete_all
|
|
88
112
|
user = User.create(:name => 'test-user')
|
|
89
113
|
assert_equal 0, user.points, 'User should start with 0 points'
|
|
90
114
|
|
data/test/merit_unit_test.rb
CHANGED
|
@@ -16,7 +16,7 @@ class MeritUnitTest < ActiveSupport::TestCase
|
|
|
16
16
|
assert !rule.applies?, 'block which expects object should return false if no argument'
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
test "Rule#badge should get related badge or raise Merit
|
|
19
|
+
test "Rule#badge should get related badge or raise Merit::BadgeNotFound" do
|
|
20
20
|
rule = Merit::Rule.new
|
|
21
21
|
rule.badge_name = 'inexistent'
|
|
22
22
|
assert_raise Merit::BadgeNotFound do
|
|
@@ -28,13 +28,16 @@ class MeritUnitTest < ActiveSupport::TestCase
|
|
|
28
28
|
assert_equal Badge.find(98), rule.badge
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
-
test "
|
|
31
|
+
test "Extends only meritable ActiveRecord models" do
|
|
32
32
|
class MeritableModel < ActiveRecord::Base
|
|
33
33
|
def self.columns; @columns ||= []; end
|
|
34
34
|
has_merit
|
|
35
35
|
end
|
|
36
|
+
class OtherModels < ActiveRecord::Base
|
|
37
|
+
def self.columns; @columns ||= []; end
|
|
38
|
+
end
|
|
36
39
|
assert MeritableModel.method_defined?(:points), 'Meritable model should respond to merit methods'
|
|
37
|
-
assert !
|
|
40
|
+
assert !OtherModels.method_defined?(:points), 'Other models shouldn\'t respond to merit methods'
|
|
38
41
|
end
|
|
39
42
|
|
|
40
43
|
# Do we need this non-documented attribute?
|
|
@@ -44,19 +47,4 @@ class MeritUnitTest < ActiveSupport::TestCase
|
|
|
44
47
|
badge_sash.set_notified!
|
|
45
48
|
assert badge_sash.notified_user
|
|
46
49
|
end
|
|
47
|
-
|
|
48
|
-
test "Badge#grant_to allow_multiple option" do
|
|
49
|
-
badge = Badge.create(:id => 99, :name => 'test-badge')
|
|
50
|
-
sash = Sash.create(:id => 99)
|
|
51
|
-
|
|
52
|
-
assert_equal 0, sash.badge_ids.count
|
|
53
|
-
|
|
54
|
-
assert badge.grant_to(sash)
|
|
55
|
-
assert_equal 1, sash.badge_ids.count
|
|
56
|
-
assert !badge.grant_to(sash)
|
|
57
|
-
assert_equal 1, sash.badge_ids.count
|
|
58
|
-
|
|
59
|
-
assert badge.grant_to(sash, :allow_multiple => true)
|
|
60
|
-
assert_equal 2, sash.badge_ids.count
|
|
61
|
-
end
|
|
62
50
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: merit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.1.
|
|
4
|
+
version: 1.1.2
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2012-
|
|
12
|
+
date: 2012-12-04 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: ambry
|
|
@@ -154,7 +154,7 @@ files:
|
|
|
154
154
|
- TESTING.txt
|
|
155
155
|
- UPGRADING.md
|
|
156
156
|
- app/models/badge.rb
|
|
157
|
-
- app/models/
|
|
157
|
+
- app/models/merit/action.rb
|
|
158
158
|
- lib/generators/active_record/install_generator.rb
|
|
159
159
|
- lib/generators/active_record/merit_generator.rb
|
|
160
160
|
- lib/generators/active_record/templates/add_fields_to_model.rb
|
|
@@ -170,14 +170,15 @@ files:
|
|
|
170
170
|
- lib/generators/merit/templates/merit_rank_rules.rb
|
|
171
171
|
- lib/merit.rb
|
|
172
172
|
- lib/merit/controller_extensions.rb
|
|
173
|
+
- lib/merit/judge.rb
|
|
173
174
|
- lib/merit/model_additions.rb
|
|
174
175
|
- lib/merit/models/active_record/badges_sash.rb
|
|
176
|
+
- lib/merit/models/active_record/merit/action.rb
|
|
175
177
|
- lib/merit/models/active_record/merit/score.rb
|
|
176
|
-
- lib/merit/models/active_record/merit_action.rb
|
|
177
178
|
- lib/merit/models/active_record/sash.rb
|
|
178
|
-
- lib/merit/models/mongo_mapper/
|
|
179
|
+
- lib/merit/models/mongo_mapper/merit/action.rb
|
|
179
180
|
- lib/merit/models/mongo_mapper/sash.rb
|
|
180
|
-
- lib/merit/models/mongoid/
|
|
181
|
+
- lib/merit/models/mongoid/merit/action.rb
|
|
181
182
|
- lib/merit/models/mongoid/sash.rb
|
|
182
183
|
- lib/merit/rule.rb
|
|
183
184
|
- lib/merit/rules_badge_methods.rb
|
data/app/models/merit_action.rb
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
require "merit/models/#{Merit.orm}/merit_action"
|
|
2
|
-
|
|
3
|
-
# MeritAction general schema
|
|
4
|
-
# ______________________________________________________________
|
|
5
|
-
# source | action | target
|
|
6
|
-
# user_id | method,value | model,id | processed
|
|
7
|
-
# ______________________________________________________________
|
|
8
|
-
# 1 | comment nil | List 8 | true
|
|
9
|
-
# 1 | vote 3 | List 12 | true
|
|
10
|
-
# 3 | follow nil | User 1 | false
|
|
11
|
-
# X | create nil | User #{generated_id} | false
|
|
12
|
-
# ______________________________________________________________
|
|
13
|
-
#
|
|
14
|
-
# Rules relate to merit_actions by action name ('controller#action' string)
|
|
15
|
-
class MeritAction
|
|
16
|
-
attr_accessible :user_id, :action_method, :action_value, :had_errors,
|
|
17
|
-
:target_model, :target_id, :processed, :log
|
|
18
|
-
|
|
19
|
-
def self.check_unprocessed_rules
|
|
20
|
-
where(:processed => false).map &:check_rules
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
# Check rules defined for a merit_action
|
|
24
|
-
def check_rules
|
|
25
|
-
unless had_errors
|
|
26
|
-
check_badge_rules
|
|
27
|
-
check_point_rules
|
|
28
|
-
end
|
|
29
|
-
processed!
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def target(to)
|
|
33
|
-
@target ||= (to == :action_user) ? action_user : other_target(to)
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def target_object(model_name = nil)
|
|
37
|
-
# Grab custom model_name from Rule, or target_model from MeritAction triggered
|
|
38
|
-
klass = model_name || target_model
|
|
39
|
-
klass.singularize.camelize.constantize.find_by_id(target_id)
|
|
40
|
-
rescue => e
|
|
41
|
-
Rails.logger.warn "[merit] no target_object found: #{e}"
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def log_activity(str)
|
|
45
|
-
self.update_attribute :log, "#{self.log}#{str}|"
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
private
|
|
49
|
-
|
|
50
|
-
def check_badge_rules
|
|
51
|
-
rules = AppBadgeRules[action_str] || []
|
|
52
|
-
rules.each { |rule| rule.apply_badges(self) }
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def check_point_rules
|
|
56
|
-
rules = AppPointRules[action_str] || []
|
|
57
|
-
rules.each { |rule| rule.apply_points(self) }
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def action_str
|
|
61
|
-
"#{target_model}\##{action_method}"
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def action_user
|
|
65
|
-
begin
|
|
66
|
-
Merit.user_model.find(user_id)
|
|
67
|
-
rescue ActiveRecord::RecordNotFound
|
|
68
|
-
Rails.logger.warn "[merit] no #{Merit.user_model} found with id #{user_id}"
|
|
69
|
-
return
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
def other_target(to)
|
|
74
|
-
begin
|
|
75
|
-
target_object.send(to)
|
|
76
|
-
rescue NoMethodError
|
|
77
|
-
Rails.logger.warn "[merit] NoMethodError on '#{target_object.inspect}.#{to}' (called from MeritAction#other_target)"
|
|
78
|
-
return
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
# Mark merit_action as processed
|
|
83
|
-
def processed!
|
|
84
|
-
self.processed = true
|
|
85
|
-
self.save
|
|
86
|
-
end
|
|
87
|
-
end
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
class MeritAction
|
|
2
|
-
include MongoMapper::Document
|
|
3
|
-
|
|
4
|
-
key :user_id, String
|
|
5
|
-
key :action_method, String
|
|
6
|
-
key :action_value, Integer
|
|
7
|
-
key :had_errors, Boolean
|
|
8
|
-
key :target_model, String
|
|
9
|
-
key :target_id, String
|
|
10
|
-
key :processed, Boolean, :default => false
|
|
11
|
-
key :log, String
|
|
12
|
-
timestamps!
|
|
13
|
-
end
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
class MeritAction
|
|
2
|
-
include Mongoid::Document
|
|
3
|
-
include Mongoid::Timestamps
|
|
4
|
-
|
|
5
|
-
belongs_to :user
|
|
6
|
-
field :action_method
|
|
7
|
-
field :action_value, :type => Integer
|
|
8
|
-
field :had_errors, :type => Boolean
|
|
9
|
-
|
|
10
|
-
belongs_to :target, :polymorphic => true
|
|
11
|
-
field :processed, :type => Boolean, :default => false
|
|
12
|
-
field :log
|
|
13
|
-
end
|