leaderboard 1.0.0 → 1.0.1
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/CHANGELOG.markdown +11 -0
- data/Gemfile +1 -1
- data/README.rdoc +28 -3
- data/VERSION +1 -1
- data/leaderboard.gemspec +6 -5
- data/lib/leaderboard.rb +27 -6
- data/test/test_leaderboard.rb +46 -1
- metadata +11 -8
data/CHANGELOG.markdown
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
# leaderboard 1.0.1 (2011-02-16)
|
2
|
+
|
3
|
+
* `redis_options` can be passed in the initializer to pass options for the connection to Redis
|
4
|
+
* `page_size` is now settable outside of the initializer
|
5
|
+
* `check_member?(member)`: Check to see whether member is in the leaderboard
|
6
|
+
* `score_and_rank_for(member, use_zero_index_for_rank = false)`: Retrieve the score and rank for a member in a single call
|
7
|
+
* `remove_members_in_score_range(min_score, max_score)`: Remove members from the leaderboard within a score range
|
8
|
+
|
9
|
+
# leaderboard 1.0.0
|
10
|
+
|
11
|
+
* Initial release
|
data/Gemfile
CHANGED
data/README.rdoc
CHANGED
@@ -12,6 +12,10 @@ Install the gem:
|
|
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.
|
15
|
+
|
16
|
+
== Compatibility
|
17
|
+
|
18
|
+
The gem has been built and tested under Ruby 1.8.7 and Ruby 1.9.2
|
15
19
|
|
16
20
|
== Usage
|
17
21
|
|
@@ -19,6 +23,18 @@ Create a new leaderboard or attach to an existing leaderboard named 'highscores'
|
|
19
23
|
|
20
24
|
ruby-1.8.7-p302 > highscore_lb = Leaderboard.new('highscores')
|
21
25
|
=> #<Leaderboard:0x1018e4250 @page_size=25, @port=6379, @host="localhost", @redis_connection=#<Redis client v2.1.1 connected to redis://localhost:6379/0 (Redis v2.1.10)>, @leaderboard_name="highscores">
|
26
|
+
|
27
|
+
If you need to pass in options for Redis, you can do this with the redis_options parameter:
|
28
|
+
|
29
|
+
redis_options = {:host => 'localhost', :port => 6379, :password => 'password', :db => 'some_redis_db'}
|
30
|
+
highscore_lb = Leaderboard.new('highscores', redis_options[:host], redis_options[:port], Leaderboard::DEFAULT_PAGE_SIZE, redis_options))
|
31
|
+
|
32
|
+
You can set the page size to something other than the default page size (25):
|
33
|
+
|
34
|
+
ruby-1.8.7-p302 > highscore_lb.page_size = 5
|
35
|
+
=> 5
|
36
|
+
ruby-1.8.7-p302 > highscore_lb
|
37
|
+
=> #<Leaderboard:0x1018e2130 @leaderboard_name="highscores", @page_size=5, @port=6379, @redis_connection=#<Redis client v2.1.1 connected to redis://localhost:6379/0 (Redis v2.1.10)>, @host="localhost", @redis_options={:host=>"localhost", :port=>6379}>
|
22
38
|
|
23
39
|
Add members to your leaderboard:
|
24
40
|
|
@@ -66,7 +82,16 @@ Get rank and score for an arbitrary list of members (e.g. friends):
|
|
66
82
|
|
67
83
|
ruby-1.8.7-p302 > highscore_lb.ranked_in_list(['member_1', 'member_62', 'member_67'], true)
|
68
84
|
=> [{:rank=>55, :member=>"member_1", :score=>1.0}, {:rank=>33, :member=>"member_62", :score=>62.0}, {:rank=>28, :member=>"member_67", :score=>67.0}]
|
69
|
-
|
85
|
+
|
86
|
+
=== Other useful methods
|
87
|
+
|
88
|
+
remove_member(member): Remove a member from the leaderboard
|
89
|
+
total_members_in_score_range(min_score, max_score): Count the number of members within a score range in the leaderboard
|
90
|
+
change_score_for(member, delta): Change the score for a member by some amount delta (delta could be positive or negative)
|
91
|
+
check_member?(member): Check to see whether member is in the leaderboard
|
92
|
+
score_and_rank_for(member, use_zero_index_for_rank = false): Retrieve the score and rank for a member in a single call
|
93
|
+
remove_members_in_score_range(min_score, max_score): Remove members from the leaderboard within a score range
|
94
|
+
|
70
95
|
== Performance Metrics
|
71
96
|
|
72
97
|
10 million sequential scores insert:
|
@@ -124,6 +149,7 @@ Average time to request an arbitrary page from the leaderboard:
|
|
124
149
|
== Future Ideas
|
125
150
|
|
126
151
|
* Bulk insert
|
152
|
+
* Atomicity for various operations?
|
127
153
|
* Is nil? OK to return if Redis returns no data or should it be []?
|
128
154
|
|
129
155
|
== Contributing to leaderboard
|
@@ -138,6 +164,5 @@ Average time to request an arbitrary page from the leaderboard:
|
|
138
164
|
|
139
165
|
== Copyright
|
140
166
|
|
141
|
-
Copyright (c) 2011 David Czarnecki. See LICENSE.txt for
|
142
|
-
further details.
|
167
|
+
Copyright (c) 2011 David Czarnecki. See LICENSE.txt for further details.
|
143
168
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.0.
|
1
|
+
1.0.1
|
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 = "1.0.
|
8
|
+
s.version = "1.0.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["David Czarnecki"]
|
12
|
-
s.date = %q{2011-
|
12
|
+
s.date = %q{2011-02-16}
|
13
13
|
s.description = %q{Leaderboards backed by Redis}
|
14
14
|
s.email = %q{dczarnecki@agoragames.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -19,6 +19,7 @@ Gem::Specification.new do |s|
|
|
19
19
|
s.files = [
|
20
20
|
".document",
|
21
21
|
".rvmrc",
|
22
|
+
"CHANGELOG.markdown",
|
22
23
|
"Gemfile",
|
23
24
|
"LICENSE.txt",
|
24
25
|
"README.rdoc",
|
@@ -46,18 +47,18 @@ Gem::Specification.new do |s|
|
|
46
47
|
s.specification_version = 3
|
47
48
|
|
48
49
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
49
|
-
s.add_runtime_dependency(%q<redis>, ["
|
50
|
+
s.add_runtime_dependency(%q<redis>, ["~> 2.1.1"])
|
50
51
|
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
51
52
|
s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
|
52
53
|
s.add_development_dependency(%q<rcov>, [">= 0"])
|
53
54
|
else
|
54
|
-
s.add_dependency(%q<redis>, ["
|
55
|
+
s.add_dependency(%q<redis>, ["~> 2.1.1"])
|
55
56
|
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
56
57
|
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
57
58
|
s.add_dependency(%q<rcov>, [">= 0"])
|
58
59
|
end
|
59
60
|
else
|
60
|
-
s.add_dependency(%q<redis>, ["
|
61
|
+
s.add_dependency(%q<redis>, ["~> 2.1.1"])
|
61
62
|
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
62
63
|
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
63
64
|
s.add_dependency(%q<rcov>, [">= 0"])
|
data/lib/leaderboard.rb
CHANGED
@@ -1,15 +1,18 @@
|
|
1
1
|
require 'redis'
|
2
2
|
|
3
3
|
class Leaderboard
|
4
|
-
VERSION = '1.0.
|
4
|
+
VERSION = '1.0.1'.freeze
|
5
|
+
|
5
6
|
DEFAULT_PAGE_SIZE = 25
|
7
|
+
DEFAULT_REDIS_HOST = 'localhost'
|
8
|
+
DEFAULT_REDIS_PORT = 6379
|
6
9
|
|
7
10
|
attr_reader :host
|
8
11
|
attr_reader :port
|
9
12
|
attr_reader :leaderboard_name
|
10
|
-
|
13
|
+
attr_accessor :page_size
|
11
14
|
|
12
|
-
def initialize(leaderboard_name, host =
|
15
|
+
def initialize(leaderboard_name, host = DEFAULT_REDIS_HOST, port = DEFAULT_REDIS_PORT, page_size = DEFAULT_PAGE_SIZE, redis_options = {})
|
13
16
|
@leaderboard_name = leaderboard_name
|
14
17
|
@host = host
|
15
18
|
@port = port
|
@@ -20,7 +23,13 @@ class Leaderboard
|
|
20
23
|
|
21
24
|
@page_size = page_size
|
22
25
|
|
23
|
-
|
26
|
+
redis_options = redis_options.dup
|
27
|
+
redis_options[:host] ||= @host
|
28
|
+
redis_options[:port] ||= @port
|
29
|
+
|
30
|
+
@redis_options = redis_options
|
31
|
+
|
32
|
+
@redis_connection = Redis.new(@redis_options)
|
24
33
|
end
|
25
34
|
|
26
35
|
def add_member(member, score)
|
@@ -58,7 +67,19 @@ class Leaderboard
|
|
58
67
|
def score_for(member)
|
59
68
|
@redis_connection.zscore(@leaderboard_name, member).to_f
|
60
69
|
end
|
61
|
-
|
70
|
+
|
71
|
+
def check_member?(member)
|
72
|
+
!@redis_connection.zscore(@leaderboard_name, member).nil?
|
73
|
+
end
|
74
|
+
|
75
|
+
def score_and_rank_for(member, use_zero_index_for_rank = false)
|
76
|
+
{:member => member, :score => score_for(member), :rank => rank_for(member, use_zero_index_for_rank)}
|
77
|
+
end
|
78
|
+
|
79
|
+
def remove_members_in_score_range(min_score, max_score)
|
80
|
+
@redis_connection.zremrangebyscore(@leaderboard_name, min_score, max_score)
|
81
|
+
end
|
82
|
+
|
62
83
|
def leaders(current_page, with_scores = true, with_rank = true, use_zero_index_for_rank = false)
|
63
84
|
if current_page < 1
|
64
85
|
current_page = 1
|
@@ -129,7 +150,7 @@ class Leaderboard
|
|
129
150
|
if member_attribute
|
130
151
|
data[:member] = leader_data_item
|
131
152
|
else
|
132
|
-
data[:score] = leader_data_item
|
153
|
+
data[:score] = leader_data_item.to_f
|
133
154
|
data[:rank] = rank_for(data[:member], use_zero_index_for_rank) if with_rank
|
134
155
|
leader_data << data
|
135
156
|
data = {}
|
data/test/test_leaderboard.rb
CHANGED
@@ -11,7 +11,7 @@ class TestLeaderboard < Test::Unit::TestCase
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def test_version
|
14
|
-
assert_equal '1.0.
|
14
|
+
assert_equal '1.0.1', Leaderboard::VERSION
|
15
15
|
end
|
16
16
|
|
17
17
|
def test_initialize_with_defaults
|
@@ -159,6 +159,51 @@ class TestLeaderboard < Test::Unit::TestCase
|
|
159
159
|
assert_equal 5, @leaderboard.score_for('member_1')
|
160
160
|
end
|
161
161
|
|
162
|
+
def test_check_member
|
163
|
+
@leaderboard.add_member('member_1', 10)
|
164
|
+
|
165
|
+
assert_equal true, @leaderboard.check_member?('member_1')
|
166
|
+
assert_equal false, @leaderboard.check_member?('member_2')
|
167
|
+
end
|
168
|
+
|
169
|
+
def test_can_change_page_size_and_have_it_reflected_in_size_of_result_set
|
170
|
+
add_members_to_leaderboard(Leaderboard::DEFAULT_PAGE_SIZE)
|
171
|
+
|
172
|
+
@leaderboard.page_size = 5
|
173
|
+
assert_equal 5, @leaderboard.total_pages
|
174
|
+
assert_equal 5, @leaderboard.leaders(1).size
|
175
|
+
end
|
176
|
+
|
177
|
+
def test_score_and_rank_for
|
178
|
+
add_members_to_leaderboard
|
179
|
+
|
180
|
+
data = @leaderboard.score_and_rank_for('member_1')
|
181
|
+
assert_equal 'member_1', data[:member]
|
182
|
+
assert_equal 1, data[:score]
|
183
|
+
assert_equal 5, data[:rank]
|
184
|
+
end
|
185
|
+
|
186
|
+
def test_remove_members_in_score_range
|
187
|
+
add_members_to_leaderboard
|
188
|
+
|
189
|
+
assert_equal 5, @leaderboard.total_members
|
190
|
+
|
191
|
+
@leaderboard.add_member('cheater_1', 100)
|
192
|
+
@leaderboard.add_member('cheater_2', 101)
|
193
|
+
@leaderboard.add_member('cheater_3', 102)
|
194
|
+
|
195
|
+
assert_equal 8, @leaderboard.total_members
|
196
|
+
|
197
|
+
@leaderboard.remove_members_in_score_range(100, 102)
|
198
|
+
|
199
|
+
assert_equal 5, @leaderboard.total_members
|
200
|
+
|
201
|
+
leaders = @leaderboard.leaders(1)
|
202
|
+
leaders.each do |leader|
|
203
|
+
assert leader[:score] < 100
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
162
207
|
private
|
163
208
|
|
164
209
|
def add_members_to_leaderboard(members_to_add = 5)
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: leaderboard
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 21
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 1.0.
|
9
|
+
- 1
|
10
|
+
version: 1.0.1
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- David Czarnecki
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-
|
18
|
+
date: 2011-02-16 00:00:00 -05:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -25,12 +25,14 @@ dependencies:
|
|
25
25
|
version_requirements: &id001 !ruby/object:Gem::Requirement
|
26
26
|
none: false
|
27
27
|
requirements:
|
28
|
-
- -
|
28
|
+
- - ~>
|
29
29
|
- !ruby/object:Gem::Version
|
30
|
-
hash:
|
30
|
+
hash: 9
|
31
31
|
segments:
|
32
|
-
-
|
33
|
-
|
32
|
+
- 2
|
33
|
+
- 1
|
34
|
+
- 1
|
35
|
+
version: 2.1.1
|
34
36
|
requirement: *id001
|
35
37
|
- !ruby/object:Gem::Dependency
|
36
38
|
type: :development
|
@@ -90,6 +92,7 @@ extra_rdoc_files:
|
|
90
92
|
files:
|
91
93
|
- .document
|
92
94
|
- .rvmrc
|
95
|
+
- CHANGELOG.markdown
|
93
96
|
- Gemfile
|
94
97
|
- LICENSE.txt
|
95
98
|
- README.rdoc
|