Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 245 lines (186 sloc) 11.68 kb
2a0b21d @czarneckid 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
9 Install the gem:
10
11 ```ruby
12 gem "leaderboard"
13 ```
14
15 Make sure your redis server is running! Redis configuration is outside the scope of this README, but
16 check out the Redis documentation, http://redis.io/documentation.
17
18 ## Compatibility
19
20 The gem has been built and tested under Ruby 1.8.7, Ruby 1.9.2 and Ruby 1.9.3
21
22 ## Usage
23
24 Create a new leaderboard or attach to an existing leaderboard named 'highscores':
25
26 ```ruby
27 ruby-1.9.2-p180 :002 > highscore_lb = Leaderboard.new('highscores')
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 ```
30
31 If you need to pass in options for Redis, you can do this in the initializer:
32
33 ```ruby
34 ruby-1.9.2-p180 :007 > redis_options = {:host => 'localhost', :port => 6379, :db => 1}
35 => {:host=>"localhost", :port=>6379, :db=>1}
36 ruby-1.9.2-p180 :008 > highscore_lb = Leaderboard.new('highscores', Leaderboard::DEFAULT_OPTIONS, redis_options)
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 ```
39
40 You can pass in an existing connection to Redis using :redis_connection in the Redis options hash:
41
42 ```ruby
43 ruby-1.9.2-p180 :009 > redis = Redis.new
44 => #<Redis client v2.2.2 connected to redis://127.0.0.1:6379/0 (Redis v2.2.5)>
45 ruby-1.9.2-p180 :010 > redis_options = {:redis_connection => redis}
46 => {:redis_connection=>#<Redis client v2.2.2 connected to redis://127.0.0.1:6379/0 (Redis v2.2.5)>}
47 ruby-1.9.2-p180 :011 > highscore_lb = Leaderboard.new('highscores', Leaderboard::DEFAULT_OPTIONS, redis_options)
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 ```
50
51 You can set the page size to something other than the default page size (25):
52
53 ```ruby
54 ruby-1.9.2-p180 :012 > highscore_lb.page_size = 5
55 => 5
56 ruby-1.9.2-p180 :013 > highscore_lb
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 ```
59
60 Add members to your leaderboard using rank_member:
61
62 ```ruby
63 ruby-1.9.2-p180 :014 > 1.upto(10) do |index|
64 ruby-1.9.2-p180 :015 > highscore_lb.rank_member("member_#{index}", index)
65 ruby-1.9.2-p180 :016?> end
66 => 1
67 ```
68
69 You can call rank_member with the same member and the leaderboard will be updated automatically.
70
71 Get some information about your leaderboard:
72
73 ```ruby
74 ruby-1.9.2-p180 :020 > highscore_lb.total_members
75 => 10
76 ruby-1.9.2-p180 :021 > highscore_lb.total_pages
77 => 1
78 ```
79
80 Get some information about a specific member(s) in the leaderboard:
81
82 ```ruby
83 ruby-1.9.2-p180 :022 > highscore_lb.score_for('member_4')
84 => 4.0
85 ruby-1.9.2-p180 :023 > highscore_lb.rank_for('member_4')
86 => 7
87 ruby-1.9.2-p180 :024 > highscore_lb.rank_for('member_10')
88 => 1
89 ```
90
91 Get page 1 in the leaderboard:
92
93 ```ruby
94 ruby-1.9.2-p180 :025 > highscore_lb.leaders(1)
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 ```
97
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`.
99 Below is an example of retrieving the first page in the leaderboard without ranks:
100
101 ```ruby
102 ruby-1.9.2-p180 :026 > highscore_lb.leaders(1, :with_rank => false)
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 ```
105
106 Below is an example of retrieving the first page in the leaderboard without scores or ranks:
107
108 ```ruby
109 ruby-1.9.2-p180 :028 > highscore_lb.leaders(1, :with_scores => false, :with_rank => false)
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 ```
112
113 Add more members to your leaderboard:
114
115 ```ruby
116 ruby-1.9.2-p180 :029 > 50.upto(95) do |index|
117 ruby-1.9.2-p180 :030 > highscore_lb.rank_member("member_#{index}", index)
118 ruby-1.9.2-p180 :031?> end
119 => 50
120 ruby-1.9.2-p180 :032 > highscore_lb.total_pages
121 => 3
122 ```
123
124 Get an "Around Me" leaderboard for a member:
125
126 ```ruby
127 ruby-1.9.2-p180 :033 > highscore_lb.around_me('member_53')
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 ```
130
131 Get rank and score for an arbitrary list of members (e.g. friends):
132
133 ```ruby
134 ruby-1.9.2-p180 :034 > highscore_lb.ranked_in_list(['member_1', 'member_62', 'member_67'])
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}]
136 ```
137
138 ### Other useful methods
139
c70a4d9 @czarneckid Updating README.markdown
czarneckid authored
140 ```
2a0b21d @czarneckid Adding markdown-ized README
czarneckid authored
141 delete_leaderboard: Delete the current leaderboard
142 remove_member(member): Remove a member from the leaderboard
143 total_members: Total # of members in the leaderboard
144 total_pages: Total # of pages in the leaderboard given the leaderboard's page_size
145 total_members_in_score_range(min_score, max_score): Count the number of members within a score range in the leaderboard
146 change_score_for(member, delta): Change the score for a member by some amount delta (delta could be positive or negative)
147 rank_for(member): Retrieve the rank for a given member in the leaderboard
148 score_for(member): Retrieve the score for a given member in the leaderboard
149 check_member?(member): Check to see whether member is in the leaderboard
150 score_and_rank_for(member): Retrieve the score and rank for a member in a single call
151 remove_members_in_score_range(min_score, max_score): Remove members from the leaderboard within a score range
152 percentile_for(member): Calculate the percentile for a given member
153 merge_leaderboards(destination, keys, options = {:aggregate => :min}): Merge leaderboards given by keys with this leaderboard into destination
154 intersect_leaderboards(destination, keys, options = {:aggregate => :min}): Intersect leaderboards given by keys with this leaderboard into destination
c70a4d9 @czarneckid Updating README.markdown
czarneckid authored
155 ```
2a0b21d @czarneckid Adding markdown-ized README
czarneckid authored
156
e551639 @czarneckid Updating README
czarneckid authored
157 Check the [online documentation](http://rubydoc.info/github/agoragames/leaderboard/master/frames) for more detail on each method.
2a0b21d @czarneckid Adding markdown-ized README
czarneckid authored
158
159 ## Performance Metrics
160
161 10 million sequential scores insert:
162
163 ```ruby
164 ruby-1.9.2-p180 :003 > highscore_lb = Leaderboard.new('highscores')
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)>>
166 ruby-1.9.2-p180 :004 > insert_time = Benchmark.measure do
167 ruby-1.9.2-p180 :005 > 1.upto(10000000) do |index|
168 ruby-1.9.2-p180 :006 > highscore_lb.rank_member("member_#{index}", index)
169 ruby-1.9.2-p180 :007?> end
170 ruby-1.9.2-p180 :008?> end
171 => 323.070000 148.560000 471.630000 (942.068307)
172 ```
173
174 Average time to request an arbitrary page from the leaderboard:
175
176 ```ruby
177 ruby-1.9.2-p180 :009 > requests_to_make = 50000
178 => 50000
179 ruby-1.9.2-p180 :010 > lb_request_time = 0
180 => 0
181 ruby-1.9.2-p180 :011 > 1.upto(requests_to_make) do
182 ruby-1.9.2-p180 :012 > lb_request_time += Benchmark.measure do
183 ruby-1.9.2-p180 :013 > highscore_lb.leaders(rand(highscore_lb.total_pages))
184 ruby-1.9.2-p180 :014?> end.total
185 ruby-1.9.2-p180 :015?> end
186 => 1
187 ruby-1.9.2-p180 :016 > p lb_request_time / requests_to_make
188 0.001513999999999998
189 => 0.001513999999999998
190 ```
191
192 10 million random scores insert:
193
194 ```ruby
195 ruby-1.9.2-p180 :018 > insert_time = Benchmark.measure do
196 ruby-1.9.2-p180 :019 > 1.upto(10000000) do |index|
197 ruby-1.9.2-p180 :020 > highscore_lb.rank_member("member_#{index}", rand(50000000))
198 ruby-1.9.2-p180 :021?> end
199 ruby-1.9.2-p180 :022?> end
200 => 338.480000 155.200000 493.680000 (2188.702475)
201 ```
202
203 Average time to request an arbitrary page from the leaderboard:
204
205 ```ruby
206 ruby-1.9.2-p180 :007 > 1.upto(requests_to_make) do
207 ruby-1.9.2-p180 :008 > lb_request_time += Benchmark.measure do
208 ruby-1.9.2-p180 :009 > highscore_lb.leaders(rand(highscore_lb.total_pages))
209 ruby-1.9.2-p180 :010?> end.total
210 ruby-1.9.2-p180 :011?> end
211 => 1
212 ruby-1.9.2-p180 :012 > p lb_request_time / requests_to_make
213 0.0014615999999999531
214 => 0.0014615999999999531
215 ```
216
217 ## Future Ideas
218
219 * Bulk insert
220
221 ## Ports
222
223 The following ports have been made of the leaderboard gem.
224
225 * Java: https://github.com/agoragames/java-leaderboard
226 * NodeJS: https://github.com/omork/node-leaderboard
227 * PHP: https://github.com/agoragames/php-leaderboard
228 * Python: https://github.com/agoragames/python-leaderboard
229 * Scala: https://github.com/agoragames/scala-leaderboard
230
231 ## Contributing to leaderboard
232
233 * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
234 * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
235 * Fork the project
236 * Start a feature/bugfix branch
237 * Commit and push until you are happy with your contribution
238 * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
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.
240
241 ## Copyright
242
243 Copyright (c) 2011 David Czarnecki. See LICENSE.txt for further details.
244
Something went wrong with that request. Please try again.