leaderboard 3.9.0 → 3.10.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.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/.travis.yml +2 -3
- data/CHANGELOG.markdown +7 -0
- data/lib/competition_ranking_leaderboard.rb +4 -0
- data/lib/leaderboard.rb +14 -5
- data/lib/leaderboard/version.rb +1 -1
- data/lib/tie_ranking_leaderboard.rb +18 -13
- data/spec/competition_ranking_leaderboard_spec.rb +17 -0
- data/spec/leaderboard_spec.rb +24 -1
- data/spec/reverse_tie_ranking_leaderboard_spec.rb +16 -0
- data/spec/tie_ranking_leaderboard_spec.rb +46 -0
- data/spec/version_spec.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0e2258ac4147cfe36d76299662c67d8ac5f360de
|
4
|
+
data.tar.gz: 50896b7f86ae70b0339551f8b0afd5ce32383353
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dba84526f444d7494f2ea0a21ec8e8fd90b408f7477c1a9abe06cc6f9ff688f90e17935bebbdb71ca2261593608e61b610d8441cda7d161dda74306294308bed
|
7
|
+
data.tar.gz: 5c25f504a4ba53fcdc109692a82b09b50fef8711ab3521b9a2a9d303c3dd9a83caa305297df510974029c5d4ba98bf696d515a6411025b82c5bc7c9f34950093
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
ruby-2.1
|
1
|
+
ruby-2.2.1
|
data/.travis.yml
CHANGED
data/CHANGELOG.markdown
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
## 3.10.0 (2015-03-20)
|
4
|
+
|
5
|
+
* Fixes TieRankingLeaderboard doesn't rank if the score is 0 [#53](https://github.com/agoragames/leaderboard/issues/53).
|
6
|
+
* Allow member data to be set in the `change_score_for(...)` method.
|
7
|
+
* Add `:include_missing` option in leaderboard request options to change
|
8
|
+
whether or not to include missing members in the result.
|
9
|
+
|
3
10
|
## 3.9.0 (2015-02-15)
|
4
11
|
|
5
12
|
* Add `global_member_data` option that allows multiple leaderboards to share the same set of member_data. [#51](https://github.com/agoragames/leaderboard/pull/51)
|
@@ -72,6 +72,10 @@ class CompetitionRankingLeaderboard < Leaderboard
|
|
72
72
|
data[@member_key] = member
|
73
73
|
unless leaderboard_options[:members_only]
|
74
74
|
data[@score_key] = responses[index * 2 + 1].to_f if responses[index * 2 + 1]
|
75
|
+
if data[@score_key] == nil
|
76
|
+
next unless leaderboard_options[:include_missing]
|
77
|
+
end
|
78
|
+
|
75
79
|
if @reverse
|
76
80
|
data[@rank_key] = @redis_connection.zcount(leaderboard_name, '-inf', "(#{data[@score_key]}") + 1 rescue nil
|
77
81
|
else
|
data/lib/leaderboard.rb
CHANGED
@@ -41,7 +41,8 @@ class Leaderboard
|
|
41
41
|
:with_member_data => false,
|
42
42
|
:page_size => nil,
|
43
43
|
:members_only => false,
|
44
|
-
:sort_by => :none
|
44
|
+
:sort_by => :none,
|
45
|
+
:include_missing => true
|
45
46
|
}
|
46
47
|
|
47
48
|
# Name of the leaderboard.
|
@@ -352,8 +353,9 @@ class Leaderboard
|
|
352
353
|
#
|
353
354
|
# @param member [String] Member name.
|
354
355
|
# @param delta [float] Score change.
|
355
|
-
|
356
|
-
|
356
|
+
# @param member_data [String] Optional member data.
|
357
|
+
def change_score_for(member, delta, member_data = nil)
|
358
|
+
change_score_for_member_in(@leaderboard_name, member, delta, member_data)
|
357
359
|
end
|
358
360
|
|
359
361
|
# Change the score for a member in the named leaderboard by a delta which can be positive or negative.
|
@@ -361,8 +363,12 @@ class Leaderboard
|
|
361
363
|
# @param leaderboard_name [String] Name of the leaderboard.
|
362
364
|
# @param member [String] Member name.
|
363
365
|
# @param delta [float] Score change.
|
364
|
-
|
365
|
-
|
366
|
+
# @param member_data [String] Optional member data.
|
367
|
+
def change_score_for_member_in(leaderboard_name, member, delta, member_data)
|
368
|
+
@redis_connection.multi do |transaction|
|
369
|
+
transaction.zincrby(leaderboard_name, delta, member)
|
370
|
+
transaction.hset(member_data_key(leaderboard_name), member, member_data) if member_data
|
371
|
+
end
|
366
372
|
end
|
367
373
|
|
368
374
|
# Retrieve the rank for a member in the leaderboard.
|
@@ -952,6 +958,9 @@ class Leaderboard
|
|
952
958
|
data[@member_key] = member
|
953
959
|
unless leaderboard_options[:members_only]
|
954
960
|
data[@rank_key] = responses[index * 2] + 1 rescue nil
|
961
|
+
if data[@rank_key] == nil
|
962
|
+
next unless leaderboard_options[:include_missing]
|
963
|
+
end
|
955
964
|
data[@score_key] = responses[index * 2 + 1].to_f if responses[index * 2 + 1]
|
956
965
|
end
|
957
966
|
|
data/lib/leaderboard/version.rb
CHANGED
@@ -50,15 +50,17 @@ class TieRankingLeaderboard < Leaderboard
|
|
50
50
|
# @param leaderboard_name [String] Name of the leaderboard.
|
51
51
|
# @param member [String] Member name.
|
52
52
|
# @param delta [float] Score change.
|
53
|
-
|
53
|
+
# @param member_data [String] Optional member data.
|
54
|
+
def change_score_for_member_in(leaderboard_name, member, delta, member_data = nil)
|
54
55
|
previous_score = score_for(member)
|
55
|
-
new_score = previous_score + delta
|
56
|
+
new_score = (previous_score || 0) + delta
|
56
57
|
|
57
58
|
total_members_at_previous_score = @redis_connection.zrevrangebyscore(leaderboard_name, previous_score, previous_score)
|
58
59
|
|
59
60
|
@redis_connection.multi do |transaction|
|
60
61
|
transaction.zadd(leaderboard_name, new_score, member)
|
61
62
|
transaction.zadd(ties_leaderboard_key(leaderboard_name), new_score, new_score.to_f.to_s)
|
63
|
+
transaction.hset(member_data_key(leaderboard_name), member, member_data) if member_data
|
62
64
|
end
|
63
65
|
|
64
66
|
if total_members_at_previous_score.length == 1
|
@@ -73,9 +75,15 @@ class TieRankingLeaderboard < Leaderboard
|
|
73
75
|
# @param score [float] Member score.
|
74
76
|
# @param member_data [String] Optional member data.
|
75
77
|
def rank_member_in(leaderboard_name, member, score, member_data = nil)
|
78
|
+
member_score = @redis_connection.zscore(leaderboard_name, member) || nil
|
79
|
+
can_delete_score = member_score &&
|
80
|
+
members_from_score_range_in(leaderboard_name, member_score, member_score).length == 1 &&
|
81
|
+
member_score != score
|
82
|
+
|
76
83
|
@redis_connection.multi do |transaction|
|
77
84
|
transaction.zadd(leaderboard_name, score, member)
|
78
85
|
transaction.zadd(ties_leaderboard_key(leaderboard_name), score, score.to_f.to_s)
|
86
|
+
transaction.zrem(ties_leaderboard_key(leaderboard_name), member_score.to_f.to_s) if can_delete_score
|
79
87
|
transaction.hset(member_data_key(leaderboard_name), member, member_data) if member_data
|
80
88
|
end
|
81
89
|
end
|
@@ -87,12 +95,8 @@ class TieRankingLeaderboard < Leaderboard
|
|
87
95
|
# @param score [float] Member score.
|
88
96
|
# @param member_data [String] Optional member data.
|
89
97
|
def rank_member_across(leaderboards, member, score, member_data = nil)
|
90
|
-
|
91
|
-
|
92
|
-
transaction.zadd(leaderboard_name, score, member)
|
93
|
-
transaction.zadd(ties_leaderboard_key(leaderboard_name), score, score.to_f.to_s)
|
94
|
-
transaction.hset(member_data_key(leaderboard_name), member, member_data) if member_data
|
95
|
-
end
|
98
|
+
leaderboards.each do |leaderboard_name|
|
99
|
+
rank_member_in(leaderboard_name, member, score, member_data)
|
96
100
|
end
|
97
101
|
end
|
98
102
|
|
@@ -105,11 +109,8 @@ class TieRankingLeaderboard < Leaderboard
|
|
105
109
|
members_and_scores.flatten!
|
106
110
|
end
|
107
111
|
|
108
|
-
|
109
|
-
|
110
|
-
transaction.zadd(leaderboard_name, member_and_score[1], member_and_score[0])
|
111
|
-
transaction.zadd(ties_leaderboard_key(leaderboard_name), member_and_score[0], member_and_score[0].to_f.to_s)
|
112
|
-
end
|
112
|
+
members_and_scores.each_slice(2) do |member_and_score|
|
113
|
+
rank_member_in(leaderboard_name, member_and_score[0], member_and_score[1])
|
113
114
|
end
|
114
115
|
end
|
115
116
|
|
@@ -242,6 +243,10 @@ class TieRankingLeaderboard < Leaderboard
|
|
242
243
|
else
|
243
244
|
data[@rank_key] = @redis_connection.zrevrank(ties_leaderboard_key(leaderboard_name), data[@score_key].to_s) + 1 rescue nil
|
244
245
|
end
|
246
|
+
|
247
|
+
if data[@rank_key] == nil
|
248
|
+
next unless leaderboard_options[:include_missing]
|
249
|
+
end
|
245
250
|
end
|
246
251
|
|
247
252
|
if leaderboard_options[:with_member_data]
|
@@ -166,5 +166,22 @@ describe 'CompetitionRankingLeaderboard' do
|
|
166
166
|
expect(members[24][:member]).to eql('member_1')
|
167
167
|
end
|
168
168
|
|
169
|
+
it 'should allow you to include or exclude missing members using the :include_missing option' do
|
170
|
+
@leaderboard = CompetitionRankingLeaderboard.new('ties', Leaderboard::DEFAULT_OPTIONS, {:host => "127.0.0.1", :db => 15})
|
171
|
+
rank_members_in_leaderboard(25)
|
172
|
+
|
173
|
+
leaders = @leaderboard.ranked_in_list(['member_1', 'member_15', 'member_25', 'member_200'])
|
174
|
+
expect(leaders.size).to be(4)
|
175
|
+
expect(leaders[0][:member]).to eql('member_1')
|
176
|
+
expect(leaders[1][:member]).to eql('member_15')
|
177
|
+
expect(leaders[2][:member]).to eql('member_25')
|
178
|
+
expect(leaders[3][:member]).to eql('member_200')
|
179
|
+
|
180
|
+
leaders = @leaderboard.ranked_in_list(['member_1', 'member_15', 'member_25', 'member_200'], :include_missing => false)
|
181
|
+
expect(leaders.size).to be(3)
|
182
|
+
expect(leaders[0][:member]).to eql('member_1')
|
183
|
+
expect(leaders[1][:member]).to eql('member_15')
|
184
|
+
expect(leaders[2][:member]).to eql('member_25')
|
185
|
+
end
|
169
186
|
end
|
170
187
|
end
|
data/spec/leaderboard_spec.rb
CHANGED
@@ -50,7 +50,7 @@ describe 'Leaderboard' do
|
|
50
50
|
@leaderboard = Leaderboard.new('name', Leaderboard::DEFAULT_OPTIONS, {:redis_connection => @redis_connection})
|
51
51
|
rank_members_in_leaderboard
|
52
52
|
|
53
|
-
expect(@redis_connection.info["connected_clients"].to_i).to be
|
53
|
+
expect(@redis_connection.info["connected_clients"].to_i).to be >= 1
|
54
54
|
end
|
55
55
|
|
56
56
|
it 'should allow you to rank a member and see that reflected in total members' do
|
@@ -304,6 +304,12 @@ describe 'Leaderboard' do
|
|
304
304
|
expect(@leaderboard.score_for('member_1')).to eql(5.0)
|
305
305
|
end
|
306
306
|
|
307
|
+
it 'should allow you to change the score and member data for a member' do
|
308
|
+
@leaderboard.change_score_for('member_1', 5, 'optional-data')
|
309
|
+
expect(@leaderboard.score_for('member_1')).to eql(5.0)
|
310
|
+
expect(@leaderboard.member_data_for('member_1')).to eql('optional-data')
|
311
|
+
end
|
312
|
+
|
307
313
|
it 'should allow you to check if a member exists' do
|
308
314
|
@leaderboard.rank_member('member_1', 10)
|
309
315
|
|
@@ -815,4 +821,21 @@ describe 'Leaderboard' do
|
|
815
821
|
expect(@redis_connection.exists("member_data")).to be_truthy
|
816
822
|
expect(@redis_connection.exists("name:member_data")).to be_falsey
|
817
823
|
end
|
824
|
+
|
825
|
+
it 'should allow you to include or exclude missing members using the :include_missing option' do
|
826
|
+
rank_members_in_leaderboard(25)
|
827
|
+
|
828
|
+
leaders = @leaderboard.ranked_in_list(['member_1', 'member_15', 'member_25', 'member_200'])
|
829
|
+
expect(leaders.size).to be(4)
|
830
|
+
expect(leaders[0][:member]).to eql('member_1')
|
831
|
+
expect(leaders[1][:member]).to eql('member_15')
|
832
|
+
expect(leaders[2][:member]).to eql('member_25')
|
833
|
+
expect(leaders[3][:member]).to eql('member_200')
|
834
|
+
|
835
|
+
leaders = @leaderboard.ranked_in_list(['member_1', 'member_15', 'member_25', 'member_200'], :include_missing => false)
|
836
|
+
expect(leaders.size).to be(3)
|
837
|
+
expect(leaders[0][:member]).to eql('member_1')
|
838
|
+
expect(leaders[1][:member]).to eql('member_15')
|
839
|
+
expect(leaders[2][:member]).to eql('member_25')
|
840
|
+
end
|
818
841
|
end
|
@@ -171,5 +171,21 @@ describe 'TieRankingLeaderboard (reverse option)' do
|
|
171
171
|
|
172
172
|
leaderboard.disconnect
|
173
173
|
end
|
174
|
+
|
175
|
+
it 'should output the correct rank when initial score is 0 and then later scores are tied' do
|
176
|
+
# See https://github.com/agoragames/leaderboard/issues/53
|
177
|
+
leaderboard = TieRankingLeaderboard.new('ties', {:reverse => true}, {:host => "127.0.0.1", :db => 15})
|
178
|
+
|
179
|
+
leaderboard.rank_members('member_1', 0, 'member_2', 0)
|
180
|
+
expect(leaderboard.rank_for('member_1')).to eq(1)
|
181
|
+
expect(leaderboard.rank_for('member_2')).to eq(1)
|
182
|
+
leaderboard.rank_members('member_1', 1, 'member_2', 1)
|
183
|
+
expect(leaderboard.rank_for('member_1')).to eq(1)
|
184
|
+
expect(leaderboard.rank_for('member_2')).to eq(1)
|
185
|
+
leaderboard.rank_members('member_1', 1, 'member_2', 1, 'member_3', 4)
|
186
|
+
expect(leaderboard.rank_for('member_3')).to eq(2)
|
187
|
+
expect(leaderboard.rank_for('member_1')).to eq(1)
|
188
|
+
expect(leaderboard.rank_for('member_2')).to eq(1)
|
189
|
+
end
|
174
190
|
end
|
175
191
|
end
|
@@ -155,6 +155,15 @@ describe 'TieRankingLeaderboard' do
|
|
155
155
|
leaderboard.disconnect
|
156
156
|
end
|
157
157
|
|
158
|
+
it 'should allow you to change the score and member data for a member' do
|
159
|
+
leaderboard = TieRankingLeaderboard.new('ties', Leaderboard::DEFAULT_OPTIONS, {:host => "127.0.0.1", :db => 15})
|
160
|
+
|
161
|
+
leaderboard.change_score_for('member_1', 10, 'optional-data')
|
162
|
+
|
163
|
+
expect(leaderboard.rank_for('member_1')).to eq(1)
|
164
|
+
expect(leaderboard.member_data_for('member_1')).to eql('optional-data')
|
165
|
+
end
|
166
|
+
|
158
167
|
it 'should have the correct rankings and scores when using change_score_for (varying scores)' do
|
159
168
|
leaderboard = TieRankingLeaderboard.new('ties', Leaderboard::DEFAULT_OPTIONS, {:host => "127.0.0.1", :db => 15})
|
160
169
|
|
@@ -300,5 +309,42 @@ describe 'TieRankingLeaderboard' do
|
|
300
309
|
expect(ttl).to be <= 10
|
301
310
|
end
|
302
311
|
end
|
312
|
+
|
313
|
+
it 'should output the correct rank when initial score is 0 and then later scores are tied' do
|
314
|
+
# See https://github.com/agoragames/leaderboard/issues/53
|
315
|
+
leaderboard = TieRankingLeaderboard.new('ties', Leaderboard::DEFAULT_OPTIONS, {:host => "127.0.0.1", :db => 15})
|
316
|
+
|
317
|
+
leaderboard.rank_members('member_1', 0, 'member_2', 0)
|
318
|
+
expect(leaderboard.rank_for('member_1')).to eq(1)
|
319
|
+
expect(leaderboard.rank_for('member_2')).to eq(1)
|
320
|
+
leaderboard.rank_members('member_1', 0, 'member_2', 0)
|
321
|
+
expect(leaderboard.rank_for('member_1')).to eq(1)
|
322
|
+
expect(leaderboard.rank_for('member_2')).to eq(1)
|
323
|
+
leaderboard.rank_members('member_1', 1, 'member_2', 1)
|
324
|
+
expect(leaderboard.rank_for('member_1')).to eq(1)
|
325
|
+
expect(leaderboard.rank_for('member_2')).to eq(1)
|
326
|
+
leaderboard.rank_members('member_1', 1, 'member_2', 1, 'member_3', 4)
|
327
|
+
expect(leaderboard.rank_for('member_3')).to eq(1)
|
328
|
+
expect(leaderboard.rank_for('member_1')).to eq(2)
|
329
|
+
expect(leaderboard.rank_for('member_2')).to eq(2)
|
330
|
+
end
|
331
|
+
|
332
|
+
it 'should allow you to include or exclude missing members using the :include_missing option' do
|
333
|
+
@leaderboard = TieRankingLeaderboard.new('ties', Leaderboard::DEFAULT_OPTIONS, {:host => "127.0.0.1", :db => 15})
|
334
|
+
rank_members_in_leaderboard(25)
|
335
|
+
|
336
|
+
leaders = @leaderboard.ranked_in_list(['member_1', 'member_15', 'member_25', 'member_200'])
|
337
|
+
expect(leaders.size).to be(4)
|
338
|
+
expect(leaders[0][:member]).to eql('member_1')
|
339
|
+
expect(leaders[1][:member]).to eql('member_15')
|
340
|
+
expect(leaders[2][:member]).to eql('member_25')
|
341
|
+
expect(leaders[3][:member]).to eql('member_200')
|
342
|
+
|
343
|
+
leaders = @leaderboard.ranked_in_list(['member_1', 'member_15', 'member_25', 'member_200'], :include_missing => false)
|
344
|
+
expect(leaders.size).to be(3)
|
345
|
+
expect(leaders[0][:member]).to eql('member_1')
|
346
|
+
expect(leaders[1][:member]).to eql('member_15')
|
347
|
+
expect(leaders[2][:member]).to eql('member_25')
|
348
|
+
end
|
303
349
|
end
|
304
350
|
end
|
data/spec/version_spec.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: leaderboard
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Czarnecki
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-03-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis
|
@@ -102,7 +102,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
102
102
|
version: '0'
|
103
103
|
requirements: []
|
104
104
|
rubyforge_project: leaderboard
|
105
|
-
rubygems_version: 2.
|
105
|
+
rubygems_version: 2.4.6
|
106
106
|
signing_key:
|
107
107
|
specification_version: 4
|
108
108
|
summary: Leaderboards backed by Redis in Ruby
|