Skip to content

Commit

Permalink
Merge branch 'version-3-proposal-rank-member-if' into version-3
Browse files Browse the repository at this point in the history
  • Loading branch information
David Czarnecki committed Nov 28, 2012
2 parents b8a0bcc + 4d42cba commit 4c84301
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 0 deletions.
30 changes: 30 additions & 0 deletions README.markdown
Expand Up @@ -236,6 +236,36 @@ friends = highscore_lb.ranked_in_list(['member_6', 'member_1', 'member_10'], :so
=> [{:member=>"member_1", :rank=>56, :score=>1.0}, {:member=>"member_6", :rank=>51, :score=>6.0}, {:member=>"member_10", :rank=>47, :score=>10.0}]
```

### Conditionally rank a member in the leaderboard

You can pass a lambda to the `rank_member_if` method to conditionally rank a member in the leaderboard. The lambda is passed the following 5 parameters:

* `member`: Member name.
* `current_score`: Current score for the member in the leaderboard. May be `nil` if the member is not currently ranked in the leaderboard.
* `score`: Member score.
* `member_data`: Optional member data.
* `leaderboard_options`: Leaderboard options, e.g. :reverse => Value of reverse option

```ruby
highscore_check = lambda do |member, current_score, score, member_data, leaderboard_options|
return true if current_score.nil?
return true if score > current_score
false
end

highscore_lb.rank_member_if(highscore_check, 'david', 1337)
highscore_lb.score_for('david')
=> 1337.0
highscore_lb.rank_member_if(highscore_check, 'david', 1336)
highscore_lb.score_for('david')
=> 1337.0
highscore_lb.rank_member_if(highscore_check, 'david', 1338)
highscore_lb.score_for('david')
=> 1338.0
```

NOTE: Use a lambda and not a proc, otherwise you will get a `LocalJumpError` as a return statement in the proc will return from the method enclosing the proc.

### Ranking multiple members in a leaderboard at once

Insert multiple data items for members and their associated scores:
Expand Down
40 changes: 40 additions & 0 deletions lib/leaderboard.rb
Expand Up @@ -127,6 +127,46 @@ def rank_member_in(leaderboard_name, member, score, member_data = nil)
end
end

# Rank a member in the leaderboard based on execution of the +rank_conditional+.
#
# The +rank_conditional+ is passed the following parameters:
# member: Member name.
# current_score: Current score for the member in the leaderboard.
# score: Member score.
# member_data: Optional member data.
# leaderboard_options: Leaderboard options, e.g. :reverse => Value of reverse option
#
# @param rank_conditional [lambda] Lambda which must return +true+ or +false+ that controls whether or not the member is ranked in the leaderboard.
# @param member [String] Member name.
# @param score [String] Member score.
# @param member_data [Hash] Optional member_data.
def rank_member_if(rank_conditional, member, score, member_data = nil)
rank_member_if_in(@leaderboard_name, rank_conditional, member, score, member_data)
end

# Rank a member in the named leaderboard based on execution of the +rank_conditional+.
#
# The +rank_conditional+ is passed the following parameters:
# member: Member name.
# current_score: Current score for the member in the leaderboard.
# score: Member score.
# member_data: Optional member data.
# leaderboard_options: Leaderboard options, e.g. :reverse => Value of reverse option
#
# @param leaderboard_name [String] Name of the leaderboard.
# @param rank_conditional [lambda] Lambda which must return +true+ or +false+ that controls whether or not the member is ranked in the leaderboard.
# @param member [String] Member name.
# @param score [String] Member score.
# @param member_data [Hash] Optional member_data.
def rank_member_if_in(leaderboard_name, rank_conditional, member, score, member_data = nil)
current_score = @redis_connection.zscore(leaderboard_name, member)
current_score = current_score.to_f if current_score

if rank_conditional.call(member, current_score, score, member_data, {:reverse => @reverse})
rank_member_in(leaderboard_name, member, score, member_data)
end
end

# Retrieve the optional member data for a given member in the leaderboard.
#
# @param member [String] Member name.
Expand Down
18 changes: 18 additions & 0 deletions spec/leaderboard_spec.rb
Expand Up @@ -642,4 +642,22 @@
ranked_members[0][:score].should be_nil
ranked_members[0][:rank].should be_nil
end

it 'should rank a member in the leaderboard with conditional execution' do
@leaderboard.reverse = true
highscore_check = lambda do |member, current_score, score, member_data, leaderboard_options|
return true if current_score.nil?
return true if score > current_score
false
end

@leaderboard.total_members.should be(0)
@leaderboard.rank_member_if(highscore_check, 'david', 1337)
@leaderboard.total_members.should be(1)
@leaderboard.score_for('david').should eql(1337.0)
@leaderboard.rank_member_if(highscore_check, 'david', 1336)
@leaderboard.score_for('david').should eql(1337.0)
@leaderboard.rank_member_if(highscore_check, 'david', 1338)
@leaderboard.score_for('david').should eql(1338.0)
end
end

0 comments on commit 4c84301

Please sign in to comment.