merit 1.2.1 → 1.2.2
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 +7 -22
- data/app/models/merit/action.rb +1 -1
- data/lib/generators/active_record/install_generator.rb +2 -6
- data/lib/generators/active_record/merit_generator.rb +2 -6
- data/lib/merit.rb +8 -2
- data/lib/merit/controller_extensions.rb +14 -13
- data/lib/merit/model_additions.rb +22 -10
- data/lib/merit/rules_rank_methods.rb +22 -16
- data/merit.gemspec +1 -1
- data/test/dummy/app/models/merit/rank_rules.rb +1 -1
- data/test/merit_unit_test.rb +13 -0
- metadata +3 -2
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -42,6 +42,7 @@ holds. Badges may have levels, and may be temporary. Define rules on
|
|
42
42
|
## Examples
|
43
43
|
|
44
44
|
```ruby
|
45
|
+
# app/models/merit/badge_rules.rb
|
45
46
|
grant_on 'comments#vote', :badge => 'relevant-commenter', :to => :user do |comment|
|
46
47
|
comment.votes.count == 5
|
47
48
|
end
|
@@ -51,23 +52,12 @@ grant_on ['users#create', 'users#update'], :badge => 'autobiographer', :temporar
|
|
51
52
|
end
|
52
53
|
```
|
53
54
|
|
54
|
-
### Check granted badges
|
55
|
-
|
56
55
|
```ruby
|
56
|
+
# Check granted badges
|
57
57
|
current_user.badges # Returns an array of badges
|
58
|
-
```
|
59
|
-
|
60
|
-
### Grant manually
|
61
|
-
|
62
|
-
You may also grant badges "by hand":
|
63
58
|
|
64
|
-
|
59
|
+
# Grant or remove manually
|
65
60
|
current_user.add_badge(badge.id)
|
66
|
-
```
|
67
|
-
|
68
|
-
Similarly you can remove badges that have been granted:
|
69
|
-
|
70
|
-
```ruby
|
71
61
|
current_user.rm_badge(badge.id)
|
72
62
|
```
|
73
63
|
|
@@ -90,6 +80,7 @@ action user or to the method(s) defined in the `:to` option. Define rules on
|
|
90
80
|
## Examples
|
91
81
|
|
92
82
|
```ruby
|
83
|
+
# app/models/merit/point_rules.rb
|
93
84
|
score 10, :to => :post_creator, :on => 'comments#create' do |comment|
|
94
85
|
comment.title.present?
|
95
86
|
end
|
@@ -102,19 +93,12 @@ score 20, :on => [
|
|
102
93
|
score 15, :on => 'reviews#create', :to => [:reviewer, :reviewed]
|
103
94
|
```
|
104
95
|
|
105
|
-
### Check awarded points
|
106
|
-
|
107
96
|
```ruby
|
97
|
+
# Check awarded points
|
108
98
|
current_user.points # Returns an integer
|
109
|
-
```
|
110
|
-
|
111
|
-
### Score manually
|
112
99
|
|
113
|
-
|
114
|
-
|
115
|
-
```ruby
|
100
|
+
# Score manually
|
116
101
|
current_user.add_points(20, 'Optional log message')
|
117
|
-
|
118
102
|
current_user.substract_points(10)
|
119
103
|
```
|
120
104
|
|
@@ -159,4 +143,5 @@ end
|
|
159
143
|
|
160
144
|
# To-do list
|
161
145
|
|
146
|
+
* Improve model_additions with http://blog.8thlight.com/josh-cheek/2012/02/03/modules-called-they-want-their-integrity-back.html
|
162
147
|
* Should namespace Badge, BadgesSash and Sash into Merit module.
|
data/app/models/merit/action.rb
CHANGED
@@ -17,7 +17,7 @@ module Merit
|
|
17
17
|
attr_accessible :user_id, :action_method, :action_value, :had_errors,
|
18
18
|
:target_model, :target_id, :processed, :log
|
19
19
|
|
20
|
-
def self.
|
20
|
+
def self.check_unprocessed
|
21
21
|
where(:processed => false).map &:check_all_rules
|
22
22
|
end
|
23
23
|
|
@@ -4,16 +4,12 @@ module ActiveRecord
|
|
4
4
|
module Generators
|
5
5
|
class InstallGenerator < Rails::Generators::Base
|
6
6
|
include Rails::Generators::Migration
|
7
|
+
|
7
8
|
source_root File.expand_path('../templates', __FILE__)
|
8
9
|
desc "add active_record merit migrations for the root objects"
|
9
10
|
|
10
11
|
def self.next_migration_number(path)
|
11
|
-
|
12
|
-
@prev_migration_nr = Time.now.utc.strftime("%Y%m%d%H%M%S").to_i
|
13
|
-
else
|
14
|
-
@prev_migration_nr += 1
|
15
|
-
end
|
16
|
-
@prev_migration_nr.to_s
|
12
|
+
ActiveRecord::Generators::Base.next_migration_number(path)
|
17
13
|
end
|
18
14
|
|
19
15
|
def copy_migrations_and_model
|
@@ -4,16 +4,12 @@ module ActiveRecord
|
|
4
4
|
module Generators
|
5
5
|
class MeritGenerator < ActiveRecord::Generators::Base
|
6
6
|
include Rails::Generators::Migration
|
7
|
+
|
7
8
|
source_root File.expand_path('../templates', __FILE__)
|
8
9
|
desc "add active_record merit migrations"
|
9
10
|
|
10
11
|
def self.next_migration_number(path)
|
11
|
-
|
12
|
-
@prev_migration_nr = Time.now.utc.strftime("%Y%m%d%H%M%S").to_i
|
13
|
-
else
|
14
|
-
@prev_migration_nr += 1
|
15
|
-
end
|
16
|
-
@prev_migration_nr.to_s
|
12
|
+
ActiveRecord::Generators::Base.next_migration_number(path)
|
17
13
|
end
|
18
14
|
|
19
15
|
def copy_migrations_and_model
|
data/lib/merit.rb
CHANGED
@@ -35,6 +35,7 @@ module Merit
|
|
35
35
|
end
|
36
36
|
|
37
37
|
class BadgeNotFound < Exception; end
|
38
|
+
class RankAttributeNotDefined < Exception; end
|
38
39
|
|
39
40
|
class Engine < Rails::Engine
|
40
41
|
config.app_generators.orm Merit.orm
|
@@ -50,8 +51,13 @@ module Merit
|
|
50
51
|
|
51
52
|
ActiveSupport.on_load(:action_controller) do
|
52
53
|
# Load application defined rules on application boot up
|
53
|
-
|
54
|
-
|
54
|
+
# Test if constant exists (doesn't while installing/generating files)
|
55
|
+
if defined? '::Merit::BadgeRules'
|
56
|
+
::Merit::AppBadgeRules = ::Merit::BadgeRules.new.defined_rules
|
57
|
+
end
|
58
|
+
if defined? '::Merit::PointRules'
|
59
|
+
::Merit::AppPointRules = ::Merit::PointRules.new.defined_rules
|
60
|
+
end
|
55
61
|
|
56
62
|
include Merit::ControllerExtensions
|
57
63
|
end
|
@@ -5,25 +5,26 @@ module Merit
|
|
5
5
|
module ControllerExtensions
|
6
6
|
def self.included(base)
|
7
7
|
base.after_filter do |controller|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
:user_id => send(Merit.current_user_method).try(:id),
|
12
|
-
:action_method => action_name,
|
13
|
-
:action_value => params[:value],
|
14
|
-
:had_errors => had_errors?,
|
15
|
-
:target_model => controller_path,
|
16
|
-
:target_id => target_id
|
17
|
-
).id
|
18
|
-
|
19
|
-
if Merit.checks_on_each_request
|
20
|
-
Merit::Action.check_unprocessed_rules
|
8
|
+
if rules_defined?
|
9
|
+
log_merit_action
|
10
|
+
Merit::Action.check_unprocessed if Merit.checks_on_each_request
|
21
11
|
end
|
22
12
|
end
|
23
13
|
end
|
24
14
|
|
25
15
|
private
|
26
16
|
|
17
|
+
def log_merit_action
|
18
|
+
Merit::Action.create(
|
19
|
+
:user_id => send(Merit.current_user_method).try(:id),
|
20
|
+
:action_method => action_name,
|
21
|
+
:action_value => params[:value],
|
22
|
+
:had_errors => had_errors?,
|
23
|
+
:target_model => controller_path,
|
24
|
+
:target_id => target_id
|
25
|
+
).id
|
26
|
+
end
|
27
|
+
|
27
28
|
def rules_defined?
|
28
29
|
action = "#{controller_path}\##{action_name}"
|
29
30
|
AppBadgeRules[action].present? || AppPointRules[action].present?
|
@@ -9,6 +9,21 @@ 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
|
11
11
|
|
12
|
+
merit_orm_specific_config
|
13
|
+
|
14
|
+
merit_delegate_methods_to_sash
|
15
|
+
merit_sash_initializer
|
16
|
+
end
|
17
|
+
|
18
|
+
# Delegate methods from meritable models to their sash
|
19
|
+
def merit_delegate_methods_to_sash
|
20
|
+
methods = %w(badge_ids badges points
|
21
|
+
add_badge rm_badge
|
22
|
+
add_points substract_points)
|
23
|
+
methods.each { |method| delegate method, to: :_sash }
|
24
|
+
end
|
25
|
+
|
26
|
+
def merit_orm_specific_config
|
12
27
|
if Merit.orm == :mongo_mapper
|
13
28
|
plugin Merit
|
14
29
|
key :sash_id, String
|
@@ -22,19 +37,16 @@ module Merit
|
|
22
37
|
where(:_id => id).first
|
23
38
|
end
|
24
39
|
end
|
40
|
+
end
|
25
41
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
%w(badge_ids badges points add_badge rm_badge add_points substract_points).each do |method|
|
32
|
-
delegate method, to: :_sash
|
33
|
-
end
|
42
|
+
# _sash initializes a sash if doesn't have one yet.
|
43
|
+
# From Rails 3.2 we can override association methods to do so
|
44
|
+
# transparently, but merit supports Rails ~> 3.0.0. See:
|
45
|
+
# http://blog.hasmanythrough.com/2012/1/20/modularized-association-methods-in-rails-3-2
|
46
|
+
def merit_sash_initializer
|
34
47
|
define_method(:_sash) do
|
35
48
|
if sash.nil?
|
36
|
-
self.
|
37
|
-
self.save(:validate => false)
|
49
|
+
self.update_attribute :sash_id, Sash.create.id
|
38
50
|
end
|
39
51
|
self.sash
|
40
52
|
end
|
@@ -26,22 +26,8 @@ module Merit
|
|
26
26
|
# Merit::RankRules.new.check_rank_rules
|
27
27
|
def check_rank_rules
|
28
28
|
defined_rules.each do |scoped_model, level_and_rules|
|
29
|
-
level_and_rules
|
30
|
-
|
31
|
-
begin
|
32
|
-
if Merit.orm == :mongoid
|
33
|
-
items = scoped_model.where(:"#{rule.level_name}".lt => level)
|
34
|
-
else
|
35
|
-
items = scoped_model.where("#{rule.level_name} < #{level}")
|
36
|
-
end
|
37
|
-
items.each do |obj|
|
38
|
-
if rule.applies?(obj)
|
39
|
-
obj.update_attribute rule.level_name, level
|
40
|
-
end
|
41
|
-
end
|
42
|
-
rescue ActiveRecord::StatementInvalid
|
43
|
-
Rails.logger.warn "[merit] Please add #{rule.level_name} column/attribute to #{scoped_model.new.class.name}"
|
44
|
-
end
|
29
|
+
level_and_rules.sort.each do |level, rule|
|
30
|
+
grant_when_applies(scoped_model, rule, level)
|
45
31
|
end
|
46
32
|
end
|
47
33
|
end
|
@@ -50,5 +36,25 @@ module Merit
|
|
50
36
|
def defined_rules
|
51
37
|
@defined_rules ||= {}
|
52
38
|
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def grant_when_applies(scoped_model, rule, level)
|
43
|
+
scope_to_promote(scoped_model, rule.level_name, level).each do |object|
|
44
|
+
if rule.applies?(object)
|
45
|
+
object.update_attribute rule.level_name, level
|
46
|
+
end
|
47
|
+
end
|
48
|
+
rescue ActiveRecord::StatementInvalid => msg
|
49
|
+
raise RankAttributeNotDefined, "Add #{rule.level_name} column to #{scoped_model.new.class.name}\n[#{msg}]"
|
50
|
+
end
|
51
|
+
|
52
|
+
def scope_to_promote(scope, level_name, level)
|
53
|
+
if Merit.orm == :mongoid
|
54
|
+
scope.where(:"#{level_name}".lt => level)
|
55
|
+
else
|
56
|
+
scope.where("#{level_name} < #{level}")
|
57
|
+
end
|
58
|
+
end
|
53
59
|
end
|
54
60
|
end
|
data/merit.gemspec
CHANGED
@@ -4,7 +4,7 @@ Gem::Specification.new do |s|
|
|
4
4
|
s.description = "Manage badges, points and rankings (reputation) of resources in a Rails application."
|
5
5
|
s.homepage = "http://github.com/tute/merit"
|
6
6
|
s.files = `git ls-files`.split("\n").reject{|f| f =~ /^\./ }
|
7
|
-
s.version = '1.2.
|
7
|
+
s.version = '1.2.2'
|
8
8
|
s.authors = ["Tute Costa"]
|
9
9
|
s.email = 'tutecosta@gmail.com'
|
10
10
|
s.add_dependency 'ambry', '~> 0.3.0'
|
data/test/merit_unit_test.rb
CHANGED
@@ -47,4 +47,17 @@ class MeritUnitTest < ActiveSupport::TestCase
|
|
47
47
|
badge_sash.set_notified!
|
48
48
|
assert badge_sash.notified_user
|
49
49
|
end
|
50
|
+
|
51
|
+
test 'unknown ranking should raise merit exception' do
|
52
|
+
class WeirdRankRules
|
53
|
+
include Merit::RankRulesMethods
|
54
|
+
def initialize
|
55
|
+
set_rank :level => 1, :to => User, :level_name => :clown do |user|
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
assert_raises Merit::RankAttributeNotDefined do
|
60
|
+
WeirdRankRules.new.check_rank_rules
|
61
|
+
end
|
62
|
+
end
|
50
63
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: merit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
4
|
+
version: 1.2.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2013-01-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ambry
|
@@ -328,3 +328,4 @@ signing_key:
|
|
328
328
|
specification_version: 3
|
329
329
|
summary: General reputation Rails engine.
|
330
330
|
test_files: []
|
331
|
+
has_rdoc:
|