Skip to content

Commit

Permalink
Consolidate rank_member_in logic.
Browse files Browse the repository at this point in the history
We sometimes need to remove a rank in the ties leaderboard.
Fixes #53.
  • Loading branch information
David Czarnecki committed Mar 19, 2015
1 parent 55da905 commit bb6854c
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 14 deletions.
2 changes: 1 addition & 1 deletion .ruby-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ruby-2.1.2
ruby-2.2.1
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
language: ruby
rvm:
- 2.1.2
- 2.2.1
services:
- redis-server
- redis-server
19 changes: 8 additions & 11 deletions lib/tie_ranking_leaderboard.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,13 @@ def change_score_for_member_in(leaderboard_name, member, delta)
# @param score [float] Member score.
# @param member_data [String] Optional member data.
def rank_member_in(leaderboard_name, member, score, member_data = nil)
member_score = @redis_connection.zscore(leaderboard_name, member) || nil
can_delete_score = member_score && members_from_score_range_in(leaderboard_name, member_score, member_score).length == 1

@redis_connection.multi do |transaction|
transaction.zadd(leaderboard_name, score, member)
transaction.zadd(ties_leaderboard_key(leaderboard_name), score, score.to_f.to_s)
transaction.zrem(ties_leaderboard_key(leaderboard_name), member_score.to_f.to_s) if can_delete_score
transaction.hset(member_data_key(leaderboard_name), member, member_data) if member_data
end
end
Expand All @@ -87,12 +91,8 @@ def rank_member_in(leaderboard_name, member, score, member_data = nil)
# @param score [float] Member score.
# @param member_data [String] Optional member data.
def rank_member_across(leaderboards, member, score, member_data = nil)
@redis_connection.multi do |transaction|
leaderboards.each do |leaderboard_name|
transaction.zadd(leaderboard_name, score, member)
transaction.zadd(ties_leaderboard_key(leaderboard_name), score, score.to_f.to_s)
transaction.hset(member_data_key(leaderboard_name), member, member_data) if member_data
end
leaderboards.each do |leaderboard_name|
rank_member_in(leaderboard_name, member, score, member_data)
end
end

Expand All @@ -105,11 +105,8 @@ def rank_members_in(leaderboard_name, *members_and_scores)
members_and_scores.flatten!
end

@redis_connection.multi do |transaction|
members_and_scores.each_slice(2) do |member_and_score|
transaction.zadd(leaderboard_name, member_and_score[1], member_and_score[0])
transaction.zadd(ties_leaderboard_key(leaderboard_name), member_and_score[0], member_and_score[0].to_f.to_s)
end
members_and_scores.each_slice(2) do |member_and_score|
rank_member_in(leaderboard_name, member_and_score[0], member_and_score[1])
end
end

Expand Down
16 changes: 16 additions & 0 deletions spec/reverse_tie_ranking_leaderboard_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -171,5 +171,21 @@

leaderboard.disconnect
end

it 'should output the correct rank when initial score is 0 and then later scores are tied' do
# See https://github.com/agoragames/leaderboard/issues/53
leaderboard = TieRankingLeaderboard.new('ties', {:reverse => true}, {:host => "127.0.0.1", :db => 15})

leaderboard.rank_members('member_1', 0, 'member_2', 0)
expect(leaderboard.rank_for('member_1')).to eq(1)
expect(leaderboard.rank_for('member_2')).to eq(1)
leaderboard.rank_members('member_1', 1, 'member_2', 1)
expect(leaderboard.rank_for('member_1')).to eq(1)
expect(leaderboard.rank_for('member_2')).to eq(1)
leaderboard.rank_members('member_1', 1, 'member_2', 1, 'member_3', 4)
expect(leaderboard.rank_for('member_3')).to eq(2)
expect(leaderboard.rank_for('member_1')).to eq(1)
expect(leaderboard.rank_for('member_2')).to eq(1)
end
end
end
16 changes: 16 additions & 0 deletions spec/tie_ranking_leaderboard_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -300,5 +300,21 @@
expect(ttl).to be <= 10
end
end

it 'should output the correct rank when initial score is 0 and then later scores are tied' do
# See https://github.com/agoragames/leaderboard/issues/53
leaderboard = TieRankingLeaderboard.new('ties', Leaderboard::DEFAULT_OPTIONS, {:host => "127.0.0.1", :db => 15})

leaderboard.rank_members('member_1', 0, 'member_2', 0)
expect(leaderboard.rank_for('member_1')).to eq(1)
expect(leaderboard.rank_for('member_2')).to eq(1)
leaderboard.rank_members('member_1', 1, 'member_2', 1)
expect(leaderboard.rank_for('member_1')).to eq(1)
expect(leaderboard.rank_for('member_2')).to eq(1)
leaderboard.rank_members('member_1', 1, 'member_2', 1, 'member_3', 4)
expect(leaderboard.rank_for('member_3')).to eq(1)
expect(leaderboard.rank_for('member_1')).to eq(2)
expect(leaderboard.rank_for('member_2')).to eq(2)
end
end
end

0 comments on commit bb6854c

Please sign in to comment.