leaderboard 3.3.0 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
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