merit 0.10.1 → 1.0.0
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 +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