leaderboard 3.3.0 → 3.4.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 ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ ZDg1OTQ1OTc4MzkzY2I4Mzc5ZjEwZWVlZTU3M2QzMTdlZDc2MmM4MQ==
5
+ data.tar.gz: !binary |-
6
+ YjEwOWE0Mzk3NjA1YjA5NzlmYzQ0OWQ0ODliN2E1NjFjYzE4MTkzMA==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ MWNmY2NjNmU5NGJkMzM0ZGViZGNiZTE5MGU4ZTZjYTIxZGYwNGJiNzU3N2Uz
10
+ YjBjOTlhZmJiYjIyNTk5NTE3NDdhYTE0Mjc5OWQ5YWI5MWEwNDQyM2U4ODQ3
11
+ MTUzMzJhYWEwN2YwNTBkNjUxZTI5OTcyMDNiYjQ5M2IyOTUzNjQ=
12
+ data.tar.gz: !binary |-
13
+ MzlmOTczZDJkMDNhNWM0ZDdmYzNlNzE5Y2VmOTcwMmI4MTVlMWY0ZjUwMWIw
14
+ ZTIxYTYwODgyMWJjYThlZjY1Yzg4YjhlNjcwMzI5NjljZmYwZjI4YzljNDgy
15
+ ZWY4NGI3ZWMzZDc0MzJiYjAwOGYyNmM2NzA0ZDVjZGJkNWQ0ZDY=
data/CHANGELOG.markdown CHANGED
@@ -1,5 +1,9 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 3.4.0 (2013-11-12)
4
+
5
+ * Added `score_for_percentile` method to be able to calculate the score for a given percentile value in the leaderboard.
6
+
3
7
  ## 3.3.0 (2013-07-17)
4
8
 
5
9
  * Added `rank_member_across` method to be able to rank a member across multiple leaderboards at once.
data/README.markdown CHANGED
@@ -319,6 +319,7 @@ highscore_lb.rank_member_across(['highscores', 'more_highscores'], 'david', 5000
319
319
  remove_members_in_score_range(min_score, max_score): Remove members from the leaderboard within a score range
320
320
  remove_members_outside_rank(rank): Remove members from the leaderboard outside a given rank
321
321
  percentile_for(member): Calculate the percentile for a given member
322
+ score_for_percentile(percentile): Calculate the score for a given percentile value in the leaderboard
322
323
  page_for(member, page_size): Determine the page where a member falls in the leaderboard
323
324
  expire_leaderboard(seconds): Expire the leaderboard in a set number of seconds.
324
325
  expire_leaderboard_at(timestamp): Expire the leaderboard at a specific UNIX timestamp.
data/lib/leaderboard.rb CHANGED
@@ -512,6 +512,45 @@ class Leaderboard
512
512
  end
513
513
  end
514
514
 
515
+ # Calculate the score for a given percentile value in the leaderboard.
516
+ #
517
+ # @param percentile [float] Percentile value (0.0 to 100.0 inclusive)
518
+ def score_for_percentile(percentile)
519
+ score_for_percentile_in(@leaderboard_name, percentile)
520
+ end
521
+
522
+ # Calculate the score for a given percentile value in the named leaderboard.
523
+ #
524
+ # See http://www.itl.nist.gov/div898/handbook/prc/section2/prc252.htm for
525
+ # implementation details (there are differing methods for calculating
526
+ # percentile scores that do not fall directly upon a ranked item; we are
527
+ # using the method specified by NIST, i.e. linear interpolation).
528
+ #
529
+ # @param percentile [float] Percentile value (0.0 to 100.0 inclusive)
530
+ def score_for_percentile_in(leaderboard_name, percentile)
531
+ return nil unless percentile.between?(0, 100)
532
+
533
+ total_members = total_members_in(leaderboard_name)
534
+ return nil if total_members < 1
535
+
536
+ if @reverse
537
+ percentile = 100 - percentile
538
+ end
539
+
540
+ index = (total_members - 1) * (percentile / 100.0)
541
+
542
+ scores = @redis_connection.zrange(
543
+ leaderboard_name, index.floor, index.ceil, :with_scores => true
544
+ ).map{ |pair| pair.last }
545
+
546
+ if index == index.floor
547
+ scores[0]
548
+ else
549
+ interpolate_fraction = index - index.floor
550
+ scores[0] + interpolate_fraction * (scores[1] - scores[0])
551
+ end
552
+ end
553
+
515
554
  # Determine the page where a member falls in the leaderboard.
516
555
  #
517
556
  # @param member [String] Member name.
@@ -1,3 +1,3 @@
1
1
  class Leaderboard
2
- VERSION = '3.3.0'.freeze
2
+ VERSION = '3.4.0'.freeze
3
3
  end
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  describe 'Leaderboard' do
4
4
  before(:each) do
5
5
  @redis_connection = Redis.new(:host => "127.0.0.1", :db => 15)
6
- @leaderboard = Leaderboard.new('name', Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS, :host => "127.0.0.1", :db => 15)
6
+ @leaderboard = Leaderboard.new('name', Leaderboard::DEFAULT_OPTIONS, {:host => "127.0.0.1", :db => 15})
7
7
  end
8
8
 
9
9
  after(:each) do
@@ -30,7 +30,7 @@ describe 'Leaderboard' do
30
30
  end
31
31
 
32
32
  it 'should set the page size to the default page size if passed an invalid value' do
33
- some_leaderboard = Leaderboard.new('name', {:page_size => 0})
33
+ some_leaderboard = Leaderboard.new('name', {:page_size => 0}, {:host => "127.0.0.1", :db => 15})
34
34
 
35
35
  some_leaderboard.page_size.should be(Leaderboard::DEFAULT_PAGE_SIZE)
36
36
  some_leaderboard.disconnect
@@ -371,8 +371,8 @@ describe 'Leaderboard' do
371
371
  end
372
372
 
373
373
  it 'should allow you to merge leaderboards' do
374
- foo = Leaderboard.new('foo', Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS, :host => "127.0.0.1", :db => 15)
375
- bar = Leaderboard.new('bar', Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS, :host => "127.0.0.1", :db => 15)
374
+ foo = Leaderboard.new('foo', Leaderboard::DEFAULT_OPTIONS, {:host => "127.0.0.1", :db => 15})
375
+ bar = Leaderboard.new('bar', Leaderboard::DEFAULT_OPTIONS, {:host => "127.0.0.1", :db => 15})
376
376
 
377
377
  foo.rank_member('foo_1', 1)
378
378
  foo.rank_member('foo_2', 2)
@@ -383,7 +383,7 @@ describe 'Leaderboard' do
383
383
  foobar_keys = foo.merge_leaderboards('foobar', ['bar'])
384
384
  foobar_keys.should be(5)
385
385
 
386
- foobar = Leaderboard.new('foobar', Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS, :host => "127.0.0.1", :db => 15)
386
+ foobar = Leaderboard.new('foobar', Leaderboard::DEFAULT_OPTIONS, {:host => "127.0.0.1", :db => 15})
387
387
  foobar.total_members.should be(5)
388
388
 
389
389
  first_leader_in_foobar = foobar.leaders(1).first
@@ -397,8 +397,8 @@ describe 'Leaderboard' do
397
397
  end
398
398
 
399
399
  it 'should allow you to intersect leaderboards' do
400
- foo = Leaderboard.new('foo', Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS, :host => "127.0.0.1", :db => 15)
401
- bar = Leaderboard.new('bar', Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS, :host => "127.0.0.1", :db => 15)
400
+ foo = Leaderboard.new('foo', Leaderboard::DEFAULT_OPTIONS, {:host => "127.0.0.1", :db => 15})
401
+ bar = Leaderboard.new('bar', Leaderboard::DEFAULT_OPTIONS, {:host => "127.0.0.1", :db => 15})
402
402
 
403
403
  foo.rank_member('foo_1', 1)
404
404
  foo.rank_member('foo_2', 2)
@@ -410,7 +410,7 @@ describe 'Leaderboard' do
410
410
  foobar_keys = foo.intersect_leaderboards('foobar', ['bar'], {:aggregate => :max})
411
411
  foobar_keys.should be(2)
412
412
 
413
- foobar = Leaderboard.new('foobar', Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS, :host => "127.0.0.1", :db => 15)
413
+ foobar = Leaderboard.new('foobar', Leaderboard::DEFAULT_OPTIONS, {:host => "127.0.0.1", :db => 15})
414
414
  foobar.total_members.should be(2)
415
415
 
416
416
  first_leader_in_foobar = foobar.leaders(1).first
@@ -479,6 +479,16 @@ describe 'Leaderboard' do
479
479
  @leaderboard.percentile_for('member_12').should eql(92)
480
480
  end
481
481
 
482
+ it 'should return the correct information when calling score_for_percentile' do
483
+ rank_members_in_leaderboard(5)
484
+
485
+ @leaderboard.score_for_percentile(0).should eql(1.0)
486
+ @leaderboard.score_for_percentile(75).should eql(4.0)
487
+ @leaderboard.score_for_percentile(87.5).should eql(4.5)
488
+ @leaderboard.score_for_percentile(93.75).should eql(4.75)
489
+ @leaderboard.score_for_percentile(100).should eql(5.0)
490
+ end
491
+
482
492
  it 'should not throw an exception when calling around_me with a member not in the leaderboard' do
483
493
  rank_members_in_leaderboard(Leaderboard::DEFAULT_PAGE_SIZE * 3 + 1)
484
494
 
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  describe 'Leaderboard (reverse option)' do
4
4
  before(:each) do
5
5
  @redis_connection = Redis.new(:host => "127.0.0.1", :db => 15)
6
- @leaderboard = Leaderboard.new('name', Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS.merge({:reverse => true}), :host => "127.0.0.1", :db => 15)
6
+ @leaderboard = Leaderboard.new('name', Leaderboard::DEFAULT_OPTIONS.merge({:reverse => true}), {:host => "127.0.0.1", :db => 15})
7
7
  end
8
8
 
9
9
  after(:each) do
@@ -186,8 +186,8 @@ describe 'Leaderboard (reverse option)' do
186
186
  end
187
187
 
188
188
  it 'should allow you to merge leaderboards' do
189
- foo = Leaderboard.new('foo', Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS, :host => "127.0.0.1", :db => 15)
190
- bar = Leaderboard.new('bar', Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS, :host => "127.0.0.1", :db => 15)
189
+ foo = Leaderboard.new('foo', Leaderboard::DEFAULT_OPTIONS, {:host => "127.0.0.1", :db => 15})
190
+ bar = Leaderboard.new('bar', Leaderboard::DEFAULT_OPTIONS, {:host => "127.0.0.1", :db => 15})
191
191
 
192
192
  foo.rank_member('foo_1', 1)
193
193
  foo.rank_member('foo_2', 2)
@@ -198,7 +198,7 @@ describe 'Leaderboard (reverse option)' do
198
198
  foobar_keys = foo.merge_leaderboards('foobar', ['bar'])
199
199
  foobar_keys.should be(5)
200
200
 
201
- foobar = Leaderboard.new('foobar', Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS, :host => "127.0.0.1", :db => 15)
201
+ foobar = Leaderboard.new('foobar', Leaderboard::DEFAULT_OPTIONS, {:host => "127.0.0.1", :db => 15})
202
202
  foobar.total_members.should be(5)
203
203
 
204
204
  first_leader_in_foobar = foobar.leaders(1).first
@@ -212,8 +212,8 @@ describe 'Leaderboard (reverse option)' do
212
212
  end
213
213
 
214
214
  it 'should allow you to intersect leaderboards' do
215
- foo = Leaderboard.new('foo', Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS, :host => "127.0.0.1", :db => 15)
216
- bar = Leaderboard.new('bar', Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS, :host => "127.0.0.1", :db => 15)
215
+ foo = Leaderboard.new('foo', Leaderboard::DEFAULT_OPTIONS, {:host => "127.0.0.1", :db => 15})
216
+ bar = Leaderboard.new('bar', Leaderboard::DEFAULT_OPTIONS, {:host => "127.0.0.1", :db => 15})
217
217
 
218
218
  foo.rank_member('foo_1', 1)
219
219
  foo.rank_member('foo_2', 2)
@@ -225,7 +225,7 @@ describe 'Leaderboard (reverse option)' do
225
225
  foobar_keys = foo.intersect_leaderboards('foobar', ['bar'], {:aggregate => :max})
226
226
  foobar_keys.should be(2)
227
227
 
228
- foobar = Leaderboard.new('foobar', Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS, :host => "127.0.0.1", :db => 15)
228
+ foobar = Leaderboard.new('foobar', Leaderboard::DEFAULT_OPTIONS, {:host => "127.0.0.1", :db => 15})
229
229
  foobar.total_members.should be(2)
230
230
 
231
231
  first_leader_in_foobar = foobar.leaders(1).first
@@ -275,6 +275,16 @@ describe 'Leaderboard (reverse option)' do
275
275
  @leaderboard.percentile_for('member_12').should eql(8)
276
276
  end
277
277
 
278
+ it 'should return the correct information when calling score_for_percentile' do
279
+ rank_members_in_leaderboard(5)
280
+
281
+ @leaderboard.score_for_percentile(0).should eql(5.0)
282
+ @leaderboard.score_for_percentile(75).should eql(2.0)
283
+ @leaderboard.score_for_percentile(87.5).should eql(1.5)
284
+ @leaderboard.score_for_percentile(93.75).should eql(1.25)
285
+ @leaderboard.score_for_percentile(100).should eql(1.0)
286
+ end
287
+
278
288
  it 'should return the correct page when calling page_for a given member in the leaderboard' do
279
289
  @leaderboard.page_for('jones').should be(0)
280
290
 
data/spec/version_spec.rb CHANGED
@@ -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
- Leaderboard::VERSION.should == '3.3.0'
5
+ Leaderboard::VERSION.should == '3.4.0'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,20 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: leaderboard
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.3.0
5
- prerelease:
4
+ version: 3.4.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - David Czarnecki
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-07-17 00:00:00.000000000 Z
11
+ date: 2013-11-12 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: redis
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
17
  - - ! '>='
20
18
  - !ruby/object:Gem::Version
@@ -22,7 +20,6 @@ dependencies:
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
24
  - - ! '>='
28
25
  - !ruby/object:Gem::Version
@@ -30,7 +27,6 @@ dependencies:
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: rake
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
31
  - - ! '>='
36
32
  - !ruby/object:Gem::Version
@@ -38,7 +34,6 @@ dependencies:
38
34
  type: :development
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
38
  - - ! '>='
44
39
  - !ruby/object:Gem::Version
@@ -46,7 +41,6 @@ dependencies:
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: rspec
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
45
  - - ! '>='
52
46
  - !ruby/object:Gem::Version
@@ -54,7 +48,6 @@ dependencies:
54
48
  type: :development
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
52
  - - ! '>='
60
53
  - !ruby/object:Gem::Version
@@ -86,33 +79,26 @@ files:
86
79
  homepage: https://github.com/agoragames/leaderboard
87
80
  licenses:
88
81
  - MIT
82
+ metadata: {}
89
83
  post_install_message:
90
84
  rdoc_options: []
91
85
  require_paths:
92
86
  - lib
93
87
  required_ruby_version: !ruby/object:Gem::Requirement
94
- none: false
95
88
  requirements:
96
89
  - - ! '>='
97
90
  - !ruby/object:Gem::Version
98
91
  version: '0'
99
- segments:
100
- - 0
101
- hash: 1089001734082897595
102
92
  required_rubygems_version: !ruby/object:Gem::Requirement
103
- none: false
104
93
  requirements:
105
94
  - - ! '>='
106
95
  - !ruby/object:Gem::Version
107
96
  version: '0'
108
- segments:
109
- - 0
110
- hash: 1089001734082897595
111
97
  requirements: []
112
98
  rubyforge_project: leaderboard
113
- rubygems_version: 1.8.25
99
+ rubygems_version: 2.1.10
114
100
  signing_key:
115
- specification_version: 3
101
+ specification_version: 4
116
102
  summary: Leaderboards backed by Redis in Ruby
117
103
  test_files:
118
104
  - spec/leaderboard_spec.rb