merit 1.4.0 → 1.5.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 +2 -2
- data/README.md +2 -3
- data/TESTING.txt +2 -5
- data/UPGRADING.md +32 -4
- data/app/models/merit/action.rb +0 -4
- data/app/models/merit/badge.rb +54 -0
- data/lib/generators/active_record/install_generator.rb +1 -0
- data/lib/generators/active_record/templates/create_merit_actions.rb +0 -1
- data/lib/generators/active_record/templates/create_merit_activity_logs.rb +15 -0
- data/lib/generators/merit/templates/merit.rb +1 -1
- data/lib/merit.rb +3 -2
- data/lib/merit/judge.rb +25 -10
- data/lib/merit/model_additions.rb +1 -1
- data/lib/merit/models/active_record/merit/action.rb +2 -0
- data/lib/merit/models/active_record/merit/activity_log.rb +10 -0
- data/lib/merit/models/active_record/merit/badges_sash.rb +20 -0
- data/lib/merit/models/active_record/merit/sash.rb +55 -0
- data/lib/merit/models/active_record/merit/score.rb +2 -1
- data/merit.gemspec +1 -1
- data/test/dummy/app/models/comment.rb +1 -0
- data/test/dummy/config/initializers/merit.rb +1 -1
- data/test/dummy/db/migrate/{20120318022217_create_merit_actions.rb → 20130329224406_create_merit_actions.rb} +1 -2
- data/test/dummy/db/migrate/20130329224407_create_merit_activity_logs.rb +15 -0
- data/test/dummy/db/migrate/{20120318022218_create_sashes.rb → 20130329224408_create_sashes.rb} +0 -0
- data/test/dummy/db/migrate/{20120318022219_create_badges_sashes.rb → 20130329224409_create_badges_sashes.rb} +0 -0
- data/test/dummy/db/migrate/{20121013174256_create_scores_and_points.rb → 20130329224410_create_scores_and_points.rb} +0 -0
- data/test/dummy/db/schema.rb +10 -3
- data/test/integration/navigation_test.rb +27 -14
- data/test/merit_unit_test.rb +15 -24
- data/test/sash_finder_test.rb +2 -2
- metadata +12 -9
- data/app/models/badge.rb +0 -52
- data/lib/merit/models/active_record/badges_sash.rb +0 -15
- data/lib/merit/models/active_record/sash.rb +0 -52
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
merit (1.
|
4
|
+
merit (1.5.0)
|
5
5
|
ambry (~> 0.3.0)
|
6
6
|
|
7
7
|
GEM
|
@@ -101,7 +101,7 @@ GEM
|
|
101
101
|
rake (>= 0.8.7)
|
102
102
|
rdoc (~> 3.4)
|
103
103
|
thor (~> 0.14.6)
|
104
|
-
rake (0.
|
104
|
+
rake (10.0.4)
|
105
105
|
rdoc (3.12)
|
106
106
|
json (~> 1.4)
|
107
107
|
rubyzip (0.9.8)
|
data/README.md
CHANGED
@@ -167,8 +167,7 @@ end
|
|
167
167
|
|
168
168
|
# To-do list
|
169
169
|
|
170
|
-
* Should namespace Badge, BadgesSash and Sash into Merit module.
|
171
170
|
* Move level from meritable model into Sash
|
172
|
-
*
|
173
|
-
|
171
|
+
* `ActivityLog` should replace `add_points` `log` parameter
|
172
|
+
* Extract logging from `Judge` to an Observer. See http://www.rubytapas.com/episodes/21-Domain-Model-Events
|
174
173
|
* FIXMES and TODOS.
|
data/TESTING.txt
CHANGED
@@ -3,12 +3,9 @@ rake
|
|
3
3
|
|
4
4
|
# Or spin up self contained testing Rails application
|
5
5
|
cd test/dummy
|
6
|
-
rails g merit:install
|
7
|
-
n
|
8
|
-
n
|
9
|
-
n
|
10
|
-
n
|
6
|
+
rails g merit:install
|
11
7
|
rails g merit user
|
8
|
+
rails g merit comment
|
12
9
|
rake db:migrate db:seed
|
13
10
|
|
14
11
|
# see it in the browser
|
data/UPGRADING.md
CHANGED
@@ -1,14 +1,42 @@
|
|
1
1
|
# Upgrading
|
2
2
|
|
3
|
+
## 1.5.0
|
4
|
+
|
5
|
+
* Adds `Merit::ActivityLog` join model between `Merit::Action` and
|
6
|
+
`Merit::BadgesSash` and `Merit::Score::Point` for logging purposes. Every
|
7
|
+
time a badge is granted or removed, or points are changed, a new
|
8
|
+
`ActivityLog` object gets created.
|
9
|
+
* Namespaces `Badge`, `Sash` and `BadgesSash` into `Merit` module. If your app
|
10
|
+
uses any of those class names, you should add a `Merit::` prefix.
|
11
|
+
* Removes undocumented `log:string` column from `merit_actions`.
|
12
|
+
|
13
|
+
Run the following migration to upgrade from 1.4.0:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
class UpgradeMeritTo150 < ActiveRecord::Migration
|
17
|
+
def self.up
|
18
|
+
remove_column :merit_actions, :log
|
19
|
+
create_table "merit_activity_logs", :force => true do |t|
|
20
|
+
t.integer "action_id"
|
21
|
+
t.string "related_change_type"
|
22
|
+
t.integer "related_change_id"
|
23
|
+
t.string "description"
|
24
|
+
t.datetime "created_at"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
```
|
29
|
+
|
3
30
|
## 1.4.0
|
4
31
|
|
5
32
|
* Removed `BadgesSash#set_notified!` undocumented method from code base.
|
6
33
|
* `:to` option for points and badges granting may now return an array of
|
7
34
|
objects. For instance:
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
# All user's comments earn points
|
38
|
+
score 2, to: :user_comments, on: 'comments#vote'
|
39
|
+
```
|
12
40
|
|
13
41
|
## to 1.3.0
|
14
42
|
|
data/app/models/merit/action.rb
CHANGED
@@ -0,0 +1,54 @@
|
|
1
|
+
module Merit
|
2
|
+
require 'ambry'
|
3
|
+
require 'ambry/active_model'
|
4
|
+
|
5
|
+
class Badge
|
6
|
+
extend Ambry::Model
|
7
|
+
extend Ambry::ActiveModel
|
8
|
+
|
9
|
+
field :id, :name, :level, :image, :description
|
10
|
+
|
11
|
+
validates_presence_of :id, :name
|
12
|
+
validates_uniqueness_of :id
|
13
|
+
|
14
|
+
filters do
|
15
|
+
def find_by_id(ids)
|
16
|
+
ids = Array.wrap(ids)
|
17
|
+
find{|b| ids.include? b[:id] }
|
18
|
+
end
|
19
|
+
def by_name(name)
|
20
|
+
find{|b| b.name == name.to_s }
|
21
|
+
end
|
22
|
+
def by_level(level)
|
23
|
+
find{|b| b.level.to_s == level.to_s }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class << self
|
28
|
+
def find_by_name_and_level(name, level)
|
29
|
+
badges = Badge.by_name(name)
|
30
|
+
badges = badges.by_level(level) unless level.nil?
|
31
|
+
if !(badge = badges.first)
|
32
|
+
raise ::Merit::BadgeNotFound, "No badge '#{name}'#{level.nil? ? '' : " with level #{level}"} found. Define it in 'config/initializers/merit.rb'."
|
33
|
+
end
|
34
|
+
badge
|
35
|
+
end
|
36
|
+
|
37
|
+
# Last badges granted
|
38
|
+
def last_granted(options = {})
|
39
|
+
options[:since_date] ||= 1.month.ago
|
40
|
+
options[:limit] ||= 10
|
41
|
+
BadgesSash.last_granted(options)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Defines Badge#meritable_models method, to get related
|
45
|
+
# entries with certain badge. For instance, Badge.find(3).users
|
46
|
+
def _define_related_entries_method(meritable_class_name)
|
47
|
+
define_method(:"#{meritable_class_name.underscore.pluralize}") do
|
48
|
+
sashes = BadgesSash.where(badge_id: self.id).pluck(:sash_id)
|
49
|
+
meritable_class_name.constantize.where(sash_id: sashes)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -14,6 +14,7 @@ module ActiveRecord
|
|
14
14
|
|
15
15
|
def copy_migrations_and_model
|
16
16
|
migration_template 'create_merit_actions.rb', 'db/migrate/create_merit_actions.rb'
|
17
|
+
migration_template 'create_merit_activity_logs.rb', 'db/migrate/create_merit_activity_logs.rb'
|
17
18
|
migration_template 'create_sashes.rb', 'db/migrate/create_sashes.rb'
|
18
19
|
migration_template 'create_badges_sashes.rb', 'db/migrate/create_badges_sashes.rb'
|
19
20
|
migration_template 'create_scores_and_points.rb', 'db/migrate/create_scores_and_points.rb'
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class CreateMeritActivityLogs < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :merit_activity_logs do |t|
|
4
|
+
t.integer :action_id
|
5
|
+
t.string :related_change_type
|
6
|
+
t.integer :related_change_id
|
7
|
+
t.string :description
|
8
|
+
t.datetime :created_at
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.down
|
13
|
+
drop_table :merit_activity_logs
|
14
|
+
end
|
15
|
+
end
|
data/lib/merit.rb
CHANGED
@@ -45,8 +45,9 @@ module Merit
|
|
45
45
|
|
46
46
|
initializer 'merit.controller' do |app|
|
47
47
|
if Merit.orm == :active_record
|
48
|
-
require 'merit/models/active_record/
|
49
|
-
require 'merit/models/active_record/badges_sash'
|
48
|
+
require 'merit/models/active_record/merit/activity_log'
|
49
|
+
require 'merit/models/active_record/merit/badges_sash'
|
50
|
+
require 'merit/models/active_record/merit/sash'
|
50
51
|
require 'merit/models/active_record/merit/score'
|
51
52
|
elsif Merit.orm == :mongoid
|
52
53
|
require "merit/models/mongoid/sash"
|
data/lib/merit/judge.rb
CHANGED
@@ -12,7 +12,7 @@ module Merit
|
|
12
12
|
# then remove it.
|
13
13
|
def apply_badges
|
14
14
|
if rule_applies?
|
15
|
-
grant_badges
|
15
|
+
grant_badges
|
16
16
|
else
|
17
17
|
remove_badges if @rule.temporary
|
18
18
|
end
|
@@ -21,26 +21,41 @@ module Merit
|
|
21
21
|
def apply_points
|
22
22
|
return unless rule_applies?
|
23
23
|
@sashes.each do |sash|
|
24
|
-
sash.add_points @rule.score
|
24
|
+
points = sash.add_points @rule.score
|
25
|
+
ActivityLog.create(
|
26
|
+
action_id: @action.id,
|
27
|
+
related_change: points
|
28
|
+
)
|
25
29
|
end
|
26
|
-
@action.log_activity "points_granted:#{@rule.score}"
|
27
30
|
end
|
28
31
|
|
29
32
|
private
|
30
33
|
|
31
34
|
def grant_badges
|
32
|
-
@sashes.each
|
33
|
-
|
34
|
-
|
35
|
+
@sashes.each do |sash|
|
36
|
+
next unless new_or_multiple?(sash)
|
37
|
+
badge_sash = sash.add_badge badge.id
|
38
|
+
ActivityLog.create(
|
39
|
+
action_id: @action.id,
|
40
|
+
related_change: badge_sash,
|
41
|
+
description: 'granted'
|
42
|
+
)
|
43
|
+
end
|
35
44
|
end
|
36
45
|
|
37
46
|
def remove_badges
|
38
|
-
@sashes.each
|
39
|
-
|
47
|
+
@sashes.each do |sash|
|
48
|
+
badge_sash = sash.rm_badge badge.id
|
49
|
+
ActivityLog.create(
|
50
|
+
action_id: @action.id,
|
51
|
+
related_change: badge_sash,
|
52
|
+
description: 'removed'
|
53
|
+
)
|
54
|
+
end
|
40
55
|
end
|
41
56
|
|
42
|
-
def new_or_multiple?
|
43
|
-
|
57
|
+
def new_or_multiple?(sash)
|
58
|
+
!sash.badge_ids.include?(badge.id) || @rule.multiple
|
44
59
|
end
|
45
60
|
|
46
61
|
def rule_applies?
|
@@ -7,7 +7,7 @@ module Merit
|
|
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
|
-
belongs_to :sash
|
10
|
+
belongs_to :sash, class_name: 'Merit::Sash'
|
11
11
|
|
12
12
|
_merit_orm_specific_config
|
13
13
|
_merit_delegate_methods_to_sash
|
@@ -2,6 +2,8 @@ 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
|
6
|
+
|
5
7
|
attr_accessible :user_id, :action_method, :action_value, :had_errors,
|
6
8
|
:target_model, :target_id, :processed, :log
|
7
9
|
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Merit
|
2
|
+
class ActivityLog < ActiveRecord::Base
|
3
|
+
self.table_name = :merit_activity_logs
|
4
|
+
|
5
|
+
belongs_to :action, class_name: Merit::Action
|
6
|
+
belongs_to :related_change, polymorphic: true
|
7
|
+
|
8
|
+
attr_accessible :action_id, :related_change, :description, :created_at
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Merit
|
2
|
+
class BadgesSash < ActiveRecord::Base
|
3
|
+
belongs_to :sash
|
4
|
+
has_many :activity_logs, class_name: Merit::ActivityLog, as: :related_change
|
5
|
+
|
6
|
+
attr_accessible :badge_id
|
7
|
+
|
8
|
+
def self.last_granted(options = {})
|
9
|
+
options[:since_date] ||= 1.month.ago
|
10
|
+
options[:limit] ||= 10
|
11
|
+
where("created_at > '#{options[:since_date]}'").
|
12
|
+
limit(options[:limit]).
|
13
|
+
map(&:badge)
|
14
|
+
end
|
15
|
+
|
16
|
+
def badge
|
17
|
+
Badge.find(badge_id)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Merit
|
2
|
+
# Sash is a container for reputation data for meritable models. It's an
|
3
|
+
# indirection between meritable models and badges and scores (one to one
|
4
|
+
# relationship).
|
5
|
+
#
|
6
|
+
# It's existence make join models like badges_users and scores_users
|
7
|
+
# unnecessary. It should be transparent at the application.
|
8
|
+
class Sash < ActiveRecord::Base
|
9
|
+
has_many :badges_sashes, :dependent => :destroy
|
10
|
+
has_many :scores, :dependent => :destroy, :class_name => 'Merit::Score'
|
11
|
+
|
12
|
+
after_create :create_scores
|
13
|
+
|
14
|
+
def badges
|
15
|
+
badge_ids.collect { |b_id| Badge.find(b_id) }
|
16
|
+
end
|
17
|
+
|
18
|
+
def badge_ids
|
19
|
+
badges_sashes.map(&:badge_id)
|
20
|
+
end
|
21
|
+
|
22
|
+
def add_badge(badge_id)
|
23
|
+
bs = BadgesSash.new(badge_id: badge_id)
|
24
|
+
self.badges_sashes << bs
|
25
|
+
bs
|
26
|
+
end
|
27
|
+
|
28
|
+
def rm_badge(badge_id)
|
29
|
+
badges_sashes.find_by_badge_id(badge_id).try(:destroy)
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
def points(category = 'default')
|
34
|
+
scores.where(:category => category).first.points
|
35
|
+
end
|
36
|
+
|
37
|
+
def add_points(num_points, log = 'Manually granted through `add_points`', category = 'default')
|
38
|
+
point = Merit::Score::Point.new
|
39
|
+
point.log = log
|
40
|
+
point.num_points = num_points
|
41
|
+
self.scores.where(:category => category).first.score_points << point
|
42
|
+
point
|
43
|
+
end
|
44
|
+
|
45
|
+
def substract_points(num_points, log = 'Manually granted through `add_points`', category = 'default')
|
46
|
+
add_points -num_points, log, category
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def create_scores
|
52
|
+
self.scores << Merit::Score.create
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -2,7 +2,7 @@ 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, :dependent => :destroy, :
|
5
|
+
has_many :score_points, :dependent => :destroy, class_name: 'Merit::Score::Point'
|
6
6
|
|
7
7
|
# Meant to display a leaderboard. Accepts options :table_name (users by
|
8
8
|
# default), :since_date (1.month.ago by default) and :limit (10 by
|
@@ -44,6 +44,7 @@ SQL
|
|
44
44
|
|
45
45
|
class Point < ActiveRecord::Base
|
46
46
|
belongs_to :score, :class_name => 'Merit::Score'
|
47
|
+
has_many :activity_logs, class_name: Merit::ActivityLog, as: :related_change
|
47
48
|
end
|
48
49
|
end
|
49
50
|
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.
|
7
|
+
s.version = '1.5.0'
|
8
8
|
s.authors = ["Tute Costa"]
|
9
9
|
s.email = 'tutecosta@gmail.com'
|
10
10
|
|
@@ -4,11 +4,10 @@ class CreateMeritActions < ActiveRecord::Migration
|
|
4
4
|
t.integer :user_id # source
|
5
5
|
t.string :action_method
|
6
6
|
t.integer :action_value
|
7
|
-
t.boolean :had_errors
|
7
|
+
t.boolean :had_errors, :default => false
|
8
8
|
t.string :target_model
|
9
9
|
t.integer :target_id
|
10
10
|
t.boolean :processed, :default => false
|
11
|
-
t.string :log, :default => nil
|
12
11
|
t.timestamps
|
13
12
|
end
|
14
13
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class CreateMeritActivityLogs < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :merit_activity_logs do |t|
|
4
|
+
t.integer :action_id
|
5
|
+
t.string :related_change_type
|
6
|
+
t.integer :related_change_id
|
7
|
+
t.string :description
|
8
|
+
t.datetime :created_at
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.down
|
13
|
+
drop_table :merit_activity_logs
|
14
|
+
end
|
15
|
+
end
|
data/test/dummy/db/migrate/{20120318022218_create_sashes.rb → 20130329224408_create_sashes.rb}
RENAMED
File without changes
|
File without changes
|
File without changes
|
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 => 20130329222238) do
|
15
15
|
|
16
16
|
create_table "badges_sashes", :force => true do |t|
|
17
17
|
t.integer "badge_id"
|
@@ -39,15 +39,22 @@ ActiveRecord::Schema.define(:version => 20130321082817) do
|
|
39
39
|
t.integer "user_id"
|
40
40
|
t.string "action_method"
|
41
41
|
t.integer "action_value"
|
42
|
-
t.boolean "had_errors"
|
42
|
+
t.boolean "had_errors", :default => false
|
43
43
|
t.string "target_model"
|
44
44
|
t.integer "target_id"
|
45
45
|
t.boolean "processed", :default => false
|
46
|
-
t.string "log"
|
47
46
|
t.datetime "created_at", :null => false
|
48
47
|
t.datetime "updated_at", :null => false
|
49
48
|
end
|
50
49
|
|
50
|
+
create_table "merit_activity_logs", :force => true do |t|
|
51
|
+
t.integer "action_id"
|
52
|
+
t.string "related_change_type"
|
53
|
+
t.integer "related_change_id"
|
54
|
+
t.string "description"
|
55
|
+
t.datetime "created_at"
|
56
|
+
end
|
57
|
+
|
51
58
|
create_table "merit_score_points", :force => true do |t|
|
52
59
|
t.integer "score_id"
|
53
60
|
t.integer "num_points", :default => 0
|
@@ -4,17 +4,19 @@ class NavigationTest < ActiveSupport::IntegrationCase
|
|
4
4
|
test 'user sign up should grant badge to itself' do
|
5
5
|
visit '/users/new'
|
6
6
|
fill_in 'Name', :with => 'Jack'
|
7
|
-
|
7
|
+
assert_difference('Merit::ActivityLog.count') do
|
8
|
+
click_button('Create User')
|
9
|
+
end
|
8
10
|
|
9
11
|
user = User.where(:name => 'Jack').first
|
10
|
-
assert_equal [Badge.by_name('just-registered').first], user.badges
|
12
|
+
assert_equal [Merit::Badge.by_name('just-registered').first], user.badges
|
11
13
|
end
|
12
14
|
|
13
15
|
test 'User#add_badge should add one badge, #rm_badge should delete one' do
|
14
16
|
user = User.create(:name => 'test-user')
|
15
17
|
assert_equal [], user.badges
|
16
18
|
|
17
|
-
badge = Badge.first
|
19
|
+
badge = Merit::Badge.first
|
18
20
|
user.add_badge badge.id
|
19
21
|
user.add_badge badge.id
|
20
22
|
assert_equal [badge, badge], user.badges
|
@@ -36,13 +38,13 @@ class NavigationTest < ActiveSupport::IntegrationCase
|
|
36
38
|
visit '/users'
|
37
39
|
visit '/users'
|
38
40
|
visit '/users'
|
39
|
-
gossip = Badge.by_name('gossip').first
|
41
|
+
gossip = Merit::Badge.by_name('gossip').first
|
40
42
|
assert_equal 3, User.first.badges.count
|
41
43
|
assert_equal [gossip, gossip, gossip], User.first.badges
|
42
44
|
|
43
45
|
# Testing with namespaced controllers
|
44
46
|
visit '/admin/users'
|
45
|
-
visited_admin = Badge.by_name('visited_admin').first
|
47
|
+
visited_admin = Merit::Badge.by_name('visited_admin').first
|
46
48
|
assert_equal 4, User.first.badges.count
|
47
49
|
assert User.first.badges.include?(visited_admin)
|
48
50
|
end
|
@@ -76,10 +78,12 @@ class NavigationTest < ActiveSupport::IntegrationCase
|
|
76
78
|
fill_in 'Name', :with => 'Hi!'
|
77
79
|
fill_in 'Comment', :with => 'Hi bro!'
|
78
80
|
fill_in 'User', :with => user.id
|
79
|
-
|
81
|
+
assert_difference('Merit::ActivityLog.count', 2) do
|
82
|
+
click_button('Create Comment')
|
83
|
+
end
|
80
84
|
|
81
|
-
assert_equal [Badge.by_name('commenter').by_level(10).first], user.reload.badges
|
82
|
-
assert_equal [Badge.by_name('has_commenter_friend').first], friend.reload.badges
|
85
|
+
assert_equal [Merit::Badge.by_name('commenter').by_level(10).first], user.reload.badges
|
86
|
+
assert_equal [Merit::Badge.by_name('has_commenter_friend').first], friend.reload.badges
|
83
87
|
|
84
88
|
# Vote (to 5) a user's comment, assert relevant-commenter badge granted
|
85
89
|
relevant_comment = user.comments.where(:votes => 8).first
|
@@ -88,7 +92,7 @@ class NavigationTest < ActiveSupport::IntegrationCase
|
|
88
92
|
click_link '2'
|
89
93
|
end
|
90
94
|
|
91
|
-
relevant_badge = Badge.by_name('relevant-commenter').first
|
95
|
+
relevant_badge = Merit::Badge.by_name('relevant-commenter').first
|
92
96
|
user_badges = User.where(:name => 'test-user').first.badges
|
93
97
|
assert user_badges.include?(relevant_badge), "User badges: #{user.badges.collect(&:name).inspect} should contain relevant-commenter badge."
|
94
98
|
|
@@ -102,14 +106,18 @@ class NavigationTest < ActiveSupport::IntegrationCase
|
|
102
106
|
click_button('Update User')
|
103
107
|
|
104
108
|
user = User.where(:name => 'long_name!').first
|
105
|
-
autobiographer_badge = Badge.by_name('autobiographer').first
|
109
|
+
autobiographer_badge = Merit::Badge.by_name('autobiographer').first
|
106
110
|
assert user.badges.include?(autobiographer_badge), "User badges: #{user.badges.collect(&:name).inspect} should contain autobiographer badge."
|
107
111
|
|
108
112
|
# Edit user's name by short name
|
109
113
|
# tests ruby code in grant_on is being executed, and removes badge
|
110
114
|
visit "/users/#{user.id}/edit"
|
111
115
|
fill_in 'Name', :with => 'abc'
|
112
|
-
|
116
|
+
assert_difference('Merit::ActivityLog.count', 2) do
|
117
|
+
click_button('Update User')
|
118
|
+
end
|
119
|
+
# Last one is point granting, previous one is badge removing
|
120
|
+
assert_equal 'removed', Merit::ActivityLog.all[-2].description
|
113
121
|
|
114
122
|
user = User.where(:name => 'abc').first
|
115
123
|
assert !user.badges.include?(autobiographer_badge), "User badges: #{user.badges.collect(&:name).inspect} should remove autobiographer badge."
|
@@ -122,7 +130,9 @@ class NavigationTest < ActiveSupport::IntegrationCase
|
|
122
130
|
|
123
131
|
visit "/users/#{user.id}/edit"
|
124
132
|
fill_in 'Name', :with => 'a'
|
125
|
-
|
133
|
+
assert_difference('Merit::ActivityLog.count', 2) do
|
134
|
+
click_button('Update User')
|
135
|
+
end
|
126
136
|
|
127
137
|
user = User.where(:name => 'a').first
|
128
138
|
assert_equal 20, user.points, 'Updating info should grant 20 points'
|
@@ -198,8 +208,11 @@ class NavigationTest < ActiveSupport::IntegrationCase
|
|
198
208
|
comment_2 = commenter.comments.create(:name => 'comment_2', :comment => 'b')
|
199
209
|
|
200
210
|
visit comments_path
|
201
|
-
|
202
|
-
|
211
|
+
# Thanks for voting point, to voted user and it's comments
|
212
|
+
assert_difference('Merit::ActivityLog.count', 4) do
|
213
|
+
within "tr#c_#{comment_2.id}" do
|
214
|
+
click_link '1'
|
215
|
+
end
|
203
216
|
end
|
204
217
|
|
205
218
|
comment_1.reload.points.must_be :==, 2
|
data/test/merit_unit_test.rb
CHANGED
@@ -21,18 +21,9 @@ class MeritUnitTest < ActiveSupport::TestCase
|
|
21
21
|
rule.badge_name = 'inexistent'
|
22
22
|
assert_raise(Merit::BadgeNotFound) { rule.badge }
|
23
23
|
|
24
|
-
badge = Badge.create(id: 98, name: 'test-badge-98')
|
24
|
+
badge = Merit::Badge.create(id: 98, name: 'test-badge-98')
|
25
25
|
rule.badge_name = badge.name
|
26
|
-
assert_equal Badge.find(98), rule.badge
|
27
|
-
end
|
28
|
-
|
29
|
-
test "Merit::Action#log_activity doesn't grow larger than 240 chars" do
|
30
|
-
msg = 'a' * 250
|
31
|
-
m = Merit::Action.create
|
32
|
-
m.log_activity(msg)
|
33
|
-
|
34
|
-
valid_lengths = msg.length > 240 && m.log.length <= 240
|
35
|
-
assert valid_lengths, 'Log shouldn\'t grow larger than 240 chars'
|
26
|
+
assert_equal Merit::Badge.find(98), rule.badge
|
36
27
|
end
|
37
28
|
|
38
29
|
test "extends only meritable ActiveRecord models" do
|
@@ -57,33 +48,33 @@ class MeritUnitTest < ActiveSupport::TestCase
|
|
57
48
|
def self.columns; @columns ||= []; end
|
58
49
|
has_merit
|
59
50
|
end
|
60
|
-
assert Badge.method_defined?(:soldiers), 'Badge#soldiers should be defined'
|
61
|
-
assert Badge.method_defined?(:players), 'Badge#players should be defined'
|
51
|
+
assert Merit::Badge.method_defined?(:soldiers), 'Badge#soldiers should be defined'
|
52
|
+
assert Merit::Badge.method_defined?(:players), 'Badge#players should be defined'
|
62
53
|
end
|
63
54
|
|
64
55
|
test "Badge#last_granted returns recently granted badges" do
|
65
56
|
# Create sashes, badges and badges_sashes
|
66
|
-
sash = Sash.create
|
67
|
-
badge = Badge.create(id: 20, name: 'test-badge-21')
|
57
|
+
sash = Merit::Sash.create
|
58
|
+
badge = Merit::Badge.create(id: 20, name: 'test-badge-21')
|
68
59
|
sash.add_badge badge.id
|
69
|
-
BadgesSash.last.update_attribute :created_at, 1.day.ago
|
60
|
+
Merit::BadgesSash.last.update_attribute :created_at, 1.day.ago
|
70
61
|
sash.add_badge badge.id
|
71
|
-
BadgesSash.last.update_attribute :created_at, 8.days.ago
|
62
|
+
Merit::BadgesSash.last.update_attribute :created_at, 8.days.ago
|
72
63
|
sash.add_badge badge.id
|
73
|
-
BadgesSash.last.update_attribute :created_at, 15.days.ago
|
64
|
+
Merit::BadgesSash.last.update_attribute :created_at, 15.days.ago
|
74
65
|
|
75
66
|
# Test method options
|
76
|
-
assert_equal Badge.last_granted(since_date: Time.now), []
|
77
|
-
assert_equal Badge.last_granted(since_date: 1.week.ago), [badge]
|
78
|
-
assert_equal Badge.last_granted(since_date: 2.weeks.ago).count, 2
|
79
|
-
assert_equal Badge.last_granted(since_date: 2.weeks.ago, limit: 1), [badge]
|
67
|
+
assert_equal Merit::Badge.last_granted(since_date: Time.now), []
|
68
|
+
assert_equal Merit::Badge.last_granted(since_date: 1.week.ago), [badge]
|
69
|
+
assert_equal Merit::Badge.last_granted(since_date: 2.weeks.ago).count, 2
|
70
|
+
assert_equal Merit::Badge.last_granted(since_date: 2.weeks.ago, limit: 1), [badge]
|
80
71
|
end
|
81
72
|
|
82
73
|
test "Merit::Score.top_scored returns scores leaderboard" do
|
83
74
|
# Create sashes and add points
|
84
|
-
sash_1 = Sash.create
|
75
|
+
sash_1 = Merit::Sash.create
|
85
76
|
sash_1.add_points(10); sash_1.add_points(10)
|
86
|
-
sash_2 = Sash.create
|
77
|
+
sash_2 = Merit::Sash.create
|
87
78
|
sash_2.add_points(5); sash_2.add_points(5)
|
88
79
|
|
89
80
|
# Test method options
|
data/test/sash_finder_test.rb
CHANGED
@@ -3,11 +3,11 @@ require 'test_helper'
|
|
3
3
|
describe Merit::SashFinder do
|
4
4
|
|
5
5
|
it 'should return an array of sashes of the target objects' do
|
6
|
-
sash_1 = Sash.new
|
6
|
+
sash_1 = Merit::Sash.new
|
7
7
|
user_1 = User.new(:_sash => sash_1)
|
8
8
|
user_1.stubs(:_sash).returns(sash_1)
|
9
9
|
|
10
|
-
sash_2 = Sash.new
|
10
|
+
sash_2 = Merit::Sash.new
|
11
11
|
user_2 = User.new
|
12
12
|
user_2.stubs(:_sash).returns(sash_2)
|
13
13
|
|
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.
|
4
|
+
version: 1.5.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: 2013-03-
|
12
|
+
date: 2013-03-30 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ambry
|
@@ -201,13 +201,14 @@ files:
|
|
201
201
|
- Rakefile
|
202
202
|
- TESTING.txt
|
203
203
|
- UPGRADING.md
|
204
|
-
- app/models/badge.rb
|
205
204
|
- app/models/merit/action.rb
|
205
|
+
- app/models/merit/badge.rb
|
206
206
|
- lib/generators/active_record/install_generator.rb
|
207
207
|
- lib/generators/active_record/merit_generator.rb
|
208
208
|
- lib/generators/active_record/templates/add_fields_to_model.rb
|
209
209
|
- lib/generators/active_record/templates/create_badges_sashes.rb
|
210
210
|
- lib/generators/active_record/templates/create_merit_actions.rb
|
211
|
+
- lib/generators/active_record/templates/create_merit_activity_logs.rb
|
211
212
|
- lib/generators/active_record/templates/create_sashes.rb
|
212
213
|
- lib/generators/active_record/templates/create_scores_and_points.rb
|
213
214
|
- lib/generators/merit/install_generator.rb
|
@@ -221,10 +222,11 @@ files:
|
|
221
222
|
- lib/merit/controller_extensions.rb
|
222
223
|
- lib/merit/judge.rb
|
223
224
|
- lib/merit/model_additions.rb
|
224
|
-
- lib/merit/models/active_record/badges_sash.rb
|
225
225
|
- lib/merit/models/active_record/merit/action.rb
|
226
|
+
- lib/merit/models/active_record/merit/activity_log.rb
|
227
|
+
- lib/merit/models/active_record/merit/badges_sash.rb
|
228
|
+
- lib/merit/models/active_record/merit/sash.rb
|
226
229
|
- lib/merit/models/active_record/merit/score.rb
|
227
|
-
- lib/merit/models/active_record/sash.rb
|
228
230
|
- lib/merit/models/mongo_mapper/merit/action.rb
|
229
231
|
- lib/merit/models/mongo_mapper/sash.rb
|
230
232
|
- lib/merit/models/mongoid/merit/action.rb
|
@@ -331,12 +333,13 @@ files:
|
|
331
333
|
- test/dummy/config/routes.rb
|
332
334
|
- test/dummy/db/migrate/20110421191249_create_users.rb
|
333
335
|
- test/dummy/db/migrate/20110421191250_create_comments.rb
|
334
|
-
- test/dummy/db/migrate/20120318022217_create_merit_actions.rb
|
335
|
-
- test/dummy/db/migrate/20120318022218_create_sashes.rb
|
336
|
-
- test/dummy/db/migrate/20120318022219_create_badges_sashes.rb
|
337
336
|
- test/dummy/db/migrate/20120318022220_add_fields_to_users.rb
|
338
|
-
- test/dummy/db/migrate/20121013174256_create_scores_and_points.rb
|
339
337
|
- test/dummy/db/migrate/20130321082817_add_fields_to_comments.rb
|
338
|
+
- test/dummy/db/migrate/20130329224406_create_merit_actions.rb
|
339
|
+
- test/dummy/db/migrate/20130329224407_create_merit_activity_logs.rb
|
340
|
+
- test/dummy/db/migrate/20130329224408_create_sashes.rb
|
341
|
+
- test/dummy/db/migrate/20130329224409_create_badges_sashes.rb
|
342
|
+
- test/dummy/db/migrate/20130329224410_create_scores_and_points.rb
|
340
343
|
- test/dummy/db/schema.rb
|
341
344
|
- test/dummy/db/seeds.rb
|
342
345
|
- test/dummy/public/404.html
|
data/app/models/badge.rb
DELETED
@@ -1,52 +0,0 @@
|
|
1
|
-
require 'ambry'
|
2
|
-
require 'ambry/active_model'
|
3
|
-
|
4
|
-
class Badge
|
5
|
-
extend Ambry::Model
|
6
|
-
extend Ambry::ActiveModel
|
7
|
-
|
8
|
-
field :id, :name, :level, :image, :description
|
9
|
-
|
10
|
-
validates_presence_of :id, :name
|
11
|
-
validates_uniqueness_of :id
|
12
|
-
|
13
|
-
filters do
|
14
|
-
def find_by_id(ids)
|
15
|
-
ids = Array.wrap(ids)
|
16
|
-
find{|b| ids.include? b[:id] }
|
17
|
-
end
|
18
|
-
def by_name(name)
|
19
|
-
find{|b| b.name == name.to_s }
|
20
|
-
end
|
21
|
-
def by_level(level)
|
22
|
-
find{|b| b.level.to_s == level.to_s }
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
class << self
|
27
|
-
def find_by_name_and_level(name, level)
|
28
|
-
badges = Badge.by_name(name)
|
29
|
-
badges = badges.by_level(level) unless level.nil?
|
30
|
-
if !(badge = badges.first)
|
31
|
-
raise ::Merit::BadgeNotFound, "No badge '#{name}'#{level.nil? ? '' : " with level #{level}"} found. Define it in 'config/initializers/merit.rb'."
|
32
|
-
end
|
33
|
-
badge
|
34
|
-
end
|
35
|
-
|
36
|
-
# Last badges granted
|
37
|
-
def last_granted(options = {})
|
38
|
-
options[:since_date] ||= 1.month.ago
|
39
|
-
options[:limit] ||= 10
|
40
|
-
BadgesSash.last_granted(options)
|
41
|
-
end
|
42
|
-
|
43
|
-
# Defines Badge#meritable_models method, to get related
|
44
|
-
# entries with certain badge. For instance, Badge.find(3).users
|
45
|
-
def _define_related_entries_method(meritable_class_name)
|
46
|
-
define_method(:"#{meritable_class_name.underscore.pluralize}") do
|
47
|
-
sashes = BadgesSash.where(badge_id: self.id).pluck(:sash_id)
|
48
|
-
meritable_class_name.constantize.where(sash_id: sashes)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
@@ -1,15 +0,0 @@
|
|
1
|
-
class BadgesSash < ActiveRecord::Base
|
2
|
-
belongs_to :sash
|
3
|
-
|
4
|
-
def self.last_granted(options = {})
|
5
|
-
options[:since_date] ||= 1.month.ago
|
6
|
-
options[:limit] ||= 10
|
7
|
-
where("created_at > '#{options[:since_date]}'").
|
8
|
-
limit(options[:limit]).
|
9
|
-
map(&:badge)
|
10
|
-
end
|
11
|
-
|
12
|
-
def badge
|
13
|
-
Badge.find(badge_id)
|
14
|
-
end
|
15
|
-
end
|
@@ -1,52 +0,0 @@
|
|
1
|
-
# Sash is a container for reputation data for meritable models. It's an
|
2
|
-
# indirection between meritable models and badges and scores (one to one
|
3
|
-
# relationship).
|
4
|
-
#
|
5
|
-
# It's existence make join models like badges_users and scores_users
|
6
|
-
# unnecessary. It should be transparent at the application.
|
7
|
-
class Sash < ActiveRecord::Base
|
8
|
-
has_many :badges_sashes, :dependent => :destroy
|
9
|
-
has_many :scores, :dependent => :destroy, :class_name => 'Merit::Score'
|
10
|
-
|
11
|
-
after_create :create_scores
|
12
|
-
|
13
|
-
def badges
|
14
|
-
badge_ids.collect { |b_id| Badge.find(b_id) }
|
15
|
-
end
|
16
|
-
|
17
|
-
def badge_ids
|
18
|
-
badges_sashes.map(&:badge_id)
|
19
|
-
end
|
20
|
-
|
21
|
-
def add_badge(badge_id)
|
22
|
-
bs = BadgesSash.new
|
23
|
-
bs.badge_id = badge_id
|
24
|
-
self.badges_sashes << bs
|
25
|
-
end
|
26
|
-
|
27
|
-
def rm_badge(badge_id)
|
28
|
-
badges_sashes.find_by_badge_id(badge_id).try(:destroy)
|
29
|
-
end
|
30
|
-
|
31
|
-
|
32
|
-
def points(category = 'default')
|
33
|
-
scores.where(:category => category).first.points
|
34
|
-
end
|
35
|
-
|
36
|
-
def add_points(num_points, log = 'Manually granted through `add_points`', category = 'default')
|
37
|
-
point = Merit::Score::Point.new
|
38
|
-
point.log = log
|
39
|
-
point.num_points = num_points
|
40
|
-
self.scores.where(:category => category).first.score_points << point
|
41
|
-
end
|
42
|
-
|
43
|
-
def substract_points(num_points, log = 'Manually granted through `add_points`', category = 'default')
|
44
|
-
add_points -num_points, log, category
|
45
|
-
end
|
46
|
-
|
47
|
-
private
|
48
|
-
|
49
|
-
def create_scores
|
50
|
-
self.scores << Merit::Score.create
|
51
|
-
end
|
52
|
-
end
|