diff --git a/README.markdown b/README.markdown index 2e9d3fa..a76efaf 100644 --- a/README.markdown +++ b/README.markdown @@ -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: diff --git a/lib/leaderboard.rb b/lib/leaderboard.rb index bee4cb7..c0ecde3 100644 --- a/lib/leaderboard.rb +++ b/lib/leaderboard.rb @@ -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. diff --git a/spec/leaderboard_spec.rb b/spec/leaderboard_spec.rb index c422860..3f5cb80 100644 --- a/spec/leaderboard_spec.rb +++ b/spec/leaderboard_spec.rb @@ -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 \ No newline at end of file