merit 0.7.1 → 0.8.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.
Files changed (71) hide show
  1. data/Gemfile +5 -0
  2. data/Gemfile.lock +13 -2
  3. data/README.md +72 -53
  4. data/TESTING.txt +7 -2
  5. data/lib/generators/merit/templates/merit.rb +1 -1
  6. data/lib/merit.rb +2 -1
  7. data/lib/merit/controller_extensions.rb +2 -2
  8. data/lib/merit/model_additions.rb +9 -2
  9. data/lib/merit/models/mongo_mapper/merit_action.rb +1 -0
  10. data/lib/merit/models/mongoid/merit_action.rb +13 -0
  11. data/lib/merit/models/mongoid/sash.rb +14 -0
  12. data/lib/merit/rule.rb +4 -2
  13. data/lib/merit/rules_rank.rb +7 -2
  14. data/merit.gemspec +5 -2
  15. data/test/dummy-mongoid/Rakefile +7 -0
  16. data/test/dummy-mongoid/app/controllers/application_controller.rb +7 -0
  17. data/test/dummy-mongoid/app/controllers/comments_controller.rb +90 -0
  18. data/test/dummy-mongoid/app/controllers/registrations_controller.rb +15 -0
  19. data/test/dummy-mongoid/app/controllers/users_controller.rb +67 -0
  20. data/test/dummy-mongoid/app/helpers/application_helper.rb +2 -0
  21. data/test/dummy-mongoid/app/models/comment.rb +17 -0
  22. data/test/dummy-mongoid/app/models/merit/badge_rules.rb +49 -0
  23. data/test/dummy-mongoid/app/models/merit/point_rules.rb +20 -0
  24. data/test/dummy-mongoid/app/models/merit/rank_rules.rb +24 -0
  25. data/test/dummy-mongoid/app/models/user.rb +30 -0
  26. data/test/dummy-mongoid/app/views/comments/_form.html.erb +29 -0
  27. data/test/dummy-mongoid/app/views/comments/edit.html.erb +6 -0
  28. data/test/dummy-mongoid/app/views/comments/index.html.erb +35 -0
  29. data/test/dummy-mongoid/app/views/comments/new.html.erb +5 -0
  30. data/test/dummy-mongoid/app/views/comments/show.html.erb +23 -0
  31. data/test/dummy-mongoid/app/views/layouts/application.html.erb +24 -0
  32. data/test/dummy-mongoid/app/views/users/_form.html.erb +22 -0
  33. data/test/dummy-mongoid/app/views/users/edit.html.erb +6 -0
  34. data/test/dummy-mongoid/app/views/users/index.html.erb +26 -0
  35. data/test/dummy-mongoid/app/views/users/new.html.erb +5 -0
  36. data/test/dummy-mongoid/app/views/users/show.html.erb +18 -0
  37. data/test/dummy-mongoid/config.ru +4 -0
  38. data/test/dummy-mongoid/config/application.rb +22 -0
  39. data/test/dummy-mongoid/config/boot.rb +10 -0
  40. data/test/dummy-mongoid/config/environment.rb +5 -0
  41. data/test/dummy-mongoid/config/environments/development.rb +25 -0
  42. data/test/dummy-mongoid/config/environments/production.rb +49 -0
  43. data/test/dummy-mongoid/config/environments/test.rb +35 -0
  44. data/test/dummy-mongoid/config/initializers/backtrace_silencers.rb +7 -0
  45. data/test/dummy-mongoid/config/initializers/inflections.rb +10 -0
  46. data/test/dummy-mongoid/config/initializers/merit.rb +37 -0
  47. data/test/dummy-mongoid/config/initializers/mime_types.rb +5 -0
  48. data/test/dummy-mongoid/config/initializers/secret_token.rb +7 -0
  49. data/test/dummy-mongoid/config/initializers/session_store.rb +8 -0
  50. data/test/dummy-mongoid/config/locales/en.yml +5 -0
  51. data/test/dummy-mongoid/config/mongoid.yml +14 -0
  52. data/test/dummy-mongoid/config/routes.rb +9 -0
  53. data/test/dummy-mongoid/db/seeds.rb +17 -0
  54. data/test/dummy-mongoid/public/404.html +26 -0
  55. data/test/dummy-mongoid/public/422.html +26 -0
  56. data/test/dummy-mongoid/public/500.html +26 -0
  57. data/test/dummy-mongoid/public/favicon.ico +0 -0
  58. data/test/dummy-mongoid/public/javascripts/application.js +2 -0
  59. data/test/dummy-mongoid/public/javascripts/controls.js +965 -0
  60. data/test/dummy-mongoid/public/javascripts/dragdrop.js +974 -0
  61. data/test/dummy-mongoid/public/javascripts/effects.js +1123 -0
  62. data/test/dummy-mongoid/public/javascripts/prototype.js +6001 -0
  63. data/test/dummy-mongoid/public/javascripts/rails.js +191 -0
  64. data/test/dummy-mongoid/public/stylesheets/.gitkeep +0 -0
  65. data/test/dummy-mongoid/public/stylesheets/scaffold.css +56 -0
  66. data/test/dummy-mongoid/script/rails +6 -0
  67. data/test/dummy/config/initializers/merit.rb +1 -1
  68. data/test/integration/navigation_test.rb +1 -1
  69. data/test/merit_unit_test.rb +3 -0
  70. data/test/test_helper.rb +20 -3
  71. metadata +92 -16
data/Gemfile CHANGED
@@ -1,2 +1,7 @@
1
1
  source "http://rubygems.org"
2
+
3
+ # TODO: Why do we need them here if they are in .gemspec?
4
+ gem 'bson_ext'
5
+ gem 'mongoid', '2.4.8'
6
+
2
7
  gemspec
data/Gemfile.lock CHANGED
@@ -1,8 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- merit (0.7.1)
5
- ambry
4
+ merit (0.8.0)
5
+ ambry (~> 0.3.0)
6
6
 
7
7
  GEM
8
8
  remote: http://rubygems.org/
@@ -37,6 +37,9 @@ GEM
37
37
  addressable (2.2.7)
38
38
  ambry (0.3.0)
39
39
  arel (3.0.2)
40
+ bson (1.6.2)
41
+ bson_ext (1.6.2)
42
+ bson (~> 1.6.2)
40
43
  builder (3.0.0)
41
44
  capybara (1.1.2)
42
45
  mime-types (>= 1.16)
@@ -61,6 +64,12 @@ GEM
61
64
  mime-types (~> 1.16)
62
65
  treetop (~> 1.4.8)
63
66
  mime-types (1.18)
67
+ mongo (1.6.2)
68
+ bson (~> 1.6.2)
69
+ mongoid (2.4.8)
70
+ activemodel (~> 3.1)
71
+ mongo (~> 1.3)
72
+ tzinfo (~> 0.3.22)
64
73
  multi_json (1.3.2)
65
74
  nokogiri (1.5.2)
66
75
  polyglot (0.3.3)
@@ -118,9 +127,11 @@ PLATFORMS
118
127
  ruby
119
128
 
120
129
  DEPENDENCIES
130
+ bson_ext
121
131
  capybara
122
132
  haml
123
133
  merit!
134
+ mongoid (= 2.4.8)
124
135
  rails (~> 3.2.3)
125
136
  simplecov
126
137
  sqlite3
data/README.md CHANGED
@@ -5,12 +5,18 @@
5
5
  [![Build Status](https://secure.travis-ci.org/tute/merit.png?branch=master)](http://travis-ci.org/tute/merit)
6
6
 
7
7
 
8
+ # Requirements
9
+
10
+ * ActiveRecord or Mongoid
11
+
8
12
  # Installation
9
13
 
10
- 1. Add <tt>gem 'merit'</tt> to your Gemfile
11
- 2. Run <tt>rails g merit:install</tt>
12
- 3. Run <tt>rails g merit MODEL_NAME</tt>
13
- 4. Run <tt>rake db:migrate</tt>
14
+ 1. Add `gem 'merit'` to your `Gemfile`
15
+ 2. Run `rails g merit:install`
16
+ 3. Run `rails g merit MODEL_NAME`
17
+ 4.
18
+ * ActiveRecord: Run `rake db:migrate`
19
+ * Mongoid: Set `config.orm = :mongoid` in `config/initializers/merit.rb`
14
20
  5. Configure reputation rules for your application
15
21
 
16
22
  ---
@@ -19,21 +25,21 @@
19
25
 
20
26
  You may give badges to any resource on your application if some condition
21
27
  holds. Badges may have levels, and may be temporary. Define rules on
22
- <tt>app/models/merit_badge_rules.rb</tt>:
23
-
24
- <tt>grant_on</tt> accepts:
25
-
26
- * <tt>'controller#action'</tt> string (similar to Rails routes)
27
- * <tt>:badge</tt> for badge name
28
- * <tt>:level</tt> for badge level
29
- * <tt>:to</tt> method name over target_object which obtains object to badge
30
- * <tt>:model_name</tt> (string) define controller's name if it differs from
31
- the model (like <tt>RegistrationsController</tt> for <tt>User</tt> model).
32
- * <tt>:multiple</tt> (boolean) badge may be granted multiple times
33
- * <tt>:temporary</tt> (boolean) if the receiver had the badge but the
34
- condition doesn't hold anymore, remove it. <tt>false</tt> by default (badges
35
- are kept forever).
36
- * <tt>&block</tt>
28
+ `app/models/merit_badge_rules.rb`:
29
+
30
+ `grant_on` accepts:
31
+
32
+ * `'controller#action'` string (similar to Rails routes)
33
+ * `:badge` for badge name
34
+ * `:level` for badge level
35
+ * `:to` method name over target_object which obtains object to badge
36
+ * `:model_name` (string) define controller's name if it differs from
37
+ the model (like `RegistrationsController` for `User` model).
38
+ * `:multiple` (boolean) badge may be granted multiple times
39
+ * `:temporary` (boolean) if the receiver had the badge but the condition
40
+ doesn't hold anymore, remove it. `false` by default (badges are kept
41
+ forever).
42
+ * `&block`
37
43
  * empty (always grants)
38
44
  * a block which evaluates to boolean (recieves target object as parameter)
39
45
  * a block with a hash composed of methods to run on the target object with
@@ -41,19 +47,24 @@ holds. Badges may have levels, and may be temporary. Define rules on
41
47
 
42
48
  ## Examples
43
49
 
44
- grant_on 'comments#vote', :badge => 'relevant-commenter', :to => :user do |comment|
45
- comment.votes.count == 5
46
- end
47
50
 
48
- grant_on ['users#create', 'users#update'], :badge => 'autobiographer', :temporary => true do |user|
49
- user.name.present? && user.address.present?
50
- end
51
+ ```ruby
52
+ grant_on 'comments#vote', :badge => 'relevant-commenter', :to => :user do |comment|
53
+ comment.votes.count == 5
54
+ end
55
+
56
+ grant_on ['users#create', 'users#update'], :badge => 'autobiographer', :temporary => true do |user|
57
+ user.name.present? && user.address.present?
58
+ end
59
+ ```
51
60
 
52
61
  ## Grant manually
53
62
 
54
63
  You may also grant badges "by hand":
55
64
 
56
- Badge.find(3).grant_to(current_user)
65
+ ```ruby
66
+ Badge.find(3).grant_to(current_user)
67
+ ```
57
68
 
58
69
  ---
59
70
 
@@ -61,21 +72,23 @@ You may also grant badges "by hand":
61
72
 
62
73
  Points are a simple integer value which are given to "meritable" resources.
63
74
  They are given on actions-triggered, either to the action user or to the
64
- method(s) defined in the <tt>:to</tt> option. Define rules on
65
- <tt>app/models/merit_point_rules.rb</tt>:
75
+ method(s) defined in the `:to` option. Define rules on
76
+ `app/models/merit_point_rules.rb`:
66
77
 
67
78
  ## Examples
68
79
 
69
- score 10, :on => [
70
- 'users#update'
71
- ]
80
+ ```ruby
81
+ score 10, :on => [
82
+ 'users#update'
83
+ ]
72
84
 
73
- score 15, :on => 'reviews#create', :to => [:reviewer, :reviewed]
85
+ score 15, :on => 'reviews#create', :to => [:reviewer, :reviewed]
74
86
 
75
- score 20, :on => [
76
- 'comments#create',
77
- 'photos#create'
78
- ]
87
+ score 20, :on => [
88
+ 'comments#create',
89
+ 'photos#create'
90
+ ]
91
+ ```
79
92
 
80
93
  ---
81
94
 
@@ -84,42 +97,48 @@ method(s) defined in the <tt>:to</tt> option. Define rules on
84
97
  5 stars is a common ranking use case. They are not given at specified actions
85
98
  like badges, you should define a cron job to test if ranks are to be granted.
86
99
 
87
- Define rules on <tt>app/models/merit_rank_rules.rb</tt>:
100
+ Define rules on `app/models/merit_rank_rules.rb`:
88
101
 
89
- <tt>set_rank</tt> accepts:
102
+ `set_rank` accepts:
90
103
 
91
- * <tt>:level</tt> ranking level (greater is better)
92
- * <tt>:to</tt> model or scope to check if new rankings apply
93
- * <tt>:level_name</tt> attribute name (default is empty and results in
94
- '<tt>level</tt>' attribute, if set it's appended like
95
- '<tt>level_#{level_name}</tt>')
104
+ * `:level` ranking level (greater is better)
105
+ * `:to` model or scope to check if new rankings apply
106
+ * `:level_name` attribute name (default is empty and results in
107
+ '`level`' attribute, if set it's appended like
108
+ '`level_#{level_name}`')
96
109
 
97
110
  Check for rules on a rake task executed in background like:
98
111
 
99
- task :cron => :environment do
100
- Merit::RankRules.new.check_rank_rules
101
- end
112
+ ```ruby
113
+ task :cron => :environment do
114
+ Merit::RankRules.new.check_rank_rules
115
+ end
116
+ ```
102
117
 
103
118
 
104
119
  ## Examples
105
120
 
106
- set_rank :level => 2, :to => Commiter.active do |commiter|
107
- commiter.branches > 1 && commiter.followers >= 10
108
- end
121
+ ```ruby
122
+ set_rank :level => 2, :to => Commiter.active do |commiter|
123
+ commiter.branches > 1 && commiter.followers >= 10
124
+ end
109
125
 
110
- set_rank :level => 3, :to => Commiter.active do |commiter|
111
- commiter.branches > 2 && commiter.followers >= 20
112
- end
126
+ set_rank :level => 3, :to => Commiter.active do |commiter|
127
+ commiter.branches > 2 && commiter.followers >= 20
128
+ end
129
+ ```
113
130
 
114
131
  ---
115
132
 
116
133
  # To-do list
117
134
 
135
+ * Can infer params[:id] = instance_variable.id in create actions, for having
136
+ the object in the rule method parameter, so we don't need it in controllers.
118
137
  * add an error handler for inexistent badges.
119
138
  * rails g merit MODEL_NAME shouldn't create general migrations again.
120
139
  * Abstract User (rule.rb#51 for instance) into a Merit option.
121
140
  * Should namespace app/models into Merit module.
122
- * rescue ActiveRecord::... should depend on ORM used (MongoMapper?)
141
+ * rescue ActiveRecord::... should depend on ORM used
123
142
  * Why 1.8.7 tests are not passing?
124
143
  * :value parameter (for star voting for example) should be configurable
125
144
  (depends on params[:value] on the controller).
data/TESTING.txt CHANGED
@@ -1,11 +1,16 @@
1
1
  cd test/dummy
2
- rails g merit:install # Next n's for not overriding already defined rules
2
+ rails g merit:install # n's for not overriding already defined badges & rules
3
+ n
3
4
  n
4
5
  n
5
6
  n
6
7
  rails g merit user
7
8
  rake db:migrate ; rake db:seed
9
+
8
10
  # see it in the browser
9
11
  rails server
12
+
10
13
  # or run tests
11
- cd ../.. ; rake test
14
+ cd ../..
15
+ rake test
16
+ ORM=mongoid rake test
@@ -3,7 +3,7 @@ Merit.setup do |config|
3
3
  # Check rules on each request or in background
4
4
  # config.checks_on_each_request = true
5
5
 
6
- # Define ORM. Could be :active_record (default) and :mongo_mapper
6
+ # Define ORM. Could be :active_record (default) and :mongo_mapper and :mongoid
7
7
  # config.orm = :active_record
8
8
  end
9
9
 
data/lib/merit.rb CHANGED
@@ -21,9 +21,10 @@ module Merit
21
21
 
22
22
  class Engine < Rails::Engine
23
23
  initializer 'merit.controller' do |app|
24
- # Merit.orm now set
25
24
  if Merit.orm == :active_record
25
+ require "merit/models/#{Merit.orm}/sash"
26
26
  require "merit/models/#{Merit.orm}/badges_sash"
27
+ elsif Merit.orm == :mongoid
27
28
  require "merit/models/#{Merit.orm}/sash"
28
29
  end
29
30
 
@@ -25,7 +25,7 @@ module Merit
25
25
  :had_errors => target_object.try(:errors).try(:present?),
26
26
  :target_model => controller_name,
27
27
  :target_id => target_id
28
- )
28
+ ).id
29
29
 
30
30
  # Check rules in after_filter?
31
31
  if Merit.checks_on_each_request
@@ -37,7 +37,7 @@ module Merit
37
37
  granted = log.split('|').select{|log| log =~ /badge_granted_to_action_user/ }
38
38
  granted.each do |badge|
39
39
  badge_id = badge.split(':').last.to_i
40
- flash[:merit] = t('merit.flashs.badge_granted', badge: Badge.find(badge_id).name)
40
+ flash[:merit] = t('merit.flashs.badge_granted', :badge => Badge.find(badge_id).name)
41
41
  end
42
42
  end
43
43
  end
@@ -10,6 +10,10 @@ module Merit
10
10
  key :sash_id, String
11
11
  key :points, Integer, :default => 0
12
12
  key :level, Integer, :default => 0
13
+ elsif Merit.orm == :mongoid
14
+ field :sash_id
15
+ field :points, :type => Integer, :default => 0
16
+ field :level, :type => Integer, :default => 0
13
17
  end
14
18
  end
15
19
  end
@@ -21,8 +25,8 @@ module Merit
21
25
 
22
26
  # Create sash if doesn't have
23
27
  def create_sash_if_none
24
- if sash.nil?
25
- self.sash = Sash.new
28
+ if self.sash.blank?
29
+ self.sash = Sash.create
26
30
  self.save(:validate => false)
27
31
  end
28
32
  end
@@ -34,3 +38,6 @@ end
34
38
  if Object.const_defined?('MongoMapper')
35
39
  MongoMapper::Document.plugin Merit
36
40
  end
41
+ if Object.const_defined?('Mongoid')
42
+ Mongoid::Document.send :include, Merit
43
+ end
@@ -8,5 +8,6 @@ class MeritAction
8
8
  key :target_model, String
9
9
  key :target_id, String
10
10
  key :processed, Boolean, :default => false
11
+ key :log, String
11
12
  timestamps!
12
13
  end
@@ -0,0 +1,13 @@
1
+ class MeritAction
2
+ include Mongoid::Document
3
+ include Mongoid::Timestamps
4
+
5
+ belongs_to :user
6
+ field :action_method
7
+ field :action_value, :type => Integer
8
+ field :had_errors, :type => Boolean
9
+
10
+ belongs_to :target, :polymorphic => true
11
+ field :processed, :type => Boolean, :default => false
12
+ field :log
13
+ end
@@ -0,0 +1,14 @@
1
+ class Sash
2
+ include Mongoid::Document
3
+ include Mongoid::Timestamps
4
+
5
+ field :badge_ids, :type => Array, :default => []
6
+
7
+ def add_badge(badge_id)
8
+ self.push(:badge_ids, badge_id)
9
+ end
10
+
11
+ def rm_badge(badge_id)
12
+ self.pull(:badge_ids, badge_id)
13
+ end
14
+ end
data/lib/merit/rule.rb CHANGED
@@ -9,7 +9,7 @@ module Merit
9
9
  def applies?(target_obj = nil)
10
10
  return true if block.nil? # no block given: always true
11
11
 
12
- case block.parameters.count
12
+ case block.arity
13
13
  when 1 # Expects target object
14
14
  if target_obj.present?
15
15
  return block.call(target_obj)
@@ -72,7 +72,9 @@ module Merit
72
72
  if @badge.nil?
73
73
  badges = Badge.by_name(badge_name)
74
74
  badges = badges.by_level(level) unless level.nil?
75
- @badge = badges.first
75
+ if !(@badge = badges.first)
76
+ raise "No badge '#{badge_name}'#{level.nil? ? '' : " with level #level"} found. Define it in 'config/initializers/merit.rb'."
77
+ end
76
78
  end
77
79
  @badge
78
80
  end
@@ -28,7 +28,12 @@ module Merit
28
28
  level_and_rules = level_and_rules.sort
29
29
  level_and_rules.each do |level, rule|
30
30
  begin
31
- scoped_model.where("#{rule.level_name} < #{level}").each do |obj|
31
+ if Merit.orm == :mongoid
32
+ items = scoped_model.where(:"#{rule.level_name}".lt => level)
33
+ else
34
+ items = scoped_model.where("#{rule.level_name} < #{level}")
35
+ end
36
+ items.each do |obj|
32
37
  if rule.applies?(obj)
33
38
  obj.update_attribute rule.level_name, level
34
39
  end
@@ -45,4 +50,4 @@ module Merit
45
50
  @defined_rules ||= {}
46
51
  end
47
52
  end
48
- end
53
+ end
data/merit.gemspec CHANGED
@@ -4,13 +4,16 @@ 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 = "0.7.1"
7
+ s.version = "0.8.0"
8
8
  s.authors = ["Tute Costa"]
9
9
  s.email = 'tutecosta@gmail.com'
10
- s.add_dependency 'ambry'
10
+ s.add_dependency 'ambry', '~> 0.3.0'
11
11
  s.add_development_dependency 'rails', '~> 3.2.3'
12
12
  s.add_development_dependency 'sqlite3'
13
13
  s.add_development_dependency 'haml'
14
14
  s.add_development_dependency 'capybara'
15
15
  s.add_development_dependency 'simplecov'
16
+ # Testing with Mongoid
17
+ s.add_development_dependency 'bson_ext'
18
+ s.add_development_dependency 'mongoid', '~> 2.0.0'
16
19
  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
+ DummyMongoid::Application.load_tasks
@@ -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