Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 321 lines (243 sloc) 12.84 kb
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
1 # leaderboard
2
3 Leaderboards backed by Redis in Ruby, http://redis.io.
4
5 Builds off ideas proposed in http://blog.agoragames.com/2011/01/01/creating-high-score-tables-leaderboards-using-redis/.
6
7 ## Installation
8
d6aaf56 David Czarnecki Updating README for installation
czarneckid authored
9 `gem install leaderboard`
10
11 or in your `Gemfile`
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
12
13 ```ruby
d6aaf56 David Czarnecki Updating README for installation
czarneckid authored
14 gem 'leaderboard'
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
15 ```
16
17 Make sure your redis server is running! Redis configuration is outside the scope of this README, but
18 check out the Redis documentation, http://redis.io/documentation.
19
20 ## Compatibility
21
22 The gem has been built and tested under Ruby 1.8.7, Ruby 1.9.2 and Ruby 1.9.3
23
24 ## Usage
25
26 Create a new leaderboard or attach to an existing leaderboard named 'highscores':
27
28 ```ruby
e5fcd73 David Czarnecki Removing irb output
czarneckid authored
29 highscore_lb = Leaderboard.new('highscores')
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
30 => #<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)>>
31 ```
32
33 If you need to pass in options for Redis, you can do this in the initializer:
34
35 ```ruby
e5fcd73 David Czarnecki Removing irb output
czarneckid authored
36 redis_options = {:host => 'localhost', :port => 6379, :db => 1}
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
37 => {:host=>"localhost", :port=>6379, :db=>1}
e5fcd73 David Czarnecki Removing irb output
czarneckid authored
38 highscore_lb = Leaderboard.new('highscores', Leaderboard::DEFAULT_OPTIONS, redis_options)
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
39 => #<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)>>
40 ```
41
0caed3e David Czarnecki Version 2.0.3
czarneckid authored
42 The `Leaderboard::DEFAULT_OPTIONS` are as follows:
43
44 ```ruby
45 DEFAULT_OPTIONS = {
46 :page_size => DEFAULT_PAGE_SIZE,
47 :reverse => false
48 }
49 ```
50
0bb74a1 David Czarnecki Updating README
czarneckid authored
51 The `DEFAULT_PAGE_SIZE` is 25.
2c0700b David Czarnecki Reverse option
czarneckid authored
52
53 You would use the option, `:reverse => true`, if you wanted a leaderboard sorted from lowest-to-highest score. You
54 may also set the `reverse` option on a leaderboard after you have created a new instance of a leaderboard.
0caed3e David Czarnecki Version 2.0.3
czarneckid authored
55
0bb74a1 David Czarnecki Updating README
czarneckid authored
56 You can pass in an existing connection to Redis using `:redis_connection` in the Redis options hash:
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
57
58 ```ruby
e5fcd73 David Czarnecki Removing irb output
czarneckid authored
59 redis = Redis.new
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
60 => #<Redis client v2.2.2 connected to redis://127.0.0.1:6379/0 (Redis v2.2.5)>
e5fcd73 David Czarnecki Removing irb output
czarneckid authored
61 redis_options = {:redis_connection => redis}
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
62 => {:redis_connection=>#<Redis client v2.2.2 connected to redis://127.0.0.1:6379/0 (Redis v2.2.5)>}
e5fcd73 David Czarnecki Removing irb output
czarneckid authored
63 highscore_lb = Leaderboard.new('highscores', Leaderboard::DEFAULT_OPTIONS, redis_options)
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
64 => #<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)>>
65 ```
66
01adbb7 John C. Bland II Updated documentation for details on instantiating multiple leaderboards...
johncblandii authored
67 To use the same connection for multiple leaderboards, reset the options hash before instantiating more leaderboards:
68
69 ```ruby
70 redis = Redis.new
71 => #<Redis client v2.2.2 connected to redis://127.0.0.1:6379/0 (Redis v2.2.5)>
72 redis_options = {:redis_connection => redis}
73 => {:redis_connection=>#<Redis client v2.2.2 connected to redis://127.0.0.1:6379/0 (Redis v2.2.5)>}
74 highscore_lb = Leaderboard.new('highscores', Leaderboard::DEFAULT_OPTIONS, redis_options)
75 redis_options = {:redis_connection => redis}
76 other_highscore_lb = Leaderboard.new('other_highscores', Leaderboard::DEFAULT_OPTIONS, redis_options)
77 ```
78
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
79 You can set the page size to something other than the default page size (25):
80
81 ```ruby
e5fcd73 David Czarnecki Removing irb output
czarneckid authored
82 highscore_lb.page_size = 5
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
83 => 5
e5fcd73 David Czarnecki Removing irb output
czarneckid authored
84 highscore_lb
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
85 => #<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)>>
86 ```
01adbb7 John C. Bland II Updated documentation for details on instantiating multiple leaderboards...
johncblandii authored
87
0bb74a1 David Czarnecki Updating README
czarneckid authored
88 Add members to your leaderboard using `rank_member`:
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
89
90 ```ruby
e5fcd73 David Czarnecki Removing irb output
czarneckid authored
91 1.upto(10) do |index|
92 highscore_lb.rank_member("member_#{index}", index)
93 end
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
94 => 1
95 ```
96
0bb74a1 David Czarnecki Updating README
czarneckid authored
97 You can call `rank_member` with the same member and the leaderboard will be updated automatically.
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
98
99 Get some information about your leaderboard:
100
101 ```ruby
e5fcd73 David Czarnecki Removing irb output
czarneckid authored
102 highscore_lb.total_members
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
103 => 10
e5fcd73 David Czarnecki Removing irb output
czarneckid authored
104 highscore_lb.total_pages
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
105 => 1
106 ```
107
108 Get some information about a specific member(s) in the leaderboard:
109
110 ```ruby
e5fcd73 David Czarnecki Removing irb output
czarneckid authored
111 highscore_lb.score_for('member_4')
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
112 => 4.0
e5fcd73 David Czarnecki Removing irb output
czarneckid authored
113 highscore_lb.rank_for('member_4')
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
114 => 7
e5fcd73 David Czarnecki Removing irb output
czarneckid authored
115 highscore_lb.rank_for('member_10')
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
116 => 1
117 ```
118
119 Get page 1 in the leaderboard:
120
121 ```ruby
e5fcd73 David Czarnecki Removing irb output
czarneckid authored
122 highscore_lb.leaders(1)
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
123 => [{: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}]
124 ```
125
126 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`.
127 Below is an example of retrieving the first page in the leaderboard without ranks:
128
129 ```ruby
e5fcd73 David Czarnecki Removing irb output
czarneckid authored
130 highscore_lb.leaders(1, :with_rank => false)
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
131 => [{: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}]
132 ```
133
134 Below is an example of retrieving the first page in the leaderboard without scores or ranks:
135
136 ```ruby
e5fcd73 David Czarnecki Removing irb output
czarneckid authored
137 highscore_lb.leaders(1, :with_scores => false, :with_rank => false)
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
138 => [{: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"}]
139 ```
140
141 Add more members to your leaderboard:
142
143 ```ruby
e5fcd73 David Czarnecki Removing irb output
czarneckid authored
144 50.upto(95) do |index|
145 highscore_lb.rank_member("member_#{index}", index)
146 end
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
147 => 50
e5fcd73 David Czarnecki Removing irb output
czarneckid authored
148 highscore_lb.total_pages
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
149 => 3
150 ```
151
0bb74a1 David Czarnecki Updating README
czarneckid authored
152 Get an "Around Me" leaderboard page for a given member, which pulls members above and below the given member:
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
153
154 ```ruby
e5fcd73 David Czarnecki Removing irb output
czarneckid authored
155 highscore_lb.around_me('member_53')
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
156 => [{: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}]
157 ```
158
0bb74a1 David Czarnecki Updating README
czarneckid authored
159 Get rank and score for an arbitrary list of members (e.g. friends) from the leaderboard:
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
160
161 ```ruby
e5fcd73 David Czarnecki Removing irb output
czarneckid authored
162 highscore_lb.ranked_in_list(['member_1', 'member_62', 'member_67'])
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
163 => [{:member=>"member_1", :rank=>56, :score=>1.0}, {:member=>"member_62", :rank=>34, :score=>62.0}, {:member=>"member_67", :rank=>29, :score=>67.0}]
83b50ae David Czarnecki Updating README and CHANGELOG
czarneckid authored
164 ```
165
166 Insert multiple data items for members and their associated scores:
167
168 As a splat:
169
170 ```ruby
171 highscore_lb.rank_members('member_1', 1, 'member_5', 5, 'member_10', 10)
172 ```
173
174 Or as an array:
175
176 ```ruby
177 highscore_lb.rank_members(['member_1', 1, 'member_5', 5, 'member_10', 10])
178 ```
179
180 Use this method to do bulk insert of data, but be mindful of the amount of data you are inserting since a single transaction can get quite large.
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
181
182 ### Other useful methods
183
c70a4d9 David Czarnecki Updating README.markdown
czarneckid authored
184 ```
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
185 delete_leaderboard: Delete the current leaderboard
186 remove_member(member): Remove a member from the leaderboard
187 total_members: Total # of members in the leaderboard
188 total_pages: Total # of pages in the leaderboard given the leaderboard's page_size
189 total_members_in_score_range(min_score, max_score): Count the number of members within a score range in the leaderboard
190 change_score_for(member, delta): Change the score for a member by some amount delta (delta could be positive or negative)
191 rank_for(member): Retrieve the rank for a given member in the leaderboard
192 score_for(member): Retrieve the score for a given member in the leaderboard
193 check_member?(member): Check to see whether member is in the leaderboard
194 score_and_rank_for(member): Retrieve the score and rank for a member in a single call
195 remove_members_in_score_range(min_score, max_score): Remove members from the leaderboard within a score range
196 percentile_for(member): Calculate the percentile for a given member
1628827 David Czarnecki page_for
czarneckid authored
197 page_for(member, page_size): Determine the page where a member falls in the leaderboard
83b50ae David Czarnecki Updating README and CHANGELOG
czarneckid authored
198 rank_members(members_and_scores): Rank an array of members in the leaderboard where you can call via (member_name, score) or pass in an array of [member_name, score]
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
199 merge_leaderboards(destination, keys, options = {:aggregate => :min}): Merge leaderboards given by keys with this leaderboard into destination
200 intersect_leaderboards(destination, keys, options = {:aggregate => :min}): Intersect leaderboards given by keys with this leaderboard into destination
c70a4d9 David Czarnecki Updating README.markdown
czarneckid authored
201 ```
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
202
e551639 David Czarnecki Updating README
czarneckid authored
203 Check the [online documentation](http://rubydoc.info/github/agoragames/leaderboard/master/frames) for more detail on each method.
01adbb7 John C. Bland II Updated documentation for details on instantiating multiple leaderboards...
johncblandii authored
204
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
205 ## Performance Metrics
206
207 10 million sequential scores insert:
208
209 ```ruby
e5fcd73 David Czarnecki Removing irb output
czarneckid authored
210 highscore_lb = Leaderboard.new('highscores')
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
211 => #<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)>>
e5fcd73 David Czarnecki Removing irb output
czarneckid authored
212
213 insert_time = Benchmark.measure do
214 1.upto(10000000) do |index|
215 highscore_lb.rank_member("member_#{index}", index)
216 end
217 end
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
218 => 323.070000 148.560000 471.630000 (942.068307)
219 ```
220
221 Average time to request an arbitrary page from the leaderboard:
222
223 ```ruby
e5fcd73 David Czarnecki Removing irb output
czarneckid authored
224 requests_to_make = 50000
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
225 => 50000
e5fcd73 David Czarnecki Removing irb output
czarneckid authored
226 lb_request_time = 0
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
227 => 0
e5fcd73 David Czarnecki Removing irb output
czarneckid authored
228 1.upto(requests_to_make) do
229 lb_request_time += Benchmark.measure do
230 highscore_lb.leaders(rand(highscore_lb.total_pages))
231 end.total
232 end
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
233 => 1
e5fcd73 David Czarnecki Removing irb output
czarneckid authored
234 p lb_request_time / requests_to_make
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
235 0.001513999999999998
236 => 0.001513999999999998
237 ```
238
239 10 million random scores insert:
240
241 ```ruby
e5fcd73 David Czarnecki Removing irb output
czarneckid authored
242 insert_time = Benchmark.measure do
243 1.upto(10000000) do |index|
244 highscore_lb.rank_member("member_#{index}", rand(50000000))
245 end
246 end
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
247 => 338.480000 155.200000 493.680000 (2188.702475)
248 ```
249
250 Average time to request an arbitrary page from the leaderboard:
251
252 ```ruby
e5fcd73 David Czarnecki Removing irb output
czarneckid authored
253 1.upto(requests_to_make) do
254 lb_request_time += Benchmark.measure do
255 highscore_lb.leaders(rand(highscore_lb.total_pages))
256 end.total
257 end
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
258 => 1
e5fcd73 David Czarnecki Removing irb output
czarneckid authored
259 p lb_request_time / requests_to_make
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
260 0.0014615999999999531
261 => 0.0014615999999999531
262 ```
263
aeae313 David Czarnecki Updating README with performance benchmarks of ranking members individua...
czarneckid authored
264 Bulk insert performance:
265
266 Ranking individual members:
267
268 ```ruby
e5fcd73 David Czarnecki Removing irb output
czarneckid authored
269 insert_time = Benchmark.measure do
270 1.upto(1000000) do |index|
271 highscore_lb.rank_member("member_#{index}", index)
272 end
273 end
aeae313 David Czarnecki Updating README with performance benchmarks of ranking members individua...
czarneckid authored
274 => 29.340000 15.050000 44.390000 ( 81.673507)
275 ```
276
277 Ranking multiple members at once:
278
279 ```ruby
e5fcd73 David Czarnecki Removing irb output
czarneckid authored
280 member_data = []
aeae313 David Czarnecki Updating README with performance benchmarks of ranking members individua...
czarneckid authored
281 => []
e5fcd73 David Czarnecki Removing irb output
czarneckid authored
282 1.upto(1000000) do |index|
283 member_data << "member_#{index}"
284 member_data << index
285 end
aeae313 David Czarnecki Updating README with performance benchmarks of ranking members individua...
czarneckid authored
286 => 1
e5fcd73 David Czarnecki Removing irb output
czarneckid authored
287 insert_time = Benchmark.measure do
288 highscore_lb.rank_members(member_data)
289 end
aeae313 David Czarnecki Updating README with performance benchmarks of ranking members individua...
czarneckid authored
290 => 22.390000 6.380000 28.770000 ( 31.144027)
291 ```
292
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
293 ## Future Ideas
294
e07ea14 David Czarnecki Add rank_members and rank_members_in methods
czarneckid authored
295 * Ideas?
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
296
297 ## Ports
298
299 The following ports have been made of the leaderboard gem.
300
301 * Java: https://github.com/agoragames/java-leaderboard
302 * NodeJS: https://github.com/omork/node-leaderboard
303 * PHP: https://github.com/agoragames/php-leaderboard
304 * Python: https://github.com/agoragames/python-leaderboard
305 * Scala: https://github.com/agoragames/scala-leaderboard
306
307 ## Contributing to leaderboard
308
309 * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
310 * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
311 * Fork the project
312 * Start a feature/bugfix branch
313 * Commit and push until you are happy with your contribution
314 * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
315 * 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.
316
317 ## Copyright
318
0caed3e David Czarnecki Version 2.0.3
czarneckid authored
319 Copyright (c) 2011-2012 David Czarnecki. See LICENSE.txt for further details.
2a0b21d David Czarnecki Adding markdown-ized README
czarneckid authored
320
Something went wrong with that request. Please try again.