class Leaderboard
Constants
- DEFAULT_LEADERBOARD_REQUEST_OPTIONS
Default options when requesting data from a leaderboard.
:with_member_data
false: Return member data along with the member names.:page_size
nil: The default page size will be used.:members_only
false: Only return the member name, not their score and rank.:sort_by
:none: The default sort for a call to `ranked_in_list`.- DEFAULT_OPTIONS
Default options when creating a leaderboard. Page size is 25 and reverse is set to false, meaning various methods will return results in highest-to-lowest order.
- DEFAULT_PAGE_SIZE
Default page size: 25
- DEFAULT_REDIS_HOST
Default Redis host: localhost
- DEFAULT_REDIS_OPTIONS
Default Redis options when creating a connection to Redis. The
DEFAULT_REDIS_HOST
andDEFAULT_REDIS_PORT
will be passed.- DEFAULT_REDIS_PORT
Default Redis post: 6379
- VERSION
Attributes
Name of the leaderboard.
Page size to be used when paging through the leaderboard.
Determines whether or not various leaderboard methods return their data in highest-to-lowest (:reverse
false) or lowest-to-highest (:reverse
true)
Public Class Methods
Create a new instance of a leaderboard.
@param leaderboard [String] Name of the leaderboard. @param options [Hash] Options for the leaderboard such as :page_size
. @param redis_options [Hash] Options for configuring Redis.
Examples
leaderboard = Leaderboard.new('highscores') leaderboard = Leaderboard.new('highscores', {:page_size => 10})
# File lib/leaderboard.rb, line 69 def initialize(leaderboard_name, options = DEFAULT_OPTIONS, redis_options = DEFAULT_REDIS_OPTIONS) leaderboard_options = DEFAULT_OPTIONS.dup leaderboard_options.merge!(options) @leaderboard_name = leaderboard_name @reverse = leaderboard_options[:reverse] @page_size = leaderboard_options[:page_size] if @page_size.nil? || @page_size < 1 @page_size = DEFAULT_PAGE_SIZE end @member_key = leaderboard_options[:member_key] @rank_key = leaderboard_options[:rank_key] @score_key = leaderboard_options[:score_key] @member_data_key = leaderboard_options[:member_data_key] @member_data_namespace = leaderboard_options[:member_data_namespace] @global_member_data = leaderboard_options[:global_member_data] @redis_connection = redis_options[:redis_connection] unless @redis_connection.nil? redis_options.delete(:redis_connection) end @redis_connection = Redis.new(redis_options) if @redis_connection.nil? end
Public Instance Methods
Retrieve all leaders from the leaderboard.
@param options [Hash] Options to be used when retrieving the leaders from the leaderboard.
@return the leaders from the leaderboard.
# File lib/leaderboard.rb, line 746 def all_leaders(options = {}) all_leaders_from(@leaderboard_name, options) end
Retrieves all leaders from the named leaderboard.
@param leaderboard_name
[String] Name of the leaderboard. @param options [Hash] Options to be used when retrieving the leaders from the named leaderboard.
@return the named leaderboard.
# File lib/leaderboard.rb, line 758 def all_leaders_from(leaderboard_name, options = {}) leaderboard_options = DEFAULT_LEADERBOARD_REQUEST_OPTIONS.dup leaderboard_options.merge!(options) if @reverse raw_leader_data = @redis_connection.zrange(leaderboard_name, 0, -1, :with_scores => false) else raw_leader_data = @redis_connection.zrevrange(leaderboard_name, 0, -1, :with_scores => false) end if raw_leader_data return ranked_in_list_in(leaderboard_name, raw_leader_data, leaderboard_options) else return [] end end
Retrieve a page of leaders from the leaderboard around a given member.
@param member [String] Member name. @param options [Hash] Options to be used when retrieving the page from the leaderboard.
@return a page of leaders from the leaderboard around a given member.
# File lib/leaderboard.rb, line 915 def around_me(member, options = {}) around_me_in(@leaderboard_name, member, options) end
Retrieve a page of leaders from the named leaderboard around a given member.
@param leaderboard_name
[String] Name of the leaderboard. @param member [String] Member name. @param options [Hash] Options to be used when retrieving the page from the named leaderboard.
@return a page of leaders from the named leaderboard around a given member. Returns an empty array for a non-existent member.
# File lib/leaderboard.rb, line 926 def around_me_in(leaderboard_name, member, options = {}) leaderboard_options = DEFAULT_LEADERBOARD_REQUEST_OPTIONS.dup leaderboard_options.merge!(options) reverse_rank_for_member = @reverse ? @redis_connection.zrank(leaderboard_name, member) : @redis_connection.zrevrank(leaderboard_name, member) return [] unless reverse_rank_for_member page_size = validate_page_size(leaderboard_options[:page_size]) || @page_size starting_offset = reverse_rank_for_member - (page_size / 2) if starting_offset < 0 starting_offset = 0 end ending_offset = (starting_offset + page_size) - 1 raw_leader_data = @reverse ? @redis_connection.zrange(leaderboard_name, starting_offset, ending_offset, :with_scores => false) : @redis_connection.zrevrange(leaderboard_name, starting_offset, ending_offset, :with_scores => false) if raw_leader_data return ranked_in_list_in(leaderboard_name, raw_leader_data, leaderboard_options) else return [] end end
Change the score for a member in the leaderboard by a score delta which can be positive or negative.
@param member [String] Member name. @param delta [float] Score change. @param member_data [String] Optional member data.
# File lib/leaderboard.rb, line 391 def change_score_for(member, delta, member_data = nil) change_score_for_member_in(@leaderboard_name, member, delta, member_data) end
Change the score for a member in the named leaderboard by a delta which can be positive or negative.
@param leaderboard_name
[String] Name of the leaderboard. @param member [String] Member name. @param delta [float] Score change. @param member_data [String] Optional member data.
# File lib/leaderboard.rb, line 401 def change_score_for_member_in(leaderboard_name, member, delta, member_data) @redis_connection.multi do |transaction| transaction.zincrby(leaderboard_name, delta, member) transaction.hset(member_data_key(leaderboard_name), member, member_data) if member_data end end
Check to see if a member exists in the leaderboard.
@param member [String] Member name.
@return true
if the member exists in the leaderboard, false
otherwise.
# File lib/leaderboard.rb, line 456 def check_member?(member) check_member_in?(@leaderboard_name, member) end
Check to see if a member exists in the named leaderboard.
@param leaderboard_name
[String] Name of the leaderboard. @param member [String] Member name.
@return true
if the member exists in the named leaderboard, false
otherwise.
# File lib/leaderboard.rb, line 466 def check_member_in?(leaderboard_name, member) !@redis_connection.zscore(leaderboard_name, member).nil? end
Delete the current leaderboard.
# File lib/leaderboard.rb, line 112 def delete_leaderboard delete_leaderboard_named(@leaderboard_name) end
Delete the named leaderboard.
@param leaderboard_name
[String] Name of the leaderboard.
# File lib/leaderboard.rb, line 119 def delete_leaderboard_named(leaderboard_name) @redis_connection.multi do |transaction| transaction.del(leaderboard_name) transaction.del(member_data_key(leaderboard_name)) end end
Disconnect the Redis connection.
# File lib/leaderboard.rb, line 107 def disconnect @redis_connection.client.disconnect end
Expire the current leaderboard in a set number of seconds. Do not use this with leaderboards that utilize member data as there is no facility to cascade the expiration out to the keys for the member data.
@param seconds [int] Number of seconds after which the leaderboard will be expired.
# File lib/leaderboard.rb, line 645 def expire_leaderboard(seconds) expire_leaderboard_for(@leaderboard_name, seconds) end
Expire the current leaderboard at a specific UNIX timestamp. Do not use this with leaderboards that utilize member data as there is no facility to cascade the expiration out to the keys for the member data.
@param timestamp [int] UNIX timestamp at which the leaderboard will be expired.
# File lib/leaderboard.rb, line 667 def expire_leaderboard_at(timestamp) expire_leaderboard_at_for(@leaderboard_name, timestamp) end
Expire the given leaderboard at a specific UNIX timestamp. Do not use this with leaderboards that utilize member data as there is no facility to cascade the expiration out to the keys for the member data.
@param leaderboard_name
[String] Name of the leaderboard. @param timestamp [int] UNIX timestamp at which the leaderboard will be expired.
# File lib/leaderboard.rb, line 677 def expire_leaderboard_at_for(leaderboard_name, timestamp) @redis_connection.multi do |transaction| transaction.expireat(leaderboard_name, timestamp) transaction.expireat(member_data_key(leaderboard_name), timestamp) end end
Expire the given leaderboard in a set number of seconds. Do not use this with leaderboards that utilize member data as there is no facility to cascade the expiration out to the keys for the member data.
@param leaderboard_name
[String] Name of the leaderboard. @param seconds [int] Number of seconds after which the leaderboard will be expired.
# File lib/leaderboard.rb, line 655 def expire_leaderboard_for(leaderboard_name, seconds) @redis_connection.multi do |transaction| transaction.expire(leaderboard_name, seconds) transaction.expire(member_data_key(leaderboard_name), seconds) end end
Intersect leaderboards given by keys with this leaderboard into a named destination leaderboard.
@param destination [String] Destination leaderboard name. @param keys [Array] Leaderboards to be merged with the current leaderboard. @param options [Hash] Options for intersecting the leaderboards.
# File lib/leaderboard.rb, line 1035 def intersect_leaderboards(destination, keys, options = {:aggregate => :sum}) @redis_connection.zinterstore(destination, keys.insert(0, @leaderboard_name), options) end
Retrieve a page of leaders from the leaderboard.
@param current_page [int] Page to retrieve from the leaderboard. @param options [Hash] Options to be used when retrieving the page from the leaderboard.
@return a page of leaders from the leaderboard.
# File lib/leaderboard.rb, line 690 def leaders(current_page, options = {}) leaders_in(@leaderboard_name, current_page, options) end
Retrieve a page of leaders from the named leaderboard.
@param leaderboard_name
[String] Name of the leaderboard. @param current_page [int] Page to retrieve from the named leaderboard. @param options [Hash] Options to be used when retrieving the page from the named leaderboard.
@return a page of leaders from the named leaderboard.
# File lib/leaderboard.rb, line 703 def leaders_in(leaderboard_name, current_page, options = {}) leaderboard_options = DEFAULT_LEADERBOARD_REQUEST_OPTIONS.dup leaderboard_options.merge!(options) if current_page < 1 current_page = 1 end page_size = validate_page_size(leaderboard_options[:page_size]) || @page_size if current_page > total_pages_in(leaderboard_name, page_size) current_page = total_pages_in(leaderboard_name, page_size) end index_for_redis = current_page - 1 starting_offset = (index_for_redis * page_size) if starting_offset < 0 starting_offset = 0 end ending_offset = (starting_offset + page_size) - 1 if @reverse raw_leader_data = @redis_connection.zrange(leaderboard_name, starting_offset, ending_offset, :with_scores => false) else raw_leader_data = @redis_connection.zrevrange(leaderboard_name, starting_offset, ending_offset, :with_scores => false) end if raw_leader_data return ranked_in_list_in(leaderboard_name, raw_leader_data, leaderboard_options) else return [] end end
Retrieve a member at the specified index from the leaderboard.
@param position [int] Position in leaderboard. @param options [Hash] Options to be used when retrieving the member from the leaderboard.
@return a member from the leaderboard.
# File lib/leaderboard.rb, line 885 def member_at(position, options = {}) member_at_in(@leaderboard_name, position, options) end
Retrieve a member at the specified index from the leaderboard.
@param leaderboard_name
[String] Name of the leaderboard. @param position [int] Position in named leaderboard. @param options [Hash] Options to be used when retrieving the member from the named leaderboard.
@return a page of leaders from the named leaderboard.
# File lib/leaderboard.rb, line 896 def member_at_in(leaderboard_name, position, options = {}) if position > 0 && position <= total_members_in(leaderboard_name) leaderboard_options = DEFAULT_LEADERBOARD_REQUEST_OPTIONS.dup leaderboard_options.merge!(options) page_size = validate_page_size(leaderboard_options[:page_size]) || @page_size current_page = (position.to_f / page_size.to_f).ceil offset = (position - 1) % page_size leaders = leaders_in(leaderboard_name, current_page, options) leaders[offset] if leaders end end
Retrieve the optional member data for a given member in the leaderboard.
@param member [String] Member name.
@return String of optional member data.
# File lib/leaderboard.rb, line 208 def member_data_for(member) member_data_for_in(@leaderboard_name, member) end
Retrieve the optional member data for a given member in the named leaderboard.
@param leaderboard_name
[String] Name of the leaderboard. @param member [String] Member name.
@return String of optional member data.
# File lib/leaderboard.rb, line 218 def member_data_for_in(leaderboard_name, member) @redis_connection.hget(member_data_key(leaderboard_name), member) end
Retrieve the optional member data for the given members in the leaderboard.
@param members [Array] Member names.
@return array of strings of optional member data.
# File lib/leaderboard.rb, line 238 def members_data_for(members) members_data_for_in(@leaderboard_name, members) end
Retrieve the optional member data for a given member in the named leaderboard.
@param leaderboard_name
[String] Name of the leaderboard. @param members [Array] Member names.
@return array of strings of optional member data.
# File lib/leaderboard.rb, line 228 def members_data_for_in(leaderboard_name, members) return [] unless members.size > 0 @redis_connection.hmget(member_data_key(leaderboard_name), *members) end
Retrieve members from the leaderboard within a given rank range.
@param starting_rank [int] Starting rank (inclusive). @param ending_rank [int] Ending rank (inclusive). @param options [Hash] Options to be used when retrieving the data from the leaderboard.
@return members from the leaderboard that fall within the given rank range.
# File lib/leaderboard.rb, line 818 def members_from_rank_range(starting_rank, ending_rank, options = {}) members_from_rank_range_in(@leaderboard_name, starting_rank, ending_rank, options) end
Retrieve members from the named leaderboard within a given rank range.
@param leaderboard_name
[String] Name of the leaderboard. @param starting_rank [int] Starting rank (inclusive). @param ending_rank [int] Ending rank (inclusive). @param options [Hash] Options to be used when retrieving the data from the leaderboard.
@return members from the leaderboard that fall within the given rank range.
# File lib/leaderboard.rb, line 830 def members_from_rank_range_in(leaderboard_name, starting_rank, ending_rank, options = {}) leaderboard_options = DEFAULT_LEADERBOARD_REQUEST_OPTIONS.dup leaderboard_options.merge!(options) starting_rank -= 1 if starting_rank < 0 starting_rank = 0 end ending_rank -= 1 if ending_rank > total_members_in(leaderboard_name) ending_rank = total_members_in(leaderboard_name) - 1 end if @reverse raw_leader_data = @redis_connection.zrange(leaderboard_name, starting_rank, ending_rank, :with_scores => false) else raw_leader_data = @redis_connection.zrevrange(leaderboard_name, starting_rank, ending_rank, :with_scores => false) end if raw_leader_data return ranked_in_list_in(leaderboard_name, raw_leader_data, leaderboard_options) else return [] end end
Retrieve members from the leaderboard within a given score range.
@param minimum_score [float] Minimum score (inclusive). @param maximum_score [float] Maximum score (inclusive). @param options [Hash] Options to be used when retrieving the data from the leaderboard.
@return members from the leaderboard that fall within the given score range.
# File lib/leaderboard.rb, line 784 def members_from_score_range(minimum_score, maximum_score, options = {}) members_from_score_range_in(@leaderboard_name, minimum_score, maximum_score, options) end
Retrieve members from the named leaderboard within a given score range.
@param leaderboard_name
[String] Name of the leaderboard. @param minimum_score [float] Minimum score (inclusive). @param maximum_score [float] Maximum score (inclusive). @param options [Hash] Options to be used when retrieving the data from the leaderboard.
@return members from the leaderboard that fall within the given score range.
# File lib/leaderboard.rb, line 796 def members_from_score_range_in(leaderboard_name, minimum_score, maximum_score, options = {}) leaderboard_options = DEFAULT_LEADERBOARD_REQUEST_OPTIONS.dup leaderboard_options.merge!(options) raw_leader_data = @reverse ? @redis_connection.zrangebyscore(leaderboard_name, minimum_score, maximum_score) : @redis_connection.zrevrangebyscore(leaderboard_name, maximum_score, minimum_score) if raw_leader_data return ranked_in_list_in(leaderboard_name, raw_leader_data, leaderboard_options) else return [] end end
Merge leaderboards given by keys with this leaderboard into a named destination leaderboard.
@param destination [String] Destination leaderboard name. @param keys [Array] Leaderboards to be merged with the current leaderboard. @param options [Hash] Options for merging the leaderboards.
# File lib/leaderboard.rb, line 1026 def merge_leaderboards(destination, keys, options = {:aggregate => :sum}) @redis_connection.zunionstore(destination, keys.insert(0, @leaderboard_name), options) end
Determine the page where a member falls in the leaderboard.
@param member [String] Member name. @param page_size
[int] Page size to be used in determining page location.
@return the page where a member falls in the leaderboard.
# File lib/leaderboard.rb, line 615 def page_for(member, page_size = DEFAULT_PAGE_SIZE) page_for_in(@leaderboard_name, member, page_size) end
Determine the page where a member falls in the named leaderboard.
@param leaderboard [String] Name of the leaderboard. @param member [String] Member name. @param page_size
[int] Page size to be used in determining page location.
@return the page where a member falls in the leaderboard.
# File lib/leaderboard.rb, line 626 def page_for_in(leaderboard_name, member, page_size = DEFAULT_PAGE_SIZE) rank_for_member = @reverse ? @redis_connection.zrank(leaderboard_name, member) : @redis_connection.zrevrank(leaderboard_name, member) if rank_for_member.nil? rank_for_member = 0 else rank_for_member += 1 end (rank_for_member.to_f / page_size.to_f).ceil end
Set the page size to be used when paging through the leaderboard. This method also has the side effect of setting the page size to the DEFAULT_PAGE_SIZE
if the page size is less than 1.
@param page_size
[int] Page size.
# File lib/leaderboard.rb, line 100 def page_size=(page_size) page_size = DEFAULT_PAGE_SIZE if page_size < 1 @page_size = page_size end
Retrieve the percentile for a member in the leaderboard.
@param member [String] Member name.
@return the percentile for a member in the leaderboard. Return nil
for a non-existent member.
# File lib/leaderboard.rb, line 544 def percentile_for(member) percentile_for_in(@leaderboard_name, member) end
Retrieve the percentile for a member in the named leaderboard.
@param leaderboard_name
[String] Name of the leaderboard. @param member [String] Member name.
@return the percentile for a member in the named leaderboard.
# File lib/leaderboard.rb, line 554 def percentile_for_in(leaderboard_name, member) return nil unless check_member_in?(leaderboard_name, member) responses = @redis_connection.multi do |transaction| transaction.zcard(leaderboard_name) transaction.zrevrank(leaderboard_name, member) end percentile = ((responses[0] - responses[1] - 1).to_f / responses[0].to_f * 100).ceil if @reverse 100 - percentile else percentile end end
Retrieve the rank for a member in the leaderboard.
@param member [String] Member name.
@return the rank for a member in the leaderboard.
# File lib/leaderboard.rb, line 413 def rank_for(member) rank_for_in(@leaderboard_name, member) end
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.
# File lib/leaderboard.rb, line 423 def rank_for_in(leaderboard_name, member) if @reverse return @redis_connection.zrank(leaderboard_name, member) + 1 rescue nil else return @redis_connection.zrevrank(leaderboard_name, member) + 1 rescue nil end end
Rank a member in the leaderboard.
@param member [String] Member name. @param score [float] Member score. @param member_data [String] Optional member data.
# File lib/leaderboard.rb, line 131 def rank_member(member, score, member_data = nil) rank_member_in(@leaderboard_name, member, score, member_data) end
Rank a member across multiple leaderboards.
@param leaderboards [Array] Leaderboard
names. @param member [String] Member name. @param score [float] Member score. @param member_data [String] Optional member data.
# File lib/leaderboard.rb, line 154 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.hset(member_data_key(leaderboard_name), member, member_data) if member_data end 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 [float] Member score. @param member_data [String] Optional member_data.
# File lib/leaderboard.rb, line 176 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 [float] Member score. @param member_data [String] Optional member_data.
# File lib/leaderboard.rb, line 194 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
Rank a member in the named leaderboard.
@param leaderboard_name
[String] Name of the leaderboard. @param member [String] Member name. @param score [float] Member score. @param member_data [String] Optional member data.
# File lib/leaderboard.rb, line 141 def rank_member_in(leaderboard_name, member, score, member_data = nil) @redis_connection.multi do |transaction| transaction.zadd(leaderboard_name, score, member) transaction.hset(member_data_key(leaderboard_name), member, member_data) if member_data end end
Rank an array of members in the leaderboard.
@param members_and_scores [Splat or Array] Variable list of members and scores
# File lib/leaderboard.rb, line 277 def rank_members(*members_and_scores) rank_members_in(@leaderboard_name, *members_and_scores) end
Rank an array of members in the named leaderboard.
@param leaderboard_name
[String] Name of the leaderboard. @param members_and_scores [Splat or Array] Variable list of members and scores
# File lib/leaderboard.rb, line 285 def rank_members_in(leaderboard_name, *members_and_scores) if members_and_scores.is_a?(Array) 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]) end end end
Retrieve a page of leaders from the leaderboard for a given list of members.
@param members [Array] Member names. @param options [Hash] Options to be used when retrieving the page from the leaderboard.
@return a page of leaders from the leaderboard for a given list of members.
# File lib/leaderboard.rb, line 962 def ranked_in_list(members, options = {}) ranked_in_list_in(@leaderboard_name, members, options) 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.
# File lib/leaderboard.rb, line 973 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] members.each_with_index do |member, index| data = {} data[@member_key] = member unless leaderboard_options[:members_only] data[@rank_key] = responses[index * 2] + 1 rescue nil if data[@rank_key] == nil next unless leaderboard_options[:include_missing] end data[@score_key] = responses[index * 2 + 1].to_f if responses[index * 2 + 1] end ranks_for_members << data end if leaderboard_options[:with_member_data] included_members = ranks_for_members.collect { |member| member[@member_key] } 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 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
Remove a member from the leaderboard.
@param member [String] Member name.
# File lib/leaderboard.rb, line 300 def remove_member(member) remove_member_from(@leaderboard_name, member) end
Remove the optional member data for a given member in the leaderboard.
@param member [String] Member name.
# File lib/leaderboard.rb, line 262 def remove_member_data(member) remove_member_data_in(@leaderboard_name, member) end
Remove the optional member data for a given member in the named leaderboard.
@param leaderboard_name
[String] Name of the leaderboard. @param member [String] Member name.
# File lib/leaderboard.rb, line 270 def remove_member_data_in(leaderboard_name, member) @redis_connection.hdel(member_data_key(leaderboard_name), member) end
Remove a member from the named leaderboard.
@param leaderboard_name
[String] Name of the leaderboard. @param member [String] Member name.
# File lib/leaderboard.rb, line 308 def remove_member_from(leaderboard_name, member) @redis_connection.multi do |transaction| transaction.zrem(leaderboard_name, member) transaction.hdel(member_data_key(leaderboard_name), member) end end
Remove members from the leaderboard in a given score range.
@param min_score [float] Minimum score. @param max_score [float] Maximum score.
# File lib/leaderboard.rb, line 505 def remove_members_in_score_range(min_score, max_score) remove_members_in_score_range_in(@leaderboard_name, min_score, max_score) end
Remove members from the named leaderboard in a given score range.
@param leaderboard_name
[String] Name of the leaderboard. @param min_score [float] Minimum score. @param max_score [float] Maximum score.
# File lib/leaderboard.rb, line 514 def remove_members_in_score_range_in(leaderboard_name, min_score, max_score) @redis_connection.zremrangebyscore(leaderboard_name, min_score, max_score) end
Remove members from the leaderboard outside a given rank.
@param rank [int] The rank (inclusive) which we should keep. @return the total number of members removed.
# File lib/leaderboard.rb, line 522 def remove_members_outside_rank(rank) remove_members_outside_rank_in(@leaderboard_name, rank) end
Remove members from the leaderboard outside a given rank.
@param leaderboard_name
[String] Name of the leaderboard. @param rank [int] The rank (inclusive) which we should keep. @return the total number of members removed.
# File lib/leaderboard.rb, line 531 def remove_members_outside_rank_in(leaderboard_name, rank) if @reverse @redis_connection.zremrangebyrank(leaderboard_name, rank, -1) else @redis_connection.zremrangebyrank(leaderboard_name, 0, -(rank) - 1) end end
Retrieve the score and rank for a member in the leaderboard.
@param member [String] Member name.
@return the score and rank for a member in the leaderboard as a Hash.
# File lib/leaderboard.rb, line 475 def score_and_rank_for(member) score_and_rank_for_in(@leaderboard_name, member) 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.
# File lib/leaderboard.rb, line 485 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] = responses[1] + 1 rescue nil {@member_key => member, @score_key => responses[0], @rank_key => responses[1]} end
Retrieve the score for a member in the leaderboard.
@param member Member name.
@return the score for a member in the leaderboard or nil
if the member is not in the leaderboard.
# File lib/leaderboard.rb, line 436 def score_for(member) score_for_in(@leaderboard_name, member) end
Retrieve the score for a member in the named leaderboard.
@param leaderboard_name
Name of the leaderboard. @param member [String] Member name.
@return the score for a member in the leaderboard or nil
if the member is not in the leaderboard.
# File lib/leaderboard.rb, line 446 def score_for_in(leaderboard_name, member) score = @redis_connection.zscore(leaderboard_name, member) score.to_f if score end
Calculate the score for a given percentile value in the leaderboard.
@param percentile [float] Percentile value (0.0 to 100.0 inclusive)
# File lib/leaderboard.rb, line 573 def score_for_percentile(percentile) score_for_percentile_in(@leaderboard_name, percentile) end
Calculate the score for a given percentile value in the named leaderboard.
See www.itl.nist.gov/div898/handbook/prc/section2/prc252.htm for implementation details (there are differing methods for calculating percentile scores that do not fall directly upon a ranked item; we are using the method specified by NIST, i.e. linear interpolation).
@param percentile [float] Percentile value (0.0 to 100.0 inclusive)
# File lib/leaderboard.rb, line 585 def score_for_percentile_in(leaderboard_name, percentile) return nil unless percentile.between?(0, 100) total_members = total_members_in(leaderboard_name) return nil if total_members < 1 if @reverse percentile = 100 - percentile end index = (total_members - 1) * (percentile / 100.0) scores = @redis_connection.zrange( leaderboard_name, index.floor, index.ceil, :with_scores => true ).map{ |pair| pair.last } if index == index.floor scores[0] else interpolate_fraction = index - index.floor scores[0] + interpolate_fraction * (scores[1] - scores[0]) end end
Retrieve members from the leaderboard within a range from 1 to the number given.
@param ending_rank [int] Ending rank (inclusive). @param options [Hash] Options to be used when retrieving the data from the leaderboard.
@return number from the leaderboard that fall within the given rank range.
# File lib/leaderboard.rb, line 863 def top(number, options = {}) top_in(@leaderboard_name, number, options) end
Retrieve members from the named leaderboard within a range from 1 to the number given.
@param leaderboard_name
[String] Name of the leaderboard. @param starting_rank [int] Starting rank (inclusive). @param ending_rank [int] Ending rank (inclusive). @param options [Hash] Options to be used when retrieving the data from the leaderboard.
@return members from the leaderboard that fall within the given rank range.
# File lib/leaderboard.rb, line 875 def top_in(leaderboard_name, number, options={}) members_from_rank_range_in(leaderboard_name, 1, number, options) end
Retrieve the total number of members in the leaderboard.
@return total number of members in the leaderboard.
# File lib/leaderboard.rb, line 318 def total_members total_members_in(@leaderboard_name) end
Retrieve the total number of members in the named leaderboard.
@param leaderboard_name
[String] Name of the leaderboard.
@return the total number of members in the named leaderboard.
# File lib/leaderboard.rb, line 327 def total_members_in(leaderboard_name) @redis_connection.zcard(leaderboard_name) end
Retrieve the total members in a given score range from the leaderboard.
@param min_score [float] Minimum score. @param max_score [float] Maximum score.
@return the total members in a given score range from the leaderboard.
# File lib/leaderboard.rb, line 357 def total_members_in_score_range(min_score, max_score) total_members_in_score_range_in(@leaderboard_name, min_score, max_score) end
Retrieve the total members in a given score range from the named leaderboard.
@param leaderboard_name
Name of the leaderboard. @param min_score [float] Minimum score. @param max_score [float] Maximum score.
@return the total members in a given score range from the named leaderboard.
# File lib/leaderboard.rb, line 368 def total_members_in_score_range_in(leaderboard_name, min_score, max_score) @redis_connection.zcount(leaderboard_name, min_score, max_score) end
Retrieve the total number of pages in the leaderboard.
@param page_size
[int, nil] Page size to be used when calculating the total number of pages.
@return the total number of pages in the leaderboard.
# File lib/leaderboard.rb, line 336 def total_pages(page_size = nil) total_pages_in(@leaderboard_name, page_size) end
Retrieve the total number of pages in the named leaderboard.
@param leaderboard_name
[String] Name of the leaderboard. @param page_size
[int, nil] Page size to be used when calculating the total number of pages.
@return the total number of pages in the named leaderboard.
# File lib/leaderboard.rb, line 346 def total_pages_in(leaderboard_name, page_size = nil) page_size ||= @page_size.to_f (total_members_in(leaderboard_name) / page_size.to_f).ceil end
Sum of scores for all members in leaderboard
@return Sum of scores for all members in leaderboard
# File lib/leaderboard.rb, line 375 def total_scores total_scores_in(@leaderboard_name) end
Sum of scores for all members in the named leaderboard
@return Sum of scores for all members in named leaderboard
# File lib/leaderboard.rb, line 382 def total_scores_in(leaderboard_name) all_leaders_from(leaderboard_name).map{|hash| hash[:score] }.inject(0, :+) end
Update the optional member data for a given member in the leaderboard.
@param member [String] Member name. @param member_data [String] Optional member data.
# File lib/leaderboard.rb, line 246 def update_member_data(member, member_data) update_member_data_in(@leaderboard_name, member, member_data) end
Update the optional member data for a given member in the named leaderboard.
@param leaderboard_name
[String] Name of the leaderboard. @param member [String] Member name. @param member_data [String] Optional member data.
# File lib/leaderboard.rb, line 255 def update_member_data_in(leaderboard_name, member, member_data) @redis_connection.hset(member_data_key(leaderboard_name), member, member_data) end
Protected Instance Methods
Key for retrieving optional member data.
@param leaderboard_name
[String] Name of the leaderboard.
@return a key in the form of leaderboard_name:member_data
# File lib/leaderboard.rb, line 1046 def member_data_key(leaderboard_name) @global_member_data == false ? "#{leaderboard_name}:#{@member_data_namespace}" : @member_data_namespace end
Validate and return the page size. Returns the DEFAULT_PAGE_SIZE
if the page size is less than 1.
@param page_size
[int] Page size.
@return the page size. Returns the DEFAULT_PAGE_SIZE
if the page size is less than 1.
# File lib/leaderboard.rb, line 1055 def validate_page_size(page_size) if page_size && page_size < 1 page_size = DEFAULT_PAGE_SIZE end page_size end