merit 1.9.0 → 2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +11 -13
- data/UPGRADING.md +8 -1
- data/lib/generators/active_record/install_generator.rb +14 -5
- data/lib/generators/active_record/merit_generator.rb +2 -1
- data/lib/generators/active_record/remove_generator.rb +5 -2
- data/lib/generators/merit/merit_generator.rb +3 -1
- data/lib/generators/merit/templates/merit_badge_rules.rb +9 -5
- data/lib/merit.rb +3 -10
- data/lib/merit/judge.rb +5 -1
- data/lib/merit/model_additions.rb +14 -13
- data/lib/merit/models/active_record/merit/action.rb +2 -2
- data/lib/merit/models/active_record/merit/activity_log.rb +2 -2
- data/lib/merit/models/active_record/merit/badges_sash.rb +2 -15
- data/lib/merit/models/active_record/merit/score.rb +1 -36
- data/lib/merit/models/base/{merit/badges_sash.rb → badges_sash.rb} +0 -0
- data/lib/merit/models/base/sash.rb +59 -0
- data/lib/merit/models/mongoid/merit/badges_sash.rb +3 -1
- data/lib/merit/rule.rb +1 -1
- data/lib/merit/rules_points_methods.rb +3 -2
- data/merit.gemspec +1 -1
- data/test/dummy/app/models/comment.rb +1 -1
- data/test/dummy/app/models/merit/point_rules.rb +1 -1
- data/test/dummy/app/models/user.rb +1 -1
- data/test/dummy/app/views/users/index.html.erb +1 -0
- data/test/integration/navigation_test.rb +1 -0
- data/test/test_helper.rb +5 -0
- data/test/unit/base_target_finder_test.rb +0 -3
- data/test/unit/merit_unit_test.rb +4 -40
- data/test/unit/rule_unit_test.rb +1 -1
- data/test/unit/rules_matcher_test.rb +6 -6
- data/test/unit/sash_finder_test.rb +1 -4
- data/test/unit/sash_test.rb +104 -0
- data/test/unit/target_finder_test.rb +4 -5
- metadata +5 -4
- data/lib/merit/models/base/merit/sash.rb +0 -45
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c0822239c7421032c0da3487684baadd738b4805
|
4
|
+
data.tar.gz: 44eb5162425fc37edb09615e7f95f112d6b8cf04
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5379050b40dd4b93083443c349cf0726ee7fa436708cb2d8a4456f86b1a2b6de5c0b2dae51205613bca4b39fb5ea6bd9afded0d45d07fbfa962654284b31c08c
|
7
|
+
data.tar.gz: 346cf4cd10a20c4bfb07b1579cf16291ec77b6906b1f22999444bc7950b797d3fc372dfc4a63e8e393c56117b211e33ca571d8bacd1a8c7c3de5209c4130f8d9
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 2.0.0
|
4
|
+
|
5
|
+
- [#146] Fixes loading paths
|
6
|
+
- [#140] Points Categories feature
|
7
|
+
- Removes deprecated methods
|
8
|
+
|
9
|
+
## 1.9.0
|
10
|
+
|
11
|
+
- [#134] Bug fix: find sashes only when rules apply.
|
12
|
+
- Adds deprecation warnings.
|
13
|
+
|
3
14
|
## 1.8.0
|
4
15
|
|
5
16
|
- [#128] Finishes Observer implementation so client applications can be
|
data/README.md
CHANGED
@@ -28,7 +28,6 @@ and Rankings.
|
|
28
28
|
- [Displaying Rankings](#displaying-rankings)
|
29
29
|
- [Getting Notifications](#getting-notifications)
|
30
30
|
- [Uninstalling Merit](#uninstalling-merit)
|
31
|
-
- [To-do List](#to-do-list)
|
32
31
|
|
33
32
|
|
34
33
|
# Installation
|
@@ -159,6 +158,7 @@ action user or to the method(s) defined in the `:to` option. Define rules on
|
|
159
158
|
* `:model_name` (optional) to specify the model name if it cannot be guessed
|
160
159
|
from the controller. (e.g. `model_name: 'User'` for `RegistrationsController`,
|
161
160
|
or `model_name: 'Comment'` for `Api::CommentsController`)
|
161
|
+
* `:category` (optional) to categorize earned points. `default` is used by default.
|
162
162
|
* `&block`
|
163
163
|
* empty (always scores)
|
164
164
|
* a block which evaluates to boolean (recieves target object as parameter)
|
@@ -167,7 +167,7 @@ action user or to the method(s) defined in the `:to` option. Define rules on
|
|
167
167
|
|
168
168
|
```ruby
|
169
169
|
# app/models/merit/point_rules.rb
|
170
|
-
score 10, to: :post_creator, on: 'comments#create' do |comment|
|
170
|
+
score 10, to: :post_creator, on: 'comments#create', category: 'comment_activity' do |comment|
|
171
171
|
comment.title.present?
|
172
172
|
end
|
173
173
|
|
@@ -186,13 +186,13 @@ score proc, on: 'photos#create'
|
|
186
186
|
|
187
187
|
```ruby
|
188
188
|
# Score manually
|
189
|
-
current_user.add_points(20)
|
190
|
-
current_user.subtract_points(10)
|
189
|
+
current_user.add_points(20, category: 'Optional category')
|
190
|
+
current_user.subtract_points(10, category: 'Optional category')
|
191
191
|
```
|
192
192
|
|
193
193
|
```ruby
|
194
194
|
# Query awarded points since a given date
|
195
|
-
score_points = current_user.
|
195
|
+
score_points = current_user.score_points(category: 'Optional category')
|
196
196
|
score_points.where("created_at > '#{1.month.ago}'").sum(:num_points)
|
197
197
|
```
|
198
198
|
|
@@ -201,9 +201,14 @@ score_points.where("created_at > '#{1.month.ago}'").sum(:num_points)
|
|
201
201
|
Meritable models have a `points` method which returns an integer:
|
202
202
|
|
203
203
|
```erb
|
204
|
-
<%= current_user.points %>
|
204
|
+
<%= current_user.points(category: 'Optional category') %>
|
205
205
|
```
|
206
206
|
|
207
|
+
If `category` left empty, it will return the sum of points for every category.
|
208
|
+
|
209
|
+
```erb
|
210
|
+
<%= current_user.points %>
|
211
|
+
```
|
207
212
|
|
208
213
|
# Rankings
|
209
214
|
|
@@ -287,10 +292,3 @@ config.add_observer 'ReputationChangeObserver'
|
|
287
292
|
3. Run `rails g merit:remove MODEL_NAME` (e.g. `user`)
|
288
293
|
4. Run `rake db:migrate`
|
289
294
|
5. Remove `merit` from your Gemfile
|
290
|
-
|
291
|
-
|
292
|
-
# To-do List
|
293
|
-
|
294
|
-
## Pre 2.0.0
|
295
|
-
|
296
|
-
* Move level from meritable model into Sash
|
data/UPGRADING.md
CHANGED
@@ -1,6 +1,13 @@
|
|
1
1
|
# Main Changes / Upgrading Notes
|
2
2
|
|
3
|
-
##
|
3
|
+
## 2.0.0
|
4
|
+
|
5
|
+
* Removes deprecated methods: `Merit::Badge.last_granted` and
|
6
|
+
`Merit::Score.top_scored`.
|
7
|
+
* Removes `add_points` `log` parameter.
|
8
|
+
* Adds points category option.
|
9
|
+
|
10
|
+
## 1.9.0
|
4
11
|
|
5
12
|
* Deprecates `Merit::Badge.last_granted` and `Merit::Score.top_scored`.
|
6
13
|
Code can be readded to client applications following instructions in:
|
@@ -13,11 +13,20 @@ module ActiveRecord
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def copy_migrations_and_model
|
16
|
-
migration_template 'create_merit_actions.rb',
|
17
|
-
|
18
|
-
|
19
|
-
migration_template '
|
20
|
-
|
16
|
+
migration_template 'create_merit_actions.rb',
|
17
|
+
'db/migrate/create_merit_actions.rb'
|
18
|
+
|
19
|
+
migration_template 'create_merit_activity_logs.rb',
|
20
|
+
'db/migrate/create_merit_activity_logs.rb'
|
21
|
+
|
22
|
+
migration_template 'create_sashes.rb',
|
23
|
+
'db/migrate/create_sashes.rb'
|
24
|
+
|
25
|
+
migration_template 'create_badges_sashes.rb',
|
26
|
+
'db/migrate/create_badges_sashes.rb'
|
27
|
+
|
28
|
+
migration_template 'create_scores_and_points.rb',
|
29
|
+
'db/migrate/create_scores_and_points.rb'
|
21
30
|
end
|
22
31
|
end
|
23
32
|
end
|
@@ -13,7 +13,8 @@ module ActiveRecord
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def copy_migrations_and_model
|
16
|
-
migration_template 'add_fields_to_model.rb',
|
16
|
+
migration_template 'add_fields_to_model.rb',
|
17
|
+
"db/migrate/add_fields_to_#{table_name}"
|
17
18
|
end
|
18
19
|
end
|
19
20
|
end
|
@@ -13,8 +13,11 @@ module ActiveRecord
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def copy_migrations_and_model
|
16
|
-
migration_template 'remove_merit_tables.rb',
|
17
|
-
|
16
|
+
migration_template 'remove_merit_tables.rb',
|
17
|
+
'db/migrate/remove_merit_tables.rb'
|
18
|
+
|
19
|
+
migration_template 'remove_fields_from_model.rb',
|
20
|
+
"db/migrate/remove_fields_from_#{table_name}"
|
18
21
|
end
|
19
22
|
end
|
20
23
|
end
|
@@ -4,7 +4,7 @@
|
|
4
4
|
# * Nothing (always grants)
|
5
5
|
# * A block which evaluates to boolean (recieves the object as parameter)
|
6
6
|
# * A block with a hash composed of methods to run on the target object with
|
7
|
-
# expected values (
|
7
|
+
# expected values (+votes: 5+ for instance).
|
8
8
|
#
|
9
9
|
# +grant_on+ can have a +:to+ method name, which called over the target object
|
10
10
|
# should retrieve the object to badge (could be +:user+, +:self+, +:follower+,
|
@@ -23,20 +23,24 @@ module Merit
|
|
23
23
|
def initialize
|
24
24
|
# If it creates user, grant badge
|
25
25
|
# Should be "current_user" after registration for badge to be granted.
|
26
|
-
# grant_on 'users#create', :
|
26
|
+
# grant_on 'users#create', badge: 'just-registered', to: :itself
|
27
27
|
|
28
28
|
# If it has 10 comments, grant commenter-10 badge
|
29
|
-
# grant_on 'comments#create', :
|
29
|
+
# grant_on 'comments#create', badge: 'commenter', level: 10 do |comment|
|
30
30
|
# comment.user.comments.count == 10
|
31
31
|
# end
|
32
32
|
|
33
33
|
# If it has 5 votes, grant relevant-commenter badge
|
34
|
-
# grant_on 'comments#vote', :
|
34
|
+
# grant_on 'comments#vote', badge: 'relevant-commenter',
|
35
|
+
# to: :user do |comment|
|
36
|
+
#
|
35
37
|
# comment.votes.count == 5
|
36
38
|
# end
|
37
39
|
|
38
40
|
# Changes his name by one wider than 4 chars (arbitrary ruby code case)
|
39
|
-
# grant_on 'registrations#update', :
|
41
|
+
# grant_on 'registrations#update', badge: 'autobiographer',
|
42
|
+
# temporary: true, model_name: 'User' do |user|
|
43
|
+
#
|
40
44
|
# user.name.length > 4
|
41
45
|
# end
|
42
46
|
end
|
data/lib/merit.rb
CHANGED
@@ -10,6 +10,8 @@ require 'merit/reputation_change_observer'
|
|
10
10
|
require 'merit/sash_finder'
|
11
11
|
require 'merit/base_target_finder'
|
12
12
|
require 'merit/target_finder'
|
13
|
+
require 'merit/models/base/sash'
|
14
|
+
require 'merit/models/base/badges_sash'
|
13
15
|
|
14
16
|
module Merit
|
15
17
|
def self.setup
|
@@ -70,9 +72,9 @@ module Merit
|
|
70
72
|
|
71
73
|
class Engine < Rails::Engine
|
72
74
|
config.app_generators.orm Merit.orm
|
75
|
+
config.eager_load_paths << File.expand_path("../merit/models/#{Merit.orm}", __FILE__)
|
73
76
|
|
74
77
|
initializer 'merit.controller' do |app|
|
75
|
-
require_models
|
76
78
|
extend_orm_with_has_merit
|
77
79
|
ActiveSupport.on_load(:action_controller) do
|
78
80
|
begin
|
@@ -87,15 +89,6 @@ module Merit
|
|
87
89
|
end
|
88
90
|
end
|
89
91
|
|
90
|
-
def require_models
|
91
|
-
require 'merit/models/base/merit/sash'
|
92
|
-
require 'merit/models/base/merit/badges_sash'
|
93
|
-
require "merit/models/#{Merit.orm}/merit/activity_log"
|
94
|
-
require "merit/models/#{Merit.orm}/merit/badges_sash"
|
95
|
-
require "merit/models/#{Merit.orm}/merit/sash"
|
96
|
-
require "merit/models/#{Merit.orm}/merit/score"
|
97
|
-
end
|
98
|
-
|
99
92
|
def extend_orm_with_has_merit
|
100
93
|
if Object.const_defined?('ActiveRecord')
|
101
94
|
ActiveRecord::Base.send :include, Merit
|
data/lib/merit/judge.rb
CHANGED
@@ -25,7 +25,7 @@ module Merit
|
|
25
25
|
def apply_points
|
26
26
|
return unless rule_applies?
|
27
27
|
sashes.each do |sash|
|
28
|
-
point = sash.add_points points
|
28
|
+
point = sash.add_points points, category: category
|
29
29
|
notify_observers(
|
30
30
|
description: "granted #{points} points",
|
31
31
|
merit_object: point,
|
@@ -79,6 +79,10 @@ module Merit
|
|
79
79
|
end
|
80
80
|
end
|
81
81
|
|
82
|
+
def category
|
83
|
+
@rule.category
|
84
|
+
end
|
85
|
+
|
82
86
|
def sashes
|
83
87
|
@sashes ||= SashFinder.find @rule, @action
|
84
88
|
end
|
@@ -9,7 +9,7 @@ module Merit
|
|
9
9
|
# https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/1079-belongs_to-dependent-destroy-should-destroy-self-before-assocation
|
10
10
|
belongs_to :sash, class_name: 'Merit::Sash'
|
11
11
|
|
12
|
-
|
12
|
+
send :"_merit_#{Merit.orm}_specific_config"
|
13
13
|
_merit_delegate_methods_to_sash
|
14
14
|
_merit_define_badge_related_entries_method
|
15
15
|
_merit_sash_initializer
|
@@ -18,21 +18,17 @@ module Merit
|
|
18
18
|
# Delegate methods from meritable models to their sash
|
19
19
|
def _merit_delegate_methods_to_sash
|
20
20
|
methods = %w(badge_ids badges points add_badge rm_badge
|
21
|
-
add_points
|
21
|
+
add_points subtract_points score_points)
|
22
22
|
methods.each { |method| delegate method, to: :_sash }
|
23
23
|
end
|
24
24
|
|
25
|
-
def
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
field :level, type: Integer, default: 0
|
33
|
-
def find_by_id(id)
|
34
|
-
where(_id: id).first
|
35
|
-
end
|
25
|
+
def _merit_active_record_specific_config
|
26
|
+
end
|
27
|
+
|
28
|
+
def _merit_mongoid_specific_config
|
29
|
+
field :level, type: Integer, default: 0
|
30
|
+
def find_by_id(id)
|
31
|
+
where(_id: id).first
|
36
32
|
end
|
37
33
|
end
|
38
34
|
|
@@ -41,6 +37,11 @@ module Merit
|
|
41
37
|
Badge._define_related_entries_method(meritable_class_name)
|
42
38
|
end
|
43
39
|
|
40
|
+
def show_attr_accessible?
|
41
|
+
defined?(ProtectedAttributes) ||
|
42
|
+
!defined?(ActionController::StrongParameters)
|
43
|
+
end
|
44
|
+
|
44
45
|
# _sash initializes a sash if doesn't have one yet.
|
45
46
|
# From Rails 3.2 we can override association methods to do so
|
46
47
|
# transparently, but merit supports Rails ~> 3.0.0. See:
|
@@ -2,9 +2,9 @@ module Merit
|
|
2
2
|
class Action < ActiveRecord::Base
|
3
3
|
self.table_name = :merit_actions
|
4
4
|
|
5
|
-
has_many :activity_logs, class_name: Merit::ActivityLog
|
5
|
+
has_many :activity_logs, class_name: 'Merit::ActivityLog'
|
6
6
|
|
7
|
-
if
|
7
|
+
if show_attr_accessible?
|
8
8
|
attr_accessible :user_id, :action_method, :action_value, :had_errors,
|
9
9
|
:target_model, :target_id, :processed, :log
|
10
10
|
end
|
@@ -2,11 +2,11 @@ module Merit
|
|
2
2
|
class ActivityLog < ActiveRecord::Base
|
3
3
|
self.table_name = :merit_activity_logs
|
4
4
|
|
5
|
-
belongs_to :action, class_name: Merit::Action
|
5
|
+
belongs_to :action, class_name: 'Merit::Action'
|
6
6
|
belongs_to :related_change, polymorphic: true
|
7
7
|
has_one :sash, through: :related_change
|
8
8
|
|
9
|
-
if
|
9
|
+
if show_attr_accessible?
|
10
10
|
attr_accessible :action_id, :related_change, :description, :created_at
|
11
11
|
end
|
12
12
|
end
|
@@ -2,22 +2,9 @@ module Merit
|
|
2
2
|
class BadgesSash < ActiveRecord::Base
|
3
3
|
include Base::BadgesSash
|
4
4
|
has_many :activity_logs,
|
5
|
-
class_name: Merit::ActivityLog,
|
5
|
+
class_name: 'Merit::ActivityLog',
|
6
6
|
as: :related_change
|
7
7
|
|
8
|
-
|
9
|
-
attr_accessible :badge_id
|
10
|
-
end
|
11
|
-
|
12
|
-
# DEPRECATED: `last_granted` will be removed from merit, please refer to:
|
13
|
-
# https://github.com/tute/merit/wiki/How-to-show-last-granted-badges
|
14
|
-
def self.last_granted(options = {})
|
15
|
-
warn '[merit] [DEPRECATION] `last_granted` will be removed from merit, please refer to: https://github.com/tute/merit/wiki/How-to-show-last-granted-badges'
|
16
|
-
options[:since_date] ||= 1.month.ago
|
17
|
-
options[:limit] ||= 10
|
18
|
-
where("created_at > '#{options[:since_date]}'")
|
19
|
-
.limit(options[:limit])
|
20
|
-
.map(&:badge)
|
21
|
-
end
|
8
|
+
attr_accessible :badge_id if show_attr_accessible?
|
22
9
|
end
|
23
10
|
end
|
@@ -6,41 +6,6 @@ module Merit
|
|
6
6
|
dependent: :destroy,
|
7
7
|
class_name: 'Merit::Score::Point'
|
8
8
|
|
9
|
-
# DEPRECATED: `top_scored` will be removed from merit, please refer to:
|
10
|
-
# https://github.com/tute/merit/wiki/How-to-show-a-points-leaderboard
|
11
|
-
def self.top_scored(options = {})
|
12
|
-
warn '[merit] [DEPRECATION] `top_scored` will be removed from merit, please refer to: https://github.com/tute/merit/wiki/How-to-show-a-points-leaderboard'
|
13
|
-
options[:table_name] ||= :users
|
14
|
-
options[:since_date] ||= 1.month.ago
|
15
|
-
options[:limit] ||= 10
|
16
|
-
|
17
|
-
alias_id_column = "#{options[:table_name].to_s.singularize}_id"
|
18
|
-
if options[:table_name] == :sashes
|
19
|
-
sash_id_column = "#{options[:table_name]}.id"
|
20
|
-
else
|
21
|
-
sash_id_column = "#{options[:table_name]}.sash_id"
|
22
|
-
end
|
23
|
-
|
24
|
-
# MeritableModel - Sash -< Scores -< ScorePoints
|
25
|
-
sql_query = <<SQL
|
26
|
-
SELECT
|
27
|
-
#{options[:table_name]}.id AS #{alias_id_column},
|
28
|
-
SUM(num_points) as sum_points
|
29
|
-
FROM #{options[:table_name]}
|
30
|
-
LEFT JOIN merit_scores ON merit_scores.sash_id = #{sash_id_column}
|
31
|
-
LEFT JOIN merit_score_points ON merit_score_points.score_id = merit_scores.id
|
32
|
-
WHERE merit_score_points.created_at > '#{options[:since_date]}'
|
33
|
-
GROUP BY #{options[:table_name]}.id, merit_scores.sash_id
|
34
|
-
ORDER BY sum_points DESC
|
35
|
-
LIMIT #{options[:limit]}
|
36
|
-
SQL
|
37
|
-
results = ActiveRecord::Base.connection.execute(sql_query)
|
38
|
-
results.map do |h|
|
39
|
-
h.keep_if { |k, v| (k == alias_id_column) || (k == 'sum_points') }
|
40
|
-
end
|
41
|
-
results
|
42
|
-
end
|
43
|
-
|
44
9
|
def points
|
45
10
|
score_points.group(:score_id).sum(:num_points).values.first || 0
|
46
11
|
end
|
@@ -49,7 +14,7 @@ SQL
|
|
49
14
|
belongs_to :score, class_name: 'Merit::Score'
|
50
15
|
has_one :sash, through: :score
|
51
16
|
has_many :activity_logs,
|
52
|
-
class_name: Merit::ActivityLog,
|
17
|
+
class_name: 'Merit::ActivityLog',
|
53
18
|
as: :related_change
|
54
19
|
end
|
55
20
|
end
|
File without changes
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Merit
|
2
|
+
module Base
|
3
|
+
module Sash
|
4
|
+
def badges
|
5
|
+
badge_ids.map { |id| Merit::Badge.find id }
|
6
|
+
end
|
7
|
+
|
8
|
+
def badge_ids
|
9
|
+
badges_sashes.map(&:badge_id)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Retrieve the number of points from a category
|
13
|
+
# By default all points are summed up
|
14
|
+
# @param category [String] The category
|
15
|
+
# @return [Integer] The number of points
|
16
|
+
def points(options = {})
|
17
|
+
if (category = options[:category])
|
18
|
+
scores.where(category: category).first.try(:points) || 0
|
19
|
+
else
|
20
|
+
scores.reduce(0) { |sum, score| sum + score.points }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Retrieve all points from a category or none if category doesn't exist
|
25
|
+
# By default retrieve all Points
|
26
|
+
# @param category [String] The category
|
27
|
+
# @return [ActiveRecord::Relation] containing the points
|
28
|
+
def score_points(options = {})
|
29
|
+
scope = Merit::Score::Point
|
30
|
+
.includes(:score)
|
31
|
+
.where('merit_scores.sash_id = ?', id)
|
32
|
+
if (category = options[:category])
|
33
|
+
scope = scope.where('merit_scores.category = ?', category)
|
34
|
+
end
|
35
|
+
scope
|
36
|
+
end
|
37
|
+
|
38
|
+
def add_points(num_points, options = {})
|
39
|
+
point = Merit::Score::Point.new
|
40
|
+
point.num_points = num_points
|
41
|
+
scores
|
42
|
+
.where(category: options[:category] || 'default')
|
43
|
+
.first_or_create
|
44
|
+
.score_points << point
|
45
|
+
point
|
46
|
+
end
|
47
|
+
|
48
|
+
def subtract_points(num_points, options = {})
|
49
|
+
add_points(-num_points, options)
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def create_scores
|
55
|
+
scores << Merit::Score.create
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -5,7 +5,9 @@ module Merit
|
|
5
5
|
include Base::BadgesSash
|
6
6
|
|
7
7
|
field :badge_id, type: Integer
|
8
|
-
|
8
|
+
|
9
|
+
attr_accessible :badge_id if show_attr_accessible?
|
10
|
+
|
9
11
|
has_many :activity_logs, class_name: 'Merit::ActivityLog', as: :related_change
|
10
12
|
|
11
13
|
def self.last_granted(options = {})
|
data/lib/merit/rule.rb
CHANGED
@@ -4,7 +4,7 @@ module Merit
|
|
4
4
|
# Could split this class between badges and rankings functionality
|
5
5
|
class Rule
|
6
6
|
attr_accessor :badge_name, :level, :to, :model_name, :level_name,
|
7
|
-
:multiple, :temporary, :score, :block
|
7
|
+
:multiple, :temporary, :score, :block, :category
|
8
8
|
|
9
9
|
# Does this rule's condition block apply?
|
10
10
|
def applies?(target_obj = nil)
|
@@ -6,15 +6,16 @@ module Merit
|
|
6
6
|
# Define rules on certaing actions for giving points
|
7
7
|
def score(points, *args, &block)
|
8
8
|
options = args.extract_options!
|
9
|
-
options
|
9
|
+
options_to = options.fetch(:to) { :action_user }
|
10
10
|
|
11
11
|
actions = Array.wrap(options[:on])
|
12
12
|
|
13
|
-
Array.wrap(
|
13
|
+
Array.wrap(options_to).each do |to|
|
14
14
|
rule = Rule.new
|
15
15
|
rule.score = points
|
16
16
|
rule.to = to
|
17
17
|
rule.block = block
|
18
|
+
rule.category = options.fetch(:category) { :default }
|
18
19
|
rule.model_name = options[:model_name] if options[:model_name]
|
19
20
|
|
20
21
|
actions.each do |action|
|
data/merit.gemspec
CHANGED
@@ -5,7 +5,7 @@ Gem::Specification.new do |s|
|
|
5
5
|
s.homepage = "http://github.com/tute/merit"
|
6
6
|
s.files = `git ls-files`.split("\n").reject{|f| f =~ /^\./ }
|
7
7
|
s.license = 'MIT'
|
8
|
-
s.version = '
|
8
|
+
s.version = '2.0.0'
|
9
9
|
s.authors = ["Tute Costa"]
|
10
10
|
s.email = 'tutecosta@gmail.com'
|
11
11
|
|
@@ -14,7 +14,7 @@ module Merit
|
|
14
14
|
score 2, to: :user_comments, on: 'comments#vote'
|
15
15
|
|
16
16
|
# Points to voted user
|
17
|
-
score 5, to: :user, on: 'comments#vote'
|
17
|
+
score 5, to: :user, on: 'comments#vote', category: 'vote'
|
18
18
|
|
19
19
|
# Example rule for using model_name in the case of namespaced controllers
|
20
20
|
score 1, to: :user, model_name: 'Comment', on: 'api/comments#show'
|
@@ -14,6 +14,7 @@
|
|
14
14
|
<td><%= user.comments.count %></td>
|
15
15
|
<td><%= user.show_badges %></td>
|
16
16
|
<td><%= user.points %></td>
|
17
|
+
<td><%= user.points(category: 'vote') %></td>
|
17
18
|
<td><%= link_to 'Show', user %>
|
18
19
|
- <%= link_to 'Edit', edit_user_path(user) %>
|
19
20
|
- <%= link_to 'Destroy', user, data: { :confirm => 'Are you sure?' }, :method => :delete %></td>
|
@@ -186,6 +186,7 @@ class NavigationTest < ActiveSupport::IntegrationCase
|
|
186
186
|
visit "/comments/#{Comment.last.id}/vote/4"
|
187
187
|
user = User.first
|
188
188
|
assert_equal 46, user.points, 'Voting comments should grant 5 points for voted, and 1 point for voting'
|
189
|
+
assert_equal 5, user.points(category: 'vote'), 'Voting comments should grant 5 points for voted in vote category'
|
189
190
|
|
190
191
|
visit '/comments/new'
|
191
192
|
fill_in 'Name', with: 'Hi'
|
data/test/test_helper.rb
CHANGED
@@ -28,5 +28,10 @@ Capybara.default_selector = :css
|
|
28
28
|
|
29
29
|
ActiveRecord::Migrator.migrate File.expand_path("../dummy/db/migrate/", __FILE__)
|
30
30
|
|
31
|
+
require "merit/models/#{Merit.orm}/merit/activity_log"
|
32
|
+
require "merit/models/#{Merit.orm}/merit/badges_sash"
|
33
|
+
require "merit/models/#{Merit.orm}/merit/sash"
|
34
|
+
require "merit/models/#{Merit.orm}/merit/score"
|
35
|
+
|
31
36
|
# Load support files
|
32
37
|
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
|
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
3
|
describe Merit::BaseTargetFinder do
|
4
|
-
|
5
4
|
describe '#find' do
|
6
5
|
describe 'rule has a model_name' do
|
7
6
|
it "should prioritize the rule's model name" do
|
@@ -39,7 +38,6 @@ describe Merit::BaseTargetFinder do
|
|
39
38
|
rule.to = :itself
|
40
39
|
rule.model_name = 'registrations'
|
41
40
|
action = Merit::Action.new(target_model: 'users', target_id: 220)
|
42
|
-
comment = Comment.new
|
43
41
|
|
44
42
|
finder = Merit::BaseTargetFinder.new(rule, action)
|
45
43
|
Rails.logger.expects(:warn)
|
@@ -47,5 +45,4 @@ describe Merit::BaseTargetFinder do
|
|
47
45
|
end
|
48
46
|
end
|
49
47
|
end
|
50
|
-
|
51
48
|
end
|
@@ -4,11 +4,9 @@ require 'test_helper'
|
|
4
4
|
class MeritUnitTest < ActiveSupport::TestCase
|
5
5
|
test 'extends only meritable ActiveRecord models' do
|
6
6
|
class User < ActiveRecord::Base
|
7
|
-
def self.columns; @columns ||= []; end
|
8
7
|
has_merit
|
9
8
|
end
|
10
9
|
class Fruit < ActiveRecord::Base
|
11
|
-
def self.columns; @columns ||= []; end
|
12
10
|
end
|
13
11
|
|
14
12
|
assert User.method_defined?(:points), 'has_merit adds methods'
|
@@ -17,50 +15,15 @@ class MeritUnitTest < ActiveSupport::TestCase
|
|
17
15
|
|
18
16
|
test 'Badges get "related_models" methods' do
|
19
17
|
class Soldier < ActiveRecord::Base
|
20
|
-
def self.columns; @columns ||= []; end
|
21
18
|
has_merit
|
22
19
|
end
|
23
20
|
class Player < ActiveRecord::Base
|
24
|
-
def self.columns; @columns ||= []; end
|
25
21
|
has_merit
|
26
22
|
end
|
27
23
|
assert Merit::Badge.method_defined?(:soldiers), 'Badge#soldiers should be defined'
|
28
24
|
assert Merit::Badge.method_defined?(:players), 'Badge#players should be defined'
|
29
25
|
end
|
30
26
|
|
31
|
-
test 'Badge#last_granted returns recently granted badges' do
|
32
|
-
# Create sashes, badges and badges_sashes
|
33
|
-
sash = Merit::Sash.create
|
34
|
-
badge = Merit::Badge.create(id: 20, name: 'test-badge-21')
|
35
|
-
sash.add_badge badge.id
|
36
|
-
Merit::BadgesSash.last.update_attribute :created_at, 1.day.ago
|
37
|
-
sash.add_badge badge.id
|
38
|
-
Merit::BadgesSash.last.update_attribute :created_at, 8.days.ago
|
39
|
-
sash.add_badge badge.id
|
40
|
-
Merit::BadgesSash.last.update_attribute :created_at, 15.days.ago
|
41
|
-
|
42
|
-
# Test method options
|
43
|
-
assert_equal Merit::Badge.last_granted(since_date: Time.now), []
|
44
|
-
assert_equal Merit::Badge.last_granted(since_date: 1.week.ago), [badge]
|
45
|
-
assert_equal Merit::Badge.last_granted(since_date: 2.weeks.ago).count, 2
|
46
|
-
assert_equal Merit::Badge.last_granted(since_date: 2.weeks.ago, limit: 1), [badge]
|
47
|
-
end
|
48
|
-
|
49
|
-
test 'Merit::Score.top_scored returns scores leaderboard' do
|
50
|
-
# Create sashes and add points
|
51
|
-
sash_1 = Merit::Sash.create
|
52
|
-
sash_1.add_points(10); sash_1.add_points(10)
|
53
|
-
sash_2 = Merit::Sash.create
|
54
|
-
sash_2.add_points(5); sash_2.add_points(5)
|
55
|
-
|
56
|
-
# Test method options
|
57
|
-
assert_equal [{'sash_id'=>sash_1.id, 'sum_points'=>20},
|
58
|
-
{'sash_id'=>sash_2.id, 'sum_points'=>10}],
|
59
|
-
Merit::Score.top_scored(table_name: :sashes)
|
60
|
-
assert_equal [{'sash_id'=>sash_1.id, 'sum_points'=>20}],
|
61
|
-
Merit::Score.top_scored(table_name: :sashes, limit: 1)
|
62
|
-
end
|
63
|
-
|
64
27
|
test 'unknown ranking raises exception' do
|
65
28
|
class WeirdRankRules
|
66
29
|
include Merit::RankRulesMethods
|
@@ -74,8 +37,9 @@ class MeritUnitTest < ActiveSupport::TestCase
|
|
74
37
|
end
|
75
38
|
|
76
39
|
test 'Badge#custom_fields_hash saves correctly' do
|
77
|
-
Merit::Badge.create(id: 99,
|
78
|
-
|
40
|
+
Merit::Badge.create(id: 99,
|
41
|
+
name: 'test-badge',
|
42
|
+
custom_fields: { key_1: 'value1' })
|
79
43
|
assert_equal 'value1', Merit::Badge.find(99).custom_fields[:key_1]
|
80
44
|
end
|
81
|
-
end
|
45
|
+
end
|
data/test/unit/rule_unit_test.rb
CHANGED
@@ -32,7 +32,7 @@ describe Merit::Rule do
|
|
32
32
|
describe '#badge' do
|
33
33
|
it 'raises exception on inexistent badge' do
|
34
34
|
@rule.badge_name = 'inexistent'
|
35
|
-
->{ @rule.badge }.must_raise Merit::BadgeNotFound
|
35
|
+
-> { @rule.badge }.must_raise Merit::BadgeNotFound
|
36
36
|
end
|
37
37
|
|
38
38
|
it 'finds related badge' do
|
@@ -5,29 +5,29 @@ describe Merit::RulesMatcher do
|
|
5
5
|
describe 'rules actions are treated as a regexp' do
|
6
6
|
it 'selects matching rules (suffix)' do
|
7
7
|
matcher = Merit::RulesMatcher.new('comments', 'update')
|
8
|
-
matcher.select_from(
|
8
|
+
matcher.select_from(
|
9
9
|
'comments#update' => 'comments#update',
|
10
10
|
'comments#up' => 'comments#up',
|
11
11
|
'comments#up$' => 'comments#up$',
|
12
12
|
'comments#up.+$' => 'comments#up.+$',
|
13
|
-
|
13
|
+
).must_be :==, ['comments#update', 'comments#up.+$']
|
14
14
|
|
15
15
|
matcher = Merit::RulesMatcher.new('comments', 'up')
|
16
|
-
matcher.select_from(
|
16
|
+
matcher.select_from(
|
17
17
|
'comments#update' => 'comments#update',
|
18
18
|
'comments#up' => 'comments#up',
|
19
19
|
'comments#up$' => 'comments#up$',
|
20
20
|
'comments#up.+$' => 'comments#up.+$',
|
21
|
-
|
21
|
+
).must_be :==, ['comments#up', 'comments#up$']
|
22
22
|
end
|
23
23
|
|
24
24
|
it 'selects matching rules (prefix)' do
|
25
25
|
matcher = Merit::RulesMatcher.new('/posts/1/comments', 'create')
|
26
|
-
matcher.select_from(
|
26
|
+
matcher.select_from(
|
27
27
|
'comments#create' => 'comments#create',
|
28
28
|
'^comments#create' => '^comments#create',
|
29
29
|
'^.*/comments#create' => '^.*/comments#create',
|
30
|
-
|
30
|
+
).must_be :==, ['^.*/comments#create']
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
3
|
describe Merit::SashFinder do
|
4
|
-
|
5
4
|
it 'should return an array of sashes of the target objects' do
|
6
5
|
sash_1 = Merit::Sash.new
|
7
6
|
user_1 = User.new
|
@@ -11,9 +10,8 @@ describe Merit::SashFinder do
|
|
11
10
|
user_2 = User.new
|
12
11
|
user_2.stubs(:_sash).returns(sash_2)
|
13
12
|
|
14
|
-
object_without_sash = OpenStruct.new
|
15
|
-
|
16
13
|
# TODO: With stub we are not exercising compact
|
14
|
+
# object_without_sash = OpenStruct.new
|
17
15
|
# users = [user_1, user_2, object_without_sash]
|
18
16
|
users = [user_1, user_2]
|
19
17
|
|
@@ -26,5 +24,4 @@ describe Merit::SashFinder do
|
|
26
24
|
sashes.must_include sash_1
|
27
25
|
sashes.must_include sash_2
|
28
26
|
end
|
29
|
-
|
30
27
|
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class SashTest < ActiveSupport::TestCase
|
4
|
+
before do
|
5
|
+
@custom_category = 'custom_category'
|
6
|
+
@sash = Merit::Sash.create
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "#add_points" do
|
10
|
+
describe "when category specified" do
|
11
|
+
it "should create a new Point with specified category" do
|
12
|
+
@sash.add_points 5, category: @custom_category
|
13
|
+
score = Merit::Score.last
|
14
|
+
point = score.score_points.last
|
15
|
+
|
16
|
+
assert_equal point.num_points, 5
|
17
|
+
assert_equal score.category, @custom_category
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should create a new Point in category default with specified number of points" do
|
22
|
+
assert_difference("Merit::Score::Point.count", 1) do
|
23
|
+
@sash.add_points 5
|
24
|
+
end
|
25
|
+
score = Merit::Score.last
|
26
|
+
point = score.score_points.last
|
27
|
+
|
28
|
+
assert_equal point.num_points, 5
|
29
|
+
assert_equal score.category, 'default'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "#subtract_points" do
|
34
|
+
describe "when category specified" do
|
35
|
+
it "should create a new Point with specified category" do
|
36
|
+
@sash.subtract_points 5, category: @custom_category
|
37
|
+
score = Merit::Score.last
|
38
|
+
point = score.score_points.last
|
39
|
+
|
40
|
+
assert_equal point.num_points, -5
|
41
|
+
assert_equal score.category, @custom_category
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should create a new Point in category default with inverse of specified number of points" do
|
46
|
+
assert_difference("Merit::Score::Point.count", 1) do
|
47
|
+
@sash.subtract_points 5
|
48
|
+
end
|
49
|
+
score = Merit::Score.last
|
50
|
+
point = score.score_points.last
|
51
|
+
|
52
|
+
assert_equal point.num_points, -5
|
53
|
+
assert_equal score.category, 'default'
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "#points" do
|
58
|
+
before do
|
59
|
+
@sash.add_points 5, category: @custom_category
|
60
|
+
@sash.add_points 7
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "when category specified" do
|
64
|
+
it "should retrieve the number of points of the category" do
|
65
|
+
assert_equal 5, @sash.points(category: @custom_category)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should retrieve the sum of points of all categories" do
|
70
|
+
assert_equal 12, @sash.points
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "#score_points" do
|
75
|
+
before do
|
76
|
+
@point1 = @sash.add_points 5, category: @custom_category
|
77
|
+
@point2 = @sash.add_points 7
|
78
|
+
end
|
79
|
+
|
80
|
+
describe "when category specified" do
|
81
|
+
it "should return the points of the category" do
|
82
|
+
assert_equal 1, @sash.score_points(category: @custom_category).size
|
83
|
+
scores = @sash.score_points(category: @custom_category)
|
84
|
+
assert scores.include? @point1
|
85
|
+
assert !scores.include?(@point2)
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should return no Points if category doesn't exist" do
|
89
|
+
assert_equal 0, @sash.score_points(category: 'non-existing').size
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should return all points" do
|
94
|
+
assert_equal 2, @sash.score_points.size
|
95
|
+
assert @sash.score_points.include? @point1
|
96
|
+
assert @sash.score_points.include? @point2
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should return no Points if no points found" do
|
100
|
+
@sash1 = Merit::Sash.create
|
101
|
+
assert_equal 0, @sash1.score_points.size
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
3
|
describe Merit::TargetFinder do
|
4
|
-
|
5
4
|
describe '#find' do
|
6
5
|
describe 'rule#to is :itself' do
|
7
6
|
it 'should return the base target' do
|
@@ -11,9 +10,10 @@ describe Merit::TargetFinder do
|
|
11
10
|
|
12
11
|
comment = Comment.new
|
13
12
|
|
14
|
-
Merit::BaseTargetFinder
|
15
|
-
stubs(:find)
|
16
|
-
|
13
|
+
Merit::BaseTargetFinder
|
14
|
+
.stubs(:find)
|
15
|
+
.with(rule, action)
|
16
|
+
.returns(comment)
|
17
17
|
|
18
18
|
finder = Merit::TargetFinder.new(rule, action)
|
19
19
|
collection = finder.find
|
@@ -94,5 +94,4 @@ describe Merit::TargetFinder do
|
|
94
94
|
end
|
95
95
|
end
|
96
96
|
end
|
97
|
-
|
98
97
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: merit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tute Costa
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-02-
|
11
|
+
date: 2014-02-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ambry
|
@@ -152,8 +152,8 @@ files:
|
|
152
152
|
- lib/merit/models/active_record/merit/badges_sash.rb
|
153
153
|
- lib/merit/models/active_record/merit/sash.rb
|
154
154
|
- lib/merit/models/active_record/merit/score.rb
|
155
|
-
- lib/merit/models/base/
|
156
|
-
- lib/merit/models/base/
|
155
|
+
- lib/merit/models/base/badges_sash.rb
|
156
|
+
- lib/merit/models/base/sash.rb
|
157
157
|
- lib/merit/models/mongoid/merit/action.rb
|
158
158
|
- lib/merit/models/mongoid/merit/activity_log.rb
|
159
159
|
- lib/merit/models/mongoid/merit/badges_sash.rb
|
@@ -246,6 +246,7 @@ files:
|
|
246
246
|
- test/unit/rule_unit_test.rb
|
247
247
|
- test/unit/rules_matcher_test.rb
|
248
248
|
- test/unit/sash_finder_test.rb
|
249
|
+
- test/unit/sash_test.rb
|
249
250
|
- test/unit/target_finder_test.rb
|
250
251
|
homepage: http://github.com/tute/merit
|
251
252
|
licenses:
|
@@ -1,45 +0,0 @@
|
|
1
|
-
module Merit
|
2
|
-
module Base
|
3
|
-
module Sash
|
4
|
-
# Methods that are common between both the active_record and mongoid sash model
|
5
|
-
def badges
|
6
|
-
badge_ids.map { |id| Merit::Badge.find id }
|
7
|
-
end
|
8
|
-
|
9
|
-
def badge_ids
|
10
|
-
badges_sashes.map(&:badge_id)
|
11
|
-
end
|
12
|
-
|
13
|
-
def points(category = 'default')
|
14
|
-
scores.where(category: category).first.points
|
15
|
-
end
|
16
|
-
|
17
|
-
def add_points(num_points, log = 'Manually granted', category = 'default')
|
18
|
-
if log != 'Manually granted'
|
19
|
-
warn '[merit] [DEPRECATION] `add_points` `log` parameter is deprecated'
|
20
|
-
end
|
21
|
-
point = Merit::Score::Point.new
|
22
|
-
point.log = log
|
23
|
-
point.num_points = num_points
|
24
|
-
scores.where(category: category).first.score_points << point
|
25
|
-
point
|
26
|
-
end
|
27
|
-
|
28
|
-
# DEPRECATED: Please use <tt>subtract_points</tt> instead.
|
29
|
-
def substract_points(num_points, log = 'Manually granted', category = 'default')
|
30
|
-
warn '[merit] [DEPRECATION] `substract_points` is deprecated. Please use `subtract_points` instead.'
|
31
|
-
subtract_points num_points, log, category
|
32
|
-
end
|
33
|
-
|
34
|
-
def subtract_points(num_points, log = 'Manually granted', category = 'default')
|
35
|
-
add_points(-num_points, log, category)
|
36
|
-
end
|
37
|
-
|
38
|
-
private
|
39
|
-
|
40
|
-
def create_scores
|
41
|
-
scores << Merit::Score.create
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|