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.
@@ -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
@@ -8,5 +8,5 @@ group :development do
8
8
  gem "rcov", ">= 0"
9
9
  end
10
10
 
11
- gem 'redis'
11
+ gem 'redis', "~> 2.1.1"
12
12
 
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.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.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-01-05}
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>, [">= 0"])
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>, [">= 0"])
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>, [">= 0"])
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.0'.freeze
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
- attr_reader :page_size
13
+ attr_accessor :page_size
11
14
 
12
- def initialize(leaderboard_name, host = 'localhost', port = 6379, page_size = DEFAULT_PAGE_SIZE)
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
- @redis_connection = Redis.new(:host => @host, :port => @port)
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 = {}
@@ -11,7 +11,7 @@ class TestLeaderboard < Test::Unit::TestCase
11
11
  end
12
12
 
13
13
  def test_version
14
- assert_equal '1.0.0', Leaderboard::VERSION
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: 23
4
+ hash: 21
5
5
  prerelease: false
6
6
  segments:
7
7
  - 1
8
8
  - 0
9
- - 0
10
- version: 1.0.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-01-05 00:00:00 -05:00
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: 3
30
+ hash: 9
31
31
  segments:
32
- - 0
33
- version: "0"
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