leaderboard 2.0.1 → 2.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ coverage
2
+ rdoc
3
+ doc
4
+ .yardoc
5
+ .bundle
6
+ pkg
7
+ Gemfile.lock
8
+ test/db/*
data/.rvmrc CHANGED
@@ -1,2 +1,3 @@
1
1
  rvm --create 1.8.7@leaderboard_gem
2
2
  rvm --create 1.9.2@leaderboard_gem
3
+ rvm --create 1.9.3@leaderboard_gem
data/CHANGELOG.markdown CHANGED
@@ -1,9 +1,16 @@
1
- # leaderboard 2.0.1 (2011-11-07)
1
+ # CHANGELOG
2
+
3
+ ## leaderboard 2.0.2 (2012-02-03)
4
+
5
+ * Fix for checking to see if a member actually exists in the leaderboard for the `around_me` calls
6
+ * Return appropriate `nil` in data returned for calls such as `percentile_for` and `ranked_in_list` for non-existent members
7
+
8
+ ## leaderboard 2.0.1 (2011-11-07)
2
9
 
3
10
  * Allow for only single options to be passed to `leaders`, `around_me` and `ranked_in_list` methods - https://github.com/agoragames/leaderboard/issues/4
4
11
  * Added `percentile_for(member)` and `percentile_for_in(leaderboard_name, member)` methods to calculate percentile for a given member
5
12
 
6
- # leaderboard 2.0.0 (2011-08-05)
13
+ ## leaderboard 2.0.0 (2011-08-05)
7
14
 
8
15
  * Change `add_member` to `rank_member` - https://github.com/agoragames/leaderboard/issues/3
9
16
  * Added `delete_leaderboard` and `delete_leaderboard_named` - https://github.com/agoragames/leaderboard/issues/2
@@ -12,33 +19,33 @@
12
19
  * Updated initializer to take a leaderboard name, `options` hash and `redis_options` hash
13
20
  * Simplified `leaders`, `around_me` and `ranked_in_list` to use an `options` hash with defaults for the previously individual parameters
14
21
 
15
- # leaderboard 1.0.6 (unreleased)
22
+ ## leaderboard 1.0.6 (unreleased)
16
23
 
17
24
  * Added `disconnect` method
18
25
  * Check for invalid page size when changing
19
26
 
20
- # leaderboard 1.0.5 (2011-05-04)
27
+ ## leaderboard 1.0.5 (2011-05-04)
21
28
 
22
29
  * Updated Rakefile to run tests under ruby 1.8.7 and ruby 1.9.2
23
30
  * Added `page_size` parameter to `total_pages_in` to allow for checking what if values in that scenario
24
31
  * Added `page_size` parameter to `leaders` and `around_me` calls
25
32
 
26
- # leaderboard 1.0.4 (2011-04-26)
33
+ ## leaderboard 1.0.4 (2011-04-26)
27
34
 
28
35
  * Minor bug fix
29
36
 
30
- # leaderboard 1.0.3 (2011-04-26)
37
+ ## leaderboard 1.0.3 (2011-04-26)
31
38
 
32
39
  * Fixing issue using total_pages in leaderboard_in call
33
40
  * Internal `massage_leader_data` method will now respect `with_scores`
34
41
 
35
- # leaderboard 1.0.2 (2011-02-25)
42
+ ## leaderboard 1.0.2 (2011-02-25)
36
43
 
37
44
  * Adding `XXX_to`, `XXX_for`, `XXX_in` and `XXX_from` methods that will allow you to set the leaderboard name to interact with outside of creating a new object
38
45
  * Added `merge_leaderboards(destination, keys, options = {:aggregate => :min})` method to merge leaderboards given by keys with this leaderboard into destination
39
46
  * Added `intersect_leaderboards(destination, keys, options = {:aggregate => :min})` method to intersect leaderboards given by keys with this leaderboard into destination
40
47
 
41
- # leaderboard 1.0.1 (2011-02-16)
48
+ ## leaderboard 1.0.1 (2011-02-16)
42
49
 
43
50
  * `redis_options` can be passed in the initializer to pass options for the connection to Redis
44
51
  * `page_size` is now settable outside of the initializer
@@ -46,6 +53,6 @@
46
53
  * `score_and_rank_for(member, use_zero_index_for_rank = false)`: Retrieve the score and rank for a member in a single call
47
54
  * `remove_members_in_score_range(min_score, max_score)`: Remove members from the leaderboard within a score range
48
55
 
49
- # leaderboard 1.0.0
56
+ ## leaderboard 1.0.0
50
57
 
51
58
  * Initial release
data/Gemfile CHANGED
@@ -1,13 +1,3 @@
1
1
  source "http://rubygems.org"
2
2
 
3
- # Add dependencies to develop your gem here.
4
- # Include everything needed to run rake, tests, features, etc.
5
- group :development do
6
- gem "bundler"
7
- gem "jeweler"
8
- gem "rcov"
9
- gem "rdoc"
10
- end
11
-
12
- gem 'redis'
13
-
3
+ gemspec
@@ -1,114 +1,143 @@
1
- = leaderboard
1
+ # leaderboard
2
2
 
3
3
  Leaderboards backed by Redis in Ruby, http://redis.io.
4
4
 
5
5
  Builds off ideas proposed in http://blog.agoragames.com/2011/01/01/creating-high-score-tables-leaderboards-using-redis/.
6
6
 
7
- == Installation
7
+ ## Installation
8
8
 
9
9
  Install the gem:
10
10
 
11
- gem "leaderboard", "~> 2.0.1"
12
-
11
+ ```ruby
12
+ gem "leaderboard"
13
+ ```
14
+
13
15
  Make sure your redis server is running! Redis configuration is outside the scope of this README, but
14
16
  check out the Redis documentation, http://redis.io/documentation.
15
17
 
16
- == Compatibility
18
+ ## Compatibility
17
19
 
18
- The gem has been built and tested under Ruby 1.8.7 and Ruby 1.9.2
20
+ The gem has been built and tested under Ruby 1.8.7, Ruby 1.9.2 and Ruby 1.9.3
19
21
 
20
- == Usage
22
+ ## Usage
21
23
 
22
24
  Create a new leaderboard or attach to an existing leaderboard named 'highscores':
23
25
 
26
+ ```ruby
24
27
  ruby-1.9.2-p180 :002 > highscore_lb = Leaderboard.new('highscores')
25
28
  => #<Leaderboard:0x0000010307b530 @leaderboard_name="highscores", @page_size=25, @redis_connection=#<Redis client v2.2.2 connected to redis://localhost:6379/0 (Redis v2.2.5)>>
29
+ ```
26
30
 
27
31
  If you need to pass in options for Redis, you can do this in the initializer:
28
32
 
33
+ ```ruby
29
34
  ruby-1.9.2-p180 :007 > redis_options = {:host => 'localhost', :port => 6379, :db => 1}
30
35
  => {:host=>"localhost", :port=>6379, :db=>1}
31
36
  ruby-1.9.2-p180 :008 > highscore_lb = Leaderboard.new('highscores', Leaderboard::DEFAULT_OPTIONS, redis_options)
32
37
  => #<Leaderboard:0x00000103095200 @leaderboard_name="highscores", @page_size=25, @redis_connection=#<Redis client v2.2.2 connected to redis://localhost:6379/1 (Redis v2.2.5)>>
38
+ ```
33
39
 
34
40
  You can pass in an existing connection to Redis using :redis_connection in the Redis options hash:
35
41
 
42
+ ```ruby
36
43
  ruby-1.9.2-p180 :009 > redis = Redis.new
37
44
  => #<Redis client v2.2.2 connected to redis://127.0.0.1:6379/0 (Redis v2.2.5)>
38
45
  ruby-1.9.2-p180 :010 > redis_options = {:redis_connection => redis}
39
46
  => {:redis_connection=>#<Redis client v2.2.2 connected to redis://127.0.0.1:6379/0 (Redis v2.2.5)>}
40
47
  ruby-1.9.2-p180 :011 > highscore_lb = Leaderboard.new('highscores', Leaderboard::DEFAULT_OPTIONS, redis_options)
41
48
  => #<Leaderboard:0x000001028791e8 @leaderboard_name="highscores", @page_size=25, @redis_connection=#<Redis client v2.2.2 connected to redis://127.0.0.1:6379/0 (Redis v2.2.5)>>
49
+ ```
42
50
 
43
51
  You can set the page size to something other than the default page size (25):
44
52
 
53
+ ```ruby
45
54
  ruby-1.9.2-p180 :012 > highscore_lb.page_size = 5
46
55
  => 5
47
56
  ruby-1.9.2-p180 :013 > highscore_lb
48
57
  => #<Leaderboard:0x000001028791e8 @leaderboard_name="highscores", @page_size=5, @redis_connection=#<Redis client v2.2.2 connected to redis://127.0.0.1:6379/0 (Redis v2.2.5)>>
58
+ ```
49
59
 
50
60
  Add members to your leaderboard using rank_member:
51
61
 
62
+ ```ruby
52
63
  ruby-1.9.2-p180 :014 > 1.upto(10) do |index|
53
64
  ruby-1.9.2-p180 :015 > highscore_lb.rank_member("member_#{index}", index)
54
65
  ruby-1.9.2-p180 :016?> end
55
66
  => 1
67
+ ```
56
68
 
57
69
  You can call rank_member with the same member and the leaderboard will be updated automatically.
58
70
 
59
71
  Get some information about your leaderboard:
60
72
 
73
+ ```ruby
61
74
  ruby-1.9.2-p180 :020 > highscore_lb.total_members
62
75
  => 10
63
76
  ruby-1.9.2-p180 :021 > highscore_lb.total_pages
64
77
  => 1
78
+ ```
65
79
 
66
80
  Get some information about a specific member(s) in the leaderboard:
67
81
 
82
+ ```ruby
68
83
  ruby-1.9.2-p180 :022 > highscore_lb.score_for('member_4')
69
84
  => 4.0
70
85
  ruby-1.9.2-p180 :023 > highscore_lb.rank_for('member_4')
71
86
  => 7
72
87
  ruby-1.9.2-p180 :024 > highscore_lb.rank_for('member_10')
73
88
  => 1
89
+ ```
74
90
 
75
91
  Get page 1 in the leaderboard:
76
92
 
93
+ ```ruby
77
94
  ruby-1.9.2-p180 :025 > highscore_lb.leaders(1)
78
95
  => [{:member=>"member_10", :rank=>1, :score=>10.0}, {:member=>"member_9", :rank=>2, :score=>9.0}, {:member=>"member_8", :rank=>3, :score=>8.0}, {:member=>"member_7", :rank=>4, :score=>7.0}, {:member=>"member_6", :rank=>5, :score=>6.0}, {:member=>"member_5", :rank=>6, :score=>5.0}, {:member=>"member_4", :rank=>7, :score=>4.0}, {:member=>"member_3", :rank=>8, :score=>3.0}, {:member=>"member_2", :rank=>9, :score=>2.0}, {:member=>"member_1", :rank=>10, :score=>1.0}]
96
+ ```
79
97
 
80
98
  You can pass various options to the calls `leaders`, `around_me` and `ranked_in_list`. Valid options are `:with_scores`, `:with_rank`, `:use_zero_index_for_rank` and `:page_size`.
81
99
  Below is an example of retrieving the first page in the leaderboard without ranks:
82
100
 
101
+ ```ruby
83
102
  ruby-1.9.2-p180 :026 > highscore_lb.leaders(1, :with_rank => false)
84
103
  => [{:member=>"member_10", :score=>9.0}, {:member=>"member_9", :score=>7.0}, {:member=>"member_8", :score=>5.0}, {:member=>"member_7", :score=>3.0}, {:member=>"member_6", :score=>1.0}, {:member=>"member_5", :score=>0.0}, {:member=>"member_4", :score=>0.0}, {:member=>"member_3", :score=>0.0}, {:member=>"member_2", :score=>0.0}, {:member=>"member_1", :score=>0.0}]
104
+ ```
85
105
 
86
106
  Below is an example of retrieving the first page in the leaderboard without scores or ranks:
87
107
 
108
+ ```ruby
88
109
  ruby-1.9.2-p180 :028 > highscore_lb.leaders(1, :with_scores => false, :with_rank => false)
89
110
  => [{:member=>"member_10"}, {:member=>"member_9"}, {:member=>"member_8"}, {:member=>"member_7"}, {:member=>"member_6"}, {:member=>"member_5"}, {:member=>"member_4"}, {:member=>"member_3"}, {:member=>"member_2"}, {:member=>"member_1"}]
111
+ ```
90
112
 
91
113
  Add more members to your leaderboard:
92
114
 
115
+ ```ruby
93
116
  ruby-1.9.2-p180 :029 > 50.upto(95) do |index|
94
117
  ruby-1.9.2-p180 :030 > highscore_lb.rank_member("member_#{index}", index)
95
118
  ruby-1.9.2-p180 :031?> end
96
119
  => 50
97
120
  ruby-1.9.2-p180 :032 > highscore_lb.total_pages
98
121
  => 3
122
+ ```
99
123
 
100
124
  Get an "Around Me" leaderboard for a member:
101
125
 
126
+ ```ruby
102
127
  ruby-1.9.2-p180 :033 > highscore_lb.around_me('member_53')
103
128
  => [{:member=>"member_65", :rank=>31, :score=>65.0}, {:member=>"member_64", :rank=>32, :score=>64.0}, {:member=>"member_63", :rank=>33, :score=>63.0}, {:member=>"member_62", :rank=>34, :score=>62.0}, {:member=>"member_61", :rank=>35, :score=>61.0}, {:member=>"member_60", :rank=>36, :score=>60.0}, {:member=>"member_59", :rank=>37, :score=>59.0}, {:member=>"member_58", :rank=>38, :score=>58.0}, {:member=>"member_57", :rank=>39, :score=>57.0}, {:member=>"member_56", :rank=>40, :score=>56.0}, {:member=>"member_55", :rank=>41, :score=>55.0}, {:member=>"member_54", :rank=>42, :score=>54.0}, {:member=>"member_53", :rank=>43, :score=>53.0}, {:member=>"member_52", :rank=>44, :score=>52.0}, {:member=>"member_51", :rank=>45, :score=>51.0}, {:member=>"member_50", :rank=>46, :score=>50.0}, {:member=>"member_10", :rank=>47, :score=>10.0}, {:member=>"member_9", :rank=>48, :score=>9.0}, {:member=>"member_8", :rank=>49, :score=>8.0}, {:member=>"member_7", :rank=>50, :score=>7.0}, {:member=>"member_6", :rank=>51, :score=>6.0}, {:member=>"member_5", :rank=>52, :score=>5.0}, {:member=>"member_4", :rank=>53, :score=>4.0}, {:member=>"member_3", :rank=>54, :score=>3.0}, {:member=>"member_2", :rank=>55, :score=>2.0}]
129
+ ```
104
130
 
105
131
  Get rank and score for an arbitrary list of members (e.g. friends):
106
132
 
133
+ ```ruby
107
134
  ruby-1.9.2-p180 :034 > highscore_lb.ranked_in_list(['member_1', 'member_62', 'member_67'])
108
135
  => [{:member=>"member_1", :rank=>56, :score=>1.0}, {:member=>"member_62", :rank=>34, :score=>62.0}, {:member=>"member_67", :rank=>29, :score=>67.0}]
109
-
110
- === Other useful methods
136
+ ```
137
+
138
+ ### Other useful methods
111
139
 
140
+ ```
112
141
  delete_leaderboard: Delete the current leaderboard
113
142
  remove_member(member): Remove a member from the leaderboard
114
143
  total_members: Total # of members in the leaderboard
@@ -123,13 +152,15 @@ Get rank and score for an arbitrary list of members (e.g. friends):
123
152
  percentile_for(member): Calculate the percentile for a given member
124
153
  merge_leaderboards(destination, keys, options = {:aggregate => :min}): Merge leaderboards given by keys with this leaderboard into destination
125
154
  intersect_leaderboards(destination, keys, options = {:aggregate => :min}): Intersect leaderboards given by keys with this leaderboard into destination
155
+ ```
126
156
 
127
- Check the online documentation for more detail, http://rubydoc.info/gems/leaderboard/
157
+ Check the [online documentation](http://rubydoc.info/github/agoragames/leaderboard/master/frames) for more detail on each method.
128
158
 
129
- == Performance Metrics
159
+ ## Performance Metrics
130
160
 
131
161
  10 million sequential scores insert:
132
162
 
163
+ ```ruby
133
164
  ruby-1.9.2-p180 :003 > highscore_lb = Leaderboard.new('highscores')
134
165
  => #<Leaderboard:0x0000010205fc50 @leaderboard_name="highscores", @page_size=25, @redis_connection=#<Redis client v2.2.2 connected to redis://localhost:6379/0 (Redis v2.2.5)>>
135
166
  ruby-1.9.2-p180 :004 > insert_time = Benchmark.measure do
@@ -138,9 +169,11 @@ Check the online documentation for more detail, http://rubydoc.info/gems/leaderb
138
169
  ruby-1.9.2-p180 :007?> end
139
170
  ruby-1.9.2-p180 :008?> end
140
171
  => 323.070000 148.560000 471.630000 (942.068307)
172
+ ```
141
173
 
142
174
  Average time to request an arbitrary page from the leaderboard:
143
175
 
176
+ ```ruby
144
177
  ruby-1.9.2-p180 :009 > requests_to_make = 50000
145
178
  => 50000
146
179
  ruby-1.9.2-p180 :010 > lb_request_time = 0
@@ -154,18 +187,22 @@ Average time to request an arbitrary page from the leaderboard:
154
187
  ruby-1.9.2-p180 :016 > p lb_request_time / requests_to_make
155
188
  0.001513999999999998
156
189
  => 0.001513999999999998
190
+ ```
157
191
 
158
192
  10 million random scores insert:
159
193
 
194
+ ```ruby
160
195
  ruby-1.9.2-p180 :018 > insert_time = Benchmark.measure do
161
196
  ruby-1.9.2-p180 :019 > 1.upto(10000000) do |index|
162
197
  ruby-1.9.2-p180 :020 > highscore_lb.rank_member("member_#{index}", rand(50000000))
163
198
  ruby-1.9.2-p180 :021?> end
164
199
  ruby-1.9.2-p180 :022?> end
165
200
  => 338.480000 155.200000 493.680000 (2188.702475)
201
+ ```
166
202
 
167
203
  Average time to request an arbitrary page from the leaderboard:
168
204
 
205
+ ```ruby
169
206
  ruby-1.9.2-p180 :007 > 1.upto(requests_to_make) do
170
207
  ruby-1.9.2-p180 :008 > lb_request_time += Benchmark.measure do
171
208
  ruby-1.9.2-p180 :009 > highscore_lb.leaders(rand(highscore_lb.total_pages))
@@ -175,12 +212,13 @@ Average time to request an arbitrary page from the leaderboard:
175
212
  ruby-1.9.2-p180 :012 > p lb_request_time / requests_to_make
176
213
  0.0014615999999999531
177
214
  => 0.0014615999999999531
215
+ ```
178
216
 
179
- == Future Ideas
217
+ ## Future Ideas
180
218
 
181
219
  * Bulk insert
182
220
 
183
- == Ports
221
+ ## Ports
184
222
 
185
223
  The following ports have been made of the leaderboard gem.
186
224
 
@@ -190,7 +228,7 @@ The following ports have been made of the leaderboard gem.
190
228
  * Python: https://github.com/agoragames/python-leaderboard
191
229
  * Scala: https://github.com/agoragames/scala-leaderboard
192
230
 
193
- == Contributing to leaderboard
231
+ ## Contributing to leaderboard
194
232
 
195
233
  * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
196
234
  * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
@@ -200,7 +238,7 @@ The following ports have been made of the leaderboard gem.
200
238
  * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
201
239
  * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
202
240
 
203
- == Copyright
241
+ ## Copyright
204
242
 
205
243
  Copyright (c) 2011 David Czarnecki. See LICENSE.txt for further details.
206
244
 
data/Rakefile CHANGED
@@ -1,31 +1,7 @@
1
- require 'rubygems'
2
1
  require 'bundler'
3
- begin
4
- Bundler.setup(:default, :development)
5
- rescue Bundler::BundlerError => e
6
- $stderr.puts e.message
7
- $stderr.puts "Run `bundle install` to install missing gems"
8
- exit e.status_code
9
- end
10
- require 'rake'
11
-
12
- require 'jeweler'
13
- Jeweler::Tasks.new do |gem|
14
- # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
- gem.name = "leaderboard"
16
- gem.homepage = "http://github.com/agoragames/leaderboard"
17
- gem.license = "MIT"
18
- gem.summary = %Q{Leaderboards backed by Redis in Ruby}
19
- gem.description = %Q{Leaderboards backed by Redis in Ruby}
20
- gem.email = "dczarnecki@agoragames.com"
21
- gem.authors = ["David Czarnecki"]
22
- # Include your dependencies below. Runtime dependencies are required when using your gem,
23
- # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
24
- # gem.add_runtime_dependency 'jabber4r', '> 0.1'
25
- # gem.add_development_dependency 'rspec', '> 1.2.3'
26
- end
27
- Jeweler::RubygemsDotOrgTasks.new
2
+ Bundler::GemHelper.install_tasks
28
3
 
4
+ require 'rake'
29
5
  require 'rake/testtask'
30
6
  Rake::TestTask.new(:test) do |test|
31
7
  test.libs << 'lib' << 'test'
@@ -33,23 +9,6 @@ Rake::TestTask.new(:test) do |test|
33
9
  test.verbose = true
34
10
  end
35
11
 
36
- require 'rcov/rcovtask'
37
- Rcov::RcovTask.new do |test|
38
- test.libs << 'test'
39
- test.pattern = 'test/**/test_*.rb'
40
- test.verbose = true
41
- end
42
-
43
- require 'rdoc/task'
44
- RDoc::Task.new do |rdoc|
45
- version = File.exist?('VERSION') ? File.read('VERSION') : ""
46
-
47
- rdoc.rdoc_dir = 'rdoc'
48
- rdoc.title = "leaderboard #{version}"
49
- rdoc.rdoc_files.include('README*')
50
- rdoc.rdoc_files.include('lib/**/*.rb')
51
- end
52
-
53
12
  REDIS_DIR = File.expand_path(File.join("..", "test"), __FILE__)
54
13
  REDIS_CNF = File.join(REDIS_DIR, "test.conf")
55
14
  REDIS_PID = File.join(REDIS_DIR, "db", "redis.pid")
@@ -58,7 +17,7 @@ REDIS_LOCATION = ENV['REDIS_LOCATION']
58
17
  task :default => :run
59
18
 
60
19
  desc "Run tests and manage server start/stop"
61
- task :run => [:start, :test_rubies, :stop]
20
+ task :run => [:start, :test, :stop]
62
21
 
63
22
  desc "Run rcov and manage server start/stop"
64
23
  task :rcoverage => [:start, :rcov, :stop]
@@ -89,5 +48,7 @@ task :stop do
89
48
  end
90
49
 
91
50
  task :test_rubies do
92
- system "rvm 1.8.7@leaderboard_gem,1.9.2@leaderboard_gem do rake test"
51
+ Rake::Task['start'].execute
52
+ system "rvm 1.8.7@leaderboard_gem,1.9.2@leaderboard_gem,1.9.3@leaderboard_gem do rake test"
53
+ Rake::Task['stop'].execute
93
54
  end
data/leaderboard.gemspec CHANGED
@@ -1,69 +1,27 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
1
  # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+
4
+ require 'leaderboard/version'
5
5
 
6
6
  Gem::Specification.new do |s|
7
- s.name = "leaderboard"
8
- s.version = "2.0.1"
7
+ s.name = "leaderboard"
8
+ s.version = Leaderboard::VERSION.dup
9
+ s.authors = ["David Czarnecki"]
10
+ s.email = ["dczarnecki@agoragames.com"]
11
+ s.homepage = "https://github.com/agoragames/leaderboard"
12
+ s.summary = %q{Leaderboards backed by Redis in Ruby}
13
+ s.description = %q{Leaderboards backed by Redis in Ruby}
9
14
 
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["David Czarnecki"]
12
- s.date = "2011-11-07"
13
- s.description = "Leaderboards backed by Redis in Ruby"
14
- s.email = "dczarnecki@agoragames.com"
15
- s.extra_rdoc_files = [
16
- "LICENSE.txt",
17
- "README.rdoc"
18
- ]
19
- s.files = [
20
- ".document",
21
- ".rvmrc",
22
- "CHANGELOG.markdown",
23
- "Gemfile",
24
- "LICENSE.txt",
25
- "README.rdoc",
26
- "Rakefile",
27
- "VERSION",
28
- "leaderboard.gemspec",
29
- "lib/leaderboard.rb",
30
- "test/db/.gitkeep",
31
- "test/helper.rb",
32
- "test/test.conf",
33
- "test/test_leaderboard.rb"
34
- ]
35
- s.homepage = "http://github.com/agoragames/leaderboard"
36
- s.licenses = ["MIT"]
37
- s.require_paths = ["lib"]
38
- s.rubygems_version = "1.8.11"
39
- s.summary = "Leaderboards backed by Redis in Ruby"
40
- s.test_files = [
41
- "test/helper.rb",
42
- "test/test_leaderboard.rb"
43
- ]
15
+ s.rubyforge_project = "leaderboard"
44
16
 
45
- if s.respond_to? :specification_version then
46
- s.specification_version = 3
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
47
21
 
48
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
49
- s.add_runtime_dependency(%q<redis>, [">= 0"])
50
- s.add_development_dependency(%q<bundler>, [">= 0"])
51
- s.add_development_dependency(%q<jeweler>, [">= 0"])
52
- s.add_development_dependency(%q<rcov>, [">= 0"])
53
- s.add_development_dependency(%q<rdoc>, [">= 0"])
54
- else
55
- s.add_dependency(%q<redis>, [">= 0"])
56
- s.add_dependency(%q<bundler>, [">= 0"])
57
- s.add_dependency(%q<jeweler>, [">= 0"])
58
- s.add_dependency(%q<rcov>, [">= 0"])
59
- s.add_dependency(%q<rdoc>, [">= 0"])
60
- end
61
- else
62
- s.add_dependency(%q<redis>, [">= 0"])
63
- s.add_dependency(%q<bundler>, [">= 0"])
64
- s.add_dependency(%q<jeweler>, [">= 0"])
65
- s.add_dependency(%q<rcov>, [">= 0"])
66
- s.add_dependency(%q<rdoc>, [">= 0"])
22
+ s.add_dependency('redis')
23
+ s.add_development_dependency('rake')
24
+ if '1.8.7'.eql?(RUBY_VERSION)
25
+ s.add_development_dependency('SystemTimer')
67
26
  end
68
27
  end
69
-
data/lib/leaderboard.rb CHANGED
@@ -1,15 +1,17 @@
1
1
  require 'redis'
2
+ require 'leaderboard/version'
2
3
 
3
4
  class Leaderboard
4
- VERSION = '2.0.1'.freeze
5
-
6
5
  DEFAULT_PAGE_SIZE = 25
6
+
7
7
  DEFAULT_OPTIONS = {
8
8
  :page_size => DEFAULT_PAGE_SIZE
9
9
  }
10
10
 
11
11
  DEFAULT_REDIS_HOST = 'localhost'
12
+
12
13
  DEFAULT_REDIS_PORT = 6379
14
+
13
15
  DEFAULT_REDIS_OPTIONS = {
14
16
  :host => DEFAULT_REDIS_HOST,
15
17
  :port => DEFAULT_REDIS_PORT
@@ -22,9 +24,22 @@ class Leaderboard
22
24
  :page_size => nil
23
25
  }
24
26
 
27
+ # Name of the leaderboard.
25
28
  attr_reader :leaderboard_name
29
+
30
+ # Page size to be used when paging through the leaderboard.
26
31
  attr_reader :page_size
27
32
 
33
+ # Create a new instance of a leaderboard.
34
+ #
35
+ # @param leaderboard [String] Name of the leaderboard.
36
+ # @param options [Hash] Options for the leaderboard such as +:page_size+.
37
+ # @param redis_options [Hash] Options for configuring Redis.
38
+ #
39
+ # Examples
40
+ #
41
+ # leaderboard = Leaderboard.new('highscores')
42
+ # leaderboard = Leaderboard.new('highscores', {:page_size => 10})
28
43
  def initialize(leaderboard_name, options = DEFAULT_OPTIONS, redis_options = DEFAULT_REDIS_OPTIONS)
29
44
  @leaderboard_name = leaderboard_name
30
45
 
@@ -40,78 +55,156 @@ class Leaderboard
40
55
 
41
56
  @redis_connection = Redis.new(redis_options) if @redis_connection.nil?
42
57
  end
43
-
58
+
59
+ # Set the page size to be used when paging through the leaderboard. This method
60
+ # also has the side effect of setting the page size to the +DEFAULT_PAGE_SIZE+
61
+ # if the page size is less than 1.
62
+ #
63
+ # @param page_size [int] Page size.
44
64
  def page_size=(page_size)
45
65
  page_size = DEFAULT_PAGE_SIZE if page_size < 1
46
66
 
47
67
  @page_size = page_size
48
68
  end
49
69
 
70
+ # Disconnect the Redis connection.
50
71
  def disconnect
51
72
  @redis_connection.client.disconnect
52
73
  end
53
74
 
75
+ # Delete the current leaderboard.
54
76
  def delete_leaderboard
55
77
  delete_leaderboard_named(@leaderboard_name)
56
78
  end
57
79
 
80
+ # Delete the named leaderboard.
81
+ #
82
+ # @param leaderboard_name [String] Name of the leaderboard.
58
83
  def delete_leaderboard_named(leaderboard_name)
59
84
  @redis_connection.del(leaderboard_name)
60
85
  end
61
86
 
87
+ # Rank a member in the leaderboard.
88
+ #
89
+ # @param member [String] Member name.
90
+ # @param score [float] Member score.
62
91
  def rank_member(member, score)
63
92
  rank_member_in(@leaderboard_name, member, score)
64
93
  end
65
94
 
95
+ # Rank a member in the named leaderboard.
96
+ #
97
+ # @param leaderboard_name [String] Name of the leaderboard.
98
+ # @param member [String] Member name.
99
+ # @param score [float] Member score.
66
100
  def rank_member_in(leaderboard_name, member, score)
67
101
  @redis_connection.zadd(leaderboard_name, score, member)
68
102
  end
69
103
 
104
+ # Remove a member from the leaderboard.
105
+ #
106
+ # @param member [String] Member name.
70
107
  def remove_member(member)
71
108
  remove_member_from(@leaderboard_name, member)
72
109
  end
73
110
 
111
+ # Remove a member from the named leaderboard.
112
+ #
113
+ # @param leaderboard_name [String] Name of the leaderboard.
114
+ # @param member [String] Member name.
74
115
  def remove_member_from(leaderboard_name, member)
75
116
  @redis_connection.zrem(leaderboard_name, member)
76
117
  end
77
118
 
119
+ # Retrieve the total number of members in the leaderboard.
120
+ #
121
+ # @return total number of members in the leaderboard.
78
122
  def total_members
79
123
  total_members_in(@leaderboard_name)
80
124
  end
81
125
 
126
+ # Retrieve the total number of members in the named leaderboard.
127
+ #
128
+ # @param leaderboard_name [String] Name of the leaderboard.
129
+ #
130
+ # @return the total number of members in the named leaderboard.
82
131
  def total_members_in(leaderboard_name)
83
132
  @redis_connection.zcard(leaderboard_name)
84
133
  end
85
134
 
135
+ # Retrieve the total number of pages in the leaderboard.
136
+ #
137
+ # @return the total number of pages in the leaderboard.
86
138
  def total_pages
87
139
  total_pages_in(@leaderboard_name)
88
140
  end
89
141
 
142
+ # Retrieve the total number of pages in the named leaderboard.
143
+ #
144
+ # @param leaderboard_name [String] Name of the leaderboard.
145
+ # @param page_size [int] Page size to be used when paging through the leaderboard.
146
+ #
147
+ # @return the total number of pages in the named leaderboard.
90
148
  def total_pages_in(leaderboard_name, page_size = nil)
91
149
  page_size ||= @page_size.to_f
92
150
  (total_members_in(leaderboard_name) / page_size.to_f).ceil
93
151
  end
94
152
 
153
+ # Retrieve the total members in a given score range from the leaderboard.
154
+ #
155
+ # @param min_score [float] Minimum score.
156
+ # @param max_score [float] Maximum score.
157
+ #
158
+ # @return the total members in a given score range from the leaderboard.
95
159
  def total_members_in_score_range(min_score, max_score)
96
160
  total_members_in_score_range_in(@leaderboard_name, min_score, max_score)
97
161
  end
98
162
 
163
+ # Retrieve the total members in a given score range from the named leaderboard.
164
+ #
165
+ # @param leaderboard_name Name of the leaderboard.
166
+ # @param min_score [float] Minimum score.
167
+ # @param max_score [float] Maximum score.
168
+ #
169
+ # @return the total members in a given score range from the named leaderboard.
99
170
  def total_members_in_score_range_in(leaderboard_name, min_score, max_score)
100
171
  @redis_connection.zcount(leaderboard_name, min_score, max_score)
101
172
  end
102
173
 
174
+ # Change the score for a member in the leaderboard by a score delta which can be positive or negative.
175
+ #
176
+ # @param member [String] Member name.
177
+ # @param delta [float] Score change.
103
178
  def change_score_for(member, delta)
104
179
  change_score_for_member_in(@leaderboard_name, member, delta)
105
180
  end
106
181
 
182
+ # Change the score for a member in the named leaderboard by a delta which can be positive or negative.
183
+ #
184
+ # @param leaderboard_name [String] Name of the leaderboard.
185
+ # @param member [String] Member name.
186
+ # @param delta [float] Score change.
107
187
  def change_score_for_member_in(leaderboard_name, member, delta)
108
188
  @redis_connection.zincrby(leaderboard_name, delta, member)
109
189
  end
110
190
 
191
+ # Retrieve the rank for a member in the leaderboard.
192
+ #
193
+ # @param member [String] Member name.
194
+ # @param use_zero_index_for_rank [boolean, false] If the returned rank should be 0-indexed.
195
+ #
196
+ # @return the rank for a member in the leaderboard.
111
197
  def rank_for(member, use_zero_index_for_rank = false)
112
198
  rank_for_in(@leaderboard_name, member, use_zero_index_for_rank)
113
199
  end
114
200
 
201
+ # Retrieve the rank for a member in the named leaderboard.
202
+ #
203
+ # @param leaderboard_name [String] Name of the leaderboard.
204
+ # @param member [String] Member name.
205
+ # @param use_zero_index_for_rank [boolean, false] If the returned rank should be 0-indexed.
206
+ #
207
+ # @return the rank for a member in the leaderboard.
115
208
  def rank_for_in(leaderboard_name, member, use_zero_index_for_rank = false)
116
209
  if use_zero_index_for_rank
117
210
  return @redis_connection.zrevrank(leaderboard_name, member)
@@ -120,26 +213,61 @@ class Leaderboard
120
213
  end
121
214
  end
122
215
 
216
+ # Retrieve the score for a member in the leaderboard.
217
+ #
218
+ # @param member Member name.
219
+ #
220
+ # @return the score for a member in the leaderboard.
123
221
  def score_for(member)
124
222
  score_for_in(@leaderboard_name, member)
125
223
  end
126
224
 
225
+ # Retrieve the score for a member in the named leaderboard.
226
+ #
227
+ # @param leaderboard_name Name of the leaderboard.
228
+ # @param member [String] Member name.
229
+ #
230
+ # @return the score for a member in the leaderboard.
127
231
  def score_for_in(leaderboard_name, member)
128
232
  @redis_connection.zscore(leaderboard_name, member).to_f
129
233
  end
130
234
 
235
+ # Check to see if a member exists in the leaderboard.
236
+ #
237
+ # @param member [String] Member name.
238
+ #
239
+ # @return +true+ if the member exists in the leaderboard, +false+ otherwise.
131
240
  def check_member?(member)
132
241
  check_member_in?(@leaderboard_name, member)
133
242
  end
134
243
 
244
+ # Check to see if a member exists in the named leaderboard.
245
+ #
246
+ # @param leaderboard_name [String] Name of the leaderboard.
247
+ # @param member [String] Member name.
248
+ #
249
+ # @return +true+ if the member exists in the named leaderboard, +false+ otherwise.
135
250
  def check_member_in?(leaderboard_name, member)
136
251
  !@redis_connection.zscore(leaderboard_name, member).nil?
137
252
  end
138
253
 
254
+ # Retrieve the score and rank for a member in the leaderboard.
255
+ #
256
+ # @param member [String] Member name.
257
+ # @param use_zero_index_for_rank [boolean, false] If the returned rank should be 0-indexed.
258
+ #
259
+ # @return the score and rank for a member in the leaderboard as a Hash.
139
260
  def score_and_rank_for(member, use_zero_index_for_rank = false)
140
261
  score_and_rank_for_in(@leaderboard_name, member, use_zero_index_for_rank)
141
262
  end
142
263
 
264
+ # Retrieve the score and rank for a member in the named leaderboard.
265
+ #
266
+ # @param leaderboard_name [String]Name of the leaderboard.
267
+ # @param member [String] Member name.
268
+ # @param use_zero_index_for_rank [boolean, false] If the returned rank should be 0-indexed.
269
+ #
270
+ # @return the score and rank for a member in the named leaderboard as a Hash.
143
271
  def score_and_rank_for_in(leaderboard_name, member, use_zero_index_for_rank = false)
144
272
  responses = @redis_connection.multi do |transaction|
145
273
  transaction.zscore(leaderboard_name, member)
@@ -154,19 +282,41 @@ class Leaderboard
154
282
  {:member => member, :score => responses[0], :rank => responses[1]}
155
283
  end
156
284
 
285
+ # Remove members from the leaderboard in a given score range.
286
+ #
287
+ # @param min_score [float] Minimum score.
288
+ # @param max_score [float] Maximum score.
157
289
  def remove_members_in_score_range(min_score, max_score)
158
290
  remove_members_in_score_range_in(@leaderboard_name, min_score, max_score)
159
291
  end
160
292
 
293
+ # Remove members from the named leaderboard in a given score range.
294
+ #
295
+ # @param leaderboard_name [String] Name of the leaderboard.
296
+ # @param min_score [float] Minimum score.
297
+ # @param max_score [float] Maximum score.
161
298
  def remove_members_in_score_range_in(leaderboard_name, min_score, max_score)
162
299
  @redis_connection.zremrangebyscore(leaderboard_name, min_score, max_score)
163
300
  end
164
301
 
302
+ # Retrieve the percentile for a member in the leaderboard.
303
+ #
304
+ # @param member [String] Member name.
305
+ #
306
+ # @return the percentile for a member in the leaderboard. Return +nil+ for a non-existent member.
165
307
  def percentile_for(member)
166
308
  percentile_for_in(@leaderboard_name, member)
167
309
  end
168
310
 
311
+ # Retrieve the percentile for a member in the named leaderboard.
312
+ #
313
+ # @param leaderboard_name [String] Name of the leaderboard.
314
+ # @param member [String] Member name.
315
+ #
316
+ # @return the percentile for a member in the named leaderboard.
169
317
  def percentile_for_in(leaderboard_name, member)
318
+ return nil unless check_member_in?(leaderboard_name, member)
319
+
170
320
  responses = @redis_connection.multi do |transaction|
171
321
  transaction.zcard(leaderboard_name)
172
322
  transaction.zrevrank(leaderboard_name, member)
@@ -175,10 +325,23 @@ class Leaderboard
175
325
  ((responses[0] - responses[1] - 1).to_f / responses[0].to_f * 100).ceil
176
326
  end
177
327
 
328
+ # Retrieve a page of leaders from the leaderboard.
329
+ #
330
+ # @param current_page [int] Page to retrieve from the leaderboard.
331
+ # @param options [Hash] Options to be used when retrieving the page from the leaderboard.
332
+ #
333
+ # @return a page of leaders from the leaderboard.
178
334
  def leaders(current_page, options = {})
179
335
  leaders_in(@leaderboard_name, current_page, options)
180
336
  end
181
337
 
338
+ # Retrieve a page of leaders from the named leaderboard.
339
+ #
340
+ # @param leaderboard_name [String] Name of the leaderboard.
341
+ # @param current_page [int] Page to retrieve from the named leaderboard.
342
+ # @param options [Hash] Options to be used when retrieving the page from the named leaderboard.
343
+ #
344
+ # @return a page of leaders from the named leaderboard.
182
345
  def leaders_in(leaderboard_name, current_page, options = {})
183
346
  leaderboard_options = DEFAULT_LEADERBOARD_REQUEST_OPTIONS.dup
184
347
  leaderboard_options.merge!(options)
@@ -210,15 +373,30 @@ class Leaderboard
210
373
  end
211
374
  end
212
375
 
376
+ # Retrieve a page of leaders from the leaderboard around a given member.
377
+ #
378
+ # @param member [String] Member name.
379
+ # @param options [Hash] Options to be used when retrieving the page from the leaderboard.
380
+ #
381
+ # @return a page of leaders from the leaderboard around a given member.
213
382
  def around_me(member, options = {})
214
383
  around_me_in(@leaderboard_name, member, options)
215
384
  end
216
385
 
386
+ # Retrieve a page of leaders from the named leaderboard around a given member.
387
+ #
388
+ # @param leaderboard_name [String] Name of the leaderboard.
389
+ # @param member [String] Member name.
390
+ # @param options [Hash] Options to be used when retrieving the page from the named leaderboard.
391
+ #
392
+ # @return a page of leaders from the named leaderboard around a given member. Returns an empty array for a non-existent member.
217
393
  def around_me_in(leaderboard_name, member, options = {})
218
394
  leaderboard_options = DEFAULT_LEADERBOARD_REQUEST_OPTIONS.dup
219
395
  leaderboard_options.merge!(options)
220
396
 
221
397
  reverse_rank_for_member = @redis_connection.zrevrank(leaderboard_name, member)
398
+
399
+ return [] unless reverse_rank_for_member
222
400
 
223
401
  page_size = validate_page_size(leaderboard_options[:page_size]) || @page_size
224
402
 
@@ -237,10 +415,23 @@ class Leaderboard
237
415
  end
238
416
  end
239
417
 
418
+ # Retrieve a page of leaders from the leaderboard for a given list of members.
419
+ #
420
+ # @param members [Array] Member names.
421
+ # @param options [Hash] Options to be used when retrieving the page from the leaderboard.
422
+ #
423
+ # @return a page of leaders from the leaderboard for a given list of members.
240
424
  def ranked_in_list(members, options = {})
241
425
  ranked_in_list_in(@leaderboard_name, members, options)
242
426
  end
243
427
 
428
+ # Retrieve a page of leaders from the named leaderboard for a given list of members.
429
+ #
430
+ # @param leaderboard_name [String] Name of the leaderboard.
431
+ # @param members [Array] Member names.
432
+ # @param options [Hash] Options to be used when retrieving the page from the named leaderboard.
433
+ #
434
+ # @return a page of leaders from the named leaderboard for a given list of members.
244
435
  def ranked_in_list_in(leaderboard_name, members, options = {})
245
436
  leaderboard_options = DEFAULT_LEADERBOARD_REQUEST_OPTIONS.dup
246
437
  leaderboard_options.merge!(options)
@@ -262,7 +453,7 @@ class Leaderboard
262
453
  if leaderboard_options[:use_zero_index_for_rank]
263
454
  data[:rank] = responses[index * 2]
264
455
  else
265
- data[:rank] = responses[index * 2] + 1
456
+ data[:rank] = responses[index * 2] + 1 rescue nil
266
457
  end
267
458
 
268
459
  data[:score] = responses[index * 2 + 1].to_f
@@ -274,7 +465,7 @@ class Leaderboard
274
465
  if leaderboard_options[:use_zero_index_for_rank]
275
466
  data[:rank] = responses[index]
276
467
  else
277
- data[:rank] = responses[index] + 1
468
+ data[:rank] = responses[index] + 1 rescue nil
278
469
  end
279
470
  end
280
471
  end
@@ -285,18 +476,31 @@ class Leaderboard
285
476
  ranks_for_members
286
477
  end
287
478
 
288
- # Merge leaderboards given by keys with this leaderboard into destination
479
+ # Merge leaderboards given by keys with this leaderboard into a named destination leaderboard.
480
+ #
481
+ # @param destination [String] Destination leaderboard name.
482
+ # @param keys [Array] Leaderboards to be merged with the current leaderboard.
483
+ # @param options [Hash] Options for merging the leaderboards.
289
484
  def merge_leaderboards(destination, keys, options = {:aggregate => :sum})
290
485
  @redis_connection.zunionstore(destination, keys.insert(0, @leaderboard_name), options)
291
486
  end
292
487
 
293
- # Intersect leaderboards given by keys with this leaderboard into destination
488
+ # Intersect leaderboards given by keys with this leaderboard into a named destination leaderboard.
489
+ #
490
+ # @param destination [String] Destination leaderboard name.
491
+ # @param keys [Array] Leaderboards to be merged with the current leaderboard.
492
+ # @param options [Hash] Options for intersecting the leaderboards.
294
493
  def intersect_leaderboards(destination, keys, options = {:aggregate => :sum})
295
494
  @redis_connection.zinterstore(destination, keys.insert(0, @leaderboard_name), options)
296
495
  end
297
496
 
298
497
  private
299
498
 
499
+ # Validate and return the page size. Returns the +DEFAULT_PAGE_SIZE+ if the page size is less than 1.
500
+ #
501
+ # @param page_size [int] Page size.
502
+ #
503
+ # @return the page size. Returns the +DEFAULT_PAGE_SIZE+ if the page size is less than 1.
300
504
  def validate_page_size(page_size)
301
505
  if page_size && page_size < 1
302
506
  page_size = DEFAULT_PAGE_SIZE
@@ -0,0 +1,3 @@
1
+ class Leaderboard
2
+ VERSION = '2.0.2'.freeze
3
+ end
@@ -0,0 +1,4 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+
4
+ require 'leaderboard'
@@ -1,4 +1,4 @@
1
- require 'helper'
1
+ require 'test_helper'
2
2
 
3
3
  class TestLeaderboard < Test::Unit::TestCase
4
4
  def setup
@@ -13,7 +13,7 @@ class TestLeaderboard < Test::Unit::TestCase
13
13
  end
14
14
 
15
15
  def test_version
16
- assert_equal '2.0.1', Leaderboard::VERSION
16
+ assert_equal '2.0.2', Leaderboard::VERSION
17
17
  end
18
18
 
19
19
  def test_initialize_with_defaults
@@ -418,7 +418,44 @@ class TestLeaderboard < Test::Unit::TestCase
418
418
  assert_equal 25, @leaderboard.percentile_for('member_4')
419
419
  assert_equal 92, @leaderboard.percentile_for('member_12')
420
420
  end
421
+
422
+ def test_around_me_for_invalid_member
423
+ rank_members_in_leaderboard(Leaderboard::DEFAULT_PAGE_SIZE * 3 + 1)
424
+
425
+ leaders_around_me = @leaderboard.around_me('jones', Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS.merge({:page_size => 3}))
426
+ assert_equal 0, leaders_around_me.size
427
+ end
428
+
429
+ def test_score_and_rank_for_non_existent_member
430
+ score_and_rank_for_member = @leaderboard.score_and_rank_for('jones')
421
431
 
432
+ assert_equal 'jones', score_and_rank_for_member[:member]
433
+ assert_equal 0.0, score_and_rank_for_member[:score]
434
+ assert_nil score_and_rank_for_member[:rank]
435
+ end
436
+
437
+ def test_ranked_in_list_for_non_existent_member
438
+ rank_members_in_leaderboard
439
+
440
+ members = ['member_1', 'member_5', 'jones']
441
+ ranked_members = @leaderboard.ranked_in_list(members)
442
+
443
+ assert_equal 3, ranked_members.size
444
+ assert_nil ranked_members[2][:rank]
445
+ end
446
+
447
+ def test_percentile_for_non_existent_member
448
+ percentile = @leaderboard.percentile_for('jones')
449
+
450
+ assert_nil percentile
451
+ end
452
+
453
+ def test_change_score_for_non_existent_member
454
+ assert_equal 0.0, @leaderboard.score_for('jones')
455
+ @leaderboard.change_score_for('jones', 5)
456
+ assert_equal 5.0, @leaderboard.score_for('jones')
457
+ end
458
+
422
459
  private
423
460
 
424
461
  def rank_members_in_leaderboard(members_to_add = 5)
metadata CHANGED
@@ -1,126 +1,92 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: leaderboard
3
- version: !ruby/object:Gem::Version
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.0.2
4
5
  prerelease:
5
- version: 2.0.1
6
6
  platform: ruby
7
- authors:
7
+ authors:
8
8
  - David Czarnecki
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
-
13
- date: 2011-11-07 00:00:00 Z
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
12
+ date: 2012-02-03 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
16
15
  name: redis
17
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: &70115900454580 !ruby/object:Gem::Requirement
18
17
  none: false
19
- requirements:
20
- - - ">="
21
- - !ruby/object:Gem::Version
22
- version: "0"
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
23
22
  type: :runtime
24
23
  prerelease: false
25
- version_requirements: *id001
26
- - !ruby/object:Gem::Dependency
27
- name: bundler
28
- requirement: &id002 !ruby/object:Gem::Requirement
24
+ version_requirements: *70115900454580
25
+ - !ruby/object:Gem::Dependency
26
+ name: rake
27
+ requirement: &70115900454140 !ruby/object:Gem::Requirement
29
28
  none: false
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: "0"
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
34
33
  type: :development
35
34
  prerelease: false
36
- version_requirements: *id002
37
- - !ruby/object:Gem::Dependency
38
- name: jeweler
39
- requirement: &id003 !ruby/object:Gem::Requirement
40
- none: false
41
- requirements:
42
- - - ">="
43
- - !ruby/object:Gem::Version
44
- version: "0"
45
- type: :development
46
- prerelease: false
47
- version_requirements: *id003
48
- - !ruby/object:Gem::Dependency
49
- name: rcov
50
- requirement: &id004 !ruby/object:Gem::Requirement
51
- none: false
52
- requirements:
53
- - - ">="
54
- - !ruby/object:Gem::Version
55
- version: "0"
56
- type: :development
57
- prerelease: false
58
- version_requirements: *id004
59
- - !ruby/object:Gem::Dependency
60
- name: rdoc
61
- requirement: &id005 !ruby/object:Gem::Requirement
62
- none: false
63
- requirements:
64
- - - ">="
65
- - !ruby/object:Gem::Version
66
- version: "0"
67
- type: :development
68
- prerelease: false
69
- version_requirements: *id005
35
+ version_requirements: *70115900454140
70
36
  description: Leaderboards backed by Redis in Ruby
71
- email: dczarnecki@agoragames.com
37
+ email:
38
+ - dczarnecki@agoragames.com
72
39
  executables: []
73
-
74
40
  extensions: []
75
-
76
- extra_rdoc_files:
77
- - LICENSE.txt
78
- - README.rdoc
79
- files:
41
+ extra_rdoc_files: []
42
+ files:
80
43
  - .document
44
+ - .gitignore
81
45
  - .rvmrc
82
46
  - CHANGELOG.markdown
83
47
  - Gemfile
84
48
  - LICENSE.txt
85
- - README.rdoc
49
+ - README.markdown
86
50
  - Rakefile
87
- - VERSION
88
51
  - leaderboard.gemspec
89
52
  - lib/leaderboard.rb
53
+ - lib/leaderboard/version.rb
90
54
  - test/db/.gitkeep
91
- - test/helper.rb
92
55
  - test/test.conf
56
+ - test/test_helper.rb
93
57
  - test/test_leaderboard.rb
94
- homepage: http://github.com/agoragames/leaderboard
95
- licenses:
96
- - MIT
58
+ homepage: https://github.com/agoragames/leaderboard
59
+ licenses: []
97
60
  post_install_message:
98
61
  rdoc_options: []
99
-
100
- require_paths:
62
+ require_paths:
101
63
  - lib
102
- required_ruby_version: !ruby/object:Gem::Requirement
64
+ required_ruby_version: !ruby/object:Gem::Requirement
103
65
  none: false
104
- requirements:
105
- - - ">="
106
- - !ruby/object:Gem::Version
107
- hash: 2535082084974160649
108
- segments:
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ segments:
109
71
  - 0
110
- version: "0"
111
- required_rubygems_version: !ruby/object:Gem::Requirement
72
+ hash: 822397651931818712
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
112
74
  none: false
113
- requirements:
114
- - - ">="
115
- - !ruby/object:Gem::Version
116
- version: "0"
75
+ requirements:
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ segments:
80
+ - 0
81
+ hash: 822397651931818712
117
82
  requirements: []
118
-
119
- rubyforge_project:
120
- rubygems_version: 1.8.11
83
+ rubyforge_project: leaderboard
84
+ rubygems_version: 1.8.10
121
85
  signing_key:
122
86
  specification_version: 3
123
87
  summary: Leaderboards backed by Redis in Ruby
124
- test_files:
125
- - test/helper.rb
88
+ test_files:
89
+ - test/db/.gitkeep
90
+ - test/test.conf
91
+ - test/test_helper.rb
126
92
  - test/test_leaderboard.rb
data/VERSION DELETED
@@ -1 +0,0 @@
1
- 2.0.1
data/test/helper.rb DELETED
@@ -1,17 +0,0 @@
1
- require 'rubygems'
2
- require 'bundler'
3
- begin
4
- Bundler.setup(:default, :development)
5
- rescue Bundler::BundlerError => e
6
- $stderr.puts e.message
7
- $stderr.puts "Run `bundle install` to install missing gems"
8
- exit e.status_code
9
- end
10
- require 'test/unit'
11
-
12
- $LOAD_PATH.unshift(File.dirname(__FILE__))
13
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
14
- require 'leaderboard'
15
-
16
- class Test::Unit::TestCase
17
- end