merit 1.4.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. data/Gemfile.lock +2 -2
  2. data/README.md +2 -3
  3. data/TESTING.txt +2 -5
  4. data/UPGRADING.md +32 -4
  5. data/app/models/merit/action.rb +0 -4
  6. data/app/models/merit/badge.rb +54 -0
  7. data/lib/generators/active_record/install_generator.rb +1 -0
  8. data/lib/generators/active_record/templates/create_merit_actions.rb +0 -1
  9. data/lib/generators/active_record/templates/create_merit_activity_logs.rb +15 -0
  10. data/lib/generators/merit/templates/merit.rb +1 -1
  11. data/lib/merit.rb +3 -2
  12. data/lib/merit/judge.rb +25 -10
  13. data/lib/merit/model_additions.rb +1 -1
  14. data/lib/merit/models/active_record/merit/action.rb +2 -0
  15. data/lib/merit/models/active_record/merit/activity_log.rb +10 -0
  16. data/lib/merit/models/active_record/merit/badges_sash.rb +20 -0
  17. data/lib/merit/models/active_record/merit/sash.rb +55 -0
  18. data/lib/merit/models/active_record/merit/score.rb +2 -1
  19. data/merit.gemspec +1 -1
  20. data/test/dummy/app/models/comment.rb +1 -0
  21. data/test/dummy/config/initializers/merit.rb +1 -1
  22. data/test/dummy/db/migrate/{20120318022217_create_merit_actions.rb → 20130329224406_create_merit_actions.rb} +1 -2
  23. data/test/dummy/db/migrate/20130329224407_create_merit_activity_logs.rb +15 -0
  24. data/test/dummy/db/migrate/{20120318022218_create_sashes.rb → 20130329224408_create_sashes.rb} +0 -0
  25. data/test/dummy/db/migrate/{20120318022219_create_badges_sashes.rb → 20130329224409_create_badges_sashes.rb} +0 -0
  26. data/test/dummy/db/migrate/{20121013174256_create_scores_and_points.rb → 20130329224410_create_scores_and_points.rb} +0 -0
  27. data/test/dummy/db/schema.rb +10 -3
  28. data/test/integration/navigation_test.rb +27 -14
  29. data/test/merit_unit_test.rb +15 -24
  30. data/test/sash_finder_test.rb +2 -2
  31. metadata +12 -9
  32. data/app/models/badge.rb +0 -52
  33. data/lib/merit/models/active_record/badges_sash.rb +0 -15
  34. data/lib/merit/models/active_record/sash.rb +0 -52
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- merit (1.4.0)
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.9.3.beta.1)
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
- * Could have a Merit::Action - Activity - {BadgesSash|Merit::Score::Point}
173
- join model with datetimes to serve as "log"
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.
@@ -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 # n's for not overriding already defined badges & rules
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
@@ -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
- ```ruby
9
- # All user's comments earn points
10
- score 2, to: :user_comments, on: 'comments#vote'
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
 
@@ -29,10 +29,6 @@ module Merit
29
29
  check_rules point_rules, :points
30
30
  end
31
31
 
32
- def log_activity(str)
33
- self.update_attribute :log, "#{self.log}#{str}|"[0,240]
34
- end
35
-
36
32
  private
37
33
 
38
34
  def check_rules(rules_array, badges_or_points)
@@ -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'
@@ -8,7 +8,6 @@ class CreateMeritActions < ActiveRecord::Migration
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
@@ -14,7 +14,7 @@ Merit.setup do |config|
14
14
  end
15
15
 
16
16
  # Create application badges (uses https://github.com/norman/ambry)
17
- # Badge.create!({
17
+ # Merit::Badge.create!({
18
18
  # :id => 1,
19
19
  # :name => 'just-registered'
20
20
  # })
@@ -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/sash'
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"
@@ -12,7 +12,7 @@ module Merit
12
12
  # then remove it.
13
13
  def apply_badges
14
14
  if rule_applies?
15
- grant_badges if new_or_multiple?
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, @action.inspect[0..240]
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 { |sash| sash.add_badge badge.id }
33
- to_action_user = (@rule.to.to_sym == :action_user ? '_to_action_user' : '')
34
- @action.log_activity "badge_granted#{to_action_user}:#{badge.id}"
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 { |sash| sash.rm_badge badge.id }
39
- @action.log_activity "badge_removed:#{badge.id}"
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
- !@sashes.map(&:badge_ids).include?(badge.id) || @rule.multiple
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, :class_name => 'Merit::Score::Point'
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
@@ -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.4.0'
7
+ s.version = '1.5.0'
8
8
  s.authors = ["Tute Costa"]
9
9
  s.email = 'tutecosta@gmail.com'
10
10
 
@@ -1,5 +1,6 @@
1
1
  class Comment < ActiveRecord::Base
2
2
  has_merit
3
+
3
4
  belongs_to :user
4
5
 
5
6
  attr_accessible :name, :comment, :user_id, :votes
@@ -41,5 +41,5 @@ badge_id = 0
41
41
  :id => (badge_id = badge_id+1),
42
42
  :name => 'gossip'
43
43
  }].each do |badge|
44
- Badge.create! badge
44
+ Merit::Badge.create! badge
45
45
  end
@@ -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
@@ -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 => 20130321082817) do
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
- click_button('Create User')
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
- click_button('Create Comment')
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
- click_button('Update User')
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
- click_button('Update User')
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
- within "tr#c_#{comment_2.id}" do
202
- click_link '1'
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
@@ -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
@@ -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.0
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-26 00:00:00.000000000 Z
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
@@ -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