merit 1.5.0 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +13 -1
- data/README.md +10 -10
- data/UPGRADING.md +14 -0
- data/app/models/merit/action.rb +9 -10
- data/app/models/merit/badge.rb +9 -6
- data/lib/generators/merit/templates/merit.rb +6 -2
- data/lib/merit.rb +3 -2
- data/lib/merit/controller_extensions.rb +10 -9
- data/lib/merit/judge.rb +9 -15
- data/lib/merit/model_additions.rb +6 -6
- data/lib/merit/models/active_record/merit/action.rb +4 -2
- data/lib/merit/models/active_record/merit/activity_log.rb +3 -1
- data/lib/merit/models/active_record/merit/badges_sash.rb +6 -2
- data/lib/merit/models/active_record/merit/sash.rb +8 -8
- data/lib/merit/models/active_record/merit/score.rb +7 -3
- data/lib/merit/models/mongo_mapper/merit/action.rb +1 -1
- data/lib/merit/models/mongo_mapper/sash.rb +1 -0
- data/lib/merit/models/mongoid/merit/action.rb +4 -4
- data/lib/merit/models/mongoid/sash.rb +1 -1
- data/lib/merit/observer.rb +13 -0
- data/lib/merit/rule.rb +1 -1
- data/lib/merit/rules_badge_methods.rb +2 -2
- data/lib/merit/rules_matcher.rb +24 -0
- data/lib/merit/rules_rank_methods.rb +12 -8
- data/lib/merit/target_finder.rb +12 -9
- data/merit.gemspec +2 -5
- data/test/dummy/app/controllers/api/users_controller.rb +5 -0
- data/test/dummy/app/controllers/comments_controller.rb +15 -49
- data/test/dummy/app/controllers/registrations_controller.rb +7 -1
- data/test/dummy/app/controllers/users_controller.rb +11 -40
- data/test/dummy/app/models/comment.rb +3 -1
- data/test/dummy/app/models/merit/badge_rules.rb +11 -8
- data/test/dummy/app/models/merit/point_rules.rb +4 -4
- data/test/dummy/app/models/merit/rank_rules.rb +1 -1
- data/test/dummy/app/models/user.rb +3 -1
- data/test/dummy/app/views/admin/users/index.html.erb +1 -1
- data/test/dummy/app/views/comments/index.html.erb +1 -1
- data/test/dummy/app/views/users/index.html.erb +1 -1
- data/test/dummy/config/application.rb +1 -1
- data/test/dummy/config/environments/development.rb +2 -3
- data/test/dummy/config/environments/production.rb +2 -0
- data/test/dummy/config/environments/test.rb +2 -3
- data/test/dummy/config/initializers/merit.rb +27 -24
- data/test/dummy/config/initializers/secret_token.rb +6 -1
- data/test/dummy/config/routes.rb +5 -2
- data/test/integration/navigation_test.rb +67 -55
- data/test/test_helper.rb +5 -20
- data/test/{base_target_finder_test.rb → unit/base_target_finder_test.rb} +1 -1
- data/test/{merit_unit_test.rb → unit/merit_unit_test.rb} +14 -32
- data/test/unit/rule_unit_test.rb +44 -0
- data/test/{sash_finder_test.rb → unit/sash_finder_test.rb} +1 -1
- data/test/{target_finder_test.rb → unit/target_finder_test.rb} +5 -5
- metadata +12 -41
- data/Gemfile.lock +0 -146
data/Gemfile
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
-
source
|
1
|
+
source 'https://rubygems.org'
|
2
2
|
|
3
3
|
gemspec
|
4
|
+
|
5
|
+
version = ENV['RAILS_VERSION'] || '3.2'
|
6
|
+
|
7
|
+
rails = case version
|
8
|
+
when 'master'
|
9
|
+
{ github: 'rails/rails' }
|
10
|
+
else
|
11
|
+
gem 'strong_parameters'
|
12
|
+
"~> #{version}.0"
|
13
|
+
end
|
14
|
+
|
15
|
+
gem 'rails', rails
|
data/README.md
CHANGED
@@ -44,11 +44,11 @@ holds. Badges may have levels, and may be temporary. Define rules on
|
|
44
44
|
|
45
45
|
```ruby
|
46
46
|
# app/models/merit/badge_rules.rb
|
47
|
-
grant_on 'comments#vote', :
|
47
|
+
grant_on 'comments#vote', badge: 'relevant-commenter', to: :user do |comment|
|
48
48
|
comment.votes.count == 5
|
49
49
|
end
|
50
50
|
|
51
|
-
grant_on ['users#create', 'users#update'], :
|
51
|
+
grant_on ['users#create', 'users#update'], badge: 'autobiographer', temporary: true do |user|
|
52
52
|
user.name.present? && user.address.present?
|
53
53
|
end
|
54
54
|
```
|
@@ -93,16 +93,16 @@ action user or to the method(s) defined in the `:to` option. Define rules on
|
|
93
93
|
|
94
94
|
```ruby
|
95
95
|
# app/models/merit/point_rules.rb
|
96
|
-
score 10, :
|
96
|
+
score 10, to: :post_creator, on: 'comments#create' do |comment|
|
97
97
|
comment.title.present?
|
98
98
|
end
|
99
99
|
|
100
|
-
score 20, :
|
100
|
+
score 20, on: [
|
101
101
|
'comments#create',
|
102
102
|
'photos#create'
|
103
103
|
]
|
104
104
|
|
105
|
-
score 15, :
|
105
|
+
score 15, on: 'reviews#create', to: [:reviewer, :reviewed]
|
106
106
|
```
|
107
107
|
|
108
108
|
```ruby
|
@@ -146,7 +146,7 @@ Define rules on `app/models/merit/rank_rules.rb`:
|
|
146
146
|
Check for rules on a rake task executed in background like:
|
147
147
|
|
148
148
|
```ruby
|
149
|
-
task :
|
149
|
+
task cron: :environment do
|
150
150
|
Merit::RankRules.new.check_rank_rules
|
151
151
|
end
|
152
152
|
```
|
@@ -155,12 +155,12 @@ end
|
|
155
155
|
## Examples
|
156
156
|
|
157
157
|
```ruby
|
158
|
-
set_rank :
|
159
|
-
|
158
|
+
set_rank level: 2, to: Committer.active do |committer|
|
159
|
+
committer.branches > 1 && committer.followers >= 10
|
160
160
|
end
|
161
161
|
|
162
|
-
set_rank :
|
163
|
-
|
162
|
+
set_rank level: 3, to: Committer.active do |committer|
|
163
|
+
committer.branches > 2 && committer.followers >= 20
|
164
164
|
end
|
165
165
|
```
|
166
166
|
|
data/UPGRADING.md
CHANGED
@@ -1,5 +1,19 @@
|
|
1
1
|
# Upgrading
|
2
2
|
|
3
|
+
## 1.6.0
|
4
|
+
|
5
|
+
* Rails 4 ready.
|
6
|
+
* Adds ability to wildcard controllers like:
|
7
|
+
```ruby
|
8
|
+
grant_on '.*search#index', badge: 'searcher', multiple: true
|
9
|
+
```
|
10
|
+
* Allows custom fields to be defined on badges [97c998f]. Example:
|
11
|
+
Merit::Badge.create!({
|
12
|
+
id: 1,
|
13
|
+
name: 'best-unicorn',
|
14
|
+
custom_fields: { category: 'fantasy' }
|
15
|
+
})
|
16
|
+
|
3
17
|
## 1.5.0
|
4
18
|
|
5
19
|
* Adds `Merit::ActivityLog` join model between `Merit::Action` and
|
data/app/models/merit/action.rb
CHANGED
@@ -15,7 +15,7 @@ require_dependency "merit/models/#{Merit.orm}/merit/action"
|
|
15
15
|
module Merit
|
16
16
|
class Action
|
17
17
|
def self.check_unprocessed
|
18
|
-
where(:
|
18
|
+
where(processed: false).map &:check_all_rules
|
19
19
|
end
|
20
20
|
|
21
21
|
# Check rules defined for a merit_action
|
@@ -23,17 +23,15 @@ module Merit
|
|
23
23
|
processed!
|
24
24
|
return if had_errors
|
25
25
|
|
26
|
-
|
27
|
-
|
28
|
-
check_rules badge_rules, :badges
|
29
|
-
check_rules point_rules, :points
|
26
|
+
check_rules rules_matcher.select_from(AppBadgeRules), :badges
|
27
|
+
check_rules rules_matcher.select_from(AppPointRules), :points
|
30
28
|
end
|
31
29
|
|
32
30
|
private
|
33
31
|
|
34
32
|
def check_rules(rules_array, badges_or_points)
|
35
33
|
rules_array.each do |rule|
|
36
|
-
judge = Judge.new sashes_to_badge(rule), rule, :
|
34
|
+
judge = Judge.new sashes_to_badge(rule), rule, action: self
|
37
35
|
judge.send :"apply_#{badges_or_points}"
|
38
36
|
end
|
39
37
|
end
|
@@ -43,14 +41,15 @@ module Merit
|
|
43
41
|
SashFinder.find(rule, self)
|
44
42
|
end
|
45
43
|
|
46
|
-
def action_str
|
47
|
-
"#{target_model}\##{action_method}"
|
48
|
-
end
|
49
|
-
|
50
44
|
# Mark merit_action as processed
|
51
45
|
def processed!
|
52
46
|
self.processed = true
|
53
47
|
self.save
|
54
48
|
end
|
49
|
+
|
50
|
+
def rules_matcher
|
51
|
+
@rules_matcher ||= ::Merit::RulesMatcher.new(target_model, action_method)
|
52
|
+
end
|
53
|
+
|
55
54
|
end
|
56
55
|
end
|
data/app/models/merit/badge.rb
CHANGED
@@ -6,7 +6,7 @@ module Merit
|
|
6
6
|
extend Ambry::Model
|
7
7
|
extend Ambry::ActiveModel
|
8
8
|
|
9
|
-
field :id, :name, :level, :image, :description
|
9
|
+
field :id, :name, :level, :image, :description, :custom_fields
|
10
10
|
|
11
11
|
validates_presence_of :id, :name
|
12
12
|
validates_uniqueness_of :id
|
@@ -14,13 +14,15 @@ module Merit
|
|
14
14
|
filters do
|
15
15
|
def find_by_id(ids)
|
16
16
|
ids = Array.wrap(ids)
|
17
|
-
find{|b| ids.include? b[:id] }
|
17
|
+
find { |b| ids.include? b[:id] }
|
18
18
|
end
|
19
|
+
|
19
20
|
def by_name(name)
|
20
|
-
find{|b| b.name == name.to_s }
|
21
|
+
find { |b| b.name == name.to_s }
|
21
22
|
end
|
23
|
+
|
22
24
|
def by_level(level)
|
23
|
-
find{|b| b.level.to_s == level.to_s }
|
25
|
+
find { |b| b.level.to_s == level.to_s }
|
24
26
|
end
|
25
27
|
end
|
26
28
|
|
@@ -28,8 +30,9 @@ module Merit
|
|
28
30
|
def find_by_name_and_level(name, level)
|
29
31
|
badges = Badge.by_name(name)
|
30
32
|
badges = badges.by_level(level) unless level.nil?
|
31
|
-
if
|
32
|
-
|
33
|
+
if (badge = badges.first).nil?
|
34
|
+
str = "No badge '#{name}' found. Define it in initializers/merit.rb"
|
35
|
+
raise ::Merit::BadgeNotFound, str
|
33
36
|
end
|
34
37
|
badge
|
35
38
|
end
|
@@ -15,6 +15,10 @@ end
|
|
15
15
|
|
16
16
|
# Create application badges (uses https://github.com/norman/ambry)
|
17
17
|
# Merit::Badge.create!({
|
18
|
-
# :
|
19
|
-
# :
|
18
|
+
# id: 1,
|
19
|
+
# name: 'just-registered'
|
20
|
+
# }, {
|
21
|
+
# id: 2,
|
22
|
+
# name: 'best-unicorn',
|
23
|
+
# custom_fields: { category: 'fantasy' }
|
20
24
|
# })
|
data/lib/merit.rb
CHANGED
@@ -2,6 +2,7 @@ require 'merit/rule'
|
|
2
2
|
require 'merit/rules_badge_methods'
|
3
3
|
require 'merit/rules_points_methods'
|
4
4
|
require 'merit/rules_rank_methods'
|
5
|
+
require 'merit/rules_matcher'
|
5
6
|
require 'merit/controller_extensions'
|
6
7
|
require 'merit/model_additions'
|
7
8
|
require 'merit/judge'
|
@@ -20,7 +21,7 @@ module Merit
|
|
20
21
|
|
21
22
|
# Define user_model_name
|
22
23
|
mattr_accessor :user_model_name
|
23
|
-
@@user_model_name =
|
24
|
+
@@user_model_name = 'User'
|
24
25
|
def self.user_model
|
25
26
|
@@user_model_name.constantize
|
26
27
|
end
|
@@ -50,7 +51,7 @@ module Merit
|
|
50
51
|
require 'merit/models/active_record/merit/sash'
|
51
52
|
require 'merit/models/active_record/merit/score'
|
52
53
|
elsif Merit.orm == :mongoid
|
53
|
-
require
|
54
|
+
require 'merit/models/mongoid/sash'
|
54
55
|
end
|
55
56
|
|
56
57
|
ActiveSupport.on_load(:action_controller) do
|
@@ -26,8 +26,7 @@ module Merit
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def rules_defined?
|
29
|
-
|
30
|
-
AppBadgeRules[action].present? || AppPointRules[action].present?
|
29
|
+
RulesMatcher.new(controller_path, action_name).any_matching?
|
31
30
|
end
|
32
31
|
|
33
32
|
def had_errors?
|
@@ -37,18 +36,20 @@ module Merit
|
|
37
36
|
def target_object
|
38
37
|
target_obj = instance_variable_get(:"@#{controller_name.singularize}")
|
39
38
|
if target_obj.nil?
|
40
|
-
|
39
|
+
str = '[merit] No object found, maybe you need a ' +
|
40
|
+
"'@#{controller_name.singularize}' variable in " +
|
41
|
+
"'#{controller_path}_controller'?"
|
42
|
+
Rails.logger.warn str
|
41
43
|
end
|
42
44
|
target_obj
|
43
45
|
end
|
44
46
|
|
45
47
|
def target_id
|
46
|
-
target_id =
|
47
|
-
# If
|
48
|
-
# then
|
49
|
-
|
50
|
-
|
51
|
-
target_id = target_object.id
|
48
|
+
target_id = target_object.try(:id)
|
49
|
+
# If target_id is nil
|
50
|
+
# then use params[:id].
|
51
|
+
if target_id.nil? && params[:id].to_s =~ /^[0-9]+$/
|
52
|
+
target_id = params[:id]
|
52
53
|
end
|
53
54
|
target_id
|
54
55
|
end
|
data/lib/merit/judge.rb
CHANGED
@@ -1,5 +1,10 @@
|
|
1
|
+
require_relative 'observer'
|
2
|
+
|
1
3
|
module Merit
|
2
4
|
class Judge
|
5
|
+
|
6
|
+
include Observer
|
7
|
+
|
3
8
|
def initialize(sashes, rule, options = {})
|
4
9
|
@sashes = sashes
|
5
10
|
@rule = rule
|
@@ -21,11 +26,8 @@ module Merit
|
|
21
26
|
def apply_points
|
22
27
|
return unless rule_applies?
|
23
28
|
@sashes.each do |sash|
|
24
|
-
|
25
|
-
|
26
|
-
action_id: @action.id,
|
27
|
-
related_change: points
|
28
|
-
)
|
29
|
+
point = sash.add_points @rule.score
|
30
|
+
notify_observers(@action.id, point)
|
29
31
|
end
|
30
32
|
end
|
31
33
|
|
@@ -35,22 +37,14 @@ module Merit
|
|
35
37
|
@sashes.each do |sash|
|
36
38
|
next unless new_or_multiple?(sash)
|
37
39
|
badge_sash = sash.add_badge badge.id
|
38
|
-
|
39
|
-
action_id: @action.id,
|
40
|
-
related_change: badge_sash,
|
41
|
-
description: 'granted'
|
42
|
-
)
|
40
|
+
notify_observers(@action.id, badge_sash, 'granted')
|
43
41
|
end
|
44
42
|
end
|
45
43
|
|
46
44
|
def remove_badges
|
47
45
|
@sashes.each do |sash|
|
48
46
|
badge_sash = sash.rm_badge badge.id
|
49
|
-
|
50
|
-
action_id: @action.id,
|
51
|
-
related_change: badge_sash,
|
52
|
-
description: 'removed'
|
53
|
-
)
|
47
|
+
notify_observers(@action.id, badge_sash, 'removed')
|
54
48
|
end
|
55
49
|
end
|
56
50
|
|
@@ -5,7 +5,7 @@ module Merit
|
|
5
5
|
def has_merit(options = {})
|
6
6
|
# MeritableModel#sash_id is more stable than Sash#meritable_model_id
|
7
7
|
# That's why MeritableModel belongs_to Sash. Can't use
|
8
|
-
# :
|
8
|
+
# dependent: destroy as it may raise FK constraint exceptions. See:
|
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
|
|
@@ -27,14 +27,14 @@ module Merit
|
|
27
27
|
if Merit.orm == :mongo_mapper
|
28
28
|
plugin Merit
|
29
29
|
key :sash_id, String
|
30
|
-
key :points, Integer, :
|
31
|
-
key :level, Integer, :
|
30
|
+
key :points, Integer, default: 0
|
31
|
+
key :level, Integer, default: 0
|
32
32
|
elsif Merit.orm == :mongoid
|
33
33
|
field :sash_id
|
34
|
-
field :points, :
|
35
|
-
field :level, :
|
34
|
+
field :points, type: Integer, default: 0
|
35
|
+
field :level, type: Integer, default: 0
|
36
36
|
def find_by_id(id)
|
37
|
-
where(:
|
37
|
+
where(_id: id).first
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
@@ -4,7 +4,9 @@ module Merit
|
|
4
4
|
|
5
5
|
has_many :activity_logs, class_name: Merit::ActivityLog
|
6
6
|
|
7
|
-
|
8
|
-
:
|
7
|
+
if Rails.version < '4'
|
8
|
+
attr_accessible :user_id, :action_method, :action_value, :had_errors,
|
9
|
+
:target_model, :target_id, :processed, :log
|
10
|
+
end
|
9
11
|
end
|
10
12
|
end
|
@@ -5,6 +5,8 @@ module Merit
|
|
5
5
|
belongs_to :action, class_name: Merit::Action
|
6
6
|
belongs_to :related_change, polymorphic: true
|
7
7
|
|
8
|
-
|
8
|
+
if Rails.version < '4'
|
9
|
+
attr_accessible :action_id, :related_change, :description, :created_at
|
10
|
+
end
|
9
11
|
end
|
10
12
|
end
|
@@ -1,9 +1,13 @@
|
|
1
1
|
module Merit
|
2
2
|
class BadgesSash < ActiveRecord::Base
|
3
3
|
belongs_to :sash
|
4
|
-
has_many :activity_logs,
|
4
|
+
has_many :activity_logs,
|
5
|
+
class_name: Merit::ActivityLog,
|
6
|
+
as: :related_change
|
5
7
|
|
6
|
-
|
8
|
+
if Rails.version < '4'
|
9
|
+
attr_accessible :badge_id
|
10
|
+
end
|
7
11
|
|
8
12
|
def self.last_granted(options = {})
|
9
13
|
options[:since_date] ||= 1.month.ago
|
@@ -6,13 +6,13 @@ module Merit
|
|
6
6
|
# It's existence make join models like badges_users and scores_users
|
7
7
|
# unnecessary. It should be transparent at the application.
|
8
8
|
class Sash < ActiveRecord::Base
|
9
|
-
has_many :badges_sashes, :
|
10
|
-
has_many :scores, :
|
9
|
+
has_many :badges_sashes, dependent: :destroy
|
10
|
+
has_many :scores, dependent: :destroy, class_name: 'Merit::Score'
|
11
11
|
|
12
12
|
after_create :create_scores
|
13
13
|
|
14
14
|
def badges
|
15
|
-
badge_ids.
|
15
|
+
badge_ids.map { |id| Badge.find id }
|
16
16
|
end
|
17
17
|
|
18
18
|
def badge_ids
|
@@ -30,19 +30,19 @@ module Merit
|
|
30
30
|
end
|
31
31
|
|
32
32
|
|
33
|
-
def points(category =
|
34
|
-
scores.where(:
|
33
|
+
def points(category = :default)
|
34
|
+
scores.where(category: category).first.points
|
35
35
|
end
|
36
36
|
|
37
|
-
def add_points(num_points, log = 'Manually granted
|
37
|
+
def add_points(num_points, log = 'Manually granted', category = :default)
|
38
38
|
point = Merit::Score::Point.new
|
39
39
|
point.log = log
|
40
40
|
point.num_points = num_points
|
41
|
-
self.scores.where(:
|
41
|
+
self.scores.where(category: category).first.score_points << point
|
42
42
|
point
|
43
43
|
end
|
44
44
|
|
45
|
-
def substract_points(num_points, log = 'Manually granted
|
45
|
+
def substract_points(num_points, log = 'Manually granted', category = :default)
|
46
46
|
add_points -num_points, log, category
|
47
47
|
end
|
48
48
|
|
@@ -2,7 +2,9 @@ module Merit
|
|
2
2
|
class Score < ActiveRecord::Base
|
3
3
|
self.table_name = :merit_scores
|
4
4
|
belongs_to :sash
|
5
|
-
has_many :score_points,
|
5
|
+
has_many :score_points,
|
6
|
+
dependent: :destroy,
|
7
|
+
class_name: 'Merit::Score::Point'
|
6
8
|
|
7
9
|
# Meant to display a leaderboard. Accepts options :table_name (users by
|
8
10
|
# default), :since_date (1.month.ago by default) and :limit (10 by
|
@@ -43,8 +45,10 @@ SQL
|
|
43
45
|
end
|
44
46
|
|
45
47
|
class Point < ActiveRecord::Base
|
46
|
-
belongs_to :score, :
|
47
|
-
has_many :activity_logs,
|
48
|
+
belongs_to :score, class_name: 'Merit::Score'
|
49
|
+
has_many :activity_logs,
|
50
|
+
class_name: Merit::ActivityLog,
|
51
|
+
as: :related_change
|
48
52
|
end
|
49
53
|
end
|
50
54
|
end
|