leaderboard 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.markdown CHANGED
@@ -1,3 +1,9 @@
1
+ # leaderboard 1.0.2 (2011-02-25)
2
+
3
+ * Adding `XXX_to`, `XXX_for`, `XXX_in` and `XXX_from` methods that will allow you to set the leaderboard name to interact with outside of creating a new object
4
+ * Added `merge_leaderboards(destination, keys, options = {:aggregate => :min})` method to merge leaderboards given by keys with this leaderboard into destination
5
+ * Added `intersect_leaderboards(destination, keys, options = {:aggregate => :min})` method to intersect leaderboards given by keys with this leaderboard into destination
6
+
1
7
  # leaderboard 1.0.1 (2011-02-16)
2
8
 
3
9
  * `redis_options` can be passed in the initializer to pass options for the connection to Redis
data/README.rdoc CHANGED
@@ -1,6 +1,6 @@
1
1
  = leaderboard
2
2
 
3
- Leaderboards backed by Redis, http://redis.io.
3
+ Leaderboards backed by Redis in Ruby, http://redis.io.
4
4
 
5
5
  Builds off ideas proposed in http://blog.agoragames.com/2011/01/01/creating-high-score-tables-leaderboards-using-redis/.
6
6
 
@@ -91,6 +91,8 @@ Get rank and score for an arbitrary list of members (e.g. friends):
91
91
  check_member?(member): Check to see whether member is in the leaderboard
92
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
93
  remove_members_in_score_range(min_score, max_score): Remove members from the leaderboard within a score range
94
+ merge_leaderboards(destination, keys, options = {:aggregate => :min}): Merge leaderboards given by keys with this leaderboard into destination
95
+ intersect_leaderboards(destination, keys, options = {:aggregate => :min}): Intersect leaderboards given by keys with this leaderboard into destination
94
96
 
95
97
  == Performance Metrics
96
98
 
data/Rakefile CHANGED
@@ -15,8 +15,8 @@ Jeweler::Tasks.new do |gem|
15
15
  gem.name = "leaderboard"
16
16
  gem.homepage = "http://github.com/agoragames/leaderboard"
17
17
  gem.license = "MIT"
18
- gem.summary = %Q{Leaderboards backed by Redis}
19
- gem.description = %Q{Leaderboards backed by Redis}
18
+ gem.summary = %Q{Leaderboards backed by Redis in Ruby}
19
+ gem.description = %Q{Leaderboards backed by Redis in Ruby}
20
20
  gem.email = "dczarnecki@agoragames.com"
21
21
  gem.authors = ["David Czarnecki"]
22
22
  # Include your dependencies below. Runtime dependencies are required when using your gem,
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.1
1
+ 1.0.2
data/leaderboard.gemspec CHANGED
@@ -5,12 +5,12 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{leaderboard}
8
- s.version = "1.0.1"
8
+ s.version = "1.0.2"
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-02-16}
13
- s.description = %q{Leaderboards backed by Redis}
12
+ s.date = %q{2011-02-25}
13
+ s.description = %q{Leaderboards backed by Redis in Ruby}
14
14
  s.email = %q{dczarnecki@agoragames.com}
15
15
  s.extra_rdoc_files = [
16
16
  "LICENSE.txt",
@@ -36,7 +36,7 @@ Gem::Specification.new do |s|
36
36
  s.licenses = ["MIT"]
37
37
  s.require_paths = ["lib"]
38
38
  s.rubygems_version = %q{1.3.7}
39
- s.summary = %q{Leaderboards backed by Redis}
39
+ s.summary = %q{Leaderboards backed by Redis in Ruby}
40
40
  s.test_files = [
41
41
  "test/helper.rb",
42
42
  "test/test_leaderboard.rb"
data/lib/leaderboard.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'redis'
2
2
 
3
3
  class Leaderboard
4
- VERSION = '1.0.1'.freeze
4
+ VERSION = '1.0.2'.freeze
5
5
 
6
6
  DEFAULT_PAGE_SIZE = 25
7
7
  DEFAULT_REDIS_HOST = 'localhost'
@@ -33,54 +33,102 @@ class Leaderboard
33
33
  end
34
34
 
35
35
  def add_member(member, score)
36
- @redis_connection.zadd(@leaderboard_name, score, member)
36
+ add_member_to(@leaderboard_name, member, score)
37
+ end
38
+
39
+ def add_member_to(leaderboard_name, member, score)
40
+ @redis_connection.zadd(leaderboard_name, score, member)
37
41
  end
38
42
 
39
43
  def remove_member(member)
40
- @redis_connection.zrem(@leaderboard_name, member)
44
+ remove_member_from(@leaderboard_name, member)
45
+ end
46
+
47
+ def remove_member_from(leaderboard_name, member)
48
+ @redis_connection.zrem(leaderboard_name, member)
41
49
  end
42
50
 
43
51
  def total_members
44
- @redis_connection.zcard(@leaderboard_name)
52
+ total_members_in(@leaderboard_name)
53
+ end
54
+
55
+ def total_members_in(leaderboard_name)
56
+ @redis_connection.zcard(leaderboard_name)
45
57
  end
46
58
 
47
59
  def total_pages
48
- (total_members / @page_size.to_f).ceil
60
+ total_pages_in(@leaderboard_name)
61
+ end
62
+
63
+ def total_pages_in(leaderboard_name)
64
+ (total_members_in(leaderboard_name) / @page_size.to_f).ceil
49
65
  end
50
66
 
51
67
  def total_members_in_score_range(min_score, max_score)
52
- @redis_connection.zcount(@leaderboard_name, min_score, max_score)
68
+ total_members_in_score_range_in(@leaderboard_name, min_score, max_score)
53
69
  end
54
70
 
55
- def change_score_for(member, delta)
56
- @redis_connection.zincrby(@leaderboard_name, delta, member)
71
+ def total_members_in_score_range_in(leaderboard_name, min_score, max_score)
72
+ @redis_connection.zcount(leaderboard_name, min_score, max_score)
57
73
  end
58
74
 
59
- def rank_for(member, use_zero_index_for_rank = false)
75
+ def change_score_for(member, delta)
76
+ change_score_for_member_in(@leaderboard_name, member, delta)
77
+ end
78
+
79
+ def change_score_for_member_in(leaderboard_name, member, delta)
80
+ @redis_connection.zincrby(leaderboard_name, delta, member)
81
+ end
82
+
83
+ def rank_for(member, use_zero_index_for_rank = false)
84
+ rank_for_in(@leaderboard_name, member, use_zero_index_for_rank)
85
+ end
86
+
87
+ def rank_for_in(leaderboard_name, member, use_zero_index_for_rank = false)
60
88
  if use_zero_index_for_rank
61
- return @redis_connection.zrevrank(@leaderboard_name, member)
89
+ return @redis_connection.zrevrank(leaderboard_name, member)
62
90
  else
63
- return @redis_connection.zrevrank(@leaderboard_name, member) + 1 rescue nil
91
+ return @redis_connection.zrevrank(leaderboard_name, member) + 1 rescue nil
64
92
  end
65
93
  end
66
94
 
67
95
  def score_for(member)
68
- @redis_connection.zscore(@leaderboard_name, member).to_f
96
+ score_for_in(@leaderboard_name, member)
69
97
  end
70
98
 
99
+ def score_for_in(leaderboard_name, member)
100
+ @redis_connection.zscore(leaderboard_name, member).to_f
101
+ end
102
+
71
103
  def check_member?(member)
72
- !@redis_connection.zscore(@leaderboard_name, member).nil?
104
+ check_member_in?(@leaderboard_name, member)
105
+ end
106
+
107
+ def check_member_in?(leaderboard_name, member)
108
+ !@redis_connection.zscore(leaderboard_name, member).nil?
73
109
  end
74
110
 
75
111
  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)}
112
+ score_and_rank_for_in(@leaderboard_name, member, use_zero_index_for_rank)
113
+ end
114
+
115
+ def score_and_rank_for_in(leaderboard_name, member, use_zero_index_for_rank = false)
116
+ {:member => member, :score => score_for_in(leaderboard_name, member), :rank => rank_for_in(leaderboard_name, member, use_zero_index_for_rank)}
77
117
  end
78
118
 
79
119
  def remove_members_in_score_range(min_score, max_score)
80
- @redis_connection.zremrangebyscore(@leaderboard_name, min_score, max_score)
120
+ remove_members_in_score_range_in(@leaderboard_name, min_score, max_score)
121
+ end
122
+
123
+ def remove_members_in_score_range_in(leaderboard_name, min_score, max_score)
124
+ @redis_connection.zremrangebyscore(leaderboard_name, min_score, max_score)
81
125
  end
82
126
 
83
127
  def leaders(current_page, with_scores = true, with_rank = true, use_zero_index_for_rank = false)
128
+ leaders_in(@leaderboard_name, current_page, with_scores, with_rank, use_zero_index_for_rank)
129
+ end
130
+
131
+ def leaders_in(leaderboard_name, current_page, with_scores = true, with_rank = true, use_zero_index_for_rank = false)
84
132
  if current_page < 1
85
133
  current_page = 1
86
134
  end
@@ -98,16 +146,20 @@ class Leaderboard
98
146
 
99
147
  ending_offset = (starting_offset + @page_size) - 1
100
148
 
101
- raw_leader_data = @redis_connection.zrevrange(@leaderboard_name, starting_offset, ending_offset, :with_scores => with_scores)
149
+ raw_leader_data = @redis_connection.zrevrange(leaderboard_name, starting_offset, ending_offset, :with_scores => with_scores)
102
150
  if raw_leader_data
103
- massage_leader_data(raw_leader_data, with_rank, use_zero_index_for_rank)
151
+ massage_leader_data(leaderboard_name, raw_leader_data, with_rank, use_zero_index_for_rank)
104
152
  else
105
153
  return nil
106
154
  end
107
155
  end
108
156
 
109
157
  def around_me(member, with_scores = true, with_rank = true, use_zero_index_for_rank = false)
110
- reverse_rank_for_member = @redis_connection.zrevrank(@leaderboard_name, member)
158
+ around_me_in(@leaderboard_name, member, with_scores, with_rank, use_zero_index_for_rank)
159
+ end
160
+
161
+ def around_me_in(leaderboard_name, member, with_scores = true, with_rank = true, use_zero_index_for_rank = false)
162
+ reverse_rank_for_member = @redis_connection.zrevrank(leaderboard_name, member)
111
163
 
112
164
  starting_offset = reverse_rank_for_member - (@page_size / 2)
113
165
  if starting_offset < 0
@@ -116,22 +168,26 @@ class Leaderboard
116
168
 
117
169
  ending_offset = (starting_offset + @page_size) - 1
118
170
 
119
- raw_leader_data = @redis_connection.zrevrange(@leaderboard_name, starting_offset, ending_offset, :with_scores => with_scores)
171
+ raw_leader_data = @redis_connection.zrevrange(leaderboard_name, starting_offset, ending_offset, :with_scores => with_scores)
120
172
  if raw_leader_data
121
- massage_leader_data(raw_leader_data, with_rank, use_zero_index_for_rank)
173
+ massage_leader_data(leaderboard_name, raw_leader_data, with_rank, use_zero_index_for_rank)
122
174
  else
123
175
  return nil
124
176
  end
125
177
  end
126
178
 
127
179
  def ranked_in_list(members, with_scores = true, use_zero_index_for_rank = false)
180
+ ranked_in_list_in(@leaderboard_name, members, with_scores, use_zero_index_for_rank)
181
+ end
182
+
183
+ def ranked_in_list_in(leaderboard_name, members, with_scores = true, use_zero_index_for_rank = false)
128
184
  ranks_for_members = []
129
185
 
130
186
  members.each do |member|
131
187
  data = {}
132
188
  data[:member] = member
133
- data[:rank] = rank_for(member, use_zero_index_for_rank)
134
- data[:score] = score_for(member) if with_scores
189
+ data[:rank] = rank_for_in(leaderboard_name, member, use_zero_index_for_rank)
190
+ data[:score] = score_for_in(leaderboard_name, member) if with_scores
135
191
 
136
192
  ranks_for_members << data
137
193
  end
@@ -139,9 +195,19 @@ class Leaderboard
139
195
  ranks_for_members
140
196
  end
141
197
 
198
+ # Merge leaderboards given by keys with this leaderboard into destination
199
+ def merge_leaderboards(destination, keys, options = {:aggregate => :sum})
200
+ @redis_connection.zunionstore(destination, keys.insert(0, @leaderboard_name), options)
201
+ end
202
+
203
+ # Intersect leaderboards given by keys with this leaderboard into destination
204
+ def intersect_leaderboards(destination, keys, options = {:aggregate => :sum})
205
+ @redis_connection.zinterstore(destination, keys.insert(0, @leaderboard_name), options)
206
+ end
207
+
142
208
  private
143
209
 
144
- def massage_leader_data(leaders, with_rank, use_zero_index_for_rank)
210
+ def massage_leader_data(leaderboard_name, leaders, with_rank, use_zero_index_for_rank)
145
211
  member_attribute = true
146
212
  leader_data = []
147
213
 
@@ -151,7 +217,7 @@ class Leaderboard
151
217
  data[:member] = leader_data_item
152
218
  else
153
219
  data[:score] = leader_data_item.to_f
154
- data[:rank] = rank_for(data[:member], use_zero_index_for_rank) if with_rank
220
+ data[:rank] = rank_for_in(leaderboard_name, data[:member], use_zero_index_for_rank) if with_rank
155
221
  leader_data << data
156
222
  data = {}
157
223
  end
@@ -1,7 +1,7 @@
1
1
  require 'helper'
2
2
 
3
3
  class TestLeaderboard < Test::Unit::TestCase
4
- def setup
4
+ def setup
5
5
  @leaderboard = Leaderboard.new('name')
6
6
  @redis_connection = Redis.new
7
7
  end
@@ -11,7 +11,7 @@ class TestLeaderboard < Test::Unit::TestCase
11
11
  end
12
12
 
13
13
  def test_version
14
- assert_equal '1.0.1', Leaderboard::VERSION
14
+ assert_equal '1.0.2', Leaderboard::VERSION
15
15
  end
16
16
 
17
17
  def test_initialize_with_defaults
@@ -204,6 +204,51 @@ class TestLeaderboard < Test::Unit::TestCase
204
204
  end
205
205
  end
206
206
 
207
+ def test_merge_leaderboards
208
+ foo = Leaderboard.new('foo')
209
+ bar = Leaderboard.new('bar')
210
+
211
+ foo.add_member('foo_1', 1)
212
+ foo.add_member('foo_2', 2)
213
+ bar.add_member('bar_1', 3)
214
+ bar.add_member('bar_2', 4)
215
+ bar.add_member('bar_3', 5)
216
+
217
+ foobar_keys = foo.merge_leaderboards('foobar', ['bar'])
218
+ assert_equal 5, foobar_keys
219
+
220
+ foobar = Leaderboard.new('foobar')
221
+ assert_equal 5, foobar.total_members
222
+
223
+ first_leader_in_foobar = foobar.leaders(1).first
224
+ assert_equal 1, first_leader_in_foobar[:rank]
225
+ assert_equal 'bar_3', first_leader_in_foobar[:member]
226
+ assert_equal 5, first_leader_in_foobar[:score]
227
+ end
228
+
229
+ def test_intersect_leaderboards
230
+ foo = Leaderboard.new('foo')
231
+ bar = Leaderboard.new('bar')
232
+
233
+ foo.add_member('foo_1', 1)
234
+ foo.add_member('foo_2', 2)
235
+ foo.add_member('bar_3', 6)
236
+ bar.add_member('bar_1', 3)
237
+ bar.add_member('foo_1', 4)
238
+ bar.add_member('bar_3', 5)
239
+
240
+ foobar_keys = foo.intersect_leaderboards('foobar', ['bar'], {:aggregate => :max})
241
+ assert_equal 2, foobar_keys
242
+
243
+ foobar = Leaderboard.new('foobar')
244
+ assert_equal 2, foobar.total_members
245
+
246
+ first_leader_in_foobar = foobar.leaders(1).first
247
+ assert_equal 1, first_leader_in_foobar[:rank]
248
+ assert_equal 'bar_3', first_leader_in_foobar[:member]
249
+ assert_equal 6, first_leader_in_foobar[:score]
250
+ end
251
+
207
252
  private
208
253
 
209
254
  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: 21
4
+ hash: 19
5
5
  prerelease: false
6
6
  segments:
7
7
  - 1
8
8
  - 0
9
- - 1
10
- version: 1.0.1
9
+ - 2
10
+ version: 1.0.2
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-02-16 00:00:00 -05:00
18
+ date: 2011-02-25 00:00:00 -05:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -80,7 +80,7 @@ dependencies:
80
80
  - 0
81
81
  version: "0"
82
82
  requirement: *id004
83
- description: Leaderboards backed by Redis
83
+ description: Leaderboards backed by Redis in Ruby
84
84
  email: dczarnecki@agoragames.com
85
85
  executables: []
86
86
 
@@ -137,7 +137,7 @@ rubyforge_project:
137
137
  rubygems_version: 1.3.7
138
138
  signing_key:
139
139
  specification_version: 3
140
- summary: Leaderboards backed by Redis
140
+ summary: Leaderboards backed by Redis in Ruby
141
141
  test_files:
142
142
  - test/helper.rb
143
143
  - test/test_leaderboard.rb