scorecard 0.0.2 → 0.0.3
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.
- checksums.yaml +4 -4
- data/README.md +45 -7
- data/app/models/scorecard/level.rb +17 -0
- data/app/models/scorecard/point.rb +8 -0
- data/db/migrate/2_create_levels.rb +12 -0
- data/lib/scorecard/card.rb +18 -0
- data/lib/scorecard/cleaner.rb +13 -0
- data/lib/scorecard/clear_worker.rb +7 -0
- data/lib/scorecard/rules.rb +2 -2
- data/lib/scorecard/{worker.rb → score_worker.rb} +2 -2
- data/lib/scorecard/scorer.rb +27 -0
- data/lib/scorecard/subscriber.rb +5 -2
- data/lib/scorecard.rb +10 -2
- data/scorecard.gemspec +1 -1
- data/spec/acceptance/clearing_points_spec.rb +46 -0
- data/spec/acceptance/levels_spec.rb +76 -0
- data/spec/acceptance/read_points_spec.rb +16 -0
- data/spec/acceptance/{points_spec.rb → score_points_spec.rb} +38 -35
- data/spec/internal/app/models/post.rb +1 -1
- data/spec/spec_helper.rb +3 -0
- metadata +17 -6
- data/lib/scorecard/points.rb +0 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1232899ca7b26af8e4a8d8af2baaae024343a688
|
4
|
+
data.tar.gz: c3c252f4debd3424989324fa5a0b239a13f90185
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2dee14b5121ee947bf95bd12d17ed95a08b36cbb5e6aa469871d7582129c0b48b3b3517f3332790adf72a75df7e1a134008a5f20f63b4c65f87ce9260a8eaa2e
|
7
|
+
data.tar.gz: 88d9897dd851e560050b03bfeb8b88258264ff0c580dcba90b7f7b1efda7eda10109e50b11aab721de74e39523c202fff8b1fd8718e031bf4cf36a2c17a27950
|
data/README.md
CHANGED
@@ -8,7 +8,7 @@ For anyone who comes across this, please note this is not yet feature complete,
|
|
8
8
|
|
9
9
|
Add this line to your application's Gemfile:
|
10
10
|
|
11
|
-
gem 'scorecard', '0.0.
|
11
|
+
gem 'scorecard', '0.0.3'
|
12
12
|
|
13
13
|
Don't forget to bundle:
|
14
14
|
|
@@ -23,36 +23,68 @@ Then, add the migrations to your app and update your database accordingly:
|
|
23
23
|
In an initializer, define the events that points are tied to - with a unique context and the number of points:
|
24
24
|
|
25
25
|
```ruby
|
26
|
-
Scorecard
|
26
|
+
Scorecard.configure do |config|
|
27
|
+
config.rules.add_rule :new_post, 50
|
28
|
+
end
|
27
29
|
```
|
28
30
|
|
29
31
|
You can also provide a block with logic for whether to award the points, the maximum number of points allowed to be awarded for this rule, and the timeframe for that limit:
|
30
32
|
|
31
33
|
```ruby
|
32
|
-
Scorecard
|
34
|
+
Scorecard.configure do |config|
|
35
|
+
config.rules.add_rule :new_post, 50, limit: 100, timeframe: :day,
|
33
36
|
if: lambda { |payload| payload[:user].posts.count <= 1 }
|
37
|
+
end
|
34
38
|
```
|
35
39
|
|
36
40
|
The payload object contains the context plus every option you send through to `score` call (see below).
|
37
41
|
|
42
|
+
For levels, you can set the central piece of logic that calculates the user's current level in the initializer as well:
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
Scorecard.configure do |config|
|
46
|
+
config.levels = lambda { |user|
|
47
|
+
if user.created_at > 2.years.ago
|
48
|
+
3
|
49
|
+
elsif user.created_at > 1.year.ago
|
50
|
+
2
|
51
|
+
else
|
52
|
+
1
|
53
|
+
end
|
54
|
+
}
|
55
|
+
end
|
56
|
+
```
|
57
|
+
|
38
58
|
## Scoring
|
39
59
|
|
40
60
|
And then, when you want to fire those events, use code much like the following:
|
41
61
|
|
42
62
|
```ruby
|
43
|
-
Scorecard::
|
63
|
+
Scorecard::Scorer.points :new_post, gameable: post
|
44
64
|
```
|
45
65
|
|
46
66
|
This presumes that the user the points are for is a method on the gameable object. If that's not the case, you can pass in a custom user:
|
47
67
|
|
48
68
|
```ruby
|
49
|
-
Scorecard::
|
69
|
+
Scorecard::Scorer.points :new_post, gameable: post, user: user
|
50
70
|
```
|
51
71
|
|
52
72
|
If you're using Sidekiq, you can push the scoring behaviour into a background worker using `score_async` instead. Make sure `scorecard` is listed below `sidekiq` in your `Gemfile`.
|
53
73
|
|
54
74
|
```ruby
|
55
|
-
Scorecard::
|
75
|
+
Scorecard::Scorer.points_async :new_post, gameable: post
|
76
|
+
```
|
77
|
+
|
78
|
+
Whenever you want to recalculate a user's level, run the following:
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
Scorecard::Scorer.level user
|
82
|
+
```
|
83
|
+
|
84
|
+
To remove points related to a given gameable object, you can use the Scorecard::Cleaner class:
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
Scorecard::Cleaner.points post
|
56
88
|
```
|
57
89
|
|
58
90
|
## Results
|
@@ -60,7 +92,13 @@ Scorecard::Points.score_async :new_post, gameable: post
|
|
60
92
|
To get the current points count for a given user:
|
61
93
|
|
62
94
|
```ruby
|
63
|
-
Scorecard::
|
95
|
+
Scorecard::Card.new(user).points
|
96
|
+
```
|
97
|
+
|
98
|
+
And the current level:
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
Scorecard::Card.new(user).level
|
64
102
|
```
|
65
103
|
|
66
104
|
## Contributing
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class Scorecard::Level < ActiveRecord::Base
|
2
|
+
self.table_name = 'scorecard_levels'
|
3
|
+
|
4
|
+
belongs_to :user, polymorphic: true
|
5
|
+
|
6
|
+
if Rails.version.to_s < '4.0.0'
|
7
|
+
attr_accessible :amount, :user
|
8
|
+
end
|
9
|
+
|
10
|
+
validates :amount, presence: true
|
11
|
+
validates :user, presence: true
|
12
|
+
validates :user_id, uniqueness: {scope: :user_type}
|
13
|
+
|
14
|
+
def self.for_user(user)
|
15
|
+
where(user_id: user.id, user_type: user.class.name).first
|
16
|
+
end
|
17
|
+
end
|
@@ -4,6 +4,10 @@ class Scorecard::Point < ActiveRecord::Base
|
|
4
4
|
belongs_to :user, polymorphic: true
|
5
5
|
belongs_to :gameable, polymorphic: true
|
6
6
|
|
7
|
+
if Rails.version.to_s < '4.0.0'
|
8
|
+
attr_accessible :context, :identifier, :amount, :gameable, :user
|
9
|
+
end
|
10
|
+
|
7
11
|
validates :context, presence: true
|
8
12
|
validates :identifier, presence: true, uniqueness: {
|
9
13
|
scope: [:context, :user_type, :user_id]
|
@@ -19,6 +23,10 @@ class Scorecard::Point < ActiveRecord::Base
|
|
19
23
|
where user_id: user.id, user_type: user.class.name
|
20
24
|
end
|
21
25
|
|
26
|
+
def self.for_gameable(gameable)
|
27
|
+
where gameable_id: gameable.id, gameable_type: gameable.class.name
|
28
|
+
end
|
29
|
+
|
22
30
|
def self.for_user_in_timeframe(context, user, timeframe)
|
23
31
|
for_context(context).for_user(user).where(created_at: timeframe)
|
24
32
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class CreateLevels < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :scorecard_levels do |table|
|
4
|
+
table.integer :amount, null: false
|
5
|
+
table.string :user_type, null: false
|
6
|
+
table.integer :user_id, null: false
|
7
|
+
table.timestamps
|
8
|
+
end
|
9
|
+
|
10
|
+
add_index :scorecard_levels, [:user_type, :user_id], unique: true
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class Scorecard::Card
|
2
|
+
attr_reader :user
|
3
|
+
|
4
|
+
def initialize(user)
|
5
|
+
@user = user
|
6
|
+
end
|
7
|
+
|
8
|
+
def level
|
9
|
+
@level ||= begin
|
10
|
+
record = Scorecard::Level.for_user(user)
|
11
|
+
record.nil? ? Scorecard.levels.call(user) : record.amount
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def points
|
16
|
+
@points ||= Scorecard::Point.for_user(user).sum(:amount)
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Scorecard::Cleaner
|
2
|
+
def self.points(gameable)
|
3
|
+
Scorecard::Point.for_gameable(gameable).each do |point|
|
4
|
+
point.destroy
|
5
|
+
ActiveSupport::Notifications.instrument 'scorecard', user: point.user
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.points_async(gameable)
|
10
|
+
Scorecard::ClearWorker.perform_in 10.seconds, gameable.class.name,
|
11
|
+
gameable.id
|
12
|
+
end
|
13
|
+
end
|
data/lib/scorecard/rules.rb
CHANGED
@@ -5,11 +5,11 @@ class Scorecard::Rules
|
|
5
5
|
@point_rules = []
|
6
6
|
end
|
7
7
|
|
8
|
-
def
|
8
|
+
def add_rule(context, amount, options = {})
|
9
9
|
point_rules << Scorecard::PointRule.new(context, amount, options)
|
10
10
|
end
|
11
11
|
|
12
|
-
def
|
12
|
+
def find_rule(context)
|
13
13
|
point_rules.detect { |rule| rule.context == context }
|
14
14
|
end
|
15
15
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
class Scorecard::
|
1
|
+
class Scorecard::ScoreWorker
|
2
2
|
include Sidekiq::Worker
|
3
3
|
|
4
4
|
def perform(context, options)
|
@@ -9,6 +9,6 @@ class Scorecard::Worker
|
|
9
9
|
options[prefix] = klass.find options.delete("#{prefix}_id")
|
10
10
|
end
|
11
11
|
|
12
|
-
Scorecard::
|
12
|
+
Scorecard::Scorer.points context.to_sym, options.symbolize_keys
|
13
13
|
end
|
14
14
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class Scorecard::Scorer
|
2
|
+
def self.level(user)
|
3
|
+
level = Scorecard::Level.for_user(user) || Scorecard::Level.new(user: user)
|
4
|
+
level.amount = Scorecard.levels.call user
|
5
|
+
return unless level.amount_changed?
|
6
|
+
|
7
|
+
level.save
|
8
|
+
ActiveSupport::Notifications.instrument 'level.scorecard', user: user
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.points(context, options)
|
12
|
+
ActiveSupport::Notifications.instrument 'points.scorecard',
|
13
|
+
options.merge(context: context)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.points_async(context, options)
|
17
|
+
[:gameable, :user].each do |prefix|
|
18
|
+
next unless options[prefix]
|
19
|
+
|
20
|
+
options["#{prefix}_id"] = options[prefix].id
|
21
|
+
options["#{prefix}_type"] = options[prefix].class.name
|
22
|
+
options.delete prefix
|
23
|
+
end
|
24
|
+
|
25
|
+
Scorecard::ScoreWorker.perform_async context, options.stringify_keys
|
26
|
+
end
|
27
|
+
end
|
data/lib/scorecard/subscriber.rb
CHANGED
@@ -14,7 +14,7 @@ class Scorecard::Subscriber
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def points(event)
|
17
|
-
rule = Scorecard.rules.
|
17
|
+
rule = Scorecard.rules.find_rule event.payload[:context]
|
18
18
|
|
19
19
|
event.payload[:amount] ||= rule.amount
|
20
20
|
event.payload[:identifier] ||= event.payload[:gameable].id
|
@@ -22,8 +22,11 @@ class Scorecard::Subscriber
|
|
22
22
|
|
23
23
|
return unless rule && rule.allowed?(event.payload)
|
24
24
|
|
25
|
-
Scorecard::Point.create(
|
25
|
+
point = Scorecard::Point.create(
|
26
26
|
event.payload.slice(:context, :amount, :identifier, :user, :gameable)
|
27
27
|
)
|
28
|
+
ActiveSupport::Notifications.instrument(
|
29
|
+
'scorecard', user: event.payload[:user]
|
30
|
+
) if point.persisted?
|
28
31
|
end
|
29
32
|
end
|
data/lib/scorecard.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
module Scorecard
|
2
|
+
mattr_accessor :levels
|
3
|
+
|
2
4
|
def self.configure
|
3
5
|
yield self
|
4
6
|
end
|
@@ -8,9 +10,15 @@ module Scorecard
|
|
8
10
|
end
|
9
11
|
end
|
10
12
|
|
13
|
+
require 'scorecard/card'
|
14
|
+
require 'scorecard/cleaner'
|
11
15
|
require 'scorecard/engine'
|
12
|
-
require 'scorecard/points'
|
13
16
|
require 'scorecard/point_rule'
|
14
17
|
require 'scorecard/rules'
|
18
|
+
require 'scorecard/scorer'
|
15
19
|
require 'scorecard/subscriber'
|
16
|
-
|
20
|
+
|
21
|
+
if defined?(Sidekiq)
|
22
|
+
require 'scorecard/clear_worker'
|
23
|
+
require 'scorecard/score_worker'
|
24
|
+
end
|
data/scorecard.gemspec
CHANGED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Clearing Points' do
|
4
|
+
let(:post) { Post.create! user: user }
|
5
|
+
let(:user) { User.create! }
|
6
|
+
|
7
|
+
before :each do
|
8
|
+
Scorecard.configure do |config|
|
9
|
+
config.rules.add_rule :new_post, 50
|
10
|
+
end
|
11
|
+
|
12
|
+
post
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'removes points for a given gameable object' do
|
16
|
+
expect(Scorecard::Card.new(user).points).to eq(50)
|
17
|
+
|
18
|
+
Scorecard::Cleaner.points(post)
|
19
|
+
|
20
|
+
expect(Scorecard::Card.new(user).points).to eq(0)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'clears points via Sidekiq' do
|
24
|
+
expect(Scorecard::Card.new(user).points).to eq(50)
|
25
|
+
|
26
|
+
Scorecard::Cleaner.points_async(post)
|
27
|
+
|
28
|
+
expect(Scorecard::Card.new(user).points).to eq(0)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "fires a generic notification" do
|
32
|
+
fired = false
|
33
|
+
|
34
|
+
subscriber = ActiveSupport::Notifications.subscribe 'scorecard' do |*args|
|
35
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
36
|
+
fired = (event.payload[:user] == user)
|
37
|
+
end
|
38
|
+
|
39
|
+
Scorecard::Cleaner.points_async(post)
|
40
|
+
|
41
|
+
ActiveSupport::Notifications.unsubscribe(subscriber)
|
42
|
+
|
43
|
+
expect(fired).to be_true
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Levels' do
|
4
|
+
let(:user) { User.create! email: 'pat-new' }
|
5
|
+
|
6
|
+
before :each do
|
7
|
+
Scorecard.configure do |config|
|
8
|
+
config.levels = lambda { |user|
|
9
|
+
user.email[/old/] ? 2 : 1
|
10
|
+
}
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
it "saves the score for the user" do
|
15
|
+
Scorecard::Scorer.level user
|
16
|
+
|
17
|
+
level = Scorecard::Level.where(user_id: user.id, user_type: 'User').first
|
18
|
+
expect(level.amount).to eq(1)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "fires a level notification when the level is first set" do
|
22
|
+
fired = false
|
23
|
+
|
24
|
+
subscriber = ActiveSupport::Notifications.subscribe 'level.scorecard' do |*args|
|
25
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
26
|
+
fired = (event.payload[:user] == user)
|
27
|
+
end
|
28
|
+
|
29
|
+
Scorecard::Scorer.level user
|
30
|
+
|
31
|
+
ActiveSupport::Notifications.unsubscribe(subscriber)
|
32
|
+
|
33
|
+
expect(fired).to be_true
|
34
|
+
end
|
35
|
+
|
36
|
+
it "fires a level notification when the level is changed" do
|
37
|
+
Scorecard::Scorer.level user
|
38
|
+
user.update_attributes(email: 'pat-old')
|
39
|
+
|
40
|
+
fired = false
|
41
|
+
|
42
|
+
subscriber = ActiveSupport::Notifications.subscribe 'level.scorecard' do |*args|
|
43
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
44
|
+
fired = (event.payload[:user] == user)
|
45
|
+
end
|
46
|
+
|
47
|
+
Scorecard::Scorer.level user
|
48
|
+
|
49
|
+
ActiveSupport::Notifications.unsubscribe(subscriber)
|
50
|
+
|
51
|
+
expect(fired).to be_true
|
52
|
+
end
|
53
|
+
|
54
|
+
it "does not fire a notification when the level remains the same" do
|
55
|
+
Scorecard::Scorer.level user
|
56
|
+
|
57
|
+
fired = false
|
58
|
+
|
59
|
+
subscriber = ActiveSupport::Notifications.subscribe 'level.scorecard' do |*args|
|
60
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
61
|
+
fired = (event.payload[:user] == user)
|
62
|
+
end
|
63
|
+
|
64
|
+
Scorecard::Scorer.level user
|
65
|
+
|
66
|
+
ActiveSupport::Notifications.unsubscribe(subscriber)
|
67
|
+
|
68
|
+
expect(fired).to be_false
|
69
|
+
end
|
70
|
+
|
71
|
+
it "retrieves stored level for a user" do
|
72
|
+
Scorecard::Scorer.level user
|
73
|
+
|
74
|
+
expect(Scorecard::Card.new(user).level).to eq(1)
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Reading points' do
|
4
|
+
it "returns the total points for a user" do
|
5
|
+
Scorecard.configure do |config|
|
6
|
+
config.rules.add_rule :new_user, 20
|
7
|
+
config.rules.add_rule :new_post, 30
|
8
|
+
end
|
9
|
+
|
10
|
+
user = User.create!
|
11
|
+
post = Post.create! user: user
|
12
|
+
Scorecard::Scorer.points :new_user, gameable: user, user: user
|
13
|
+
|
14
|
+
expect(Scorecard::Card.new(user).points).to eq(50)
|
15
|
+
end
|
16
|
+
end
|
@@ -1,19 +1,15 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe '
|
4
|
-
before :each do
|
5
|
-
Scorecard.rules.clear
|
6
|
-
end
|
7
|
-
|
3
|
+
describe 'Scoring points' do
|
8
4
|
it "stores points for configured behaviour" do
|
9
5
|
Scorecard.configure do |config|
|
10
|
-
config.rules.
|
6
|
+
config.rules.add_rule :new_post, 50
|
11
7
|
end
|
12
8
|
|
13
9
|
user = User.create!
|
14
10
|
post = Post.create! user: user
|
15
11
|
|
16
|
-
Scorecard::Point.where(
|
12
|
+
points = Scorecard::Point.where(
|
17
13
|
context: 'new_post',
|
18
14
|
amount: 50,
|
19
15
|
identifier: post.id.to_s,
|
@@ -21,12 +17,13 @@ describe 'Scorecard' do
|
|
21
17
|
user_type: 'User',
|
22
18
|
gameable_id: post.id,
|
23
19
|
gameable_type: 'Post'
|
24
|
-
)
|
20
|
+
)
|
21
|
+
expect(points).to_not be_empty
|
25
22
|
end
|
26
23
|
|
27
24
|
it "only stores points when provided logic passes" do
|
28
25
|
Scorecard.configure do |config|
|
29
|
-
config.rules.
|
26
|
+
config.rules.add_rule :new_post, 50,
|
30
27
|
if: lambda { |payload| Post.count <= 1 }
|
31
28
|
end
|
32
29
|
|
@@ -34,22 +31,23 @@ describe 'Scorecard' do
|
|
34
31
|
post = Post.create! user: user
|
35
32
|
post = Post.create! user: user
|
36
33
|
|
37
|
-
Scorecard::Point.where(
|
34
|
+
count = Scorecard::Point.where(
|
38
35
|
context: 'new_post',
|
39
36
|
amount: 50
|
40
|
-
).count
|
37
|
+
).count
|
38
|
+
expect(count).to eq(1)
|
41
39
|
end
|
42
40
|
|
43
41
|
it "does not double-up on points for the same event" do
|
44
42
|
Scorecard.configure do |config|
|
45
|
-
config.rules.
|
43
|
+
config.rules.add_rule :new_post, 50
|
46
44
|
end
|
47
45
|
|
48
46
|
user = User.create!
|
49
47
|
post = Post.create! user: user
|
50
|
-
Scorecard::
|
48
|
+
Scorecard::Scorer.points :new_post, gameable: post
|
51
49
|
|
52
|
-
Scorecard::Point.where(
|
50
|
+
count = Scorecard::Point.where(
|
53
51
|
context: 'new_post',
|
54
52
|
amount: 50,
|
55
53
|
identifier: post.id.to_s,
|
@@ -57,12 +55,13 @@ describe 'Scorecard' do
|
|
57
55
|
user_type: 'User',
|
58
56
|
gameable_id: post.id,
|
59
57
|
gameable_type: 'Post'
|
60
|
-
).count
|
58
|
+
).count
|
59
|
+
expect(count).to eq(1)
|
61
60
|
end
|
62
61
|
|
63
62
|
it "respects limit options" do
|
64
63
|
Scorecard.configure do |config|
|
65
|
-
config.rules.
|
64
|
+
config.rules.add_rule :new_post, 50, limit: 100
|
66
65
|
end
|
67
66
|
|
68
67
|
user = User.create!
|
@@ -70,36 +69,32 @@ describe 'Scorecard' do
|
|
70
69
|
post = Post.create! user: user
|
71
70
|
post = Post.create! user: user
|
72
71
|
|
73
|
-
Scorecard::Point.where(
|
74
|
-
|
75
|
-
amount: 50
|
76
|
-
).count.should == 2
|
72
|
+
count = Scorecard::Point.where(context: 'new_post', amount: 50).count
|
73
|
+
expect(count).to eq(2)
|
77
74
|
end
|
78
75
|
|
79
76
|
it "respects timeframe options" do
|
80
77
|
Scorecard.configure do |config|
|
81
|
-
config.rules.
|
78
|
+
config.rules.add_rule :new_post, 50, timeframe: :day
|
82
79
|
end
|
83
80
|
|
84
81
|
user = User.create!
|
85
82
|
post = Post.create! user: user
|
86
83
|
post = Post.create! user: user
|
87
84
|
|
88
|
-
Scorecard::Point.where(
|
89
|
-
|
90
|
-
amount: 50
|
91
|
-
).count.should == 1
|
85
|
+
count = Scorecard::Point.where(context: 'new_post', amount: 50).count
|
86
|
+
expect(count).to eq(1)
|
92
87
|
end
|
93
88
|
|
94
89
|
it "allows for processing via Sidekiq" do
|
95
90
|
Scorecard.configure do |config|
|
96
|
-
config.rules.
|
91
|
+
config.rules.add_rule :new_user, 20
|
97
92
|
end
|
98
93
|
|
99
94
|
user = User.create!
|
100
|
-
Scorecard::
|
95
|
+
Scorecard::Scorer.points_async :new_user, gameable: user, user: user
|
101
96
|
|
102
|
-
Scorecard::Point.where(
|
97
|
+
points = Scorecard::Point.where(
|
103
98
|
context: 'new_user',
|
104
99
|
amount: 20,
|
105
100
|
identifier: user.id.to_s,
|
@@ -107,19 +102,27 @@ describe 'Scorecard' do
|
|
107
102
|
user_type: 'User',
|
108
103
|
gameable_id: user.id,
|
109
104
|
gameable_type: 'User'
|
110
|
-
)
|
105
|
+
)
|
106
|
+
expect(points).to_not be_empty
|
111
107
|
end
|
112
108
|
|
113
|
-
it "
|
109
|
+
it "fires a generic notification" do
|
114
110
|
Scorecard.configure do |config|
|
115
|
-
config.rules.
|
116
|
-
|
111
|
+
config.rules.add_rule :new_post, 50
|
112
|
+
end
|
113
|
+
|
114
|
+
fired = false
|
115
|
+
user = User.create!
|
116
|
+
|
117
|
+
subscriber = ActiveSupport::Notifications.subscribe 'scorecard' do |*args|
|
118
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
119
|
+
fired = (event.payload[:user] == user)
|
117
120
|
end
|
118
121
|
|
119
|
-
user = User.create!
|
120
122
|
post = Post.create! user: user
|
121
|
-
Scorecard::Points.score :new_user, gameable: user, user: user
|
122
123
|
|
123
|
-
|
124
|
+
ActiveSupport::Notifications.unsubscribe(subscriber)
|
125
|
+
|
126
|
+
expect(fired).to be_true
|
124
127
|
end
|
125
128
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: scorecard
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pat Allan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-10-
|
11
|
+
date: 2013-10-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -93,18 +93,26 @@ files:
|
|
93
93
|
- LICENSE.txt
|
94
94
|
- README.md
|
95
95
|
- Rakefile
|
96
|
+
- app/models/scorecard/level.rb
|
96
97
|
- app/models/scorecard/point.rb
|
97
98
|
- config.ru
|
98
99
|
- db/migrate/1_create_points.rb
|
100
|
+
- db/migrate/2_create_levels.rb
|
99
101
|
- lib/scorecard.rb
|
102
|
+
- lib/scorecard/card.rb
|
103
|
+
- lib/scorecard/cleaner.rb
|
104
|
+
- lib/scorecard/clear_worker.rb
|
100
105
|
- lib/scorecard/engine.rb
|
101
106
|
- lib/scorecard/point_rule.rb
|
102
|
-
- lib/scorecard/points.rb
|
103
107
|
- lib/scorecard/rules.rb
|
108
|
+
- lib/scorecard/score_worker.rb
|
109
|
+
- lib/scorecard/scorer.rb
|
104
110
|
- lib/scorecard/subscriber.rb
|
105
|
-
- lib/scorecard/worker.rb
|
106
111
|
- scorecard.gemspec
|
107
|
-
- spec/acceptance/
|
112
|
+
- spec/acceptance/clearing_points_spec.rb
|
113
|
+
- spec/acceptance/levels_spec.rb
|
114
|
+
- spec/acceptance/read_points_spec.rb
|
115
|
+
- spec/acceptance/score_points_spec.rb
|
108
116
|
- spec/internal/app/models/post.rb
|
109
117
|
- spec/internal/app/models/user.rb
|
110
118
|
- spec/internal/config/database.yml
|
@@ -138,7 +146,10 @@ signing_key:
|
|
138
146
|
specification_version: 4
|
139
147
|
summary: Rails Engine for common scorecard patterns
|
140
148
|
test_files:
|
141
|
-
- spec/acceptance/
|
149
|
+
- spec/acceptance/clearing_points_spec.rb
|
150
|
+
- spec/acceptance/levels_spec.rb
|
151
|
+
- spec/acceptance/read_points_spec.rb
|
152
|
+
- spec/acceptance/score_points_spec.rb
|
142
153
|
- spec/internal/app/models/post.rb
|
143
154
|
- spec/internal/app/models/user.rb
|
144
155
|
- spec/internal/config/database.yml
|
data/lib/scorecard/points.rb
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
class Scorecard::Points
|
2
|
-
def self.score(context, options)
|
3
|
-
ActiveSupport::Notifications.instrument 'points.scorecard',
|
4
|
-
options.merge(context: context)
|
5
|
-
end
|
6
|
-
|
7
|
-
def self.score_async(context, options)
|
8
|
-
[:gameable, :user].each do |prefix|
|
9
|
-
next unless options[prefix]
|
10
|
-
|
11
|
-
options["#{prefix}_id"] = options[prefix].id
|
12
|
-
options["#{prefix}_type"] = options[prefix].class.name
|
13
|
-
options.delete prefix
|
14
|
-
end
|
15
|
-
|
16
|
-
Scorecard::Worker.perform_async context, options.stringify_keys
|
17
|
-
end
|
18
|
-
|
19
|
-
def self.for(user)
|
20
|
-
Scorecard::Point.for_user(user).sum(:amount)
|
21
|
-
end
|
22
|
-
end
|