leaderboard 3.9.0 → 3.10.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7f50c3a3b7020a18f0f1748f97bfd562958d13ea
4
- data.tar.gz: 3f322fef803e8b098265f4282c2131c93ca75507
3
+ metadata.gz: 0e2258ac4147cfe36d76299662c67d8ac5f360de
4
+ data.tar.gz: 50896b7f86ae70b0339551f8b0afd5ce32383353
5
5
  SHA512:
6
- metadata.gz: 1060d4cc1804f92ca1005bc39e7ecab287faa29e6a926fad090f6403f666b68cde60152d5c93608715484b1094f8608fc15b6798cec0dbe7f926782475de1f23
7
- data.tar.gz: 0eca6086b8c7db70f505d933749173c3dc91399c23fe1ee91920f8e7d3b380c2506761d7195af6b84aaecd4d11091fa6ce91d93364cd427f8d3eed43be67c7d7
6
+ metadata.gz: dba84526f444d7494f2ea0a21ec8e8fd90b408f7477c1a9abe06cc6f9ff688f90e17935bebbdb71ca2261593608e61b610d8441cda7d161dda74306294308bed
7
+ data.tar.gz: 5c25f504a4ba53fcdc109692a82b09b50fef8711ab3521b9a2a9d303c3dd9a83caa305297df510974029c5d4ba98bf696d515a6411025b82c5bc7c9f34950093
@@ -1 +1 @@
1
- ruby-2.1.2
1
+ ruby-2.2.1
@@ -1,6 +1,5 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.1.2
4
- - 1.9.3
3
+ - 2.2.1
5
4
  services:
6
- - redis-server
5
+ - redis-server
@@ -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
@@ -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
- def change_score_for(member, delta)
356
- change_score_for_member_in(@leaderboard_name, member, delta)
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
- def change_score_for_member_in(leaderboard_name, member, delta)
365
- @redis_connection.zincrby(leaderboard_name, delta, member)
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
 
@@ -1,3 +1,3 @@
1
1
  class Leaderboard
2
- VERSION = '3.9.0'.freeze
2
+ VERSION = '3.10.0'.freeze
3
3
  end
@@ -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
- def change_score_for_member_in(leaderboard_name, member, delta)
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
- @redis_connection.multi do |transaction|
91
- leaderboards.each do |leaderboard_name|
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
- @redis_connection.multi do |transaction|
109
- members_and_scores.each_slice(2) do |member_and_score|
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
@@ -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(1)
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
@@ -2,6 +2,6 @@ require 'spec_helper'
2
2
 
3
3
  describe 'Leaderboard::VERSION' do
4
4
  it 'should be the correct version' do
5
- expect(Leaderboard::VERSION).to eq('3.9.0')
5
+ expect(Leaderboard::VERSION).to eq('3.10.0')
6
6
  end
7
7
  end
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.9.0
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-02-15 00:00:00.000000000 Z
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.2.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