leaderboard 1.0.5 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.rvmrc +2 -1
- data/CHANGELOG.markdown +14 -0
- data/Gemfile +1 -1
- data/README.rdoc +103 -78
- data/Rakefile +2 -2
- data/VERSION +1 -1
- data/leaderboard.gemspec +9 -10
- data/lib/leaderboard.rb +108 -65
- data/test/test_leaderboard.rb +128 -53
- metadata +51 -84
data/.rvmrc
CHANGED
@@ -1 +1,2 @@
|
|
1
|
-
rvm 1.8.7
|
1
|
+
rvm --create 1.8.7@leaderboard_gem
|
2
|
+
rvm --create 1.9.2@leaderboard_gem
|
data/CHANGELOG.markdown
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
# leaderboard 2.0.0 (2011-08-05)
|
2
|
+
|
3
|
+
* Change `add_member` to `rank_member` - https://github.com/agoragames/leaderboard/issues/3
|
4
|
+
* Added `delete_leaderboard` and `delete_leaderboard_named` - https://github.com/agoragames/leaderboard/issues/2
|
5
|
+
* Ability to pass in an existing Redis connection in initializer - https://github.com/agoragames/leaderboard/issues/1
|
6
|
+
* Added transaction support for `score_and_rank_for`, `leaders`, `around_me` and `ranked_in_list`
|
7
|
+
* Updated initializer to take a leaderboard name, `options` hash and `redis_options` hash
|
8
|
+
* Simplified `leaders`, `around_me` and `ranked_in_list` to use an `options` hash with defaults for the previously individual parameters
|
9
|
+
|
10
|
+
# leaderboard 1.0.6 (unreleased)
|
11
|
+
|
12
|
+
* Added `disconnect` method
|
13
|
+
* Check for invalid page size when changing
|
14
|
+
|
1
15
|
# leaderboard 1.0.5 (2011-05-04)
|
2
16
|
|
3
17
|
* Updated Rakefile to run tests under ruby 1.8.7 and ruby 1.9.2
|
data/Gemfile
CHANGED
data/README.rdoc
CHANGED
@@ -8,7 +8,7 @@ Builds off ideas proposed in http://blog.agoragames.com/2011/01/01/creating-high
|
|
8
8
|
|
9
9
|
Install the gem:
|
10
10
|
|
11
|
-
|
11
|
+
gem "leaderboard", "~> 2.0.0"
|
12
12
|
|
13
13
|
Make sure your redis server is running! Redis configuration is outside the scope of this README, but
|
14
14
|
check out the Redis documentation, http://redis.io/documentation.
|
@@ -21,138 +21,163 @@ The gem has been built and tested under Ruby 1.8.7 and Ruby 1.9.2
|
|
21
21
|
|
22
22
|
Create a new leaderboard or attach to an existing leaderboard named 'highscores':
|
23
23
|
|
24
|
-
ruby-1.
|
25
|
-
=> #<Leaderboard:
|
26
|
-
|
27
|
-
If you need to pass in options for Redis, you can do this
|
24
|
+
ruby-1.9.2-p180 :002 > highscore_lb = Leaderboard.new('highscores')
|
25
|
+
=> #<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)>>
|
26
|
+
|
27
|
+
If you need to pass in options for Redis, you can do this in the initializer:
|
28
|
+
|
29
|
+
ruby-1.9.2-p180 :007 > redis_options = {:host => 'localhost', :port => 6379, :db => 1}
|
30
|
+
=> {:host=>"localhost", :port=>6379, :db=>1}
|
31
|
+
ruby-1.9.2-p180 :008 > highscore_lb = Leaderboard.new('highscores', Leaderboard::DEFAULT_OPTIONS, redis_options)
|
32
|
+
=> #<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)>>
|
28
33
|
|
29
|
-
|
30
|
-
highscore_lb = Leaderboard.new('highscores', redis_options[:host], redis_options[:port], Leaderboard::DEFAULT_PAGE_SIZE, redis_options))
|
34
|
+
You can pass in an existing connection to Redis using :redis_connection in the Redis options hash:
|
31
35
|
|
36
|
+
ruby-1.9.2-p180 :009 > redis = Redis.new
|
37
|
+
=> #<Redis client v2.2.2 connected to redis://127.0.0.1:6379/0 (Redis v2.2.5)>
|
38
|
+
ruby-1.9.2-p180 :010 > redis_options = {:redis_connection => redis}
|
39
|
+
=> {:redis_connection=>#<Redis client v2.2.2 connected to redis://127.0.0.1:6379/0 (Redis v2.2.5)>}
|
40
|
+
ruby-1.9.2-p180 :011 > highscore_lb = Leaderboard.new('highscores', Leaderboard::DEFAULT_OPTIONS, redis_options)
|
41
|
+
=> #<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)>>
|
42
|
+
|
32
43
|
You can set the page size to something other than the default page size (25):
|
33
44
|
|
34
|
-
ruby-1.
|
45
|
+
ruby-1.9.2-p180 :012 > highscore_lb.page_size = 5
|
35
46
|
=> 5
|
36
|
-
ruby-1.
|
37
|
-
=> #<Leaderboard:
|
47
|
+
ruby-1.9.2-p180 :013 > highscore_lb
|
48
|
+
=> #<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)>>
|
38
49
|
|
39
|
-
Add members to your leaderboard:
|
50
|
+
Add members to your leaderboard using rank_member:
|
40
51
|
|
41
|
-
ruby-1.
|
42
|
-
ruby-1.
|
43
|
-
ruby-1.
|
52
|
+
ruby-1.9.2-p180 :014 > 1.upto(10) do |index|
|
53
|
+
ruby-1.9.2-p180 :015 > highscore_lb.rank_member("member_#{index}", index)
|
54
|
+
ruby-1.9.2-p180 :016?> end
|
44
55
|
=> 1
|
45
|
-
|
56
|
+
|
57
|
+
You can call rank_member with the same member and the leaderboard will be updated automatically.
|
58
|
+
|
46
59
|
Get some information about your leaderboard:
|
47
60
|
|
48
|
-
ruby-1.
|
61
|
+
ruby-1.9.2-p180 :020 > highscore_lb.total_members
|
49
62
|
=> 10
|
50
|
-
ruby-1.
|
63
|
+
ruby-1.9.2-p180 :021 > highscore_lb.total_pages
|
51
64
|
=> 1
|
52
65
|
|
53
66
|
Get some information about a specific member(s) in the leaderboard:
|
54
67
|
|
55
|
-
ruby-1.
|
68
|
+
ruby-1.9.2-p180 :022 > highscore_lb.score_for('member_4')
|
56
69
|
=> 4.0
|
57
|
-
ruby-1.
|
70
|
+
ruby-1.9.2-p180 :023 > highscore_lb.rank_for('member_4')
|
58
71
|
=> 7
|
59
|
-
ruby-1.
|
72
|
+
ruby-1.9.2-p180 :024 > highscore_lb.rank_for('member_10')
|
60
73
|
=> 1
|
61
74
|
|
62
75
|
Get page 1 in the leaderboard:
|
63
76
|
|
64
|
-
ruby-1.
|
65
|
-
|
77
|
+
ruby-1.9.2-p180 :025 > highscore_lb.leaders(1)
|
78
|
+
=> [{: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}]
|
66
79
|
|
80
|
+
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
|
+
Below is an example of retrieving the first page in the leaderboard without ranks:
|
82
|
+
|
83
|
+
ruby-1.9.2-p180 :026 > highscore_lb.leaders(1, :with_scores => true, :with_rank => false, :use_zero_index_for_rank => false)
|
84
|
+
=> [{: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}]
|
85
|
+
|
86
|
+
Below is an example of retrieving the first page in the leaderboard without scores or ranks:
|
87
|
+
|
88
|
+
ruby-1.9.2-p180 :028 > highscore_lb.leaders(1, :with_scores => false, :with_rank => false)
|
89
|
+
=> [{: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"}]
|
90
|
+
|
67
91
|
Add more members to your leaderboard:
|
68
92
|
|
69
|
-
ruby-1.
|
70
|
-
ruby-1.
|
71
|
-
ruby-1.
|
93
|
+
ruby-1.9.2-p180 :029 > 50.upto(95) do |index|
|
94
|
+
ruby-1.9.2-p180 :030 > highscore_lb.rank_member("member_#{index}", index)
|
95
|
+
ruby-1.9.2-p180 :031?> end
|
72
96
|
=> 50
|
73
|
-
ruby-1.
|
97
|
+
ruby-1.9.2-p180 :032 > highscore_lb.total_pages
|
74
98
|
=> 3
|
75
99
|
|
76
100
|
Get an "Around Me" leaderboard for a member:
|
77
101
|
|
78
|
-
ruby-1.
|
79
|
-
|
102
|
+
ruby-1.9.2-p180 :033 > highscore_lb.around_me('member_53')
|
103
|
+
=> [{: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}]
|
80
104
|
|
81
105
|
Get rank and score for an arbitrary list of members (e.g. friends):
|
82
106
|
|
83
|
-
ruby-1.
|
84
|
-
=> [{:
|
85
|
-
|
107
|
+
ruby-1.9.2-p180 :034 > highscore_lb.ranked_in_list(['member_1', 'member_62', 'member_67'])
|
108
|
+
=> [{: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
|
+
|
86
110
|
=== Other useful methods
|
87
111
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
112
|
+
delete_leaderboard: Delete the current leaderboard
|
113
|
+
remove_member(member): Remove a member from the leaderboard
|
114
|
+
total_members: Total # of members in the leaderboard
|
115
|
+
total_pages: Total # of pages in the leaderboard given the leaderboard's page_size
|
116
|
+
total_members_in_score_range(min_score, max_score): Count the number of members within a score range in the leaderboard
|
117
|
+
change_score_for(member, delta): Change the score for a member by some amount delta (delta could be positive or negative)
|
118
|
+
rank_for(member): Retrieve the rank for a given member in the leaderboard
|
119
|
+
score_for(member): Retrieve the score for a given member in the leaderboard
|
120
|
+
check_member?(member): Check to see whether member is in the leaderboard
|
121
|
+
score_and_rank_for(member): Retrieve the score and rank for a member in a single call
|
122
|
+
remove_members_in_score_range(min_score, max_score): Remove members from the leaderboard within a score range
|
123
|
+
merge_leaderboards(destination, keys, options = {:aggregate => :min}): Merge leaderboards given by keys with this leaderboard into destination
|
124
|
+
intersect_leaderboards(destination, keys, options = {:aggregate => :min}): Intersect leaderboards given by keys with this leaderboard into destination
|
125
|
+
|
126
|
+
Check the online documentation for more detail, http://rubydoc.info/gems/leaderboard/
|
96
127
|
|
97
128
|
== Performance Metrics
|
98
129
|
|
99
130
|
10 million sequential scores insert:
|
100
131
|
|
101
|
-
ruby-1.
|
102
|
-
|
103
|
-
ruby-1.
|
104
|
-
ruby-1.
|
105
|
-
ruby-1.
|
106
|
-
|
132
|
+
ruby-1.9.2-p180 :003 > highscore_lb = Leaderboard.new('highscores')
|
133
|
+
=> #<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)>>
|
134
|
+
ruby-1.9.2-p180 :004 > insert_time = Benchmark.measure do
|
135
|
+
ruby-1.9.2-p180 :005 > 1.upto(10000000) do |index|
|
136
|
+
ruby-1.9.2-p180 :006 > highscore_lb.rank_member("member_#{index}", index)
|
137
|
+
ruby-1.9.2-p180 :007?> end
|
138
|
+
ruby-1.9.2-p180 :008?> end
|
139
|
+
=> 323.070000 148.560000 471.630000 (942.068307)
|
107
140
|
|
108
141
|
Average time to request an arbitrary page from the leaderboard:
|
109
142
|
|
110
|
-
ruby-1.
|
143
|
+
ruby-1.9.2-p180 :009 > requests_to_make = 50000
|
111
144
|
=> 50000
|
112
|
-
ruby-1.
|
145
|
+
ruby-1.9.2-p180 :010 > lb_request_time = 0
|
113
146
|
=> 0
|
114
|
-
ruby-1.
|
115
|
-
ruby-1.
|
116
|
-
ruby-1.
|
117
|
-
ruby-1.
|
118
|
-
ruby-1.
|
147
|
+
ruby-1.9.2-p180 :011 > 1.upto(requests_to_make) do
|
148
|
+
ruby-1.9.2-p180 :012 > lb_request_time += Benchmark.measure do
|
149
|
+
ruby-1.9.2-p180 :013 > highscore_lb.leaders(rand(highscore_lb.total_pages))
|
150
|
+
ruby-1.9.2-p180 :014?> end.total
|
151
|
+
ruby-1.9.2-p180 :015?> end
|
119
152
|
=> 1
|
120
|
-
ruby-1.
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
153
|
+
ruby-1.9.2-p180 :016 > p lb_request_time / requests_to_make
|
154
|
+
0.001513999999999998
|
155
|
+
=> 0.001513999999999998
|
156
|
+
|
125
157
|
10 million random scores insert:
|
126
158
|
|
127
|
-
ruby-1.
|
128
|
-
ruby-1.
|
129
|
-
ruby-1.
|
130
|
-
ruby-1.
|
131
|
-
ruby-1.
|
132
|
-
=>
|
159
|
+
ruby-1.9.2-p180 :018 > insert_time = Benchmark.measure do
|
160
|
+
ruby-1.9.2-p180 :019 > 1.upto(10000000) do |index|
|
161
|
+
ruby-1.9.2-p180 :020 > highscore_lb.rank_member("member_#{index}", rand(50000000))
|
162
|
+
ruby-1.9.2-p180 :021?> end
|
163
|
+
ruby-1.9.2-p180 :022?> end
|
164
|
+
=> 338.480000 155.200000 493.680000 (2188.702475)
|
133
165
|
|
134
166
|
Average time to request an arbitrary page from the leaderboard:
|
135
167
|
|
136
|
-
ruby-1.
|
137
|
-
|
138
|
-
ruby-1.
|
139
|
-
|
140
|
-
ruby-1.
|
141
|
-
ruby-1.8.7-p302 > lb_request_time += Benchmark.measure do
|
142
|
-
ruby-1.8.7-p302 > highscore_lb.leaders(rand(highscore_lb.total_pages))
|
143
|
-
ruby-1.8.7-p302 ?> end.total
|
144
|
-
ruby-1.8.7-p302 ?> end
|
168
|
+
ruby-1.9.2-p180 :007 > 1.upto(requests_to_make) do
|
169
|
+
ruby-1.9.2-p180 :008 > lb_request_time += Benchmark.measure do
|
170
|
+
ruby-1.9.2-p180 :009 > highscore_lb.leaders(rand(highscore_lb.total_pages))
|
171
|
+
ruby-1.9.2-p180 :010?> end.total
|
172
|
+
ruby-1.9.2-p180 :011?> end
|
145
173
|
=> 1
|
146
|
-
ruby-1.
|
147
|
-
|
148
|
-
|
149
|
-
=> nil
|
174
|
+
ruby-1.9.2-p180 :012 > p lb_request_time / requests_to_make
|
175
|
+
0.0014615999999999531
|
176
|
+
=> 0.0014615999999999531
|
150
177
|
|
151
178
|
== Future Ideas
|
152
179
|
|
153
180
|
* Bulk insert
|
154
|
-
* Atomicity for various operations?
|
155
|
-
* Is nil? OK to return if Redis returns no data or should it be []?
|
156
181
|
|
157
182
|
== Contributing to leaderboard
|
158
183
|
|
data/Rakefile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
2.0.0
|
data/leaderboard.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{leaderboard}
|
8
|
-
s.version = "
|
8
|
+
s.version = "2.0.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
-
s.authors = [
|
12
|
-
s.date = %q{2011-
|
11
|
+
s.authors = [%q{David Czarnecki}]
|
12
|
+
s.date = %q{2011-08-17}
|
13
13
|
s.description = %q{Leaderboards backed by Redis in Ruby}
|
14
14
|
s.email = %q{dczarnecki@agoragames.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -33,9 +33,9 @@ Gem::Specification.new do |s|
|
|
33
33
|
"test/test_leaderboard.rb"
|
34
34
|
]
|
35
35
|
s.homepage = %q{http://github.com/agoragames/leaderboard}
|
36
|
-
s.licenses = [
|
37
|
-
s.require_paths = [
|
38
|
-
s.rubygems_version = %q{1.
|
36
|
+
s.licenses = [%q{MIT}]
|
37
|
+
s.require_paths = [%q{lib}]
|
38
|
+
s.rubygems_version = %q{1.8.6}
|
39
39
|
s.summary = %q{Leaderboards backed by Redis in Ruby}
|
40
40
|
s.test_files = [
|
41
41
|
"test/helper.rb",
|
@@ -43,22 +43,21 @@ Gem::Specification.new do |s|
|
|
43
43
|
]
|
44
44
|
|
45
45
|
if s.respond_to? :specification_version then
|
46
|
-
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
47
46
|
s.specification_version = 3
|
48
47
|
|
49
48
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
50
|
-
s.add_runtime_dependency(%q<redis>, ["~> 2.
|
49
|
+
s.add_runtime_dependency(%q<redis>, ["~> 2.2.0"])
|
51
50
|
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
52
51
|
s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
|
53
52
|
s.add_development_dependency(%q<rcov>, [">= 0"])
|
54
53
|
else
|
55
|
-
s.add_dependency(%q<redis>, ["~> 2.
|
54
|
+
s.add_dependency(%q<redis>, ["~> 2.2.0"])
|
56
55
|
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
57
56
|
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
58
57
|
s.add_dependency(%q<rcov>, [">= 0"])
|
59
58
|
end
|
60
59
|
else
|
61
|
-
s.add_dependency(%q<redis>, ["~> 2.
|
60
|
+
s.add_dependency(%q<redis>, ["~> 2.2.0"])
|
62
61
|
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
63
62
|
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
64
63
|
s.add_dependency(%q<rcov>, [">= 0"])
|
data/lib/leaderboard.rb
CHANGED
@@ -1,42 +1,69 @@
|
|
1
1
|
require 'redis'
|
2
2
|
|
3
3
|
class Leaderboard
|
4
|
-
VERSION = '
|
4
|
+
VERSION = '2.0.0'.freeze
|
5
5
|
|
6
6
|
DEFAULT_PAGE_SIZE = 25
|
7
|
+
DEFAULT_OPTIONS = {
|
8
|
+
:page_size => DEFAULT_PAGE_SIZE
|
9
|
+
}
|
10
|
+
|
7
11
|
DEFAULT_REDIS_HOST = 'localhost'
|
8
|
-
DEFAULT_REDIS_PORT = 6379
|
12
|
+
DEFAULT_REDIS_PORT = 6379
|
13
|
+
DEFAULT_REDIS_OPTIONS = {
|
14
|
+
:host => DEFAULT_REDIS_HOST,
|
15
|
+
:port => DEFAULT_REDIS_PORT
|
16
|
+
}
|
17
|
+
|
18
|
+
DEFAULT_LEADERBOARD_REQUEST_OPTIONS = {
|
19
|
+
:with_scores => true,
|
20
|
+
:with_rank => true,
|
21
|
+
:use_zero_index_for_rank => false,
|
22
|
+
:page_size => nil
|
23
|
+
}
|
9
24
|
|
10
|
-
attr_reader :host
|
11
|
-
attr_reader :port
|
12
25
|
attr_reader :leaderboard_name
|
13
26
|
attr_accessor :page_size
|
14
27
|
|
15
|
-
def initialize(leaderboard_name,
|
28
|
+
def initialize(leaderboard_name, options = DEFAULT_OPTIONS, redis_options = DEFAULT_REDIS_OPTIONS)
|
16
29
|
@leaderboard_name = leaderboard_name
|
17
|
-
@host = host
|
18
|
-
@port = port
|
19
30
|
|
20
|
-
|
21
|
-
|
31
|
+
@page_size = options[:page_size]
|
32
|
+
if @page_size < 1
|
33
|
+
@page_size = DEFAULT_PAGE_SIZE
|
22
34
|
end
|
23
35
|
|
36
|
+
@redis_connection = redis_options[:redis_connection]
|
37
|
+
unless @redis_connection.nil?
|
38
|
+
redis_options.delete(:redis_connection)
|
39
|
+
end
|
40
|
+
|
41
|
+
@redis_connection = Redis.new(redis_options) if @redis_connection.nil?
|
42
|
+
end
|
43
|
+
|
44
|
+
def page_size=(page_size)
|
45
|
+
page_size = DEFAULT_PAGE_SIZE if page_size < 1
|
46
|
+
|
24
47
|
@page_size = page_size
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
48
|
+
end
|
49
|
+
|
50
|
+
def disconnect
|
51
|
+
@redis_connection.client.disconnect
|
52
|
+
end
|
53
|
+
|
54
|
+
def delete_leaderboard
|
55
|
+
delete_leaderboard_named(@leaderboard_name)
|
56
|
+
end
|
57
|
+
|
58
|
+
def delete_leaderboard_named(leaderboard_name)
|
59
|
+
@redis_connection.del(leaderboard_name)
|
33
60
|
end
|
34
61
|
|
35
|
-
def
|
36
|
-
|
62
|
+
def rank_member(member, score)
|
63
|
+
rank_member_in(@leaderboard_name, member, score)
|
37
64
|
end
|
38
65
|
|
39
|
-
def
|
66
|
+
def rank_member_in(leaderboard_name, member, score)
|
40
67
|
@redis_connection.zadd(leaderboard_name, score, member)
|
41
68
|
end
|
42
69
|
|
@@ -114,7 +141,17 @@ class Leaderboard
|
|
114
141
|
end
|
115
142
|
|
116
143
|
def score_and_rank_for_in(leaderboard_name, member, use_zero_index_for_rank = false)
|
117
|
-
|
144
|
+
responses = @redis_connection.multi do |transaction|
|
145
|
+
transaction.zscore(leaderboard_name, member)
|
146
|
+
transaction.zrevrank(leaderboard_name, member)
|
147
|
+
end
|
148
|
+
|
149
|
+
responses[0] = responses[0].to_f
|
150
|
+
if !use_zero_index_for_rank
|
151
|
+
responses[1] = responses[1] + 1 rescue nil
|
152
|
+
end
|
153
|
+
|
154
|
+
{:member => member, :score => responses[0], :rank => responses[1]}
|
118
155
|
end
|
119
156
|
|
120
157
|
def remove_members_in_score_range(min_score, max_score)
|
@@ -125,17 +162,17 @@ class Leaderboard
|
|
125
162
|
@redis_connection.zremrangebyscore(leaderboard_name, min_score, max_score)
|
126
163
|
end
|
127
164
|
|
128
|
-
def leaders(current_page,
|
129
|
-
leaders_in(@leaderboard_name, current_page,
|
165
|
+
def leaders(current_page, options = DEFAULT_LEADERBOARD_REQUEST_OPTIONS)
|
166
|
+
leaders_in(@leaderboard_name, current_page, options)
|
130
167
|
end
|
131
168
|
|
132
|
-
def leaders_in(leaderboard_name, current_page,
|
169
|
+
def leaders_in(leaderboard_name, current_page, options = DEFAULT_LEADERBOARD_REQUEST_OPTIONS)
|
133
170
|
if current_page < 1
|
134
171
|
current_page = 1
|
135
172
|
end
|
173
|
+
|
174
|
+
page_size = validate_page_size(options[:page_size]) || @page_size
|
136
175
|
|
137
|
-
page_size ||= @page_size
|
138
|
-
|
139
176
|
if current_page > total_pages_in(leaderboard_name, page_size)
|
140
177
|
current_page = total_pages_in(leaderboard_name, page_size)
|
141
178
|
end
|
@@ -148,23 +185,23 @@ class Leaderboard
|
|
148
185
|
end
|
149
186
|
|
150
187
|
ending_offset = (starting_offset + page_size) - 1
|
151
|
-
|
152
|
-
raw_leader_data = @redis_connection.zrevrange(leaderboard_name, starting_offset, ending_offset, :with_scores =>
|
188
|
+
|
189
|
+
raw_leader_data = @redis_connection.zrevrange(leaderboard_name, starting_offset, ending_offset, :with_scores => false)
|
153
190
|
if raw_leader_data
|
154
|
-
|
191
|
+
return ranked_in_list_in(leaderboard_name, raw_leader_data, options)
|
155
192
|
else
|
156
|
-
return
|
193
|
+
return []
|
157
194
|
end
|
158
195
|
end
|
159
196
|
|
160
|
-
def around_me(member,
|
161
|
-
around_me_in(@leaderboard_name, member,
|
197
|
+
def around_me(member, options = DEFAULT_LEADERBOARD_REQUEST_OPTIONS)
|
198
|
+
around_me_in(@leaderboard_name, member, options)
|
162
199
|
end
|
163
200
|
|
164
|
-
def around_me_in(leaderboard_name, member,
|
201
|
+
def around_me_in(leaderboard_name, member, options = DEFAULT_LEADERBOARD_REQUEST_OPTIONS)
|
165
202
|
reverse_rank_for_member = @redis_connection.zrevrank(leaderboard_name, member)
|
166
203
|
|
167
|
-
page_size
|
204
|
+
page_size = validate_page_size(options[:page_size]) || @page_size
|
168
205
|
|
169
206
|
starting_offset = reverse_rank_for_member - (page_size / 2)
|
170
207
|
if starting_offset < 0
|
@@ -173,26 +210,50 @@ class Leaderboard
|
|
173
210
|
|
174
211
|
ending_offset = (starting_offset + page_size) - 1
|
175
212
|
|
176
|
-
raw_leader_data = @redis_connection.zrevrange(leaderboard_name, starting_offset, ending_offset, :with_scores =>
|
213
|
+
raw_leader_data = @redis_connection.zrevrange(leaderboard_name, starting_offset, ending_offset, :with_scores => false)
|
177
214
|
if raw_leader_data
|
178
|
-
|
215
|
+
return ranked_in_list_in(leaderboard_name, raw_leader_data, options)
|
179
216
|
else
|
180
|
-
return
|
217
|
+
return []
|
181
218
|
end
|
182
219
|
end
|
183
220
|
|
184
|
-
def ranked_in_list(members,
|
185
|
-
ranked_in_list_in(@leaderboard_name, members,
|
221
|
+
def ranked_in_list(members, options = DEFAULT_LEADERBOARD_REQUEST_OPTIONS)
|
222
|
+
ranked_in_list_in(@leaderboard_name, members, options)
|
186
223
|
end
|
187
224
|
|
188
|
-
def ranked_in_list_in(leaderboard_name, members,
|
225
|
+
def ranked_in_list_in(leaderboard_name, members, options = DEFAULT_LEADERBOARD_REQUEST_OPTIONS)
|
189
226
|
ranks_for_members = []
|
190
227
|
|
191
|
-
|
228
|
+
responses = @redis_connection.multi do |transaction|
|
229
|
+
members.each do |member|
|
230
|
+
transaction.zrevrank(leaderboard_name, member) if options[:with_rank]
|
231
|
+
transaction.zscore(leaderboard_name, member) if options[:with_scores]
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
members.each_with_index do |member, index|
|
192
236
|
data = {}
|
193
237
|
data[:member] = member
|
194
|
-
|
195
|
-
|
238
|
+
if options[:with_scores]
|
239
|
+
if options[:with_rank]
|
240
|
+
if options[:use_zero_index_for_rank]
|
241
|
+
data[:rank] = responses[index * 2]
|
242
|
+
else
|
243
|
+
data[:rank] = responses[index * 2] + 1
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
data[:score] = responses[index * 2 + 1].to_f
|
248
|
+
else
|
249
|
+
if options[:with_rank]
|
250
|
+
if options[:use_zero_index_for_rank]
|
251
|
+
data[:rank] = responses[index]
|
252
|
+
else
|
253
|
+
data[:rank] = responses[index] + 1
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
196
257
|
|
197
258
|
ranks_for_members << data
|
198
259
|
end
|
@@ -212,29 +273,11 @@ class Leaderboard
|
|
212
273
|
|
213
274
|
private
|
214
275
|
|
215
|
-
def
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
data = {}
|
220
|
-
leaders.each do |leader_data_item|
|
221
|
-
if member_attribute
|
222
|
-
data[:member] = leader_data_item
|
223
|
-
else
|
224
|
-
data[:score] = leader_data_item.to_f
|
225
|
-
data[:rank] = rank_for_in(leaderboard_name, data[:member], use_zero_index_for_rank) if with_rank
|
226
|
-
leader_data << data
|
227
|
-
data = {}
|
228
|
-
end
|
229
|
-
|
230
|
-
if with_scores
|
231
|
-
member_attribute = !member_attribute
|
232
|
-
else
|
233
|
-
leader_data << data
|
234
|
-
data = {}
|
235
|
-
end
|
276
|
+
def validate_page_size(page_size)
|
277
|
+
if page_size && page_size < 1
|
278
|
+
page_size = DEFAULT_PAGE_SIZE
|
236
279
|
end
|
237
280
|
|
238
|
-
|
281
|
+
page_size
|
239
282
|
end
|
240
283
|
end
|
data/test/test_leaderboard.rb
CHANGED
@@ -8,64 +8,92 @@ class TestLeaderboard < Test::Unit::TestCase
|
|
8
8
|
|
9
9
|
def teardown
|
10
10
|
@redis_connection.flushdb
|
11
|
+
@leaderboard.disconnect
|
12
|
+
@redis_connection.client.disconnect
|
11
13
|
end
|
12
14
|
|
13
15
|
def test_version
|
14
|
-
assert_equal '
|
16
|
+
assert_equal '2.0.0', Leaderboard::VERSION
|
15
17
|
end
|
16
18
|
|
17
19
|
def test_initialize_with_defaults
|
18
20
|
assert_equal 'name', @leaderboard.leaderboard_name
|
19
|
-
assert_equal 'localhost', @leaderboard.host
|
20
|
-
assert_equal 6379, @leaderboard.port
|
21
21
|
assert_equal Leaderboard::DEFAULT_PAGE_SIZE, @leaderboard.page_size
|
22
22
|
end
|
23
23
|
|
24
|
+
def test_disconnect
|
25
|
+
assert_equal nil, @leaderboard.disconnect
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_will_automatically_reconnect_after_a_disconnect
|
29
|
+
assert_equal 0, @leaderboard.total_members
|
30
|
+
rank_members_in_leaderboard(5)
|
31
|
+
assert_equal 5, @leaderboard.total_members
|
32
|
+
assert_equal nil, @leaderboard.disconnect
|
33
|
+
assert_equal 5, @leaderboard.total_members
|
34
|
+
end
|
35
|
+
|
24
36
|
def test_page_size_is_default_page_size_if_set_to_invalid_value
|
25
|
-
|
37
|
+
some_leaderboard = Leaderboard.new('name', {:page_size => 0})
|
26
38
|
|
27
|
-
assert_equal Leaderboard::DEFAULT_PAGE_SIZE,
|
39
|
+
assert_equal Leaderboard::DEFAULT_PAGE_SIZE, some_leaderboard.page_size
|
40
|
+
some_leaderboard.disconnect
|
28
41
|
end
|
29
42
|
|
30
|
-
def
|
31
|
-
|
43
|
+
def test_delete_leaderboard
|
44
|
+
rank_members_in_leaderboard
|
45
|
+
|
46
|
+
assert_equal true, @redis_connection.exists('name')
|
47
|
+
@leaderboard.delete_leaderboard
|
48
|
+
assert_equal false, @redis_connection.exists('name')
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_can_pass_existing_redis_connection_to_initializer
|
52
|
+
@leaderboard = Leaderboard.new('name', Leaderboard::DEFAULT_OPTIONS, {:redis_connection => @redis_connection})
|
53
|
+
rank_members_in_leaderboard
|
54
|
+
|
55
|
+
assert_equal 1, @redis_connection.info["connected_clients"].to_i
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_rank_member_and_total_members
|
59
|
+
@leaderboard.rank_member('member', 1)
|
32
60
|
|
33
61
|
assert_equal 1, @leaderboard.total_members
|
34
62
|
end
|
35
63
|
|
36
64
|
def test_total_members_in_score_range
|
37
|
-
|
65
|
+
rank_members_in_leaderboard(5)
|
38
66
|
|
39
67
|
assert_equal 3, @leaderboard.total_members_in_score_range(2, 4)
|
40
68
|
end
|
41
69
|
|
42
70
|
def test_rank_for
|
43
|
-
|
71
|
+
rank_members_in_leaderboard(5)
|
44
72
|
|
45
73
|
assert_equal 2, @leaderboard.rank_for('member_4')
|
46
74
|
assert_equal 1, @leaderboard.rank_for('member_4', true)
|
47
75
|
end
|
48
76
|
|
49
77
|
def test_score_for
|
50
|
-
|
78
|
+
rank_members_in_leaderboard(5)
|
51
79
|
|
52
80
|
assert_equal 4, @leaderboard.score_for('member_4')
|
53
81
|
end
|
54
82
|
|
55
83
|
def test_total_pages
|
56
|
-
|
84
|
+
rank_members_in_leaderboard(10)
|
57
85
|
|
58
86
|
assert_equal 1, @leaderboard.total_pages
|
59
87
|
|
60
88
|
@redis_connection.flushdb
|
61
89
|
|
62
|
-
|
90
|
+
rank_members_in_leaderboard(Leaderboard::DEFAULT_PAGE_SIZE + 1)
|
63
91
|
|
64
92
|
assert_equal 2, @leaderboard.total_pages
|
65
93
|
end
|
66
94
|
|
67
95
|
def test_leaders
|
68
|
-
|
96
|
+
rank_members_in_leaderboard(25)
|
69
97
|
|
70
98
|
assert_equal 25, @leaderboard.total_members
|
71
99
|
|
@@ -79,7 +107,7 @@ class TestLeaderboard < Test::Unit::TestCase
|
|
79
107
|
end
|
80
108
|
|
81
109
|
def test_leaders_with_multiple_pages
|
82
|
-
|
110
|
+
rank_members_in_leaderboard(Leaderboard::DEFAULT_PAGE_SIZE * 3 + 1)
|
83
111
|
|
84
112
|
assert_equal Leaderboard::DEFAULT_PAGE_SIZE * 3 + 1, @leaderboard.total_members
|
85
113
|
|
@@ -102,8 +130,21 @@ class TestLeaderboard < Test::Unit::TestCase
|
|
102
130
|
assert_equal 1, leaders.size
|
103
131
|
end
|
104
132
|
|
133
|
+
def test_leaders_without_retrieving_scores_and_ranks
|
134
|
+
rank_members_in_leaderboard(Leaderboard::DEFAULT_PAGE_SIZE)
|
135
|
+
|
136
|
+
assert_equal Leaderboard::DEFAULT_PAGE_SIZE, @leaderboard.total_members
|
137
|
+
leaders = @leaderboard.leaders(1, {:with_scores => false, :with_ranks => false})
|
138
|
+
|
139
|
+
member_25 = {:member => 'member_25'}
|
140
|
+
assert_equal member_25, leaders[0]
|
141
|
+
|
142
|
+
member_1 = {:member => 'member_1'}
|
143
|
+
assert_equal member_1, leaders[24]
|
144
|
+
end
|
145
|
+
|
105
146
|
def test_around_me
|
106
|
-
|
147
|
+
rank_members_in_leaderboard(Leaderboard::DEFAULT_PAGE_SIZE * 3 + 1)
|
107
148
|
|
108
149
|
assert_equal Leaderboard::DEFAULT_PAGE_SIZE * 3 + 1, @leaderboard.total_members
|
109
150
|
|
@@ -118,12 +159,12 @@ class TestLeaderboard < Test::Unit::TestCase
|
|
118
159
|
end
|
119
160
|
|
120
161
|
def test_ranked_in_list
|
121
|
-
|
162
|
+
rank_members_in_leaderboard(Leaderboard::DEFAULT_PAGE_SIZE)
|
122
163
|
|
123
164
|
assert_equal Leaderboard::DEFAULT_PAGE_SIZE, @leaderboard.total_members
|
124
165
|
|
125
166
|
members = ['member_1', 'member_5', 'member_10']
|
126
|
-
ranked_members = @leaderboard.ranked_in_list(members,
|
167
|
+
ranked_members = @leaderboard.ranked_in_list(members, Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS)
|
127
168
|
|
128
169
|
assert_equal 3, ranked_members.size
|
129
170
|
|
@@ -137,8 +178,25 @@ class TestLeaderboard < Test::Unit::TestCase
|
|
137
178
|
assert_equal 10, ranked_members[2][:score]
|
138
179
|
end
|
139
180
|
|
181
|
+
def test_ranked_in_list_without_scores
|
182
|
+
rank_members_in_leaderboard(Leaderboard::DEFAULT_PAGE_SIZE)
|
183
|
+
|
184
|
+
assert_equal Leaderboard::DEFAULT_PAGE_SIZE, @leaderboard.total_members
|
185
|
+
|
186
|
+
members = ['member_1', 'member_5', 'member_10']
|
187
|
+
ranked_members = @leaderboard.ranked_in_list(members, {:with_scores => false, :with_rank => true, :use_zero_index_for_rank => false})
|
188
|
+
|
189
|
+
assert_equal 3, ranked_members.size
|
190
|
+
|
191
|
+
assert_equal 25, ranked_members[0][:rank]
|
192
|
+
|
193
|
+
assert_equal 21, ranked_members[1][:rank]
|
194
|
+
|
195
|
+
assert_equal 16, ranked_members[2][:rank]
|
196
|
+
end
|
197
|
+
|
140
198
|
def test_remove_member
|
141
|
-
|
199
|
+
rank_members_in_leaderboard(Leaderboard::DEFAULT_PAGE_SIZE)
|
142
200
|
|
143
201
|
assert_equal Leaderboard::DEFAULT_PAGE_SIZE, @leaderboard.total_members
|
144
202
|
|
@@ -149,7 +207,7 @@ class TestLeaderboard < Test::Unit::TestCase
|
|
149
207
|
end
|
150
208
|
|
151
209
|
def test_change_score_for
|
152
|
-
@leaderboard.
|
210
|
+
@leaderboard.rank_member('member_1', 5)
|
153
211
|
assert_equal 5, @leaderboard.score_for('member_1')
|
154
212
|
|
155
213
|
@leaderboard.change_score_for('member_1', 5)
|
@@ -160,22 +218,31 @@ class TestLeaderboard < Test::Unit::TestCase
|
|
160
218
|
end
|
161
219
|
|
162
220
|
def test_check_member
|
163
|
-
@leaderboard.
|
221
|
+
@leaderboard.rank_member('member_1', 10)
|
164
222
|
|
165
223
|
assert_equal true, @leaderboard.check_member?('member_1')
|
166
224
|
assert_equal false, @leaderboard.check_member?('member_2')
|
167
225
|
end
|
168
226
|
|
169
227
|
def test_can_change_page_size_and_have_it_reflected_in_size_of_result_set
|
170
|
-
|
228
|
+
rank_members_in_leaderboard(Leaderboard::DEFAULT_PAGE_SIZE)
|
171
229
|
|
172
230
|
@leaderboard.page_size = 5
|
231
|
+
|
173
232
|
assert_equal 5, @leaderboard.total_pages
|
174
233
|
assert_equal 5, @leaderboard.leaders(1).size
|
175
234
|
end
|
176
235
|
|
236
|
+
def test_cannot_set_page_size_to_invalid_page_size
|
237
|
+
rank_members_in_leaderboard(Leaderboard::DEFAULT_PAGE_SIZE)
|
238
|
+
|
239
|
+
@leaderboard.page_size = 0
|
240
|
+
assert_equal 1, @leaderboard.total_pages
|
241
|
+
assert_equal Leaderboard::DEFAULT_PAGE_SIZE, @leaderboard.leaders(1).size
|
242
|
+
end
|
243
|
+
|
177
244
|
def test_score_and_rank_for
|
178
|
-
|
245
|
+
rank_members_in_leaderboard
|
179
246
|
|
180
247
|
data = @leaderboard.score_and_rank_for('member_1')
|
181
248
|
assert_equal 'member_1', data[:member]
|
@@ -184,13 +251,13 @@ class TestLeaderboard < Test::Unit::TestCase
|
|
184
251
|
end
|
185
252
|
|
186
253
|
def test_remove_members_in_score_range
|
187
|
-
|
254
|
+
rank_members_in_leaderboard
|
188
255
|
|
189
256
|
assert_equal 5, @leaderboard.total_members
|
190
257
|
|
191
|
-
@leaderboard.
|
192
|
-
@leaderboard.
|
193
|
-
@leaderboard.
|
258
|
+
@leaderboard.rank_member('cheater_1', 100)
|
259
|
+
@leaderboard.rank_member('cheater_2', 101)
|
260
|
+
@leaderboard.rank_member('cheater_3', 102)
|
194
261
|
|
195
262
|
assert_equal 8, @leaderboard.total_members
|
196
263
|
|
@@ -205,14 +272,14 @@ class TestLeaderboard < Test::Unit::TestCase
|
|
205
272
|
end
|
206
273
|
|
207
274
|
def test_merge_leaderboards
|
208
|
-
foo = Leaderboard.new('foo')
|
275
|
+
foo = Leaderboard.new('foo')
|
209
276
|
bar = Leaderboard.new('bar')
|
210
277
|
|
211
|
-
foo.
|
212
|
-
foo.
|
213
|
-
bar.
|
214
|
-
bar.
|
215
|
-
bar.
|
278
|
+
foo.rank_member('foo_1', 1)
|
279
|
+
foo.rank_member('foo_2', 2)
|
280
|
+
bar.rank_member('bar_1', 3)
|
281
|
+
bar.rank_member('bar_2', 4)
|
282
|
+
bar.rank_member('bar_3', 5)
|
216
283
|
|
217
284
|
foobar_keys = foo.merge_leaderboards('foobar', ['bar'])
|
218
285
|
assert_equal 5, foobar_keys
|
@@ -224,18 +291,22 @@ class TestLeaderboard < Test::Unit::TestCase
|
|
224
291
|
assert_equal 1, first_leader_in_foobar[:rank]
|
225
292
|
assert_equal 'bar_3', first_leader_in_foobar[:member]
|
226
293
|
assert_equal 5, first_leader_in_foobar[:score]
|
294
|
+
|
295
|
+
foo.disconnect
|
296
|
+
bar.disconnect
|
297
|
+
foobar.disconnect
|
227
298
|
end
|
228
299
|
|
229
300
|
def test_intersect_leaderboards
|
230
301
|
foo = Leaderboard.new('foo')
|
231
302
|
bar = Leaderboard.new('bar')
|
232
303
|
|
233
|
-
foo.
|
234
|
-
foo.
|
235
|
-
foo.
|
236
|
-
bar.
|
237
|
-
bar.
|
238
|
-
bar.
|
304
|
+
foo.rank_member('foo_1', 1)
|
305
|
+
foo.rank_member('foo_2', 2)
|
306
|
+
foo.rank_member('bar_3', 6)
|
307
|
+
bar.rank_member('bar_1', 3)
|
308
|
+
bar.rank_member('foo_1', 4)
|
309
|
+
bar.rank_member('bar_3', 5)
|
239
310
|
|
240
311
|
foobar_keys = foo.intersect_leaderboards('foobar', ['bar'], {:aggregate => :max})
|
241
312
|
assert_equal 2, foobar_keys
|
@@ -247,53 +318,57 @@ class TestLeaderboard < Test::Unit::TestCase
|
|
247
318
|
assert_equal 1, first_leader_in_foobar[:rank]
|
248
319
|
assert_equal 'bar_3', first_leader_in_foobar[:member]
|
249
320
|
assert_equal 6, first_leader_in_foobar[:score]
|
321
|
+
|
322
|
+
foo.disconnect
|
323
|
+
bar.disconnect
|
324
|
+
foobar.disconnect
|
250
325
|
end
|
251
326
|
|
252
327
|
def test_massage_leader_data_respects_with_scores
|
253
|
-
|
328
|
+
rank_members_in_leaderboard(25)
|
254
329
|
|
255
330
|
assert_equal 25, @leaderboard.total_members
|
256
331
|
|
257
|
-
leaders = @leaderboard.leaders(1,
|
332
|
+
leaders = @leaderboard.leaders(1, {:with_scores => false, :with_rank => false})
|
258
333
|
assert_not_nil leaders[0][:member]
|
259
334
|
assert_nil leaders[0][:score]
|
260
335
|
assert_nil leaders[0][:rank]
|
261
336
|
|
262
337
|
@leaderboard.page_size = 25
|
263
|
-
leaders = @leaderboard.leaders(1,
|
338
|
+
leaders = @leaderboard.leaders(1, {:with_scores => false, :with_rank => false})
|
264
339
|
assert_equal 25, leaders.size
|
265
340
|
|
266
341
|
@leaderboard.page_size = Leaderboard::DEFAULT_PAGE_SIZE
|
267
|
-
leaders = @leaderboard.leaders(1,
|
342
|
+
leaders = @leaderboard.leaders(1, Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS)
|
268
343
|
assert_not_nil leaders[0][:member]
|
269
344
|
assert_not_nil leaders[0][:score]
|
270
345
|
assert_not_nil leaders[0][:rank]
|
271
346
|
|
272
347
|
@leaderboard.page_size = 25
|
273
|
-
leaders = @leaderboard.leaders(1,
|
348
|
+
leaders = @leaderboard.leaders(1, Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS)
|
274
349
|
assert_equal 25, leaders.size
|
275
350
|
end
|
276
351
|
|
277
352
|
def test_total_pages_in_with_new_page_size
|
278
|
-
|
353
|
+
rank_members_in_leaderboard(25)
|
279
354
|
|
280
355
|
assert_equal 1, @leaderboard.total_pages_in(@leaderboard.leaderboard_name)
|
281
356
|
assert_equal 5, @leaderboard.total_pages_in(@leaderboard.leaderboard_name, 5)
|
282
357
|
end
|
283
358
|
|
284
359
|
def test_leaders_call_with_new_page_size
|
285
|
-
|
360
|
+
rank_members_in_leaderboard(25)
|
286
361
|
|
287
|
-
assert_equal 5, @leaderboard.leaders(1,
|
288
|
-
assert_equal 10, @leaderboard.leaders(1,
|
289
|
-
assert_equal 10, @leaderboard.leaders(2,
|
290
|
-
assert_equal 5, @leaderboard.leaders(3,
|
362
|
+
assert_equal 5, @leaderboard.leaders(1, Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS.merge({:page_size => 5})).size
|
363
|
+
assert_equal 10, @leaderboard.leaders(1, Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS.merge({:page_size => 10})).size
|
364
|
+
assert_equal 10, @leaderboard.leaders(2, Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS.merge({:page_size => 10})).size
|
365
|
+
assert_equal 5, @leaderboard.leaders(3, Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS.merge({:page_size => 10})).size
|
291
366
|
end
|
292
367
|
|
293
368
|
def test_around_me_call_with_new_page_size
|
294
|
-
|
369
|
+
rank_members_in_leaderboard(Leaderboard::DEFAULT_PAGE_SIZE * 3 + 1)
|
295
370
|
|
296
|
-
leaders_around_me = @leaderboard.around_me('member_30',
|
371
|
+
leaders_around_me = @leaderboard.around_me('member_30', Leaderboard::DEFAULT_LEADERBOARD_REQUEST_OPTIONS.merge({:page_size => 3}))
|
297
372
|
assert_equal 3, leaders_around_me.size
|
298
373
|
assert_equal 'member_31', leaders_around_me[0][:member]
|
299
374
|
assert_equal 'member_29', leaders_around_me[2][:member]
|
@@ -301,9 +376,9 @@ class TestLeaderboard < Test::Unit::TestCase
|
|
301
376
|
|
302
377
|
private
|
303
378
|
|
304
|
-
def
|
379
|
+
def rank_members_in_leaderboard(members_to_add = 5)
|
305
380
|
1.upto(members_to_add) do |index|
|
306
|
-
@leaderboard.
|
381
|
+
@leaderboard.rank_member("member_#{index}", index)
|
307
382
|
end
|
308
383
|
end
|
309
384
|
end
|
metadata
CHANGED
@@ -1,95 +1,68 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: leaderboard
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease:
|
6
|
-
segments:
|
7
|
-
- 1
|
8
|
-
- 0
|
9
|
-
- 5
|
10
|
-
version: 1.0.5
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 2.0.0
|
5
|
+
prerelease:
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- David Czarnecki
|
14
9
|
autorequire:
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
dependencies:
|
21
|
-
- !ruby/object:Gem::Dependency
|
22
|
-
type: :runtime
|
23
|
-
prerelease: false
|
12
|
+
date: 2011-08-17 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
24
15
|
name: redis
|
25
|
-
|
16
|
+
requirement: &70114074547140 !ruby/object:Gem::Requirement
|
26
17
|
none: false
|
27
|
-
requirements:
|
18
|
+
requirements:
|
28
19
|
- - ~>
|
29
|
-
- !ruby/object:Gem::Version
|
30
|
-
|
31
|
-
|
32
|
-
- 2
|
33
|
-
- 1
|
34
|
-
- 1
|
35
|
-
version: 2.1.1
|
36
|
-
requirement: *id001
|
37
|
-
- !ruby/object:Gem::Dependency
|
38
|
-
type: :development
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 2.2.0
|
22
|
+
type: :runtime
|
39
23
|
prerelease: false
|
24
|
+
version_requirements: *70114074547140
|
25
|
+
- !ruby/object:Gem::Dependency
|
40
26
|
name: bundler
|
41
|
-
|
27
|
+
requirement: &70114074545640 !ruby/object:Gem::Requirement
|
42
28
|
none: false
|
43
|
-
requirements:
|
29
|
+
requirements:
|
44
30
|
- - ~>
|
45
|
-
- !ruby/object:Gem::Version
|
46
|
-
hash: 23
|
47
|
-
segments:
|
48
|
-
- 1
|
49
|
-
- 0
|
50
|
-
- 0
|
31
|
+
- !ruby/object:Gem::Version
|
51
32
|
version: 1.0.0
|
52
|
-
requirement: *id002
|
53
|
-
- !ruby/object:Gem::Dependency
|
54
33
|
type: :development
|
55
34
|
prerelease: false
|
35
|
+
version_requirements: *70114074545640
|
36
|
+
- !ruby/object:Gem::Dependency
|
56
37
|
name: jeweler
|
57
|
-
|
38
|
+
requirement: &70114074543920 !ruby/object:Gem::Requirement
|
58
39
|
none: false
|
59
|
-
requirements:
|
40
|
+
requirements:
|
60
41
|
- - ~>
|
61
|
-
- !ruby/object:Gem::Version
|
62
|
-
hash: 7
|
63
|
-
segments:
|
64
|
-
- 1
|
65
|
-
- 5
|
66
|
-
- 2
|
42
|
+
- !ruby/object:Gem::Version
|
67
43
|
version: 1.5.2
|
68
|
-
requirement: *id003
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
44
|
type: :development
|
71
45
|
prerelease: false
|
46
|
+
version_requirements: *70114074543920
|
47
|
+
- !ruby/object:Gem::Dependency
|
72
48
|
name: rcov
|
73
|
-
|
49
|
+
requirement: &70114074539440 !ruby/object:Gem::Requirement
|
74
50
|
none: false
|
75
|
-
requirements:
|
76
|
-
- -
|
77
|
-
- !ruby/object:Gem::Version
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
requirement: *id004
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *70114074539440
|
83
58
|
description: Leaderboards backed by Redis in Ruby
|
84
59
|
email: dczarnecki@agoragames.com
|
85
60
|
executables: []
|
86
|
-
|
87
61
|
extensions: []
|
88
|
-
|
89
|
-
extra_rdoc_files:
|
62
|
+
extra_rdoc_files:
|
90
63
|
- LICENSE.txt
|
91
64
|
- README.rdoc
|
92
|
-
files:
|
65
|
+
files:
|
93
66
|
- .document
|
94
67
|
- .rvmrc
|
95
68
|
- CHANGELOG.markdown
|
@@ -104,40 +77,34 @@ files:
|
|
104
77
|
- test/helper.rb
|
105
78
|
- test/test.conf
|
106
79
|
- test/test_leaderboard.rb
|
107
|
-
has_rdoc: true
|
108
80
|
homepage: http://github.com/agoragames/leaderboard
|
109
|
-
licenses:
|
81
|
+
licenses:
|
110
82
|
- MIT
|
111
83
|
post_install_message:
|
112
84
|
rdoc_options: []
|
113
|
-
|
114
|
-
require_paths:
|
85
|
+
require_paths:
|
115
86
|
- lib
|
116
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
117
88
|
none: false
|
118
|
-
requirements:
|
119
|
-
- -
|
120
|
-
- !ruby/object:Gem::Version
|
121
|
-
|
122
|
-
segments:
|
89
|
+
requirements:
|
90
|
+
- - ! '>='
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: '0'
|
93
|
+
segments:
|
123
94
|
- 0
|
124
|
-
|
125
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
95
|
+
hash: -2495258638232902776
|
96
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
126
97
|
none: false
|
127
|
-
requirements:
|
128
|
-
- -
|
129
|
-
- !ruby/object:Gem::Version
|
130
|
-
|
131
|
-
segments:
|
132
|
-
- 0
|
133
|
-
version: "0"
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
134
102
|
requirements: []
|
135
|
-
|
136
103
|
rubyforge_project:
|
137
|
-
rubygems_version: 1.
|
104
|
+
rubygems_version: 1.8.6
|
138
105
|
signing_key:
|
139
106
|
specification_version: 3
|
140
107
|
summary: Leaderboards backed by Redis in Ruby
|
141
|
-
test_files:
|
108
|
+
test_files:
|
142
109
|
- test/helper.rb
|
143
110
|
- test/test_leaderboard.rb
|