qalam_merit 4.0.4

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.
Files changed (138) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +5 -0
  3. data/CODE_OF_CONDUCT.md +84 -0
  4. data/Gemfile +10 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +358 -0
  7. data/RELEASING.md +16 -0
  8. data/Rakefile +44 -0
  9. data/TESTING.txt +12 -0
  10. data/config/locales/en.yml +5 -0
  11. data/lib/merit.rb +95 -0
  12. data/lib/merit/badge_rules_methods.rb +30 -0
  13. data/lib/merit/base_target_finder.rb +31 -0
  14. data/lib/merit/class_methods.rb +41 -0
  15. data/lib/merit/controller_extensions.rb +71 -0
  16. data/lib/merit/generators/active_record/install_generator.rb +40 -0
  17. data/lib/merit/generators/active_record/merit_generator.rb +25 -0
  18. data/lib/merit/generators/active_record/remove_generator.rb +30 -0
  19. data/lib/merit/generators/active_record/templates/add_merit_fields_to_model.erb +7 -0
  20. data/lib/merit/generators/active_record/templates/create_badges_sashes.erb +14 -0
  21. data/lib/merit/generators/active_record/templates/create_merit_actions.erb +16 -0
  22. data/lib/merit/generators/active_record/templates/create_merit_activity_logs.erb +12 -0
  23. data/lib/merit/generators/active_record/templates/create_merit_badges.erb +17 -0
  24. data/lib/merit/generators/active_record/templates/create_sashes.erb +8 -0
  25. data/lib/merit/generators/active_record/templates/create_scores_and_points.erb +16 -0
  26. data/lib/merit/generators/active_record/templates/remove_merit_fields_from_model.erb +7 -0
  27. data/lib/merit/generators/active_record/templates/remove_merit_tables.erb +12 -0
  28. data/lib/merit/generators/install_generator.rb +30 -0
  29. data/lib/merit/generators/merit_generator.rb +33 -0
  30. data/lib/merit/generators/remove_generator.rb +23 -0
  31. data/lib/merit/generators/templates/badge.erb +69 -0
  32. data/lib/merit/generators/templates/merit.erb +18 -0
  33. data/lib/merit/generators/templates/merit_badge_rules.erb +50 -0
  34. data/lib/merit/generators/templates/merit_point_rules.erb +31 -0
  35. data/lib/merit/generators/templates/merit_rank_rules.erb +32 -0
  36. data/lib/merit/generators/templates/qalam_badge_sash.erb +20 -0
  37. data/lib/merit/generators/templates/qalam_sash.erb +14 -0
  38. data/lib/merit/judge.rb +102 -0
  39. data/lib/merit/models/action_concern.rb +50 -0
  40. data/lib/merit/models/active_record/action.rb +11 -0
  41. data/lib/merit/models/active_record/activity_log.rb +11 -0
  42. data/lib/merit/models/active_record/badges_sash.rb +13 -0
  43. data/lib/merit/models/active_record/sash.rb +32 -0
  44. data/lib/merit/models/active_record/score.rb +25 -0
  45. data/lib/merit/models/badges_sash_concern.rb +13 -0
  46. data/lib/merit/models/base/badges_sash.rb +15 -0
  47. data/lib/merit/models/base/sash.rb +55 -0
  48. data/lib/merit/models/sash_concern.rb +53 -0
  49. data/lib/merit/point_rules_methods.rb +33 -0
  50. data/lib/merit/rank_rules_methods.rb +58 -0
  51. data/lib/merit/reputation_change_observer.rb +19 -0
  52. data/lib/merit/rule.rb +35 -0
  53. data/lib/merit/rules_matcher.rb +24 -0
  54. data/lib/merit/sash_finder.rb +15 -0
  55. data/lib/merit/target_finder.rb +43 -0
  56. data/qalam_merit.gemspec +23 -0
  57. data/test/dummy/Rakefile +7 -0
  58. data/test/dummy/app/controllers/admin/users_controller.rb +9 -0
  59. data/test/dummy/app/controllers/api/comments_controller.rb +5 -0
  60. data/test/dummy/app/controllers/api/users_controller.rb +5 -0
  61. data/test/dummy/app/controllers/application_controller.rb +7 -0
  62. data/test/dummy/app/controllers/comments_controller.rb +56 -0
  63. data/test/dummy/app/controllers/registrations_controller.rb +21 -0
  64. data/test/dummy/app/controllers/users_controller.rb +38 -0
  65. data/test/dummy/app/helpers/application_helper.rb +2 -0
  66. data/test/dummy/app/models/address.rb +3 -0
  67. data/test/dummy/app/models/comment.rb +13 -0
  68. data/test/dummy/app/models/dummy_observer.rb +3 -0
  69. data/test/dummy/app/models/merit/badge_rules.rb +66 -0
  70. data/test/dummy/app/models/merit/point_rules.rb +44 -0
  71. data/test/dummy/app/models/merit/rank_rules.rb +24 -0
  72. data/test/dummy/app/models/user.rb +23 -0
  73. data/test/dummy/app/views/admin/users/index.html.erb +26 -0
  74. data/test/dummy/app/views/comments/_form.html.erb +29 -0
  75. data/test/dummy/app/views/comments/edit.html.erb +6 -0
  76. data/test/dummy/app/views/comments/index.html.erb +35 -0
  77. data/test/dummy/app/views/comments/new.html.erb +5 -0
  78. data/test/dummy/app/views/comments/show.html.erb +23 -0
  79. data/test/dummy/app/views/layouts/application.html.erb +24 -0
  80. data/test/dummy/app/views/users/_form.html.erb +22 -0
  81. data/test/dummy/app/views/users/edit.html.erb +6 -0
  82. data/test/dummy/app/views/users/index.html.erb +27 -0
  83. data/test/dummy/app/views/users/new.html.erb +5 -0
  84. data/test/dummy/app/views/users/show.html.erb +18 -0
  85. data/test/dummy/config.ru +4 -0
  86. data/test/dummy/config/application.rb +26 -0
  87. data/test/dummy/config/application_api_only.rb +28 -0
  88. data/test/dummy/config/boot.rb +10 -0
  89. data/test/dummy/config/database.yml +22 -0
  90. data/test/dummy/config/environment.rb +5 -0
  91. data/test/dummy/config/environment_api_only.rb +7 -0
  92. data/test/dummy/config/environments/development.rb +24 -0
  93. data/test/dummy/config/environments/production.rb +51 -0
  94. data/test/dummy/config/environments/test.rb +36 -0
  95. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  96. data/test/dummy/config/initializers/inflections.rb +10 -0
  97. data/test/dummy/config/initializers/merit.rb +47 -0
  98. data/test/dummy/config/initializers/mime_types.rb +5 -0
  99. data/test/dummy/config/initializers/new_framework_defaults.rb +3 -0
  100. data/test/dummy/config/initializers/secret_token.rb +12 -0
  101. data/test/dummy/config/initializers/session_store.rb +8 -0
  102. data/test/dummy/config/locales/en.yml +5 -0
  103. data/test/dummy/config/mongoid.yml +13 -0
  104. data/test/dummy/config/routes.rb +16 -0
  105. data/test/dummy/db/migrate/20110421191249_create_users.rb +12 -0
  106. data/test/dummy/db/migrate/20110421191250_create_comments.rb +16 -0
  107. data/test/dummy/db/migrate/20120318022220_add_fields_to_users.rb +11 -0
  108. data/test/dummy/db/migrate/20130321082817_add_fields_to_comments.rb +11 -0
  109. data/test/dummy/db/migrate/20130329224406_create_merit_actions.rb +18 -0
  110. data/test/dummy/db/migrate/20130329224407_create_merit_activity_logs.rb +15 -0
  111. data/test/dummy/db/migrate/20130329224408_create_sashes.rb +11 -0
  112. data/test/dummy/db/migrate/20130329224409_create_badges_sashes.rb +16 -0
  113. data/test/dummy/db/migrate/20130329224410_create_scores_and_points.rb +20 -0
  114. data/test/dummy/db/migrate/20140211144001_create_addresses.rb +11 -0
  115. data/test/dummy/db/migrate/20140819133931_add_target_data_to_merit_actions.rb +5 -0
  116. data/test/dummy/db/schema.rb +89 -0
  117. data/test/dummy/db/seeds.rb +19 -0
  118. data/test/dummy/public/404.html +26 -0
  119. data/test/dummy/public/422.html +26 -0
  120. data/test/dummy/public/500.html +26 -0
  121. data/test/dummy/public/favicon.ico +0 -0
  122. data/test/dummy/public/rails.js +404 -0
  123. data/test/dummy/public/stylesheets/.gitkeep +0 -0
  124. data/test/dummy/public/stylesheets/scaffold.css +56 -0
  125. data/test/dummy/script/rails +6 -0
  126. data/test/integration/navigation_test.rb +332 -0
  127. data/test/support/integration_case.rb +5 -0
  128. data/test/test_helper.rb +40 -0
  129. data/test/unit/action_test.rb +12 -0
  130. data/test/unit/base_target_finder_test.rb +64 -0
  131. data/test/unit/merit_unit_test.rb +33 -0
  132. data/test/unit/rule_unit_test.rb +57 -0
  133. data/test/unit/rules_matcher_test.rb +37 -0
  134. data/test/unit/sash_finder_test.rb +27 -0
  135. data/test/unit/sash_test.rb +104 -0
  136. data/test/unit/score_test.rb +13 -0
  137. data/test/unit/target_finder_test.rb +98 -0
  138. metadata +360 -0
@@ -0,0 +1,58 @@
1
+ module Merit
2
+ # 5 stars is a common ranking use case. They are not given at specified
3
+ # actions like badges, you should define a cron job to test if ranks are to
4
+ # be granted.
5
+ #
6
+ # +set_rank+ accepts:
7
+ # * :+level+ ranking level (greater is better)
8
+ # * :+to+ model or scope to check if new rankings apply
9
+ # * :+level_name+ attribute name (default is empty and results in 'level'
10
+ # attribute, if set it's appended like 'level_#{level_name}')
11
+ module RankRulesMethods
12
+ # Populates +defined_rules+ hash with following hierarchy:
13
+ # defined_rules[ModelToRank] = { levels => blocks }
14
+ def set_rank(*args, &block)
15
+ options = args.extract_options!
16
+
17
+ rule = Rule.new
18
+ rule.block = block
19
+ if options[:level_name].present?
20
+ rule.level_name = "level_#{options[:level_name]}"
21
+ else
22
+ rule.level_name = 'level'
23
+ end
24
+
25
+ defined_rules[options[:to]] ||= {}
26
+ defined_rules[options[:to]].merge!(options[:level] => rule)
27
+ end
28
+
29
+ # Not part of merit after_filter. To be called asynchronously:
30
+ # Merit::RankRules.new.check_rank_rules
31
+ def check_rank_rules
32
+ defined_rules.each do |scoped_model, level_and_rules|
33
+ level_and_rules.sort.each do |level, rule|
34
+ grant_when_applies(scoped_model, rule, level)
35
+ end
36
+ end
37
+ end
38
+
39
+ # Currently defined rules
40
+ def defined_rules
41
+ @defined_rules ||= {}
42
+ end
43
+
44
+ private
45
+
46
+ def grant_when_applies(scoped_model, rule, level)
47
+ scoped_model.where("#{rule.level_name} < #{level}").each do |object|
48
+ next unless rule.applies?(object)
49
+
50
+ object.update_attribute rule.level_name, level
51
+ end
52
+ rescue ActiveRecord::StatementInvalid
53
+ str = "Error while granting rankings. Probably you need to add
54
+ #{rule.level_name} column to #{scoped_model.class.name}."
55
+ raise RankAttributeNotDefined, str
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,19 @@
1
+ module Merit
2
+ class ReputationChangeObserver
3
+ def update(changed_data)
4
+ ActivityLog.create(
5
+ description: changed_data[:description],
6
+ related_change: related_change(changed_data),
7
+ action_id: changed_data[:merit_action_id]
8
+ )
9
+ end
10
+
11
+ private
12
+
13
+ def related_change(data)
14
+ if data[:merit_object].respond_to?(:activity_logs)
15
+ data[:merit_object]
16
+ end
17
+ end
18
+ end
19
+ end
data/lib/merit/rule.rb ADDED
@@ -0,0 +1,35 @@
1
+ module Merit
2
+ # Rules has a badge name and level, a target to badge, a conditions block
3
+ # and a temporary option.
4
+ # Could split this class between badges and rankings functionality
5
+ class Rule
6
+ attr_accessor :badge_id, :badge_name, :level, :to, :model_name, :level_name,
7
+ :multiple, :temporary, :score, :block, :category
8
+
9
+ # Does this rule's condition block apply?
10
+ def applies?(target_obj = nil)
11
+ return true if block.nil? # no block given: always true
12
+
13
+ case block.arity
14
+ when 1 # Expects target object
15
+ if target_obj.present?
16
+ block.call(target_obj)
17
+ else
18
+ Rails.logger.warn '[merit] no target_obj found on Rule#applies?'
19
+ false
20
+ end
21
+ when 0
22
+ block.call
23
+ end
24
+ end
25
+
26
+ # Get rule's related Badge.
27
+ def badge
28
+ if badge_id
29
+ Merit::Badge.find(badge_id)
30
+ else
31
+ Merit::Badge.find_by_name_and_level(badge_name, level)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,24 @@
1
+ module Merit
2
+ class RulesMatcher
3
+ def initialize(path, action_name)
4
+ @path = path
5
+ @action_name = action_name
6
+ end
7
+
8
+ def select_from(rules)
9
+ rules.select do |glob, _|
10
+ entire_path =~ /^#{Regexp.new(glob)}$/
11
+ end.values.flatten
12
+ end
13
+
14
+ def any_matching?
15
+ select_from(AppBadgeRules).any? || select_from(AppPointRules).any?
16
+ end
17
+
18
+ private
19
+
20
+ def entire_path
21
+ @entire_path ||= [@path, @action_name].join('#')
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,15 @@
1
+ module Merit
2
+ class SashFinder
3
+ def self.find(rule, action)
4
+ targets(rule, action).map(&:_sash)
5
+ rescue NoMethodError
6
+ Rails.logger.warn "[merit] Couldn't find model to grant reputation to. " \
7
+ "Refer to https://github.com/tute/merit/issues/171#issuecomment-44185696."
8
+ []
9
+ end
10
+
11
+ def self.targets(rule, action)
12
+ TargetFinder.find(rule, action).compact
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,43 @@
1
+ module Merit
2
+ class TargetFinder < Struct.new(:rule, :action)
3
+ def self.find(*args)
4
+ new(*args).find
5
+ end
6
+
7
+ def find
8
+ target = case rule.to
9
+ when :itself then
10
+ base_target
11
+ when :action_user then
12
+ action_user
13
+ else
14
+ other_target
15
+ end
16
+ Array.wrap(target)
17
+ end
18
+
19
+ private
20
+
21
+ def base_target
22
+ BaseTargetFinder.find rule, action
23
+ end
24
+
25
+ def action_user
26
+ user = Merit.user_model.find_by_id action.user_id
27
+ if user.nil?
28
+ user_model = Merit.user_model
29
+ str = "[merit] no #{user_model} found with id #{action.user_id}"
30
+ Rails.logger.warn str
31
+ end
32
+ user
33
+ end
34
+
35
+ def other_target
36
+ base_target.send rule.to
37
+ rescue NoMethodError
38
+ str = "[merit] NoMethodError on `#{base_target.class.name}##{rule.to}`" \
39
+ ' (called from Merit::TargetFinder#other_target)'
40
+ Rails.logger.warn str
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,23 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "qalam_merit"
3
+ s.summary = "Reputation engine for Rails apps"
4
+ s.description = "Manage badges, points and rankings (reputation) in your Rails app."
5
+ s.homepage = "https://github.com/merit-gem/merit"
6
+ s.files = `git ls-files`.split("\n").reject{|f| f =~ /^\./ }
7
+ s.test_files = `git ls-files -- test/*`.split("\n")
8
+ s.license = 'MIT'
9
+ s.version = '4.0.4'
10
+ s.authors = ["Tute Costa", "Ahmed Abdelhamid"]
11
+ s.email = ['tutecosta@gmail.com', 'a.hamid@nezam.io']
12
+
13
+ s.required_ruby_version = '>= 2.3.0'
14
+
15
+ s.add_runtime_dependency 'zeitwerk'
16
+
17
+ s.add_development_dependency 'rails', '>= 5.1.6'
18
+ s.add_development_dependency 'capybara'
19
+ s.add_development_dependency 'simplecov'
20
+ s.add_development_dependency 'rubocop'
21
+ s.add_development_dependency 'minitest-rails'
22
+ s.add_development_dependency 'mocha'
23
+ end
@@ -0,0 +1,7 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require File.expand_path('../config/application', __FILE__)
5
+ require 'rake'
6
+
7
+ Dummy::Application.load_tasks
@@ -0,0 +1,9 @@
1
+ class Admin::UsersController < ApplicationController
2
+ def index
3
+ @users = User.all
4
+ respond_to do |format|
5
+ format.html # index.html.erb
6
+ format.xml { render :xml => @users }
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ class Api::CommentsController < ApplicationController
2
+ def show
3
+ render json: {}
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class Api::UsersController < ApplicationController
2
+ def index
3
+ render json: User.all.to_json
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ class ApplicationController < ActionController::Base
2
+ protect_from_forgery
3
+
4
+ def current_user
5
+ @current_user ||= User.first
6
+ end
7
+ end
@@ -0,0 +1,56 @@
1
+ class CommentsController < ApplicationController
2
+ def index
3
+ @comments = Comment.all
4
+ end
5
+
6
+ def show
7
+ @comment = Comment.find(params[:id])
8
+ end
9
+
10
+ def new
11
+ @comment = Comment.new
12
+ end
13
+
14
+ def edit
15
+ @comment = Comment.find(params[:id])
16
+ end
17
+
18
+ def vote
19
+ @comment = Comment.find(params[:id])
20
+ @comment.votes += params[:value].to_i
21
+ @comment.save
22
+ redirect_to(comments_url, :notice => 'Vote added!')
23
+ end
24
+
25
+ def create
26
+ @comment = Comment.new(comment_params)
27
+ if @comment.save
28
+ redirect_to(@comment, :notice => 'Comment was successfully created.')
29
+ else
30
+ render "new"
31
+ end
32
+ end
33
+
34
+ def update
35
+ @comment = Comment.find(params[:id])
36
+ if @comment.update(comment_params)
37
+ redirect_to(@comment, :notice => 'Comment was successfully updated.')
38
+ else
39
+ render "edit"
40
+ end
41
+ end
42
+
43
+ # DELETE /comments/1
44
+ # DELETE /comments/1.xml
45
+ def destroy
46
+ @comment = Comment.find(params[:id])
47
+ @comment.destroy
48
+ redirect_to(comments_url)
49
+ end
50
+
51
+ private
52
+
53
+ def comment_params
54
+ params.require(:comment).permit!
55
+ end
56
+ end
@@ -0,0 +1,21 @@
1
+ class RegistrationsController < ApplicationController
2
+ def update
3
+ @user = User.find(params[:id])
4
+
5
+ respond_to do |format|
6
+ if @user.update(user_params)
7
+ format.html { redirect_to(@user, :notice => 'User was successfully updated.') }
8
+ format.xml { head :ok }
9
+ else
10
+ format.html { render "edit" }
11
+ format.xml { render :xml => @user.errors, :status => :unprocessable_entity }
12
+ end
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def user_params
19
+ params.require(:user).permit!
20
+ end
21
+ end
@@ -0,0 +1,38 @@
1
+ class UsersController < ApplicationController
2
+ def index
3
+ @users = User.all
4
+ end
5
+
6
+ def show
7
+ @user = User.find(params[:id])
8
+ end
9
+
10
+ def new
11
+ @user = User.new
12
+ end
13
+
14
+ def edit
15
+ @user = User.find(params[:id])
16
+ end
17
+
18
+ def create
19
+ @user = User.new(user_params)
20
+ if @user.save
21
+ redirect_to(@user, :notice => 'User was successfully created.')
22
+ else
23
+ render "new"
24
+ end
25
+ end
26
+
27
+ def destroy
28
+ @user = User.find(params[:id])
29
+ @user.destroy
30
+ redirect_to(users_url)
31
+ end
32
+
33
+ private
34
+
35
+ def user_params
36
+ params.require(:user).permit!
37
+ end
38
+ end
@@ -0,0 +1,2 @@
1
+ module ApplicationHelper
2
+ end
@@ -0,0 +1,3 @@
1
+ class Address < ActiveRecord::Base
2
+ belongs_to :user
3
+ end
@@ -0,0 +1,13 @@
1
+ class Comment < ActiveRecord::Base
2
+ def friend
3
+ User.find_by_name('friend')
4
+ end
5
+
6
+ has_merit
7
+
8
+ belongs_to :user
9
+
10
+ validates :name, :comment, :user_id, :presence => true
11
+
12
+ delegate :comments, :to => :user, :prefix => true
13
+ end
@@ -0,0 +1,3 @@
1
+ class DummyObserver
2
+ def update(*); end
3
+ end
@@ -0,0 +1,66 @@
1
+ # +grant_on+ accepts:
2
+ # * Nothing (always grants)
3
+ # * A block which evaluates to boolean (recieves the object as parameter)
4
+ # * A block with a hash composed of methods to run on the target object with
5
+ # expected values (+votes: 5+ for instance).
6
+ #
7
+ # +grant_on+ can have a +:to+ method name, which called over the target object
8
+ # should retrieve the object to badge (could be +:user+, +:self+, +:follower+,
9
+ # etc). If it's not defined merit will apply the badge to the user who
10
+ # triggered the action (:action_user by default). If it's :itself, it badges
11
+ # the created object (new user for instance).
12
+ #
13
+ # The :temporary option indicates that if the condition doesn't hold but the
14
+ # badge is granted, then it's removed. It's false by default (badges are kept
15
+ # forever).
16
+
17
+ module Merit
18
+ class BadgeRules
19
+ include Merit::BadgeRulesMethods
20
+
21
+ def initialize
22
+ # If it creates user, grant badge
23
+ # Should be "current_user" after registration for badge to be granted.
24
+ # Example rule with block with no parameters
25
+ grant_on 'users#create', badge: 'just-registered', to: :itself do
26
+ true
27
+ end
28
+
29
+ # For regression test: if condition doesn't hold, it doesn't check
30
+ # sashes to badge. Useful for polymorphic relations where not every
31
+ # model has reputation. See https://github.com/tute/merit/issues/134.
32
+ grant_on 'users#create', badge: 'just-registered', to: :model_with_no_reputation do |user|
33
+ user.model_with_no_reputation.respond_to?(:_sash)
34
+ end
35
+
36
+ # Example rule for multiple badge granting
37
+ grant_on 'users#index', badge: 'gossip', multiple: true
38
+
39
+ # Example rule for badge granting in namespaced controllers
40
+ grant_on 'admin/users#index', badge: 'visited_admin'
41
+
42
+ # Example rule for testing badge granting in differently namespaced controllers.
43
+ grant_on '.*users#index', badge: 'wildcard_badge', multiple: true
44
+
45
+ # If it has 10 comments, grant commenter-10 badge, find badge by badge_id
46
+ grant_on 'comments#create', badge_id: 1, level: 10 do |comment|
47
+ comment.user.comments.count >= 10
48
+ end
49
+ # Testing badge granting in more than one rule per action with different targets
50
+ grant_on 'comments#create', badge: 'has_commenter_friend', to: :friend do |comment|
51
+ comment.user.comments.count >= 10
52
+ end
53
+
54
+ # If it has at least 10 votes, grant relevant-commenter badge
55
+ grant_on 'comments#vote', badge: 'relevant-commenter', to: :user do |comment|
56
+ comment.votes >= 10
57
+ end
58
+
59
+ # Changes his name by one wider than 4 chars (arbitrary ruby code and custom model_name)
60
+ # This badge is temporary (user may lose it)
61
+ grant_on 'registrations#update', badge: 'autobiographer', temporary: true, model_name: 'User' do |user|
62
+ user.name.length > 4
63
+ end
64
+ end
65
+ end
66
+ end