Class: Leaderboard
- Inherits:
-
Object
- Object
- Leaderboard
- Defined in:
- lib/leaderboard.rb,
lib/leaderboard/version.rb
Direct Known Subclasses
Constant Summary collapse
- DEFAULT_PAGE_SIZE =
Default page size: 25
25
- 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.
{ :page_size => DEFAULT_PAGE_SIZE, :reverse => false, :member_key => :member, :rank_key => :rank, :score_key => :score, :member_data_key => :member_data, :member_data_namespace => 'member_data', :global_member_data => false }
- DEFAULT_REDIS_HOST =
Default Redis host: localhost
'localhost'
- DEFAULT_REDIS_PORT =
Default Redis post: 6379
6379
- DEFAULT_REDIS_OPTIONS =
Default Redis options when creating a connection to Redis. The
DEFAULT_REDIS_HOST
andDEFAULT_REDIS_PORT
will be passed. { :host => DEFAULT_REDIS_HOST, :port => DEFAULT_REDIS_PORT }
- 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`. { :with_member_data => false, :page_size => nil, :members_only => false, :sort_by => :none, :include_missing => true }
- VERSION =
'3.12.0'.freeze
Instance Attribute Summary collapse
-
#leaderboard_name ⇒ Object
readonly
Name of the leaderboard.
-
#page_size ⇒ Object
Page size to be used when paging through the leaderboard.
-
#reverse ⇒ Object
Determines whether or not various leaderboard methods return their data in highest-to-lowest (
:reverse
false) or lowest-to-highest (:reverse
true).
Instance Method Summary collapse
-
#all_leaders(options = {}) ⇒ Object
(also: #all_members)
Retrieve all leaders from the leaderboard.
-
#all_leaders_from(leaderboard_name, options = {}) ⇒ Object
(also: #all_members_from)
Retrieves all leaders from the named leaderboard.
-
#around_me(member, options = {}) ⇒ Object
Retrieve a page of leaders from the leaderboard around a given member.
-
#around_me_in(leaderboard_name, member, options = {}) ⇒ Object
Retrieve a page of leaders from the named leaderboard around a given member.
-
#change_score_for(member, delta, member_data = nil) ⇒ Object
Change the score for a member in the leaderboard by a score delta which can be positive or negative.
-
#change_score_for_member_in(leaderboard_name, member, delta, member_data) ⇒ Object
Change the score for a member in the named leaderboard by a delta which can be positive or negative.
-
#check_member?(member) ⇒ Boolean
Check to see if a member exists in the leaderboard.
-
#check_member_in?(leaderboard_name, member) ⇒ Boolean
Check to see if a member exists in the named leaderboard.
-
#delete_leaderboard ⇒ Object
Delete the current leaderboard.
-
#delete_leaderboard_named(leaderboard_name) ⇒ Object
Delete the named leaderboard.
-
#disconnect ⇒ Object
Disconnect the Redis connection.
-
#expire_leaderboard(seconds) ⇒ Object
Expire the current leaderboard in a set number of seconds.
-
#expire_leaderboard_at(timestamp) ⇒ Object
Expire the current leaderboard at a specific UNIX timestamp.
-
#expire_leaderboard_at_for(leaderboard_name, timestamp) ⇒ Object
Expire the given leaderboard at a specific UNIX timestamp.
-
#expire_leaderboard_for(leaderboard_name, seconds) ⇒ Object
Expire the given leaderboard in a set number of seconds.
-
#initialize(leaderboard_name, options = DEFAULT_OPTIONS, redis_options = DEFAULT_REDIS_OPTIONS) ⇒ Leaderboard
constructor
Create a new instance of a leaderboard.
-
#intersect_leaderboards(destination, keys, options = {:aggregate => :sum}) ⇒ Object
Intersect leaderboards given by keys with this leaderboard into a named destination leaderboard.
-
#leaders(current_page, options = {}) ⇒ Object
(also: #members)
Retrieve a page of leaders from the leaderboard.
-
#leaders_in(leaderboard_name, current_page, options = {}) ⇒ Object
(also: #members_in)
Retrieve a page of leaders from the named leaderboard.
-
#member_at(position, options = {}) ⇒ Object
Retrieve a member at the specified index from the leaderboard.
-
#member_at_in(leaderboard_name, position, options = {}) ⇒ Object
Retrieve a member at the specified index from the leaderboard.
-
#member_data_for(member) ⇒ Object
Retrieve the optional member data for a given member in the leaderboard.
-
#member_data_for_in(leaderboard_name, member) ⇒ Object
Retrieve the optional member data for a given member in the named leaderboard.
-
#members_data_for(members) ⇒ Object
Retrieve the optional member data for the given members in the leaderboard.
-
#members_data_for_in(leaderboard_name, members) ⇒ Object
Retrieve the optional member data for a given member in the named leaderboard.
-
#members_from_rank_range(starting_rank, ending_rank, options = {}) ⇒ Object
Retrieve members from the leaderboard within a given rank range.
-
#members_from_rank_range_in(leaderboard_name, starting_rank, ending_rank, options = {}) ⇒ Object
Retrieve members from the named leaderboard within a given rank range.
-
#members_from_score_range(minimum_score, maximum_score, options = {}) ⇒ Object
Retrieve members from the leaderboard within a given score range.
-
#members_from_score_range_in(leaderboard_name, minimum_score, maximum_score, options = {}) ⇒ Object
Retrieve members from the named leaderboard within a given score range.
-
#merge_leaderboards(destination, keys, options = {:aggregate => :sum}) ⇒ Object
Merge leaderboards given by keys with this leaderboard into a named destination leaderboard.
-
#page_for(member, page_size = DEFAULT_PAGE_SIZE) ⇒ Object
Determine the page where a member falls in the leaderboard.
-
#page_for_in(leaderboard_name, member, page_size = DEFAULT_PAGE_SIZE) ⇒ Object
Determine the page where a member falls in the named leaderboard.
-
#percentile_for(member) ⇒ Object
Retrieve the percentile for a member in the leaderboard.
-
#percentile_for_in(leaderboard_name, member) ⇒ Object
Retrieve the percentile for a member in the named leaderboard.
-
#rank_for(member) ⇒ Object
Retrieve the rank for a member in the leaderboard.
-
#rank_for_in(leaderboard_name, member) ⇒ Object
Retrieve the rank for a member in the named leaderboard.
-
#rank_member(member, score, member_data = nil) ⇒ Object
Rank a member in the leaderboard.
-
#rank_member_across(leaderboards, member, score, member_data = nil) ⇒ Object
Rank a member across multiple leaderboards.
-
#rank_member_if(rank_conditional, member, score, member_data = nil) ⇒ Object
Rank a member in the leaderboard based on execution of the
rank_conditional
. -
#rank_member_if_in(leaderboard_name, rank_conditional, member, score, member_data = nil) ⇒ Object
Rank a member in the named leaderboard based on execution of the
rank_conditional
. -
#rank_member_in(leaderboard_name, member, score, member_data = nil) ⇒ Object
Rank a member in the named leaderboard.
-
#rank_members(*members_and_scores) ⇒ Object
Rank an array of members in the leaderboard.
-
#rank_members_in(leaderboard_name, *members_and_scores) ⇒ Object
Rank an array of members in the named leaderboard.
-
#ranked_in_list(members, options = {}) ⇒ Object
Retrieve a page of leaders from the leaderboard for a given list of members.
-
#ranked_in_list_in(leaderboard_name, members, options = {}) ⇒ Object
Retrieve a page of leaders from the named leaderboard for a given list of members.
-
#remove_member(member) ⇒ Object
Remove a member from the leaderboard.
-
#remove_member_data(member) ⇒ Object
Remove the optional member data for a given member in the leaderboard.
-
#remove_member_data_in(leaderboard_name, member) ⇒ Object
Remove the optional member data for a given member in the named leaderboard.
-
#remove_member_from(leaderboard_name, member) ⇒ Object
Remove a member from the named leaderboard.
-
#remove_members_in_score_range(min_score, max_score) ⇒ Object
Remove members from the leaderboard in a given score range.
-
#remove_members_in_score_range_in(leaderboard_name, min_score, max_score) ⇒ Object
Remove members from the named leaderboard in a given score range.
-
#remove_members_outside_rank(rank) ⇒ Object
Remove members from the leaderboard outside a given rank.
-
#remove_members_outside_rank_in(leaderboard_name, rank) ⇒ Object
Remove members from the leaderboard outside a given rank.
-
#score_and_rank_for(member) ⇒ Object
Retrieve the score and rank for a member in the leaderboard.
-
#score_and_rank_for_in(leaderboard_name, member) ⇒ Object
Retrieve the score and rank for a member in the named leaderboard.
-
#score_for(member) ⇒ Object
Retrieve the score for a member in the leaderboard.
-
#score_for_in(leaderboard_name, member) ⇒ Object
Retrieve the score for a member in the named leaderboard.
-
#score_for_percentile(percentile) ⇒ Object
Calculate the score for a given percentile value in the leaderboard.
-
#score_for_percentile_in(leaderboard_name, percentile) ⇒ Object
Calculate the score for a given percentile value in the named leaderboard.
-
#top(number, options = {}) ⇒ Object
Retrieve members from the leaderboard within a range from 1 to the number given.
-
#top_in(leaderboard_name, number, options = {}) ⇒ Object
Retrieve members from the named leaderboard within a range from 1 to the number given.
-
#total_members ⇒ Object
Retrieve the total number of members in the leaderboard.
-
#total_members_in(leaderboard_name) ⇒ Object
Retrieve the total number of members in the named leaderboard.
-
#total_members_in_score_range(min_score, max_score) ⇒ Object
Retrieve the total members in a given score range from the leaderboard.
-
#total_members_in_score_range_in(leaderboard_name, min_score, max_score) ⇒ Object
Retrieve the total members in a given score range from the named leaderboard.
-
#total_pages(page_size = nil) ⇒ Object
Retrieve the total number of pages in the leaderboard.
-
#total_pages_in(leaderboard_name, page_size = nil) ⇒ Object
Retrieve the total number of pages in the named leaderboard.
-
#total_scores ⇒ Object
Sum of scores for all members in leaderboard.
-
#update_member_data(member, member_data) ⇒ Object
Update the optional member data for a given member in the leaderboard.
-
#update_member_data_in(leaderboard_name, member, member_data) ⇒ Object
Update the optional member data for a given member in the named leaderboard.
Constructor Details
#initialize(leaderboard_name, options = DEFAULT_OPTIONS, redis_options = DEFAULT_REDIS_OPTIONS) ⇒ Leaderboard
Create a new instance of a leaderboard.
Examples
leaderboard = Leaderboard.new('highscores')
leaderboard = Leaderboard.new('highscores', {:page_size => 10})
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/leaderboard.rb', line 69 def initialize(leaderboard_name, = DEFAULT_OPTIONS, = DEFAULT_REDIS_OPTIONS) = DEFAULT_OPTIONS.dup .merge!() @leaderboard_name = leaderboard_name @reverse = [:reverse] @page_size = [:page_size] if @page_size.nil? || @page_size < 1 @page_size = DEFAULT_PAGE_SIZE end @member_key = [:member_key] @rank_key = [:rank_key] @score_key = [:score_key] @member_data_key = [:member_data_key] @member_data_namespace = [:member_data_namespace] @global_member_data = [:global_member_data] @redis_connection = [:redis_connection] unless @redis_connection.nil? .delete(:redis_connection) end @redis_connection = Redis.new() if @redis_connection.nil? end |
Instance Attribute Details
#leaderboard_name ⇒ Object (readonly)
Name of the leaderboard.
49 50 51 |
# File 'lib/leaderboard.rb', line 49 def leaderboard_name @leaderboard_name end |
#page_size ⇒ Object
Page size to be used when paging through the leaderboard.
52 53 54 |
# File 'lib/leaderboard.rb', line 52 def page_size @page_size end |
#reverse ⇒ Object
Determines whether or not various leaderboard methods return their data in highest-to-lowest (:reverse
false) or lowest-to-highest (:reverse
true)
57 58 59 |
# File 'lib/leaderboard.rb', line 57 def reverse @reverse end |
Instance Method Details
#all_leaders(options = {}) ⇒ Object Also known as: all_members
Retrieve all leaders from the leaderboard.
739 740 741 |
# File 'lib/leaderboard.rb', line 739 def all_leaders( = {}) all_leaders_from(@leaderboard_name, ) end |
#all_leaders_from(leaderboard_name, options = {}) ⇒ Object Also known as: all_members_from
Retrieves all leaders from the named leaderboard.
751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 |
# File 'lib/leaderboard.rb', line 751 def all_leaders_from(leaderboard_name, = {}) = DEFAULT_LEADERBOARD_REQUEST_OPTIONS.dup .merge!() 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, ) else return [] end end |
#around_me(member, options = {}) ⇒ Object
Retrieve a page of leaders from the leaderboard around a given member.
908 909 910 |
# File 'lib/leaderboard.rb', line 908 def around_me(member, = {}) around_me_in(@leaderboard_name, member, ) end |
#around_me_in(leaderboard_name, member, options = {}) ⇒ Object
Retrieve a page of leaders from the named leaderboard around a given member.
919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 |
# File 'lib/leaderboard.rb', line 919 def around_me_in(leaderboard_name, member, = {}) = DEFAULT_LEADERBOARD_REQUEST_OPTIONS.dup .merge!() 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([: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, ) else return [] end end |
#change_score_for(member, delta, member_data = nil) ⇒ Object
Change the score for a member in the leaderboard by a score delta which can be positive or negative.
384 385 386 |
# File 'lib/leaderboard.rb', line 384 def change_score_for(member, delta, member_data = nil) change_score_for_member_in(@leaderboard_name, member, delta, member_data) end |
#change_score_for_member_in(leaderboard_name, member, delta, member_data) ⇒ Object
Change the score for a member in the named leaderboard by a delta which can be positive or negative.
394 395 396 397 398 399 |
# File 'lib/leaderboard.rb', line 394 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_member?(member) ⇒ Boolean
Check to see if a member exists in the leaderboard.
449 450 451 |
# File 'lib/leaderboard.rb', line 449 def check_member?(member) check_member_in?(@leaderboard_name, member) end |
#check_member_in?(leaderboard_name, member) ⇒ Boolean
Check to see if a member exists in the named leaderboard.
459 460 461 |
# File 'lib/leaderboard.rb', line 459 def check_member_in?(leaderboard_name, member) !@redis_connection.zscore(leaderboard_name, member).nil? end |
#delete_leaderboard ⇒ Object
Delete the current leaderboard.
112 113 114 |
# File 'lib/leaderboard.rb', line 112 def delete_leaderboard delete_leaderboard_named(@leaderboard_name) end |
#delete_leaderboard_named(leaderboard_name) ⇒ Object
Delete the named leaderboard.
119 120 121 122 123 124 |
# 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 ⇒ Object
Disconnect the Redis connection.
107 108 109 |
# File 'lib/leaderboard.rb', line 107 def disconnect @redis_connection.client.disconnect end |
#expire_leaderboard(seconds) ⇒ Object
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.
638 639 640 |
# File 'lib/leaderboard.rb', line 638 def expire_leaderboard(seconds) expire_leaderboard_for(@leaderboard_name, seconds) end |
#expire_leaderboard_at(timestamp) ⇒ Object
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.
660 661 662 |
# File 'lib/leaderboard.rb', line 660 def expire_leaderboard_at() expire_leaderboard_at_for(@leaderboard_name, ) end |
#expire_leaderboard_at_for(leaderboard_name, timestamp) ⇒ Object
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.
670 671 672 673 674 675 |
# File 'lib/leaderboard.rb', line 670 def expire_leaderboard_at_for(leaderboard_name, ) @redis_connection.multi do |transaction| transaction.expireat(leaderboard_name, ) transaction.expireat(member_data_key(leaderboard_name), ) end end |
#expire_leaderboard_for(leaderboard_name, seconds) ⇒ Object
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.
648 649 650 651 652 653 |
# File 'lib/leaderboard.rb', line 648 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(destination, keys, options = {:aggregate => :sum}) ⇒ Object
Intersect leaderboards given by keys with this leaderboard into a named destination leaderboard.
1028 1029 1030 |
# File 'lib/leaderboard.rb', line 1028 def intersect_leaderboards(destination, keys, = {:aggregate => :sum}) @redis_connection.zinterstore(destination, keys.insert(0, @leaderboard_name), ) end |
#leaders(current_page, options = {}) ⇒ Object Also known as: members
Retrieve a page of leaders from the leaderboard.
683 684 685 |
# File 'lib/leaderboard.rb', line 683 def leaders(current_page, = {}) leaders_in(@leaderboard_name, current_page, ) end |
#leaders_in(leaderboard_name, current_page, options = {}) ⇒ Object Also known as: members_in
Retrieve a page of leaders from the named leaderboard.
696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 |
# File 'lib/leaderboard.rb', line 696 def leaders_in(leaderboard_name, current_page, = {}) = DEFAULT_LEADERBOARD_REQUEST_OPTIONS.dup .merge!() if current_page < 1 current_page = 1 end page_size = validate_page_size([: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, ) else return [] end end |
#member_at(position, options = {}) ⇒ Object
Retrieve a member at the specified index from the leaderboard.
878 879 880 |
# File 'lib/leaderboard.rb', line 878 def member_at(position, = {}) member_at_in(@leaderboard_name, position, ) end |
#member_at_in(leaderboard_name, position, options = {}) ⇒ Object
Retrieve a member at the specified index from the leaderboard.
889 890 891 892 893 894 895 896 897 898 899 900 |
# File 'lib/leaderboard.rb', line 889 def member_at_in(leaderboard_name, position, = {}) if position > 0 && position <= total_members_in(leaderboard_name) = DEFAULT_LEADERBOARD_REQUEST_OPTIONS.dup .merge!() page_size = validate_page_size([: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, ) leaders[offset] if leaders end end |
#member_data_for(member) ⇒ Object
Retrieve the optional member data for a given member in the leaderboard.
208 209 210 |
# File 'lib/leaderboard.rb', line 208 def member_data_for(member) member_data_for_in(@leaderboard_name, member) end |
#member_data_for_in(leaderboard_name, member) ⇒ Object
Retrieve the optional member data for a given member in the named leaderboard.
218 219 220 |
# File 'lib/leaderboard.rb', line 218 def member_data_for_in(leaderboard_name, member) @redis_connection.hget(member_data_key(leaderboard_name), member) end |
#members_data_for(members) ⇒ Object
Retrieve the optional member data for the given members in the leaderboard.
238 239 240 |
# File 'lib/leaderboard.rb', line 238 def members_data_for(members) members_data_for_in(@leaderboard_name, members) end |
#members_data_for_in(leaderboard_name, members) ⇒ Object
Retrieve the optional member data for a given member in the named leaderboard.
228 229 230 231 |
# 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 |
#members_from_rank_range(starting_rank, ending_rank, options = {}) ⇒ Object
Retrieve members from the leaderboard within a given rank range.
811 812 813 |
# File 'lib/leaderboard.rb', line 811 def members_from_rank_range(starting_rank, ending_rank, = {}) members_from_rank_range_in(@leaderboard_name, starting_rank, ending_rank, ) end |
#members_from_rank_range_in(leaderboard_name, starting_rank, ending_rank, options = {}) ⇒ Object
Retrieve members from the named leaderboard within a given rank range.
823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 |
# File 'lib/leaderboard.rb', line 823 def members_from_rank_range_in(leaderboard_name, starting_rank, ending_rank, = {}) = DEFAULT_LEADERBOARD_REQUEST_OPTIONS.dup .merge!() 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, ) else return [] end end |
#members_from_score_range(minimum_score, maximum_score, options = {}) ⇒ Object
Retrieve members from the leaderboard within a given score range.
777 778 779 |
# File 'lib/leaderboard.rb', line 777 def members_from_score_range(minimum_score, maximum_score, = {}) members_from_score_range_in(@leaderboard_name, minimum_score, maximum_score, ) end |
#members_from_score_range_in(leaderboard_name, minimum_score, maximum_score, options = {}) ⇒ Object
Retrieve members from the named leaderboard within a given score range.
789 790 791 792 793 794 795 796 797 798 799 800 801 802 |
# File 'lib/leaderboard.rb', line 789 def members_from_score_range_in(leaderboard_name, minimum_score, maximum_score, = {}) = DEFAULT_LEADERBOARD_REQUEST_OPTIONS.dup .merge!() 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, ) else return [] end end |
#merge_leaderboards(destination, keys, options = {:aggregate => :sum}) ⇒ Object
Merge leaderboards given by keys with this leaderboard into a named destination leaderboard.
1019 1020 1021 |
# File 'lib/leaderboard.rb', line 1019 def merge_leaderboards(destination, keys, = {:aggregate => :sum}) @redis_connection.zunionstore(destination, keys.insert(0, @leaderboard_name), ) end |
#page_for(member, page_size = DEFAULT_PAGE_SIZE) ⇒ Object
Determine the page where a member falls in the leaderboard.
608 609 610 |
# File 'lib/leaderboard.rb', line 608 def page_for(member, page_size = DEFAULT_PAGE_SIZE) page_for_in(@leaderboard_name, member, page_size) end |
#page_for_in(leaderboard_name, member, page_size = DEFAULT_PAGE_SIZE) ⇒ Object
Determine the page where a member falls in the named leaderboard.
619 620 621 622 623 624 625 626 627 628 629 630 631 |
# File 'lib/leaderboard.rb', line 619 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 |
#percentile_for(member) ⇒ Object
Retrieve the percentile for a member in the leaderboard.
537 538 539 |
# File 'lib/leaderboard.rb', line 537 def percentile_for(member) percentile_for_in(@leaderboard_name, member) end |
#percentile_for_in(leaderboard_name, member) ⇒ Object
Retrieve the percentile for a member in the named leaderboard.
547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 |
# File 'lib/leaderboard.rb', line 547 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 |
#rank_for(member) ⇒ Object
Retrieve the rank for a member in the leaderboard.
406 407 408 |
# File 'lib/leaderboard.rb', line 406 def rank_for(member) rank_for_in(@leaderboard_name, member) end |
#rank_for_in(leaderboard_name, member) ⇒ Object
Retrieve the rank for a member in the named leaderboard.
416 417 418 419 420 421 422 |
# File 'lib/leaderboard.rb', line 416 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_member(member, score, member_data = nil) ⇒ Object
Rank a member in the leaderboard.
131 132 133 |
# 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_member_across(leaderboards, member, score, member_data = nil) ⇒ Object
Rank a member across multiple leaderboards.
154 155 156 157 158 159 160 161 |
# 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_member_if(rank_conditional, member, score, member_data = nil) ⇒ Object
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
176 177 178 |
# 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_member_if_in(leaderboard_name, rank_conditional, member, score, member_data = nil) ⇒ Object
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
194 195 196 197 198 199 200 201 |
# 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_member_in(leaderboard_name, member, score, member_data = nil) ⇒ Object
Rank a member in the named leaderboard.
141 142 143 144 145 146 |
# 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_members(*members_and_scores) ⇒ Object
Rank an array of members in the leaderboard.
277 278 279 |
# File 'lib/leaderboard.rb', line 277 def rank_members(*members_and_scores) rank_members_in(@leaderboard_name, *members_and_scores) end |
#rank_members_in(leaderboard_name, *members_and_scores) ⇒ Object
Rank an array of members in the named leaderboard.
285 286 287 288 289 290 291 292 293 294 295 |
# 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 |
#ranked_in_list(members, options = {}) ⇒ Object
Retrieve a page of leaders from the leaderboard for a given list of members.
955 956 957 |
# File 'lib/leaderboard.rb', line 955 def ranked_in_list(members, = {}) ranked_in_list_in(@leaderboard_name, members, ) end |
#ranked_in_list_in(leaderboard_name, members, options = {}) ⇒ Object
Retrieve a page of leaders from the named leaderboard for a given list of members.
966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 |
# File 'lib/leaderboard.rb', line 966 def ranked_in_list_in(leaderboard_name, members, = {}) = DEFAULT_LEADERBOARD_REQUEST_OPTIONS.dup .merge!() 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 [:members_only] members.each_with_index do |member, index| data = {} data[@member_key] = member unless [:members_only] data[@rank_key] = responses[index * 2] + 1 rescue nil if data[@rank_key] == nil next unless [:include_missing] end data[@score_key] = responses[index * 2 + 1].to_f if responses[index * 2 + 1] end ranks_for_members << data end if [: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 [: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_member(member) ⇒ Object
Remove a member from the leaderboard.
300 301 302 |
# File 'lib/leaderboard.rb', line 300 def remove_member(member) remove_member_from(@leaderboard_name, member) end |
#remove_member_data(member) ⇒ Object
Remove the optional member data for a given member in the leaderboard.
262 263 264 |
# File 'lib/leaderboard.rb', line 262 def remove_member_data(member) remove_member_data_in(@leaderboard_name, member) end |
#remove_member_data_in(leaderboard_name, member) ⇒ Object
Remove the optional member data for a given member in the named leaderboard.
270 271 272 |
# 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_member_from(leaderboard_name, member) ⇒ Object
Remove a member from the named leaderboard.
308 309 310 311 312 313 |
# 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_in_score_range(min_score, max_score) ⇒ Object
Remove members from the leaderboard in a given score range.
498 499 500 |
# File 'lib/leaderboard.rb', line 498 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_in_score_range_in(leaderboard_name, min_score, max_score) ⇒ Object
Remove members from the named leaderboard in a given score range.
507 508 509 |
# File 'lib/leaderboard.rb', line 507 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_outside_rank(rank) ⇒ Object
Remove members from the leaderboard outside a given rank.
515 516 517 |
# File 'lib/leaderboard.rb', line 515 def remove_members_outside_rank(rank) remove_members_outside_rank_in(@leaderboard_name, rank) end |
#remove_members_outside_rank_in(leaderboard_name, rank) ⇒ Object
Remove members from the leaderboard outside a given rank.
524 525 526 527 528 529 530 |
# File 'lib/leaderboard.rb', line 524 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 |
#score_and_rank_for(member) ⇒ Object
Retrieve the score and rank for a member in the leaderboard.
468 469 470 |
# File 'lib/leaderboard.rb', line 468 def score_and_rank_for(member) score_and_rank_for_in(@leaderboard_name, member) end |
#score_and_rank_for_in(leaderboard_name, member) ⇒ Object
Retrieve the score and rank for a member in the named leaderboard.
478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 |
# File 'lib/leaderboard.rb', line 478 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 |
#score_for(member) ⇒ Object
Retrieve the score for a member in the leaderboard.
429 430 431 |
# File 'lib/leaderboard.rb', line 429 def score_for(member) score_for_in(@leaderboard_name, member) end |
#score_for_in(leaderboard_name, member) ⇒ Object
Retrieve the score for a member in the named leaderboard.
439 440 441 442 |
# File 'lib/leaderboard.rb', line 439 def score_for_in(leaderboard_name, member) score = @redis_connection.zscore(leaderboard_name, member) score.to_f if score end |
#score_for_percentile(percentile) ⇒ Object
Calculate the score for a given percentile value in the leaderboard.
566 567 568 |
# File 'lib/leaderboard.rb', line 566 def score_for_percentile(percentile) score_for_percentile_in(@leaderboard_name, percentile) end |
#score_for_percentile_in(leaderboard_name, percentile) ⇒ Object
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).
578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 |
# File 'lib/leaderboard.rb', line 578 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 |
#top(number, options = {}) ⇒ Object
Retrieve members from the leaderboard within a range from 1 to the number given.
856 857 858 |
# File 'lib/leaderboard.rb', line 856 def top(number, = {}) top_in(@leaderboard_name, number, ) end |
#top_in(leaderboard_name, number, options = {}) ⇒ Object
Retrieve members from the named leaderboard within a range from 1 to the number given.
868 869 870 |
# File 'lib/leaderboard.rb', line 868 def top_in(leaderboard_name, number, ={}) members_from_rank_range_in(leaderboard_name, 1, number, ) end |
#total_members ⇒ Object
Retrieve the total number of members in the leaderboard.
318 319 320 |
# File 'lib/leaderboard.rb', line 318 def total_members total_members_in(@leaderboard_name) end |
#total_members_in(leaderboard_name) ⇒ Object
Retrieve the total number of members in the named leaderboard.
327 328 329 |
# File 'lib/leaderboard.rb', line 327 def total_members_in(leaderboard_name) @redis_connection.zcard(leaderboard_name) end |
#total_members_in_score_range(min_score, max_score) ⇒ Object
Retrieve the total members in a given score range from the leaderboard.
357 358 359 |
# 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 |
#total_members_in_score_range_in(leaderboard_name, min_score, max_score) ⇒ Object
Retrieve the total members in a given score range from the named leaderboard.
368 369 370 |
# 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 |
#total_pages(page_size = nil) ⇒ Object
Retrieve the total number of pages in the leaderboard.
336 337 338 |
# File 'lib/leaderboard.rb', line 336 def total_pages(page_size = nil) total_pages_in(@leaderboard_name, page_size) end |
#total_pages_in(leaderboard_name, page_size = nil) ⇒ Object
Retrieve the total number of pages in the named leaderboard.
346 347 348 349 |
# 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 |
#total_scores ⇒ Object
Sum of scores for all members in leaderboard
375 376 377 |
# File 'lib/leaderboard.rb', line 375 def total_scores all_leaders.map{|hash| hash[:score] }.inject(0, :+) end |
#update_member_data(member, member_data) ⇒ Object
Update the optional member data for a given member in the leaderboard.
246 247 248 |
# File 'lib/leaderboard.rb', line 246 def update_member_data(member, member_data) update_member_data_in(@leaderboard_name, member, member_data) end |
#update_member_data_in(leaderboard_name, member, member_data) ⇒ Object
Update the optional member data for a given member in the named leaderboard.
255 256 257 |
# 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 |