Permalink
Browse files

Support for storing optional member data for a given leaderboard.

- Integrate #11/
- Modified various methods in #11 to be more descriptive
- Added member_data_for and update_member_data methods
- Updated .gitignore for Redis .pid files
  • Loading branch information...
1 parent 48c6ce0 commit db2c926f598ef71584339037d2977ddcf95eee09 @czarneckid czarneckid committed Jun 9, 2012
Showing with 81 additions and 25 deletions.
  1. +1 −0 .gitignore
  2. +3 −1 README.markdown
  3. +58 −18 lib/leaderboard.rb
  4. +0 −1 spec/db/redis.pid
  5. +18 −4 spec/leaderboard_spec.rb
  6. +1 −1 spec/spec_helper.rb
View
1 .gitignore
@@ -8,3 +8,4 @@ pkg
Gemfile.lock
test/db/*
*.rdb
+spec/db/*.pid
View
4 README.markdown
@@ -184,7 +184,9 @@ Use this method to do bulk insert of data, but be mindful of the amount of data
### Other useful methods
```
- delete_leaderboard: Delete the current leaderboard
+ delete_leaderboard: Delete the current leaderboard
+ member_data_for(member): Retrieve the optional member data for a given member in the leaderboard
+ update_member_data(member, member_data): Update the optional member data for a given member in the leaderboard
remove_member(member): Remove a member from the leaderboard
total_members: Total # of members in the leaderboard
total_pages: Total # of pages in the leaderboard given the leaderboard's page_size
View
76 lib/leaderboard.rb
@@ -29,6 +29,7 @@ class Leaderboard
# Default options when requesting data from a leaderboard.
# +:with_scores+ true: Return scores along with the member names.
# +:with_rank+ true: Return ranks along with the member names.
+ # +:with_data+ false: Return member data along with the member names.
# +:use_zero_index_for_rank+ false: If you want to 0-index ranks.
# +:page_size+ nil: The default page size will be used.
DEFAULT_LEADERBOARD_REQUEST_OPTIONS = {
@@ -109,32 +110,60 @@ def delete_leaderboard_named(leaderboard_name)
#
# @param member [String] Member name.
# @param score [float] Member score.
- def rank_member(member, score, data=nil)
- rank_member_in(@leaderboard_name, member, score, data)
+ # @param member_data [Hash] Optional member data.
+ def rank_member(member, score, member_data = nil)
+ rank_member_in(@leaderboard_name, member, score, member_data)
end
# Rank a member in the named leaderboard.
#
# @param leaderboard_name [String] Name of the leaderboard.
# @param member [String] Member name.
# @param score [float] Member score.
- def rank_member_in(leaderboard_name, member, score, data)
- @redis_connection.zadd(leaderboard_name, score, member)
- add_member_data_in(leaderboard_name, member, data)
+ # @param member_data [Hash] Optional member data.
+ def rank_member_in(leaderboard_name, member, score, member_data)
+ @redis_connection.multi do |transaction|
+ transaction.zadd(leaderboard_name, score, member)
+ if member_data
+ transaction.hmset(member_data_key(leaderboard_name, member), *member_data.flatten)
+ end
+ end
end
- def add_member_data_in(leaderboard_name, member, data={})
- if data && data.size > 0
- @redis_connection.hmset(data_key_for_member_in(leaderboard_name, member), *data.flatten)
- end
+ # Retrieve the optional member data for a given member in the leaderboard.
+ #
+ # @param member [String] Member name.
+ #
+ # @return Hash of optional member data.
+ def member_data_for(member)
+ member_data_for_in(@leaderboard_name, member)
end
- def data_for_member_in(leaderboard_name, member)
- @redis_connection.hgetall(data_key_for_member_in(leaderboard_name, member))
- end
+ # Retrieve the optional member data for a given member in the named leaderboard.
+ #
+ # @param leaderboard_name [String] Name of the leaderboard.
+ # @param member [String] Member name.
+ #
+ # @return Hash of optional member data.
+ def member_data_for_in(leaderboard_name, member)
+ @redis_connection.hgetall(member_data_key(leaderboard_name, member))
+ end
- def data_key_for_member_in(leaderboard_name, member)
- "#{leaderboard_name}:data:#{member}"
+ # Update the optional member data for a given member in the leaderboard.
+ #
+ # @param member [String] Member name.
+ # @param member_data [Hash] Optional member data.
+ def update_member_data(member, member_data)
+ update_member_data_in(@leaderboard_name, member, member_data)
+ end
+
+ # Update the optional member data for a given member in the named leaderboard.
+ #
+ # @param leaderboard_name [String] Name of the leaderboard.
+ # @param member [String] Member name.
+ # @param member_data [Hash] Optional member data.
+ def update_member_data_in(leaderboard_name, member, member_data)
+ @redis_connection.hmset(member_data_key(leaderboard_name, member), *member_data.flatten)
end
# Rank an array of members in the leaderboard.
@@ -172,8 +201,10 @@ def remove_member(member)
# @param leaderboard_name [String] Name of the leaderboard.
# @param member [String] Member name.
def remove_member_from(leaderboard_name, member)
- @redis_connection.zrem(leaderboard_name, member)
- @redis_connection.del(data_key_for_member_in(leaderboard_name, member))
+ @redis_connection.multi do |transaction|
+ transaction.zrem(leaderboard_name, member)
+ transaction.del(member_data_key(leaderboard_name, member))
+ end
end
# Retrieve the total number of members in the leaderboard.
@@ -593,8 +624,7 @@ def ranked_in_list_in(leaderboard_name, members, options = {})
end
if leaderboard_options[:with_data]
- extra_data = data_for_member_in(leaderboard_name, member)
- data[:data] = extra_data if extra_data && extra_data.size > 0
+ data[:member_data] = member_data_for_in(leaderboard_name, member)
end
ranks_for_members << data
@@ -623,6 +653,16 @@ def intersect_leaderboards(destination, keys, options = {:aggregate => :sum})
private
+ # Key for retrieving optional member data.
+ #
+ # @param leaderboard_name [String] Name of the leaderboard.
+ # @param member [String] Member name.
+ #
+ # @return a key in the form of +leaderboard_name:data:member+
+ def member_data_key(leaderboard_name, member)
+ "#{leaderboard_name}:member_data:#{member}"
+ end
+
# Validate and return the page size. Returns the +DEFAULT_PAGE_SIZE+ if the page size is less than 1.
#
# @param page_size [int] Page size.
View
1 spec/db/redis.pid
@@ -1 +0,0 @@
-82118
View
22 spec/leaderboard_spec.rb
@@ -140,19 +140,33 @@
end
it 'should allow you to retrieve leaders with extra data' do
- Time.stub(:now).and_return(123)
rank_members_in_leaderboard(Leaderboard::DEFAULT_PAGE_SIZE)
@leaderboard.total_members.should be(Leaderboard::DEFAULT_PAGE_SIZE)
- leaders = @leaderboard.leaders(1, {:with_scores => false, :with_rank => false, :with_data=>true})
+ leaders = @leaderboard.leaders(1, {:with_scores => false, :with_rank => false, :with_data => true})
- member_25 = {:member => 'member_25', :data=>{ "date"=>"123" }}
+ member_25 = {:member => 'member_25', :member_data => { "member_name" => "Leaderboard member 25" }}
leaders[0].should == member_25
- member_1 = {:member => 'member_1', :data=>{ "date"=>"123" }}
+ member_1 = {:member => 'member_1', :member_data => { "member_name" => "Leaderboard member 1" }}
leaders[24].should == member_1
end
+ it 'should allow you to retrieve optional member data' do
+ @leaderboard.rank_member('member_id', 1, {'username' => 'member_name', 'other_data_key' => 'other_data_value'})
+
+ @leaderboard.member_data_for('unknown_member').should == {}
+ @leaderboard.member_data_for('member_id').should == {'username' => 'member_name', 'other_data_key' => 'other_data_value'}
+ end
+
+ it 'should allow you to update optional member data' do
+ @leaderboard.rank_member('member_id', 1, {'username' => 'member_name'})
+
+ @leaderboard.member_data_for('member_id').should == {'username' => 'member_name'}
+ @leaderboard.update_member_data('member_id', {'other_data_key' => 'other_data_value'})
+ @leaderboard.member_data_for('member_id').should == {'username' => 'member_name', 'other_data_key' => 'other_data_value'}
+ end
+
it 'should allow you to call leaders with various options that respect the defaults for the options not passed in' do
rank_members_in_leaderboard(Leaderboard::DEFAULT_PAGE_SIZE + 1)
View
2 spec/spec_helper.rb
@@ -9,7 +9,7 @@
# @param members_to_add [int] Number of members to add to the leaderboard.
def rank_members_in_leaderboard(members_to_add = 5)
1.upto(members_to_add) do |index|
- @leaderboard.rank_member("member_#{index}", index, { :date => Time.now.to_i })
+ @leaderboard.rank_member("member_#{index}", index, { :member_name => "Leaderboard member #{index}" })
end
end
end

0 comments on commit db2c926

Please sign in to comment.