require 'leaderboard' class CompetitionRankingLeaderboard < Leaderboard # Retrieve the rank for a member in the named leaderboard. # # @param leaderboard_name [String] Name of the leaderboard. # @param member [String] Member name. # # @return the rank for a member in the leaderboard. def rank_for_in(leaderboard_name, member) member_score = score_for_in(leaderboard_name, member) if @reverse return @redis_connection.zcount(leaderboard_name, '-inf', "(#{member_score}") + 1 rescue nil else return @redis_connection.zcount(leaderboard_name, "(#{member_score}", '+inf') + 1 rescue nil end end # Retrieve the score and rank for a member in the named leaderboard. # # @param leaderboard_name [String]Name of the leaderboard. # @param member [String] Member name. # # @return the score and rank for a member in the named leaderboard as a Hash. def score_and_rank_for_in(leaderboard_name, member) responses = @redis_connection.multi do |transaction| transaction.zscore(leaderboard_name, member) if @reverse transaction.zrank(leaderboard_name, member) else transaction.zrevrank(leaderboard_name, member) end end responses[0] = responses[0].to_f if responses[0] responses[1] = if @reverse @redis_connection.zcount(leaderboard_name, '-inf', "(#{responses[0]}") + 1 rescue nil else @redis_connection.zcount(leaderboard_name, "(#{responses[0]}", '+inf') + 1 rescue nil end {@member_key => member, @score_key => responses[0], @rank_key => responses[1]} end # Retrieve a page of leaders from the named leaderboard for a given list of members. # # @param leaderboard_name [String] Name of the leaderboard. # @param members [Array] Member names. # @param options [Hash] Options to be used when retrieving the page from the named leaderboard. # # @return a page of leaders from the named leaderboard for a given list of members. def ranked_in_list_in(leaderboard_name, members, options = {}) leaderboard_options = DEFAULT_LEADERBOARD_REQUEST_OPTIONS.dup leaderboard_options.merge!(options) ranks_for_members = [] responses = @redis_connection.multi do |transaction| members.each do |member| if @reverse transaction.zrank(leaderboard_name, member) else transaction.zrevrank(leaderboard_name, member) end transaction.zscore(leaderboard_name, member) end end unless leaderboard_options[:members_only] included_members = [] scores = [] members.each_with_index do |member, index| data = {} data[@member_key] = member unless leaderboard_options[:members_only] data[@score_key] = responses[index * 2 + 1].to_f if responses[index * 2 + 1] if data[@score_key] == nil next unless leaderboard_options[:include_missing] end end included_members << member scores << data[@score_key] ranks_for_members << data end if leaderboard_options[:with_member_data] members_data_for_in(leaderboard_name, included_members).each_with_index do |member_data, index| ranks_for_members[index][@member_data_key] = member_data end end rankings_for_members_having_scores_in(leaderboard_name, included_members, scores).each_with_index do |rank, index| ranks_for_members[index][@rank_key] = rank end case leaderboard_options[:sort_by] when :rank ranks_for_members = ranks_for_members.sort_by { |member| member[@rank_key] } when :score ranks_for_members = ranks_for_members.sort_by { |member| member[@score_key] } end ranks_for_members end protected # Retrieve a list of the rankings of leaders given their member names and scores # # @param leaderboard_name [String] Name of the leaderboard. # @param members [Array] Member names. # @param scores [Array] a list of scores for the members, aligned with the member names # # @return a list of the rankings for the passed in members and scores def rankings_for_members_having_scores_in(leaderboard_name, members, scores) @redis_connection.multi do |transaction| members.each_with_index do |member, index| if @reverse transaction.zcount(leaderboard_name, '-inf', "(#{scores[index]}") else transaction.zcount(leaderboard_name, "(#{scores[index]}", '+inf') end end end.map{|rank| rank ? rank + 1 : rank} end end