leaderboard 2.0.5 → 2.0.6

Sign up to get free protection for your applications and to get access to all the features.
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format nested
data/CHANGELOG.markdown CHANGED
@@ -1,5 +1,9 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## leaderboard 2.0.6 (2012-04-26)
4
+
5
+ * Added accessor for +reverse+ option so that you can set reverse after creating a leaderboard to see results in either highest-to-lowest or lowest-to-highest order.
6
+
3
7
  ## leaderboard 2.0.5 (2012-03-14)
4
8
 
5
9
  * Added `rank_members(members_and_scores)` and `rank_members_in(leaderboard_name, members_and_scores)` allowing you to pass in some variable number of `member_name, score` and so on or an actual array of those data items. 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.
data/README.markdown CHANGED
@@ -26,16 +26,16 @@ The gem has been built and tested under Ruby 1.8.7, Ruby 1.9.2 and Ruby 1.9.3
26
26
  Create a new leaderboard or attach to an existing leaderboard named 'highscores':
27
27
 
28
28
  ```ruby
29
- ruby-1.9.2-p180 :002 > highscore_lb = Leaderboard.new('highscores')
29
+ highscore_lb = Leaderboard.new('highscores')
30
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
31
  ```
32
32
 
33
33
  If you need to pass in options for Redis, you can do this in the initializer:
34
34
 
35
35
  ```ruby
36
- ruby-1.9.2-p180 :007 > redis_options = {:host => 'localhost', :port => 6379, :db => 1}
36
+ redis_options = {:host => 'localhost', :port => 6379, :db => 1}
37
37
  => {:host=>"localhost", :port=>6379, :db=>1}
38
- ruby-1.9.2-p180 :008 > highscore_lb = Leaderboard.new('highscores', Leaderboard::DEFAULT_OPTIONS, redis_options)
38
+ highscore_lb = Leaderboard.new('highscores', Leaderboard::DEFAULT_OPTIONS, redis_options)
39
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
40
  ```
41
41
 
@@ -48,63 +48,66 @@ DEFAULT_OPTIONS = {
48
48
  }
49
49
  ```
50
50
 
51
- You would use the option, `:reverse => true`, if you wanted a leaderboard sorted from lowest to highest score.
51
+ The `DEFAULT_PAGE_SIZE` is 25.
52
52
 
53
- You can pass in an existing connection to Redis using :redis_connection in the Redis options hash:
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.
55
+
56
+ You can pass in an existing connection to Redis using `:redis_connection` in the Redis options hash:
54
57
 
55
58
  ```ruby
56
- ruby-1.9.2-p180 :009 > redis = Redis.new
59
+ redis = Redis.new
57
60
  => #<Redis client v2.2.2 connected to redis://127.0.0.1:6379/0 (Redis v2.2.5)>
58
- ruby-1.9.2-p180 :010 > redis_options = {:redis_connection => redis}
61
+ redis_options = {:redis_connection => redis}
59
62
  => {:redis_connection=>#<Redis client v2.2.2 connected to redis://127.0.0.1:6379/0 (Redis v2.2.5)>}
60
- ruby-1.9.2-p180 :011 > highscore_lb = Leaderboard.new('highscores', Leaderboard::DEFAULT_OPTIONS, redis_options)
63
+ highscore_lb = Leaderboard.new('highscores', Leaderboard::DEFAULT_OPTIONS, redis_options)
61
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)>>
62
65
  ```
63
66
 
64
67
  You can set the page size to something other than the default page size (25):
65
68
 
66
69
  ```ruby
67
- ruby-1.9.2-p180 :012 > highscore_lb.page_size = 5
70
+ highscore_lb.page_size = 5
68
71
  => 5
69
- ruby-1.9.2-p180 :013 > highscore_lb
72
+ highscore_lb
70
73
  => #<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)>>
71
74
  ```
72
75
 
73
- Add members to your leaderboard using rank_member:
76
+ Add members to your leaderboard using `rank_member`:
74
77
 
75
78
  ```ruby
76
- ruby-1.9.2-p180 :014 > 1.upto(10) do |index|
77
- ruby-1.9.2-p180 :015 > highscore_lb.rank_member("member_#{index}", index)
78
- ruby-1.9.2-p180 :016?> end
79
+ 1.upto(10) do |index|
80
+ highscore_lb.rank_member("member_#{index}", index)
81
+ end
79
82
  => 1
80
83
  ```
81
84
 
82
- You can call rank_member with the same member and the leaderboard will be updated automatically.
85
+ You can call `rank_member` with the same member and the leaderboard will be updated automatically.
83
86
 
84
87
  Get some information about your leaderboard:
85
88
 
86
89
  ```ruby
87
- ruby-1.9.2-p180 :020 > highscore_lb.total_members
90
+ highscore_lb.total_members
88
91
  => 10
89
- ruby-1.9.2-p180 :021 > highscore_lb.total_pages
92
+ highscore_lb.total_pages
90
93
  => 1
91
94
  ```
92
95
 
93
96
  Get some information about a specific member(s) in the leaderboard:
94
97
 
95
98
  ```ruby
96
- ruby-1.9.2-p180 :022 > highscore_lb.score_for('member_4')
99
+ highscore_lb.score_for('member_4')
97
100
  => 4.0
98
- ruby-1.9.2-p180 :023 > highscore_lb.rank_for('member_4')
101
+ highscore_lb.rank_for('member_4')
99
102
  => 7
100
- ruby-1.9.2-p180 :024 > highscore_lb.rank_for('member_10')
103
+ highscore_lb.rank_for('member_10')
101
104
  => 1
102
105
  ```
103
106
 
104
107
  Get page 1 in the leaderboard:
105
108
 
106
109
  ```ruby
107
- ruby-1.9.2-p180 :025 > highscore_lb.leaders(1)
110
+ highscore_lb.leaders(1)
108
111
  => [{: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}]
109
112
  ```
110
113
 
@@ -112,39 +115,39 @@ You can pass various options to the calls `leaders`, `around_me` and `ranked_in_
112
115
  Below is an example of retrieving the first page in the leaderboard without ranks:
113
116
 
114
117
  ```ruby
115
- ruby-1.9.2-p180 :026 > highscore_lb.leaders(1, :with_rank => false)
118
+ highscore_lb.leaders(1, :with_rank => false)
116
119
  => [{: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}]
117
120
  ```
118
121
 
119
122
  Below is an example of retrieving the first page in the leaderboard without scores or ranks:
120
123
 
121
124
  ```ruby
122
- ruby-1.9.2-p180 :028 > highscore_lb.leaders(1, :with_scores => false, :with_rank => false)
125
+ highscore_lb.leaders(1, :with_scores => false, :with_rank => false)
123
126
  => [{: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"}]
124
127
  ```
125
128
 
126
129
  Add more members to your leaderboard:
127
130
 
128
131
  ```ruby
129
- ruby-1.9.2-p180 :029 > 50.upto(95) do |index|
130
- ruby-1.9.2-p180 :030 > highscore_lb.rank_member("member_#{index}", index)
131
- ruby-1.9.2-p180 :031?> end
132
+ 50.upto(95) do |index|
133
+ highscore_lb.rank_member("member_#{index}", index)
134
+ end
132
135
  => 50
133
- ruby-1.9.2-p180 :032 > highscore_lb.total_pages
136
+ highscore_lb.total_pages
134
137
  => 3
135
138
  ```
136
139
 
137
- Get an "Around Me" leaderboard for a member:
140
+ Get an "Around Me" leaderboard page for a given member, which pulls members above and below the given member:
138
141
 
139
142
  ```ruby
140
- ruby-1.9.2-p180 :033 > highscore_lb.around_me('member_53')
143
+ highscore_lb.around_me('member_53')
141
144
  => [{: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}]
142
145
  ```
143
146
 
144
- Get rank and score for an arbitrary list of members (e.g. friends):
147
+ Get rank and score for an arbitrary list of members (e.g. friends) from the leaderboard:
145
148
 
146
149
  ```ruby
147
- ruby-1.9.2-p180 :034 > highscore_lb.ranked_in_list(['member_1', 'member_62', 'member_67'])
150
+ highscore_lb.ranked_in_list(['member_1', 'member_62', 'member_67'])
148
151
  => [{:member=>"member_1", :rank=>56, :score=>1.0}, {:member=>"member_62", :rank=>34, :score=>62.0}, {:member=>"member_67", :rank=>29, :score=>67.0}]
149
152
  ```
150
153
 
@@ -192,30 +195,31 @@ Check the [online documentation](http://rubydoc.info/github/agoragames/leaderboa
192
195
  10 million sequential scores insert:
193
196
 
194
197
  ```ruby
195
- ruby-1.9.2-p180 :003 > highscore_lb = Leaderboard.new('highscores')
198
+ highscore_lb = Leaderboard.new('highscores')
196
199
  => #<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)>>
197
- ruby-1.9.2-p180 :004 > insert_time = Benchmark.measure do
198
- ruby-1.9.2-p180 :005 > 1.upto(10000000) do |index|
199
- ruby-1.9.2-p180 :006 > highscore_lb.rank_member("member_#{index}", index)
200
- ruby-1.9.2-p180 :007?> end
201
- ruby-1.9.2-p180 :008?> end
200
+
201
+ insert_time = Benchmark.measure do
202
+ 1.upto(10000000) do |index|
203
+ highscore_lb.rank_member("member_#{index}", index)
204
+ end
205
+ end
202
206
  => 323.070000 148.560000 471.630000 (942.068307)
203
207
  ```
204
208
 
205
209
  Average time to request an arbitrary page from the leaderboard:
206
210
 
207
211
  ```ruby
208
- ruby-1.9.2-p180 :009 > requests_to_make = 50000
212
+ requests_to_make = 50000
209
213
  => 50000
210
- ruby-1.9.2-p180 :010 > lb_request_time = 0
214
+ lb_request_time = 0
211
215
  => 0
212
- ruby-1.9.2-p180 :011 > 1.upto(requests_to_make) do
213
- ruby-1.9.2-p180 :012 > lb_request_time += Benchmark.measure do
214
- ruby-1.9.2-p180 :013 > highscore_lb.leaders(rand(highscore_lb.total_pages))
215
- ruby-1.9.2-p180 :014?> end.total
216
- ruby-1.9.2-p180 :015?> end
216
+ 1.upto(requests_to_make) do
217
+ lb_request_time += Benchmark.measure do
218
+ highscore_lb.leaders(rand(highscore_lb.total_pages))
219
+ end.total
220
+ end
217
221
  => 1
218
- ruby-1.9.2-p180 :016 > p lb_request_time / requests_to_make
222
+ p lb_request_time / requests_to_make
219
223
  0.001513999999999998
220
224
  => 0.001513999999999998
221
225
  ```
@@ -223,24 +227,24 @@ Average time to request an arbitrary page from the leaderboard:
223
227
  10 million random scores insert:
224
228
 
225
229
  ```ruby
226
- ruby-1.9.2-p180 :018 > insert_time = Benchmark.measure do
227
- ruby-1.9.2-p180 :019 > 1.upto(10000000) do |index|
228
- ruby-1.9.2-p180 :020 > highscore_lb.rank_member("member_#{index}", rand(50000000))
229
- ruby-1.9.2-p180 :021?> end
230
- ruby-1.9.2-p180 :022?> end
230
+ insert_time = Benchmark.measure do
231
+ 1.upto(10000000) do |index|
232
+ highscore_lb.rank_member("member_#{index}", rand(50000000))
233
+ end
234
+ end
231
235
  => 338.480000 155.200000 493.680000 (2188.702475)
232
236
  ```
233
237
 
234
238
  Average time to request an arbitrary page from the leaderboard:
235
239
 
236
240
  ```ruby
237
- ruby-1.9.2-p180 :007 > 1.upto(requests_to_make) do
238
- ruby-1.9.2-p180 :008 > lb_request_time += Benchmark.measure do
239
- ruby-1.9.2-p180 :009 > highscore_lb.leaders(rand(highscore_lb.total_pages))
240
- ruby-1.9.2-p180 :010?> end.total
241
- ruby-1.9.2-p180 :011?> end
241
+ 1.upto(requests_to_make) do
242
+ lb_request_time += Benchmark.measure do
243
+ highscore_lb.leaders(rand(highscore_lb.total_pages))
244
+ end.total
245
+ end
242
246
  => 1
243
- ruby-1.9.2-p180 :012 > p lb_request_time / requests_to_make
247
+ p lb_request_time / requests_to_make
244
248
  0.0014615999999999531
245
249
  => 0.0014615999999999531
246
250
  ```
@@ -250,27 +254,27 @@ Bulk insert performance:
250
254
  Ranking individual members:
251
255
 
252
256
  ```ruby
253
- 1.9.3p0 :015 > insert_time = Benchmark.measure do
254
- 1.9.3p0 :016 > 1.upto(1000000) do |index|
255
- 1.9.3p0 :017 > highscore_lb.rank_member("member_#{index}", index)
256
- 1.9.3p0 :018?> end
257
- 1.9.3p0 :019?> end
257
+ insert_time = Benchmark.measure do
258
+ 1.upto(1000000) do |index|
259
+ highscore_lb.rank_member("member_#{index}", index)
260
+ end
261
+ end
258
262
  => 29.340000 15.050000 44.390000 ( 81.673507)
259
263
  ```
260
264
 
261
265
  Ranking multiple members at once:
262
266
 
263
267
  ```ruby
264
- 1.9.3p0 :020 > member_data = []
268
+ member_data = []
265
269
  => []
266
- 1.9.3p0 :021 > 1.upto(1000000) do |index|
267
- 1.9.3p0 :022 > member_data << "member_#{index}"
268
- 1.9.3p0 :023?> member_data << index
269
- 1.9.3p0 :024?> end
270
+ 1.upto(1000000) do |index|
271
+ member_data << "member_#{index}"
272
+ member_data << index
273
+ end
270
274
  => 1
271
- 1.9.3p0 :025 > insert_time = Benchmark.measure do
272
- 1.9.3p0 :026 > highscore_lb.rank_members(member_data)
273
- 1.9.3p0 :027?> end
275
+ insert_time = Benchmark.measure do
276
+ highscore_lb.rank_members(member_data)
277
+ end
274
278
  => 22.390000 6.380000 28.770000 ( 31.144027)
275
279
  ```
276
280
 
data/Rakefile CHANGED
@@ -1,15 +1,15 @@
1
- require 'bundler/gem_tasks'
2
- require 'rake'
3
- require 'rake/testtask'
4
-
5
- Rake::TestTask.new(:test) do |test|
6
- test.libs << 'lib' << 'test'
7
- test.pattern = 'test/**/test_*.rb'
8
- test.verbose = true
9
- # test.warning = true
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec) do |spec|
7
+ spec.pattern = 'spec/**/*_spec.rb'
8
+ spec.rspec_opts = ['--backtrace']
9
+ # spec.ruby_opts = ['-w']
10
10
  end
11
11
 
12
- REDIS_DIR = File.expand_path(File.join("..", "test"), __FILE__)
12
+ REDIS_DIR = File.expand_path(File.join("..", "spec"), __FILE__)
13
13
  REDIS_CNF = File.join(REDIS_DIR, "test.conf")
14
14
  REDIS_PID = File.join(REDIS_DIR, "db", "redis.pid")
15
15
  REDIS_LOCATION = ENV['REDIS_LOCATION']
@@ -17,7 +17,7 @@ REDIS_LOCATION = ENV['REDIS_LOCATION']
17
17
  task :default => :run
18
18
 
19
19
  desc "Run tests and manage server start/stop"
20
- task :run => [:start, :test, :stop]
20
+ task :run => [:start, :spec, :stop]
21
21
 
22
22
  desc "Start the Redis server"
23
23
  task :start do
@@ -46,6 +46,6 @@ end
46
46
 
47
47
  task :test_rubies do
48
48
  Rake::Task['start'].execute
49
- system "rvm 1.8.7@leaderboard_gem,1.9.2@leaderboard_gem,1.9.3@leaderboard_gem do rake test"
49
+ system "rvm 1.8.7@leaderboard_gem,1.9.2@leaderboard_gem,1.9.3@leaderboard_gem do rake spec"
50
50
  Rake::Task['stop'].execute
51
51
  end
data/leaderboard.gemspec CHANGED
@@ -24,4 +24,5 @@ Gem::Specification.new do |s|
24
24
  if '1.8.7'.eql?(RUBY_VERSION)
25
25
  s.add_development_dependency('SystemTimer')
26
26
  end
27
+ s.add_development_dependency('rspec')
27
28
  end
data/lib/leaderboard.rb CHANGED
@@ -1,23 +1,36 @@
1
1
  require 'redis'
2
2
  require 'leaderboard/version'
3
3
 
4
- class Leaderboard
4
+ class Leaderboard
5
+ # Default page size: 25
5
6
  DEFAULT_PAGE_SIZE = 25
6
7
 
8
+ # Default options when creating a leaderboard. Page size is 25 and reverse
9
+ # is set to false, meaning various methods will return results in
10
+ # highest-to-lowest order.
7
11
  DEFAULT_OPTIONS = {
8
12
  :page_size => DEFAULT_PAGE_SIZE,
9
13
  :reverse => false
10
14
  }
11
15
 
16
+ # Default Redis host: localhost
12
17
  DEFAULT_REDIS_HOST = 'localhost'
13
18
 
19
+ # Default Redis post: 6379
14
20
  DEFAULT_REDIS_PORT = 6379
15
21
 
22
+ # Default Redis options when creating a connection to Redis. The
23
+ # +DEFAULT_REDIS_HOST+ and +DEFAULT_REDIS_PORT+ will be passed.
16
24
  DEFAULT_REDIS_OPTIONS = {
17
25
  :host => DEFAULT_REDIS_HOST,
18
26
  :port => DEFAULT_REDIS_PORT
19
27
  }
20
28
 
29
+ # Default options when requesting data from a leaderboard.
30
+ # +:with_scores+ true: Return scores along with the member names.
31
+ # +:with_rank+ true: Return ranks along with the member names.
32
+ # +:use_zero_index_for_rank+ false: If you want to 0-index ranks.
33
+ # +:page_size+ nil: The default page size will be used.
21
34
  DEFAULT_LEADERBOARD_REQUEST_OPTIONS = {
22
35
  :with_scores => true,
23
36
  :with_rank => true,
@@ -30,6 +43,11 @@ class Leaderboard
30
43
 
31
44
  # Page size to be used when paging through the leaderboard.
32
45
  attr_reader :page_size
46
+
47
+ # Determines whether or not various leaderboard methods return their
48
+ # data in highest-to-lowest (+:reverse+ false) or
49
+ # lowest-to-highest (+:reverse+ true)
50
+ attr_accessor :reverse
33
51
 
34
52
  # Create a new instance of a leaderboard.
35
53
  #
@@ -1,3 +1,4 @@
1
1
  class Leaderboard
2
- VERSION = '2.0.5'.freeze
2
+ # Leaderboard version
3
+ VERSION = '2.0.6'.freeze
3
4
  end
data/spec/db/.gitkeep ADDED
File without changes
@@ -0,0 +1,503 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Leaderboard' do
4
+ before(:each) do
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)
7
+ end
8
+
9
+ after(:each) do
10
+ @redis_connection.flushdb
11
+ @leaderboard.disconnect
12
+ @redis_connection.client.disconnect
13
+ end
14
+
15
+ it 'should be initialized with defaults' do
16
+ @leaderboard.leaderboard_name.should == 'name'
17
+ @leaderboard.page_size.should == Leaderboard::DEFAULT_PAGE_SIZE
18
+ end
19
+
20
+ it 'should be able to disconnect its connection to Redis' do
21
+ @leaderboard.disconnect.should be_nil
22
+ end
23
+
24
+ it 'should automatically reconnect to Redis after a disconnect' do
25
+ @leaderboard.total_members.should be(0)
26
+ rank_members_in_leaderboard(5)
27
+ @leaderboard.total_members.should be(5)
28
+ @leaderboard.disconnect.should be_nil
29
+ @leaderboard.total_members.should be(5)
30
+ end
31
+
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})
34
+
35
+ some_leaderboard.page_size.should be(Leaderboard::DEFAULT_PAGE_SIZE)
36
+ some_leaderboard.disconnect
37
+ end
38
+
39
+ it 'should allow you to delete a leaderboard' do
40
+ rank_members_in_leaderboard
41
+
42
+ @redis_connection.exists('name').should be_true
43
+ @leaderboard.delete_leaderboard
44
+ @redis_connection.exists('name').should be_false
45
+ end
46
+
47
+ it 'should allow you to pass in an existing redis connection in the initializer' do
48
+ @leaderboard = Leaderboard.new('name', Leaderboard::DEFAULT_OPTIONS, {:redis_connection => @redis_connection})
49
+ rank_members_in_leaderboard
50
+
51
+ @redis_connection.info["connected_clients"].to_i.should be(1)
52
+ end
53
+
54
+ it 'should allow you to rank a member and see that reflected in total members' do
55
+ @leaderboard.rank_member('member', 1)
56
+
57
+ @leaderboard.total_members.should be(1)
58
+ end
59
+
60
+ it 'should return the correct number of members in a given score range' do
61
+ rank_members_in_leaderboard(5)
62
+
63
+ @leaderboard.total_members_in_score_range(2, 4).should be(3)
64
+ end
65
+
66
+ it 'should return the correct rank when calling rank_for' do
67
+ rank_members_in_leaderboard(5)
68
+
69
+ @leaderboard.rank_for('member_4').should be(2)
70
+ @leaderboard.rank_for('member_4', true).should be(1)
71
+ end
72
+
73
+ it 'should return the correct score when calling score_for' do
74
+ rank_members_in_leaderboard(5)
75
+
76
+ @leaderboard.score_for('member_4').should == 4
77
+ end
78
+
79
+ it 'should return the correct total pages' do
80
+ rank_members_in_leaderboard(10)
81
+
82
+ @leaderboard.total_pages.should be(1)
83
+
84
+ @redis_connection.flushdb
85
+
86
+ rank_members_in_leaderboard(Leaderboard::DEFAULT_PAGE_SIZE + 1)
87
+
88
+ @leaderboard.total_pages.should be(2)
89
+ end
90
+
91
+ it 'should return the correct list when calling leaders' do
92
+ rank_members_in_leaderboard(25)
93
+
94
+ @leaderboard.total_members.should be(25)
95
+
96
+ leaders = @leaderboard.leaders(1)
97
+
98
+ leaders.size.should be(25)
99
+ leaders[0][:member].should == 'member_25'
100
+ leaders[-2][:member].should == 'member_2'
101
+ leaders[-1][:member].should == 'member_1'
102
+ leaders[-1][:score].to_i.should be(1)
103
+ end
104
+
105
+ it 'should return the correct number of members when calling leaders with multiple pages' do
106
+ rank_members_in_leaderboard(Leaderboard::DEFAULT_PAGE_SIZE * 3 + 1)
107
+
108
+ @leaderboard.total_members.should be(Leaderboard::DEFAULT_PAGE_SIZE * 3 + 1)
109
+
110
+ leaders = @leaderboard.leaders(1)
111
+ leaders.size.should be(@leaderboard.page_size)
112
+
113
+ leaders = @leaderboard.leaders(2)
114
+ leaders.size.should be(@leaderboard.page_size)
115
+
116
+ leaders = @leaderboard.leaders(3)
117
+ leaders.size.should be(@leaderboard.page_size)
118
+
119
+ leaders = @leaderboard.leaders(4)
120
+ leaders.size.should be(1)
121
+
122
+ leaders = @leaderboard.leaders(-5)
123
+ leaders.size.should be(@leaderboard.page_size)
124
+
125
+ leaders = @leaderboard.leaders(10)
126
+ leaders.size.should be(1)
127
+ end
128
+
129
+ it 'should allow you to retrieve leaders without scores and ranks' do
130
+ rank_members_in_leaderboard(Leaderboard::DEFAULT_PAGE_SIZE)
131
+
132
+ @leaderboard.total_members.should be(Leaderboard::DEFAULT_PAGE_SIZE)
133
+ leaders = @leaderboard.leaders(1, {:with_scores => false, :with_rank => false})
134
+
135
+ member_25 = {:member => 'member_25'}
136
+ leaders[0].should == member_25
137
+
138
+ member_1 = {:member => 'member_1'}
139
+ leaders[24].should == member_1
140
+ end
141
+
142
+ it 'should allow you to call leaders with various options that respect the defaults for the options not passed in' do
143
+ rank_members_in_leaderboard(Leaderboard::DEFAULT_PAGE_SIZE + 1)
144
+
145
+ leaders = @leaderboard.leaders(1, :page_size => 1)
146
+ leaders.size.should be(1)
147
+
148
+ leaders = @leaderboard.leaders(1, :with_rank => false)
149
+ leaders.size.should be(Leaderboard::DEFAULT_PAGE_SIZE)
150
+ member_26 = {:member => 'member_26', :score => 26}
151
+ member_25 = {:member => 'member_25', :score => 25}
152
+ member_24 = {:member => 'member_24', :score => 24}
153
+ leaders[0].should == member_26
154
+ leaders[1].should == member_25
155
+ leaders[2].should == member_24
156
+
157
+ leaders = @leaderboard.leaders(1, :with_scores => false)
158
+ leaders.size.should be(Leaderboard::DEFAULT_PAGE_SIZE)
159
+ member_26 = {:member => 'member_26', :rank => 1}
160
+ member_25 = {:member => 'member_25', :rank => 2}
161
+ leaders[0].should == member_26
162
+ leaders[1].should == member_25
163
+
164
+ leaders = @leaderboard.leaders(1, :with_scores => false, :with_rank => false)
165
+ leaders.size.should be(Leaderboard::DEFAULT_PAGE_SIZE)
166
+ member_26 = {:member => 'member_26'}
167
+ member_25 = {:member => 'member_25'}
168
+ leaders[0].should == member_26
169
+ leaders[1].should == member_25
170
+
171
+ leaders = @leaderboard.leaders(1, :with_rank => false, :page_size => 1)
172
+ leaders.size.should be(1)
173
+ member_26 = {:member => 'member_26', :score => 26}
174
+ leaders[0].should == member_26
175
+ end
176
+
177
+ it 'should return the correct information when calling around_me' do
178
+ rank_members_in_leaderboard(Leaderboard::DEFAULT_PAGE_SIZE * 3 + 1)
179
+
180
+ @leaderboard.total_members.should be(Leaderboard::DEFAULT_PAGE_SIZE * 3 + 1)
181
+
182
+ leaders_around_me = @leaderboard.around_me('member_30')
183
+ (leaders_around_me.size / 2).should be(@leaderboard.page_size / 2)
184
+
185
+ leaders_around_me = @leaderboard.around_me('member_1')
186
+ leaders_around_me.size.should be(@leaderboard.page_size / 2 + 1)
187
+
188
+ leaders_around_me = @leaderboard.around_me('member_76')
189
+ (leaders_around_me.size / 2).should be(@leaderboard.page_size / 2)
190
+ end
191
+
192
+ it 'should return the correct information when calling ranked_in_list' do
193
+ rank_members_in_leaderboard(Leaderboard::DEFAULT_PAGE_SIZE)
194
+
195
+ @leaderboard.total_members.should be(Leaderboard::DEFAULT_PAGE_SIZE)
196
+
197
+ members = ['member_1', 'member_5', 'member_10']
198
+ ranked_members = @leaderboard.ranked_in_list(members, Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS)
199
+
200
+ ranked_members.size.should be(3)
201
+
202
+ ranked_members[0][:rank].should be(25)
203
+ ranked_members[0][:score].should == 1
204
+
205
+ ranked_members[1][:rank].should be(21)
206
+ ranked_members[1][:score].should == 5
207
+
208
+ ranked_members[2][:rank].should be(16)
209
+ ranked_members[2][:score].should == 10
210
+ end
211
+
212
+ it 'should return the correct information when calling ranked_in_list without scores' do
213
+ rank_members_in_leaderboard(Leaderboard::DEFAULT_PAGE_SIZE)
214
+
215
+ @leaderboard.total_members.should be(Leaderboard::DEFAULT_PAGE_SIZE)
216
+
217
+ members = ['member_1', 'member_5', 'member_10']
218
+ ranked_members = @leaderboard.ranked_in_list(members, {:with_scores => false, :with_rank => true, :use_zero_index_for_rank => false})
219
+
220
+ ranked_members.size.should be(3)
221
+ ranked_members[0][:rank].should be(25)
222
+ ranked_members[1][:rank].should be(21)
223
+ ranked_members[2][:rank].should be(16)
224
+ end
225
+
226
+ it 'should allow you to remove members' do
227
+ rank_members_in_leaderboard(Leaderboard::DEFAULT_PAGE_SIZE)
228
+
229
+ @leaderboard.total_members.should be(Leaderboard::DEFAULT_PAGE_SIZE)
230
+
231
+ @leaderboard.remove_member('member_1')
232
+
233
+ @leaderboard.total_members.should be(Leaderboard::DEFAULT_PAGE_SIZE - 1)
234
+ @leaderboard.rank_for('member_1').should be_nil
235
+ end
236
+
237
+ it 'should allow you to change the score for a member' do
238
+ @leaderboard.rank_member('member_1', 5)
239
+ @leaderboard.score_for('member_1').should == 5
240
+
241
+ @leaderboard.change_score_for('member_1', 5)
242
+ @leaderboard.score_for('member_1').should == 10
243
+
244
+ @leaderboard.change_score_for('member_1', -5)
245
+ @leaderboard.score_for('member_1').should == 5
246
+ end
247
+
248
+ it 'should allow you to check if a member exists' do
249
+ @leaderboard.rank_member('member_1', 10)
250
+
251
+ @leaderboard.check_member?('member_1').should be_true
252
+ @leaderboard.check_member?('member_2').should be_false
253
+ end
254
+
255
+ it 'should allow you to change the page size and have that reflected in the size of the result set' do
256
+ rank_members_in_leaderboard(Leaderboard::DEFAULT_PAGE_SIZE)
257
+
258
+ @leaderboard.page_size = 5
259
+
260
+ @leaderboard.total_pages.should be(5)
261
+ @leaderboard.leaders(1).size.should be(5)
262
+ end
263
+
264
+ it 'should not allow you to set the page size to an invalid page size' do
265
+ rank_members_in_leaderboard(Leaderboard::DEFAULT_PAGE_SIZE)
266
+
267
+ @leaderboard.page_size = 0
268
+ @leaderboard.total_pages.should be(1)
269
+ @leaderboard.leaders(1).size.should be(Leaderboard::DEFAULT_PAGE_SIZE)
270
+ end
271
+
272
+ it 'should return the correct information when calling score_and_rank_for' do
273
+ rank_members_in_leaderboard
274
+
275
+ data = @leaderboard.score_and_rank_for('member_1')
276
+ data[:member].should == 'member_1'
277
+ data[:score].should == 1
278
+ data[:rank].should == 5
279
+ end
280
+
281
+ it 'should allow you to remove members in a given score range' do
282
+ rank_members_in_leaderboard
283
+
284
+ @leaderboard.total_members.should be(5)
285
+
286
+ @leaderboard.rank_member('cheater_1', 100)
287
+ @leaderboard.rank_member('cheater_2', 101)
288
+ @leaderboard.rank_member('cheater_3', 102)
289
+
290
+ @leaderboard.total_members.should be(8)
291
+
292
+ @leaderboard.remove_members_in_score_range(100, 102)
293
+
294
+ @leaderboard.total_members.should be(5)
295
+
296
+ leaders = @leaderboard.leaders(1)
297
+ leaders.each do |leader|
298
+ leader[:score].should be < 100
299
+ end
300
+ end
301
+
302
+ it 'should allow you to merge leaderboards' do
303
+ foo = Leaderboard.new('foo', Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS, :host => "127.0.0.1", :db => 15)
304
+ bar = Leaderboard.new('bar', Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS, :host => "127.0.0.1", :db => 15)
305
+
306
+ foo.rank_member('foo_1', 1)
307
+ foo.rank_member('foo_2', 2)
308
+ bar.rank_member('bar_1', 3)
309
+ bar.rank_member('bar_2', 4)
310
+ bar.rank_member('bar_3', 5)
311
+
312
+ foobar_keys = foo.merge_leaderboards('foobar', ['bar'])
313
+ foobar_keys.should be(5)
314
+
315
+ foobar = Leaderboard.new('foobar', Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS, :host => "127.0.0.1", :db => 15)
316
+ foobar.total_members.should be(5)
317
+
318
+ first_leader_in_foobar = foobar.leaders(1).first
319
+ first_leader_in_foobar[:rank].should == 1
320
+ first_leader_in_foobar[:member].should == 'bar_3'
321
+ first_leader_in_foobar[:score].should == 5
322
+
323
+ foo.disconnect
324
+ bar.disconnect
325
+ foobar.disconnect
326
+ end
327
+
328
+ it 'should allow you to intersect leaderboards' do
329
+ foo = Leaderboard.new('foo', Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS, :host => "127.0.0.1", :db => 15)
330
+ bar = Leaderboard.new('bar', Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS, :host => "127.0.0.1", :db => 15)
331
+
332
+ foo.rank_member('foo_1', 1)
333
+ foo.rank_member('foo_2', 2)
334
+ foo.rank_member('bar_3', 6)
335
+ bar.rank_member('bar_1', 3)
336
+ bar.rank_member('foo_1', 4)
337
+ bar.rank_member('bar_3', 5)
338
+
339
+ foobar_keys = foo.intersect_leaderboards('foobar', ['bar'], {:aggregate => :max})
340
+ foobar_keys.should be(2)
341
+
342
+ foobar = Leaderboard.new('foobar', Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS, :host => "127.0.0.1", :db => 15)
343
+ foobar.total_members.should be(2)
344
+
345
+ first_leader_in_foobar = foobar.leaders(1).first
346
+ first_leader_in_foobar[:rank].should == 1
347
+ first_leader_in_foobar[:member].should == 'bar_3'
348
+ first_leader_in_foobar[:score].should == 6
349
+
350
+ foo.disconnect
351
+ bar.disconnect
352
+ foobar.disconnect
353
+ end
354
+
355
+ it 'should respect the with_scores option in the massage_leader_data method' do
356
+ rank_members_in_leaderboard(25)
357
+
358
+ @leaderboard.total_members.should be(25)
359
+
360
+ leaders = @leaderboard.leaders(1, {:with_scores => false, :with_rank => false})
361
+ leaders[0][:member].should_not be_nil
362
+ leaders[0][:score].should be_nil
363
+ leaders[0][:rank].should be_nil
364
+
365
+ @leaderboard.page_size = 25
366
+ leaders = @leaderboard.leaders(1, {:with_scores => false, :with_rank => false})
367
+ leaders.size.should be(25)
368
+
369
+ @leaderboard.page_size = Leaderboard::DEFAULT_PAGE_SIZE
370
+ leaders = @leaderboard.leaders(1, Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS)
371
+ leaders[0][:member].should_not be_nil
372
+ leaders[0][:score].should_not be_nil
373
+ leaders[0][:rank].should_not be_nil
374
+
375
+ @leaderboard.page_size = 25
376
+ leaders = @leaderboard.leaders(1, Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS)
377
+ leaders.size.should be(25)
378
+ end
379
+
380
+ it 'should return the correct number of pages when calling total_pages_in page size option' do
381
+ rank_members_in_leaderboard(25)
382
+
383
+ @leaderboard.total_pages_in(@leaderboard.leaderboard_name).should be(1)
384
+ @leaderboard.total_pages_in(@leaderboard.leaderboard_name, 5).should be(5)
385
+ end
386
+
387
+ it 'should return the correct number of members when calling leaders with a page_size option' do
388
+ rank_members_in_leaderboard(25)
389
+
390
+ @leaderboard.leaders(1, Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS.merge({:page_size => 5})).size.should be(5)
391
+ @leaderboard.leaders(1, Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS.merge({:page_size => 10})).size.should be(10)
392
+ @leaderboard.leaders(2, Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS.merge({:page_size => 10})).size.should be(10)
393
+ @leaderboard.leaders(3, Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS.merge({:page_size => 10})).size.should be(5)
394
+ end
395
+
396
+ it 'should return the correct number of members when calling around_me with a page_size options' do
397
+ rank_members_in_leaderboard(Leaderboard::DEFAULT_PAGE_SIZE * 3 + 1)
398
+
399
+ leaders_around_me = @leaderboard.around_me('member_30', Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS.merge({:page_size => 3}))
400
+ leaders_around_me.size.should be(3)
401
+ leaders_around_me[0][:member].should == 'member_31'
402
+ leaders_around_me[2][:member].should == 'member_29'
403
+ end
404
+
405
+ it 'should return the correct information when calling percentile_for' do
406
+ rank_members_in_leaderboard(12)
407
+
408
+ @leaderboard.percentile_for('member_1').should == 0
409
+ @leaderboard.percentile_for('member_2').should == 9
410
+ @leaderboard.percentile_for('member_3').should == 17
411
+ @leaderboard.percentile_for('member_4').should == 25
412
+ @leaderboard.percentile_for('member_12').should == 92
413
+ end
414
+
415
+ it 'should not throw an exception when calling around_me with a member not in the leaderboard' do
416
+ rank_members_in_leaderboard(Leaderboard::DEFAULT_PAGE_SIZE * 3 + 1)
417
+
418
+ leaders_around_me = @leaderboard.around_me('jones', Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS.merge({:page_size => 3}))
419
+ leaders_around_me.size.should be(0)
420
+ end
421
+
422
+ it 'should not throw an exception when calling score_and_rank_for with a member not in the leaderboard' do
423
+ score_and_rank_for_member = @leaderboard.score_and_rank_for('jones')
424
+
425
+ score_and_rank_for_member[:member].should == 'jones'
426
+ score_and_rank_for_member[:score].should == 0.0
427
+ score_and_rank_for_member[:rank].should be_nil
428
+ end
429
+
430
+ it 'should not throw an exception when calling ranked_in_list with a member not in the leaderboard' do
431
+ rank_members_in_leaderboard
432
+
433
+ members = ['member_1', 'member_5', 'jones']
434
+ ranked_members = @leaderboard.ranked_in_list(members)
435
+
436
+ ranked_members.size.should be(3)
437
+ ranked_members[2][:rank].should be_nil
438
+ end
439
+
440
+ it 'should not throw an exception when calling percentile_for with a member not in the leaderboard' do
441
+ percentile = @leaderboard.percentile_for('jones')
442
+
443
+ percentile.should be_nil
444
+ end
445
+
446
+ it 'should allow you to change the score for a member not in the leaderboard' do
447
+ @leaderboard.score_for('jones').should == 0.0
448
+ @leaderboard.change_score_for('jones', 5)
449
+ @leaderboard.score_for('jones').should == 5.0
450
+ end
451
+
452
+ it 'should return the correct page when calling page_for a given member in the leaderboard' do
453
+ @leaderboard.page_for('jones').should be(0)
454
+
455
+ rank_members_in_leaderboard(20)
456
+
457
+ @leaderboard.page_for('member_17').should be(1)
458
+ @leaderboard.page_for('member_11').should be(1)
459
+ @leaderboard.page_for('member_10').should be(1)
460
+ @leaderboard.page_for('member_1').should be(1)
461
+
462
+ @leaderboard.page_for('member_17', 10).should be(1)
463
+ @leaderboard.page_for('member_11', 10).should be(1)
464
+ @leaderboard.page_for('member_10', 10).should be(2)
465
+ @leaderboard.page_for('member_1', 10).should be(2)
466
+ end
467
+
468
+ it 'should allow you to rank multiple members with a variable number of arguments' do
469
+ @leaderboard.total_members.should be(0)
470
+ @leaderboard.rank_members('member_1', 1, 'member_10', 10)
471
+ @leaderboard.total_members.should be(2)
472
+ @leaderboard.leaders(1).first[:member].should == 'member_10'
473
+ end
474
+
475
+ it 'should allow you to rank multiple members with an array' do
476
+ @leaderboard.total_members.should be(0)
477
+ @leaderboard.rank_members(['member_1', 1, 'member_10', 10])
478
+ @leaderboard.total_members.should be(2)
479
+ @leaderboard.leaders(1).first[:member].should == 'member_10'
480
+ end
481
+
482
+ it 'should allow you to set reverse after creating a leaderboard to see results in highest-to-lowest or lowest-to-highest order' do
483
+ rank_members_in_leaderboard(25)
484
+
485
+ leaders = @leaderboard.leaders(1)
486
+
487
+ leaders.size.should be(25)
488
+ leaders[0][:member].should == 'member_25'
489
+ leaders[-2][:member].should == 'member_2'
490
+ leaders[-1][:member].should == 'member_1'
491
+ leaders[-1][:score].to_i.should be(1)
492
+
493
+ @leaderboard.reverse = true
494
+
495
+ leaders = @leaderboard.leaders(1)
496
+
497
+ leaders.size.should be(25)
498
+ leaders[0][:member].should == 'member_1'
499
+ leaders[-2][:member].should == 'member_24'
500
+ leaders[-1][:member].should == 'member_25'
501
+ leaders[-1][:score].to_i.should be(25)
502
+ end
503
+ end