leaderboard 1.0.1 → 1.0.2

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 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