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 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