merit 1.4.0 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|