merit 0.7.1 → 0.8.0

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