From d8977ecbea6e15ef68fa8f241ab433011ce69a54 Mon Sep 17 00:00:00 2001 From: David Czarnecki Date: Tue, 27 Nov 2012 21:05:37 -0500 Subject: [PATCH 1/5] Added rank_member_if to rank a member in the leaderboard based on execution of a conditional --- README.markdown | 20 ++++++++++++++++++++ lib/leaderboard.rb | 38 ++++++++++++++++++++++++++++++++++++++ spec/leaderboard_spec.rb | 17 +++++++++++++++++ 3 files changed, 75 insertions(+) diff --git a/README.markdown b/README.markdown index 2e9d3fa..44e8062 100644 --- a/README.markdown +++ b/README.markdown @@ -236,6 +236,26 @@ 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: + +```ruby +highscore_check = lambda do |member, current_score, score, member_data| + 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 +``` + ### 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..0466159 100644 --- a/lib/leaderboard.rb +++ b/lib/leaderboard.rb @@ -127,6 +127,44 @@ 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 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. + # + # @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 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. + # + # @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) + 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..5b1f26b 100644 --- a/spec/leaderboard_spec.rb +++ b/spec/leaderboard_spec.rb @@ -642,4 +642,21 @@ 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 + highscore_check = lambda do |member, current_score, score, member_data| + 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 From 0715e798fac32764f2a41bad7636d14c450bf8a2 Mon Sep 17 00:00:00 2001 From: David Czarnecki Date: Tue, 27 Nov 2012 21:17:08 -0500 Subject: [PATCH 2/5] Added NOTE on using lambda for the rank_conditional --- README.markdown | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.markdown b/README.markdown index 44e8062..6ee4063 100644 --- a/README.markdown +++ b/README.markdown @@ -256,6 +256,8 @@ 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: From 8bcc4e6c40884e157f2d14245ec1817872c40b0d Mon Sep 17 00:00:00 2001 From: David Czarnecki Date: Wed, 28 Nov 2012 01:16:38 -0500 Subject: [PATCH 3/5] Add leaderboard_options passed to rank_conditional lambda. --- README.markdown | 10 +++++++++- lib/leaderboard.rb | 8 +++++--- spec/leaderboard_spec.rb | 3 ++- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/README.markdown b/README.markdown index 6ee4063..a2f64d4 100644 --- a/README.markdown +++ b/README.markdown @@ -238,8 +238,16 @@ friends = highscore_lb.ranked_in_list(['member_6', 'member_1', 'member_10'], :so 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. +* `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| +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 diff --git a/lib/leaderboard.rb b/lib/leaderboard.rb index 0466159..c0ecde3 100644 --- a/lib/leaderboard.rb +++ b/lib/leaderboard.rb @@ -129,11 +129,12 @@ def rank_member_in(leaderboard_name, member, score, member_data = nil) # Rank a member in the leaderboard based on execution of the +rank_conditional+. # - # The conditional is passed the following parameters: + # 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. @@ -145,11 +146,12 @@ def rank_member_if(rank_conditional, member, score, member_data = nil) # Rank a member in the named leaderboard based on execution of the +rank_conditional+. # - # The conditional is passed the following parameters: + # 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. @@ -160,7 +162,7 @@ def rank_member_if_in(leaderboard_name, rank_conditional, member, score, member_ 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) + if rank_conditional.call(member, current_score, score, member_data, {:reverse => @reverse}) rank_member_in(leaderboard_name, member, score, member_data) end end diff --git a/spec/leaderboard_spec.rb b/spec/leaderboard_spec.rb index 5b1f26b..3f5cb80 100644 --- a/spec/leaderboard_spec.rb +++ b/spec/leaderboard_spec.rb @@ -644,7 +644,8 @@ end it 'should rank a member in the leaderboard with conditional execution' do - highscore_check = lambda do |member, current_score, score, member_data| + @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 From d5dbb4bb00759be435d9b68da1de04d981c747f8 Mon Sep 17 00:00:00 2001 From: David Czarnecki Date: Wed, 28 Nov 2012 01:18:54 -0500 Subject: [PATCH 4/5] Updating README --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index a2f64d4..6904947 100644 --- a/README.markdown +++ b/README.markdown @@ -236,7 +236,7 @@ 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: +### 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: From 4d42cba75509e1f12fde27f126540e353f12da8d Mon Sep 17 00:00:00 2001 From: David Czarnecki Date: Wed, 28 Nov 2012 01:27:52 -0500 Subject: [PATCH 5/5] Updating README --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 6904947..a76efaf 100644 --- a/README.markdown +++ b/README.markdown @@ -241,7 +241,7 @@ friends = highscore_lb.ranked_in_list(['member_6', 'member_1', 'member_10'], :so 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. +* `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