merit 0.7.1 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +5 -0
- data/Gemfile.lock +13 -2
- data/README.md +72 -53
- data/TESTING.txt +7 -2
- data/lib/generators/merit/templates/merit.rb +1 -1
- data/lib/merit.rb +2 -1
- data/lib/merit/controller_extensions.rb +2 -2
- data/lib/merit/model_additions.rb +9 -2
- data/lib/merit/models/mongo_mapper/merit_action.rb +1 -0
- data/lib/merit/models/mongoid/merit_action.rb +13 -0
- data/lib/merit/models/mongoid/sash.rb +14 -0
- data/lib/merit/rule.rb +4 -2
- data/lib/merit/rules_rank.rb +7 -2
- data/merit.gemspec +5 -2
- data/test/dummy-mongoid/Rakefile +7 -0
- data/test/dummy-mongoid/app/controllers/application_controller.rb +7 -0
- data/test/dummy-mongoid/app/controllers/comments_controller.rb +90 -0
- data/test/dummy-mongoid/app/controllers/registrations_controller.rb +15 -0
- data/test/dummy-mongoid/app/controllers/users_controller.rb +67 -0
- data/test/dummy-mongoid/app/helpers/application_helper.rb +2 -0
- data/test/dummy-mongoid/app/models/comment.rb +17 -0
- data/test/dummy-mongoid/app/models/merit/badge_rules.rb +49 -0
- data/test/dummy-mongoid/app/models/merit/point_rules.rb +20 -0
- data/test/dummy-mongoid/app/models/merit/rank_rules.rb +24 -0
- data/test/dummy-mongoid/app/models/user.rb +30 -0
- data/test/dummy-mongoid/app/views/comments/_form.html.erb +29 -0
- data/test/dummy-mongoid/app/views/comments/edit.html.erb +6 -0
- data/test/dummy-mongoid/app/views/comments/index.html.erb +35 -0
- data/test/dummy-mongoid/app/views/comments/new.html.erb +5 -0
- data/test/dummy-mongoid/app/views/comments/show.html.erb +23 -0
- data/test/dummy-mongoid/app/views/layouts/application.html.erb +24 -0
- data/test/dummy-mongoid/app/views/users/_form.html.erb +22 -0
- data/test/dummy-mongoid/app/views/users/edit.html.erb +6 -0
- data/test/dummy-mongoid/app/views/users/index.html.erb +26 -0
- data/test/dummy-mongoid/app/views/users/new.html.erb +5 -0
- data/test/dummy-mongoid/app/views/users/show.html.erb +18 -0
- data/test/dummy-mongoid/config.ru +4 -0
- data/test/dummy-mongoid/config/application.rb +22 -0
- data/test/dummy-mongoid/config/boot.rb +10 -0
- data/test/dummy-mongoid/config/environment.rb +5 -0
- data/test/dummy-mongoid/config/environments/development.rb +25 -0
- data/test/dummy-mongoid/config/environments/production.rb +49 -0
- data/test/dummy-mongoid/config/environments/test.rb +35 -0
- data/test/dummy-mongoid/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy-mongoid/config/initializers/inflections.rb +10 -0
- data/test/dummy-mongoid/config/initializers/merit.rb +37 -0
- data/test/dummy-mongoid/config/initializers/mime_types.rb +5 -0
- data/test/dummy-mongoid/config/initializers/secret_token.rb +7 -0
- data/test/dummy-mongoid/config/initializers/session_store.rb +8 -0
- data/test/dummy-mongoid/config/locales/en.yml +5 -0
- data/test/dummy-mongoid/config/mongoid.yml +14 -0
- data/test/dummy-mongoid/config/routes.rb +9 -0
- data/test/dummy-mongoid/db/seeds.rb +17 -0
- data/test/dummy-mongoid/public/404.html +26 -0
- data/test/dummy-mongoid/public/422.html +26 -0
- data/test/dummy-mongoid/public/500.html +26 -0
- data/test/dummy-mongoid/public/favicon.ico +0 -0
- data/test/dummy-mongoid/public/javascripts/application.js +2 -0
- data/test/dummy-mongoid/public/javascripts/controls.js +965 -0
- data/test/dummy-mongoid/public/javascripts/dragdrop.js +974 -0
- data/test/dummy-mongoid/public/javascripts/effects.js +1123 -0
- data/test/dummy-mongoid/public/javascripts/prototype.js +6001 -0
- data/test/dummy-mongoid/public/javascripts/rails.js +191 -0
- data/test/dummy-mongoid/public/stylesheets/.gitkeep +0 -0
- data/test/dummy-mongoid/public/stylesheets/scaffold.css +56 -0
- data/test/dummy-mongoid/script/rails +6 -0
- data/test/dummy/config/initializers/merit.rb +1 -1
- data/test/integration/navigation_test.rb +1 -1
- data/test/merit_unit_test.rb +3 -0
- data/test/test_helper.rb +20 -3
- metadata +92 -16
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
merit (0.
|
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
|
11
|
-
2. Run
|
12
|
-
3. Run
|
13
|
-
4.
|
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
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
*
|
27
|
-
*
|
28
|
-
*
|
29
|
-
*
|
30
|
-
*
|
31
|
-
the model (like
|
32
|
-
*
|
33
|
-
*
|
34
|
-
|
35
|
-
|
36
|
-
*
|
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
|
-
|
49
|
-
|
50
|
-
|
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
|
-
|
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
|
65
|
-
|
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
|
-
|
70
|
-
|
71
|
-
|
80
|
+
```ruby
|
81
|
+
score 10, :on => [
|
82
|
+
'users#update'
|
83
|
+
]
|
72
84
|
|
73
|
-
|
85
|
+
score 15, :on => 'reviews#create', :to => [:reviewer, :reviewed]
|
74
86
|
|
75
|
-
|
76
|
-
|
77
|
-
|
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
|
100
|
+
Define rules on `app/models/merit_rank_rules.rb`:
|
88
101
|
|
89
|
-
|
102
|
+
`set_rank` accepts:
|
90
103
|
|
91
|
-
*
|
92
|
-
*
|
93
|
-
*
|
94
|
-
'
|
95
|
-
'
|
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
|
-
|
100
|
-
|
101
|
-
|
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
|
-
|
107
|
-
|
108
|
-
|
121
|
+
```ruby
|
122
|
+
set_rank :level => 2, :to => Commiter.active do |commiter|
|
123
|
+
commiter.branches > 1 && commiter.followers >= 10
|
124
|
+
end
|
109
125
|
|
110
|
-
|
111
|
-
|
112
|
-
|
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
|
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 #
|
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 ../..
|
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
|
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.
|
25
|
-
self.sash = Sash.
|
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
|
@@ -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.
|
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
|
data/lib/merit/rules_rank.rb
CHANGED
@@ -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
|
-
|
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
|
+
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
|