merit 0.10.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +1 -1
- data/README.md +11 -15
- data/TESTING.txt +1 -1
- data/UPGRADING.md +44 -0
- data/app/models/badge.rb +14 -9
- data/app/models/merit_action.rb +36 -29
- data/lib/generators/active_record/install_generator.rb +1 -0
- data/lib/generators/active_record/templates/add_fields_to_model.rb +1 -4
- data/lib/generators/active_record/templates/create_merit_actions.rb +1 -1
- data/lib/generators/active_record/templates/create_sashes.rb +1 -1
- data/lib/generators/active_record/templates/create_scores_and_points.rb +19 -0
- data/lib/merit.rb +6 -3
- data/lib/merit/controller_extensions.rb +18 -21
- data/lib/merit/model_additions.rb +19 -6
- data/lib/merit/models/active_record/merit/score.rb +16 -0
- data/lib/merit/models/active_record/sash.rb +17 -2
- data/lib/merit/rule.rb +29 -21
- data/lib/merit/rules_points.rb +11 -6
- data/merit.gemspec +1 -1
- data/test/dummy-mongoid/app/models/user.rb +1 -2
- data/test/dummy/app/models/merit/point_rules.rb +7 -1
- data/test/dummy/app/models/user.rb +1 -2
- data/test/dummy/db/migrate/20120318022218_create_sashes.rb +1 -1
- data/test/dummy/db/migrate/20120318022220_add_fields_to_users.rb +1 -4
- data/test/dummy/db/migrate/20121013174256_create_scores_and_points.rb +19 -0
- data/test/dummy/db/schema.rb +12 -2
- data/test/integration/navigation_test.rb +18 -1
- data/test/merit_unit_test.rb +28 -14
- data/test/test_helper.rb +1 -3
- metadata +6 -4
- data/config/locales/en.yml +0 -4
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -23,7 +23,7 @@ NOTE: Mongoid support is experimental.
|
|
23
23
|
|
24
24
|
You may give badges to any resource on your application if some condition
|
25
25
|
holds. Badges may have levels, and may be temporary. Define rules on
|
26
|
-
`app/models/
|
26
|
+
`app/models/merit/badge_rules.rb`:
|
27
27
|
|
28
28
|
`grant_on` accepts:
|
29
29
|
|
@@ -64,6 +64,12 @@ You may also grant badges "by hand" (optionally multiple times):
|
|
64
64
|
Badge.find(3).grant_to(current_user, :allow_multiple => true)
|
65
65
|
```
|
66
66
|
|
67
|
+
Similarly you can take back badges that have been granted:
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
Badge.find(3).delete_from(current_user)
|
71
|
+
```
|
72
|
+
|
67
73
|
---
|
68
74
|
|
69
75
|
# Defining point rules
|
@@ -71,7 +77,7 @@ Badge.find(3).grant_to(current_user, :allow_multiple => true)
|
|
71
77
|
Points are a simple integer value which are given to "meritable" resources.
|
72
78
|
They are given on actions-triggered, either to the action user or to the
|
73
79
|
method(s) defined in the `:to` option. Define rules on
|
74
|
-
`app/models/
|
80
|
+
`app/models/merit/point_rules.rb`:
|
75
81
|
|
76
82
|
## Examples
|
77
83
|
|
@@ -99,7 +105,7 @@ score 15, :on => 'reviews#create', :to => [:reviewer, :reviewed]
|
|
99
105
|
5 stars is a common ranking use case. They are not given at specified actions
|
100
106
|
like badges, you should define a cron job to test if ranks are to be granted.
|
101
107
|
|
102
|
-
Define rules on `app/models/
|
108
|
+
Define rules on `app/models/merit/rank_rules.rb`:
|
103
109
|
|
104
110
|
`set_rank` accepts:
|
105
111
|
|
@@ -134,20 +140,10 @@ end
|
|
134
140
|
|
135
141
|
# To-do list
|
136
142
|
|
137
|
-
*
|
143
|
+
* target_object should be configurable (now it's singularized controller name)
|
144
|
+
* Translate comments from spanish in `rules_badge.rb`.
|
138
145
|
* Should namespace app/models into Merit module.
|
139
|
-
* rescue ActiveRecord::... should depend on ORM used
|
140
146
|
* :value parameter (for star voting for example) should be configurable
|
141
147
|
(depends on params[:value] on the controller).
|
142
148
|
* Make fixtures for integration testing (now creating objects on test file!).
|
143
149
|
* Rules should be cached? Calling *Rules.new more than once
|
144
|
-
* Add badges_sashes timestamps
|
145
|
-
|
146
|
-
---
|
147
|
-
|
148
|
-
# Contributors
|
149
|
-
|
150
|
-
* [Tute Costa](https://github.com/tute)
|
151
|
-
* [Juan Schwindt](https://github.com/jschwindt)
|
152
|
-
* [Eric Knudtson](https://github.com/ek) ([Chef Surfing](https://chefsurfing.com/))
|
153
|
-
* [A4bandas media](https://github.com/a4bandas)
|
data/TESTING.txt
CHANGED
data/UPGRADING.md
CHANGED
@@ -1,5 +1,49 @@
|
|
1
1
|
# Upgrading
|
2
2
|
|
3
|
+
## to 1.0.0
|
4
|
+
|
5
|
+
Points granting history is now logged.
|
6
|
+
|
7
|
+
* Attribute `points` and method `points=` don't exist anymore (method `points`
|
8
|
+
still works for querying number of points for a resource).
|
9
|
+
* There are new methods `add_points(num_points, log_message)` and
|
10
|
+
`remove_points(num_points, log_message)` in meritable resources to manually
|
11
|
+
change their amount of points, keeping a history log.
|
12
|
+
|
13
|
+
Run the following migration to have the new DB tables:
|
14
|
+
|
15
|
+
class UpgradeMerit < ActiveRecord::Migration
|
16
|
+
def self.up
|
17
|
+
create_table :merit_scores do |t|
|
18
|
+
t.references :sash
|
19
|
+
t.string :category, :default => 'default'
|
20
|
+
end
|
21
|
+
|
22
|
+
create_table :merit_score_points do |t|
|
23
|
+
t.references :score
|
24
|
+
t.integer :num_points, :default => 0
|
25
|
+
t.string :log
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.down
|
30
|
+
drop_table :merit_scores
|
31
|
+
drop_table :merit_score_points
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# This will create a single point entry log, with previous points granted
|
36
|
+
# to each meritable resource. Code example for a User class.
|
37
|
+
class UpgradeMeritableResources < ActiveRecord::Migration
|
38
|
+
def up
|
39
|
+
User.find_each do |user|
|
40
|
+
user.sash.scores << Merit::Score.create
|
41
|
+
user.add_points(user.points, 'Initial merit points import.')
|
42
|
+
end
|
43
|
+
remove_column :users, :points
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
3
47
|
## to 0.10.0
|
4
48
|
|
5
49
|
`badges_sashes` table gets a primary key `id` column. Run the following migration:
|
data/app/models/badge.rb
CHANGED
@@ -26,29 +26,34 @@ class Badge
|
|
26
26
|
# Grant badge to sash
|
27
27
|
# Accepts :allow_multiple boolean option, defaults to false
|
28
28
|
def grant_to(object_or_sash, *args)
|
29
|
-
object_or_sash.create_sash_if_none unless object_or_sash.kind_of?(Sash)
|
30
|
-
sash = object_or_sash.respond_to?(:sash) ? object_or_sash.sash : object_or_sash
|
31
|
-
|
32
29
|
options = args.extract_options!
|
33
30
|
options[:allow_multiple] ||= false
|
31
|
+
sash = sash_from(object_or_sash)
|
34
32
|
|
35
33
|
if !sash.badge_ids.include?(id) || options[:allow_multiple]
|
36
34
|
sash.add_badge(id)
|
37
|
-
|
35
|
+
true
|
38
36
|
else
|
39
|
-
|
37
|
+
false
|
40
38
|
end
|
41
39
|
end
|
42
40
|
|
43
41
|
# Take out badge from sash
|
44
42
|
def delete_from(object_or_sash)
|
45
|
-
|
46
|
-
sash = object_or_sash.respond_to?(:sash) ? object_or_sash.sash : object_or_sash
|
43
|
+
sash = sash_from(object_or_sash)
|
47
44
|
if sash.badge_ids.include?(id)
|
48
45
|
sash.rm_badge(id)
|
49
|
-
|
46
|
+
true
|
47
|
+
else
|
48
|
+
false
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def sash_from(object_or_sash)
|
53
|
+
if object_or_sash.kind_of?(Sash)
|
54
|
+
object_or_sash
|
50
55
|
else
|
51
|
-
|
56
|
+
object_or_sash.sash || object_or_sash.create_sash_and_scores
|
52
57
|
end
|
53
58
|
end
|
54
59
|
end
|
data/app/models/merit_action.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
require "merit/models/#{Merit.orm}/merit_action"
|
2
2
|
|
3
3
|
class MeritAction
|
4
|
-
attr_accessible :user_id, :action_method, :action_value, :had_errors,
|
4
|
+
attr_accessible :user_id, :action_method, :action_value, :had_errors,
|
5
|
+
:target_model, :target_id, :processed, :log
|
5
6
|
|
6
7
|
# Check rules defined for a merit_action
|
7
8
|
def check_rules
|
@@ -13,38 +14,44 @@ class MeritAction
|
|
13
14
|
end
|
14
15
|
|
15
16
|
def check_badge_rules
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
defined_rules.each do |rule|
|
20
|
-
rule.grant_or_delete_badge(self)
|
21
|
-
end
|
17
|
+
return if badge_rules.nil?
|
18
|
+
badge_rules.each { |rule| rule.grant_or_delete_badge(self) }
|
22
19
|
end
|
23
20
|
|
24
21
|
def check_point_rules
|
25
|
-
|
26
|
-
|
22
|
+
return if point_rules.nil?
|
23
|
+
point_rules.each { |rule| rule.grant_points(self) }
|
24
|
+
end
|
25
|
+
|
26
|
+
def badge_rules
|
27
|
+
@badge_rules ||= Merit::BadgeRules.new.defined_rules[action_str] || []
|
28
|
+
end
|
29
|
+
def point_rules
|
30
|
+
@point_rules ||= Merit::PointRules.new.actions_to_point[action_str] || []
|
31
|
+
end
|
32
|
+
def action_str
|
33
|
+
"#{target_model}\##{action_method}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def target(to)
|
37
|
+
@target ||= (to == :action_user) ? action_user : other_target(to)
|
38
|
+
end
|
39
|
+
|
40
|
+
def action_user
|
41
|
+
begin
|
42
|
+
Merit.user_model.find(user_id)
|
43
|
+
rescue ActiveRecord::RecordNotFound
|
44
|
+
Rails.logger.warn "[merit] no #{Merit.user_model} found with id #{user_id}"
|
45
|
+
return
|
46
|
+
end
|
47
|
+
end
|
27
48
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
return
|
35
|
-
end
|
36
|
-
else
|
37
|
-
begin
|
38
|
-
target = target_object.send(to)
|
39
|
-
rescue NoMethodError
|
40
|
-
Rails.logger.warn "[merit] No target_object found on check_rules."
|
41
|
-
return
|
42
|
-
end
|
43
|
-
end
|
44
|
-
target.points += point_rule[:score]
|
45
|
-
target.save
|
46
|
-
log!("points_granted:#{point_rule[:score]}")
|
47
|
-
end
|
49
|
+
def other_target(to)
|
50
|
+
begin
|
51
|
+
target_object.send(to)
|
52
|
+
rescue NoMethodError
|
53
|
+
Rails.logger.warn "[merit] NoMethodError on '#{target_object.inspect}.#{to}' (called from MeritAction#other_target)"
|
54
|
+
return
|
48
55
|
end
|
49
56
|
end
|
50
57
|
|
@@ -20,6 +20,7 @@ module ActiveRecord
|
|
20
20
|
migration_template 'create_merit_actions.rb', 'db/migrate/create_merit_actions.rb'
|
21
21
|
migration_template 'create_sashes.rb', 'db/migrate/create_sashes.rb'
|
22
22
|
migration_template 'create_badges_sashes.rb', 'db/migrate/create_badges_sashes.rb'
|
23
|
+
migration_template 'create_scores_and_points.rb', 'db/migrate/create_scores_and_points.rb'
|
23
24
|
end
|
24
25
|
end
|
25
26
|
end
|
@@ -1,15 +1,12 @@
|
|
1
1
|
class AddFieldsTo<%= table_name.camelize %> < ActiveRecord::Migration
|
2
2
|
def self.up
|
3
3
|
add_column :<%= table_name %>, :sash_id, :integer
|
4
|
-
add_column :<%= table_name %>, :points, :integer, :default => 0
|
5
4
|
add_column :<%= table_name %>, :level, :integer, :default => 0
|
6
5
|
<%- resource = table_name.singularize -%>
|
7
|
-
<%= resource.camelize %>.all.each{|<%= resource %>| <%= resource %>.update_attribute(:points, 0) } # Update existing entries
|
8
6
|
end
|
9
7
|
|
10
8
|
def self.down
|
11
9
|
remove_column :<%= table_name %>, :sash_id
|
12
|
-
remove_column :<%= table_name %>, :points
|
13
10
|
remove_column :<%= table_name %>, :level
|
14
11
|
end
|
15
|
-
end
|
12
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class CreateScoresAndPoints < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :merit_scores do |t|
|
4
|
+
t.references :sash
|
5
|
+
t.string :category, :default => 'default'
|
6
|
+
end
|
7
|
+
|
8
|
+
create_table :merit_score_points do |t|
|
9
|
+
t.references :score
|
10
|
+
t.integer :num_points, :default => 0
|
11
|
+
t.string :log
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.down
|
16
|
+
drop_table :merit_scores
|
17
|
+
drop_table :merit_score_points
|
18
|
+
end
|
19
|
+
end
|
data/lib/merit.rb
CHANGED
@@ -33,15 +33,18 @@ module Merit
|
|
33
33
|
yield self
|
34
34
|
end
|
35
35
|
|
36
|
+
class BadgeNotFound < Exception; end
|
37
|
+
|
36
38
|
class Engine < Rails::Engine
|
37
39
|
config.app_generators.orm Merit.orm
|
38
40
|
|
39
41
|
initializer 'merit.controller' do |app|
|
40
42
|
if Merit.orm == :active_record
|
41
|
-
require
|
42
|
-
require
|
43
|
+
require 'merit/models/active_record/sash'
|
44
|
+
require 'merit/models/active_record/badges_sash'
|
45
|
+
require 'merit/models/active_record/merit/score'
|
43
46
|
elsif Merit.orm == :mongoid
|
44
|
-
require "merit/models
|
47
|
+
require "merit/models/mongoid/sash"
|
45
48
|
end
|
46
49
|
|
47
50
|
ActiveSupport.on_load(:action_controller) do
|
@@ -9,21 +9,11 @@ module Merit
|
|
9
9
|
badge_rules = BadgeRules.new
|
10
10
|
point_rules = PointRules.new
|
11
11
|
if badge_rules.defined_rules[action].present? || point_rules.actions_to_point[action].present?
|
12
|
-
# TODO: target_object should be configurable (now it's singularized controller name)
|
13
|
-
target_object = instance_variable_get(:"@#{controller_name.singularize}")
|
14
|
-
# Set target_id from params or from current instance variable
|
15
|
-
target_id = params[:id] || target_object.try(:id)
|
16
|
-
# id nil, or string (friendly_id); target_object found
|
17
|
-
if target_object.present? && (target_id.nil? || !(target_id =~ /^[0-9]+$/))
|
18
|
-
target_id = target_object.id
|
19
|
-
end
|
20
|
-
|
21
|
-
# TODO: value should be configurable (now it's params[:value] set in the controller)
|
22
12
|
merit_action_id = MeritAction.create(
|
23
13
|
:user_id => send(Merit.current_user_method).try(:id),
|
24
14
|
:action_method => action_name,
|
25
15
|
:action_value => params[:value],
|
26
|
-
:had_errors => target_object.try(:errors).try(:present?),
|
16
|
+
:had_errors => target_object.try(:errors).try(:present?) || false,
|
27
17
|
:target_model => controller_name,
|
28
18
|
:target_id => target_id
|
29
19
|
).id
|
@@ -31,19 +21,26 @@ module Merit
|
|
31
21
|
# Check rules in after_filter?
|
32
22
|
if Merit.checks_on_each_request
|
33
23
|
badge_rules.check_new_actions
|
34
|
-
|
35
|
-
# Show flash msg?
|
36
|
-
if (log = MeritAction.find(merit_action_id).log)
|
37
|
-
# Badges granted to current_user
|
38
|
-
granted = log.split('|').select{|log| log =~ /badge_granted_to_action_user/ }
|
39
|
-
granted.each do |badge|
|
40
|
-
badge_id = badge.split(':').last.to_i
|
41
|
-
flash[:merit] = t('merit.flashs.badge_granted', :badge => t("badges.#{Badge.find(badge_id).name}.name"))
|
42
|
-
end
|
43
|
-
end
|
44
24
|
end
|
45
25
|
end
|
46
26
|
end
|
47
27
|
end
|
28
|
+
|
29
|
+
def target_object
|
30
|
+
target_obj = instance_variable_get(:"@#{controller_name.singularize}")
|
31
|
+
if target_obj.nil?
|
32
|
+
Rails.logger.warn("[merit] No object found, maybe you need a '@#{controller_name.singularize}' variable in '#{controller_name}_controller'?")
|
33
|
+
end
|
34
|
+
target_obj
|
35
|
+
end
|
36
|
+
|
37
|
+
def target_id
|
38
|
+
target_id = params[:id] || target_object.try(:id)
|
39
|
+
# using friendly_id if id nil or string (), and target_object found
|
40
|
+
if target_object.present? && (target_id.nil? || !(target_id =~ /^[0-9]+$/))
|
41
|
+
target_id = target_object.id
|
42
|
+
end
|
43
|
+
target_id
|
44
|
+
end
|
48
45
|
end
|
49
46
|
end
|
@@ -17,25 +17,38 @@ module Merit
|
|
17
17
|
field :sash_id
|
18
18
|
field :points, :type => Integer, :default => 0
|
19
19
|
field :level, :type => Integer, :default => 0
|
20
|
+
def find_by_id(id)
|
21
|
+
where(:_id => id).first
|
22
|
+
end
|
20
23
|
end
|
21
24
|
end
|
25
|
+
end
|
22
26
|
|
23
|
-
|
24
|
-
|
27
|
+
# Delegate relationship methods from meritable models to their sash
|
28
|
+
%w(badge_ids badges points).each do |method|
|
29
|
+
define_method(method) do
|
30
|
+
_sash = sash || create_sash_and_scores
|
31
|
+
_sash.send method
|
25
32
|
end
|
26
33
|
end
|
27
34
|
|
28
|
-
def
|
29
|
-
|
30
|
-
|
35
|
+
def add_points(num_points, log = 'Manually granted through `add_points`', category = 'default')
|
36
|
+
_sash = sash || create_sash_and_scores
|
37
|
+
_sash.add_points num_points, log, category
|
38
|
+
end
|
39
|
+
def substract_points(num_points, log = 'Manually granted through `add_points`', category = 'default')
|
40
|
+
_sash = sash || create_sash_and_scores
|
41
|
+
_sash.substract_points num_points, log, category
|
31
42
|
end
|
32
43
|
|
33
44
|
# Create sash if doesn't have
|
34
|
-
def
|
45
|
+
def create_sash_and_scores
|
35
46
|
if self.sash.blank?
|
36
47
|
self.sash = Sash.create
|
48
|
+
self.sash.scores << Merit::Score.create
|
37
49
|
self.save(:validate => false)
|
38
50
|
end
|
51
|
+
self.sash
|
39
52
|
end
|
40
53
|
end
|
41
54
|
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Merit
|
2
|
+
class Score < ActiveRecord::Base
|
3
|
+
self.table_name = :merit_scores
|
4
|
+
belongs_to :sash
|
5
|
+
has_many :score_points, :dependent => :destroy, :class_name => 'Merit::Score::Point'
|
6
|
+
|
7
|
+
def points
|
8
|
+
score_points.group(:score_id).sum(:num_points).values.first || 0
|
9
|
+
end
|
10
|
+
|
11
|
+
class Point < ActiveRecord::Base
|
12
|
+
self.table_name = :merit_score_points
|
13
|
+
belongs_to :score, :class_name => 'Merit::Score'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -1,21 +1,36 @@
|
|
1
1
|
class Sash < ActiveRecord::Base
|
2
2
|
has_many :badges_sashes, :dependent => :destroy
|
3
|
+
has_many :scores, :dependent => :destroy, :class_name => 'Merit::Score'
|
3
4
|
|
4
5
|
def badges
|
5
6
|
badge_ids.collect { |b_id| Badge.find(b_id) }
|
6
7
|
end
|
7
8
|
|
8
9
|
def badge_ids
|
9
|
-
badges_sashes.
|
10
|
+
badges_sashes.map(&:badge_id)
|
10
11
|
end
|
11
12
|
|
12
13
|
def add_badge(badge_id)
|
13
14
|
bs = BadgesSash.new
|
14
15
|
bs.badge_id = badge_id
|
15
|
-
badges_sashes << bs
|
16
|
+
self.badges_sashes << bs
|
16
17
|
end
|
17
18
|
|
18
19
|
def rm_badge(badge_id)
|
19
20
|
badges_sashes.find_by_badge_id(badge_id).try(:destroy)
|
20
21
|
end
|
22
|
+
|
23
|
+
def points(category = 'default')
|
24
|
+
scores.where(:category => category).first.points
|
25
|
+
end
|
26
|
+
|
27
|
+
def add_points(num_points, log = 'Manually granted through `add_points`', category = 'default')
|
28
|
+
point = Merit::Score::Point.new
|
29
|
+
point.log = log
|
30
|
+
point.num_points = num_points
|
31
|
+
self.scores.where(:category => category).first.score_points << point
|
32
|
+
end
|
33
|
+
def substract_points(num_points, log = 'Manually granted through `add_points`', category = 'default')
|
34
|
+
add_points -num_points, log, category
|
35
|
+
end
|
21
36
|
end
|
data/lib/merit/rule.rb
CHANGED
@@ -3,7 +3,8 @@ module Merit
|
|
3
3
|
# and a temporary option.
|
4
4
|
# Could split this class between badges and rankings functionality
|
5
5
|
class Rule
|
6
|
-
attr_accessor :badge_name, :level, :to, :
|
6
|
+
attr_accessor :badge_name, :level, :to, :model_name, :level_name,
|
7
|
+
:multiple, :temporary, :score, :block
|
7
8
|
|
8
9
|
# Does this rule's condition block apply?
|
9
10
|
def applies?(target_obj = nil)
|
@@ -12,13 +13,14 @@ module Merit
|
|
12
13
|
case block.arity
|
13
14
|
when 1 # Expects target object
|
14
15
|
if target_obj.present?
|
15
|
-
|
16
|
+
block.call(target_obj)
|
16
17
|
else
|
18
|
+
# TODO RAISE ERROR
|
17
19
|
Rails.logger.warn "[merit] no target_obj found on Rule#applies?"
|
18
|
-
|
20
|
+
false
|
19
21
|
end
|
20
22
|
else # evaluates to boolean. block.arity returns 0 in Ruby 1.9.3 and -1 in Ruby 1.8.7
|
21
|
-
|
23
|
+
block.call
|
22
24
|
end
|
23
25
|
end
|
24
26
|
|
@@ -28,7 +30,7 @@ module Merit
|
|
28
30
|
# then remove it.
|
29
31
|
def grant_or_delete_badge(action)
|
30
32
|
unless (sash = sash_to_badge(action))
|
31
|
-
Rails.logger.warn "[merit] no sash found on Rule#grant_or_delete_badge"
|
33
|
+
Rails.logger.warn "[merit] no sash found on Rule#grant_or_delete_badge for action #{action.inspect}"
|
32
34
|
return
|
33
35
|
end
|
34
36
|
|
@@ -44,24 +46,30 @@ module Merit
|
|
44
46
|
end
|
45
47
|
end
|
46
48
|
|
49
|
+
def grant_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!("points_granted:#{self.score}")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
47
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
|
48
65
|
def sash_to_badge(action)
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
else
|
55
|
-
begin
|
56
|
-
action.target_object(model_name).send(to)
|
57
|
-
rescue NoMethodError
|
58
|
-
Rails.logger.warn "[merit] #{action.target_model.singularize.camelize}.find(#{action.target_id}) not found, no badges giving today"
|
59
|
-
return
|
60
|
-
end
|
61
|
-
end
|
66
|
+
if to == :itself
|
67
|
+
target = action.target_object(model_name)
|
68
|
+
else
|
69
|
+
target = action.target(to)
|
70
|
+
end
|
62
71
|
if target
|
63
|
-
target.
|
64
|
-
target.sash
|
72
|
+
target.sash || target.create_sash_and_scores
|
65
73
|
end
|
66
74
|
end
|
67
75
|
|
@@ -71,7 +79,7 @@ module Merit
|
|
71
79
|
badges = Badge.by_name(badge_name)
|
72
80
|
badges = badges.by_level(level) unless level.nil?
|
73
81
|
if !(@badge = badges.first)
|
74
|
-
raise "No badge '#{badge_name}'#{level.nil? ? '' : " with level #level"} found. Define it in 'config/initializers/merit.rb'."
|
82
|
+
raise BadgeNotFound, "No badge '#{badge_name}'#{level.nil? ? '' : " with level #level"} found. Define it in 'config/initializers/merit.rb'."
|
75
83
|
end
|
76
84
|
end
|
77
85
|
@badge
|
data/lib/merit/rules_points.rb
CHANGED
@@ -9,12 +9,17 @@ module Merit
|
|
9
9
|
options[:to] ||= [:action_user]
|
10
10
|
|
11
11
|
actions = Array.wrap(options[:on])
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
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
|
+
|
19
|
+
actions.each do |action|
|
20
|
+
actions_to_point[action] ||= []
|
21
|
+
actions_to_point[action] << rule
|
22
|
+
end
|
18
23
|
end
|
19
24
|
end
|
20
25
|
|
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 = '0.
|
7
|
+
s.version = '1.0.0'
|
8
8
|
s.authors = ["Tute Costa"]
|
9
9
|
s.email = 'tutecosta@gmail.com'
|
10
10
|
s.add_dependency 'ambry', '~> 0.3.0'
|
@@ -11,8 +11,7 @@ class User
|
|
11
11
|
attr_accessible :name
|
12
12
|
|
13
13
|
def show_badges
|
14
|
-
|
15
|
-
badges_uniq = Badge.find_by_id(sash.badge_ids)
|
14
|
+
badges_uniq = Badge.find_by_id(badge_ids)
|
16
15
|
badges_uniq.collect{|b| "#{b.name.capitalize}#{badge_status(b)}" }.join(', ')
|
17
16
|
end
|
18
17
|
|
@@ -6,8 +6,7 @@ class User < ActiveRecord::Base
|
|
6
6
|
attr_accessible :name
|
7
7
|
|
8
8
|
def show_badges
|
9
|
-
|
10
|
-
badges_uniq = Badge.find_by_id(sash.badge_ids)
|
9
|
+
badges_uniq = Badge.find_by_id(badge_ids)
|
11
10
|
badges_uniq.collect{|b| "#{b.name.capitalize}#{badge_status(b)}" }.join(', ')
|
12
11
|
end
|
13
12
|
|
@@ -1,14 +1,11 @@
|
|
1
1
|
class AddFieldsToUsers < ActiveRecord::Migration
|
2
2
|
def self.up
|
3
3
|
add_column :users, :sash_id, :integer
|
4
|
-
add_column :users, :points, :integer, :default => 0
|
5
4
|
add_column :users, :level, :integer, :default => 0
|
6
|
-
User.all.each{|user| user.update_attribute(:points, 0) } # Update existing entries
|
7
5
|
end
|
8
6
|
|
9
7
|
def self.down
|
10
8
|
remove_column :users, :sash_id
|
11
|
-
remove_column :users, :points
|
12
9
|
remove_column :users, :level
|
13
10
|
end
|
14
|
-
end
|
11
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class CreateScoresAndPoints < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :merit_scores do |t|
|
4
|
+
t.references :sash
|
5
|
+
t.string :category, :default => 'default'
|
6
|
+
end
|
7
|
+
|
8
|
+
create_table :merit_score_points do |t|
|
9
|
+
t.references :score
|
10
|
+
t.integer :num_points, :default => 0
|
11
|
+
t.string :log
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.down
|
16
|
+
drop_table :merit_scores
|
17
|
+
drop_table :merit_score_points
|
18
|
+
end
|
19
|
+
end
|
data/test/dummy/db/schema.rb
CHANGED
@@ -11,7 +11,7 @@
|
|
11
11
|
#
|
12
12
|
# It's strongly recommended to check this file into your version control system.
|
13
13
|
|
14
|
-
ActiveRecord::Schema.define(:version =>
|
14
|
+
ActiveRecord::Schema.define(:version => 20121013174256) do
|
15
15
|
|
16
16
|
create_table "badges_sashes", :force => true do |t|
|
17
17
|
t.integer "badge_id"
|
@@ -46,6 +46,17 @@ ActiveRecord::Schema.define(:version => 20120318022220) do
|
|
46
46
|
t.datetime "updated_at", :null => false
|
47
47
|
end
|
48
48
|
|
49
|
+
create_table "merit_score_points", :force => true do |t|
|
50
|
+
t.integer "score_id"
|
51
|
+
t.integer "num_points", :default => 0
|
52
|
+
t.string "log"
|
53
|
+
end
|
54
|
+
|
55
|
+
create_table "merit_scores", :force => true do |t|
|
56
|
+
t.integer "sash_id"
|
57
|
+
t.string "category", :default => "default"
|
58
|
+
end
|
59
|
+
|
49
60
|
create_table "sashes", :force => true do |t|
|
50
61
|
t.datetime "created_at", :null => false
|
51
62
|
t.datetime "updated_at", :null => false
|
@@ -56,7 +67,6 @@ ActiveRecord::Schema.define(:version => 20120318022220) do
|
|
56
67
|
t.datetime "created_at", :null => false
|
57
68
|
t.datetime "updated_at", :null => false
|
58
69
|
t.integer "sash_id"
|
59
|
-
t.integer "points", :default => 0
|
60
70
|
t.integer "level", :default => 0
|
61
71
|
end
|
62
72
|
|
@@ -32,6 +32,14 @@ class NavigationTest < ActiveSupport::IntegrationCase
|
|
32
32
|
end
|
33
33
|
assert user.badges.empty?, 'Should not have badges'
|
34
34
|
|
35
|
+
assert_equal 0, user.points
|
36
|
+
assert_equal 0, Merit::Score::Point.count
|
37
|
+
user.add_points 15
|
38
|
+
assert_equal 15, user.points
|
39
|
+
user.substract_points 15
|
40
|
+
assert_equal 0, user.points
|
41
|
+
assert_equal 2, Merit::Score::Point.count
|
42
|
+
|
35
43
|
# Make tenth comment, assert 10-commenter badge granted
|
36
44
|
visit '/comments/new'
|
37
45
|
fill_in 'Name', :with => 'Hi!'
|
@@ -100,7 +108,16 @@ class NavigationTest < ActiveSupport::IntegrationCase
|
|
100
108
|
click_button('Create Comment')
|
101
109
|
|
102
110
|
user = User.where(:name => 'a').first
|
103
|
-
assert_equal
|
111
|
+
assert_equal 20, user.points, 'Commenting should not grant 20 points if name.length <= 4'
|
112
|
+
|
113
|
+
visit '/comments/new'
|
114
|
+
fill_in 'Name', :with => 'Hi there!'
|
115
|
+
fill_in 'Comment', :with => 'Hi bro!'
|
116
|
+
fill_in 'User', :with => user.id
|
117
|
+
click_button('Create Comment')
|
118
|
+
|
119
|
+
user = User.where(:name => 'a').first
|
120
|
+
assert_equal 40, user.points, 'Commenting should grant 20 points if name.length > 4'
|
104
121
|
|
105
122
|
visit "/comments/#{Comment.last.id}/vote/4"
|
106
123
|
user = User.first
|
data/test/merit_unit_test.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
3
|
class MeritUnitTest < ActiveSupport::TestCase
|
4
|
-
test "Rule
|
4
|
+
test "Rule#applies? should depend on provided block" do
|
5
5
|
rule = Merit::Rule.new
|
6
6
|
assert rule.applies?, 'empty conditions should make rule apply'
|
7
7
|
|
@@ -11,27 +11,41 @@ class MeritUnitTest < ActiveSupport::TestCase
|
|
11
11
|
|
12
12
|
rule.block = lambda{|obj| obj.length >= 4 }
|
13
13
|
assert rule.applies?(str), 'block should make rule apply'
|
14
|
+
|
15
|
+
rule.block = lambda{|obj| true }
|
16
|
+
assert !rule.applies?, 'block which expects object should return false if no argument'
|
14
17
|
end
|
15
18
|
|
16
|
-
test "
|
17
|
-
|
18
|
-
|
19
|
+
test "Rule#badge should get related badge or raise Merit exception" do
|
20
|
+
rule = Merit::Rule.new
|
21
|
+
rule.badge_name = 'inexistent'
|
22
|
+
assert_raise Merit::BadgeNotFound do
|
23
|
+
rule.badge
|
24
|
+
end
|
19
25
|
|
20
|
-
Badge.create(:id =>
|
21
|
-
|
22
|
-
|
23
|
-
assert_equal Badge.find(99), badge_sash.badge
|
26
|
+
badge = Badge.create(:id => 98, :name => 'test-badge-98')
|
27
|
+
rule.badge_name = badge.name
|
28
|
+
assert_equal Badge.find(98), rule.badge
|
24
29
|
end
|
25
30
|
|
31
|
+
# TODO: Test and refactor:
|
32
|
+
# Rule: grant_or_delete_badge(action), sash_to_badge
|
33
|
+
# Badge: delete_from
|
34
|
+
# MeritAction: target(to), action_user, other_target(to), target_object(model_name = nil)
|
35
|
+
|
36
|
+
|
26
37
|
test "Badge#grant_to allow_multiple option" do
|
27
38
|
badge = Badge.create(:id => 99, :name => 'test-badge')
|
28
39
|
sash = Sash.create(:id => 99)
|
40
|
+
|
29
41
|
assert_equal 0, sash.badge_ids.count
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
42
|
+
|
43
|
+
assert badge.grant_to(sash)
|
44
|
+
assert_equal 1, sash.badge_ids.count
|
45
|
+
assert !badge.grant_to(sash)
|
46
|
+
assert_equal 1, sash.badge_ids.count
|
47
|
+
|
48
|
+
assert badge.grant_to(sash, :allow_multiple => true)
|
49
|
+
assert_equal 2, sash.badge_ids.count
|
36
50
|
end
|
37
51
|
end
|
data/test/test_helper.rb
CHANGED
@@ -31,7 +31,6 @@ Capybara.default_driver = :rack_test
|
|
31
31
|
Capybara.default_selector = :css
|
32
32
|
|
33
33
|
if ENV["ORM"] == "mongoid"
|
34
|
-
puts 'Testing with ORM Mongoid'
|
35
34
|
class ActiveSupport::TestCase
|
36
35
|
def teardown
|
37
36
|
Mongoid.database.collections.each do |collection|
|
@@ -39,8 +38,7 @@ if ENV["ORM"] == "mongoid"
|
|
39
38
|
end
|
40
39
|
end
|
41
40
|
end
|
42
|
-
else
|
43
|
-
puts 'Testing with ORM ActiveRecord'
|
41
|
+
else # ActiveRecord
|
44
42
|
# Run any available migration
|
45
43
|
ActiveRecord::Migrator.migrate File.expand_path("../dummy/db/migrate/", __FILE__)
|
46
44
|
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: 0.
|
4
|
+
version: 1.0.0
|
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-10-26 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ambry
|
@@ -155,13 +155,13 @@ files:
|
|
155
155
|
- UPGRADING.md
|
156
156
|
- app/models/badge.rb
|
157
157
|
- app/models/merit_action.rb
|
158
|
-
- config/locales/en.yml
|
159
158
|
- lib/generators/active_record/install_generator.rb
|
160
159
|
- lib/generators/active_record/merit_generator.rb
|
161
160
|
- lib/generators/active_record/templates/add_fields_to_model.rb
|
162
161
|
- lib/generators/active_record/templates/create_badges_sashes.rb
|
163
162
|
- lib/generators/active_record/templates/create_merit_actions.rb
|
164
163
|
- lib/generators/active_record/templates/create_sashes.rb
|
164
|
+
- lib/generators/active_record/templates/create_scores_and_points.rb
|
165
165
|
- lib/generators/merit/install_generator.rb
|
166
166
|
- lib/generators/merit/merit_generator.rb
|
167
167
|
- lib/generators/merit/templates/merit.rb
|
@@ -172,6 +172,7 @@ files:
|
|
172
172
|
- lib/merit/controller_extensions.rb
|
173
173
|
- lib/merit/model_additions.rb
|
174
174
|
- lib/merit/models/active_record/badges_sash.rb
|
175
|
+
- lib/merit/models/active_record/merit/score.rb
|
175
176
|
- lib/merit/models/active_record/merit_action.rb
|
176
177
|
- lib/merit/models/active_record/sash.rb
|
177
178
|
- lib/merit/models/mongo_mapper/merit_action.rb
|
@@ -279,6 +280,7 @@ files:
|
|
279
280
|
- test/dummy/db/migrate/20120318022218_create_sashes.rb
|
280
281
|
- test/dummy/db/migrate/20120318022219_create_badges_sashes.rb
|
281
282
|
- test/dummy/db/migrate/20120318022220_add_fields_to_users.rb
|
283
|
+
- test/dummy/db/migrate/20121013174256_create_scores_and_points.rb
|
282
284
|
- test/dummy/db/schema.rb
|
283
285
|
- test/dummy/db/seeds.rb
|
284
286
|
- test/dummy/public/404.html
|
@@ -318,7 +320,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
318
320
|
version: '0'
|
319
321
|
requirements: []
|
320
322
|
rubyforge_project:
|
321
|
-
rubygems_version: 1.8.
|
323
|
+
rubygems_version: 1.8.24
|
322
324
|
signing_key:
|
323
325
|
specification_version: 3
|
324
326
|
summary: General reputation Rails engine.
|
data/config/locales/en.yml
DELETED