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.1'.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.
-
#total_scores_in(leaderboard_name) ⇒ Object
Sum of scores for all members in the named 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.
746 747 748 |
# File 'lib/leaderboard.rb', line 746 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.
758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 |
# File 'lib/leaderboard.rb', line 758 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.
915 916 917 |
# File 'lib/leaderboard.rb', line 915 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.
926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 |
# File 'lib/leaderboard.rb', line 926 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.
391 392 393 |
# 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_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.
401 402 403 404 405 406 |
# 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_member?(member) ⇒ Boolean
Check to see if a member exists in the leaderboard.
456 457 458 |
# File 'lib/leaderboard.rb', line 456 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.
466 467 468 |
# File 'lib/leaderboard.rb', line 466 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.
645 646 647 |
# File 'lib/leaderboard.rb', line 645 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.
667 668 669 |
# File 'lib/leaderboard.rb', line 667 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.
677 678 679 680 681 682 |
# File 'lib/leaderboard.rb', line 677 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.
655 656 657 658 659 660 |
# 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(destination, keys, options = {:aggregate => :sum}) ⇒ Object
Intersect leaderboards given by keys with this leaderboard into a named destination leaderboard.
1035 1036 1037 |
# File 'lib/leaderboard.rb', line 1035 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.
690 691 692 |
# File 'lib/leaderboard.rb', line 690 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.
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 731 732 733 734 735 736 737 |
# File 'lib/leaderboard.rb', line 703 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.
885 886 887 |
# File 'lib/leaderboard.rb', line 885 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.
896 897 898 899 900 901 902 903 904 905 906 907 |
# File 'lib/leaderboard.rb', line 896 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.
818 819 820 |
# File 'lib/leaderboard.rb', line 818 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.
830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 |
# File 'lib/leaderboard.rb', line 830 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.
784 785 786 |
# File 'lib/leaderboard.rb', line 784 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.
796 797 798 799 800 801 802 803 804 805 806 807 808 809 |
# File 'lib/leaderboard.rb', line 796 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.
1026 1027 1028 |
# File 'lib/leaderboard.rb', line 1026 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.
615 616 617 |
# File 'lib/leaderboard.rb', line 615 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.
626 627 628 629 630 631 632 633 634 635 636 637 638 |
# 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 |
#percentile_for(member) ⇒ Object
Retrieve the percentile for a member in the leaderboard.
544 545 546 |
# File 'lib/leaderboard.rb', line 544 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.
554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 |
# 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 |
#rank_for(member) ⇒ Object
Retrieve the rank for a member in the leaderboard.
413 414 415 |
# File 'lib/leaderboard.rb', line 413 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.
423 424 425 426 427 428 429 |
# 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_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.
962 963 964 |
# File 'lib/leaderboard.rb', line 962 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.
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 1013 1014 1015 1016 1017 1018 1019 |
# File 'lib/leaderboard.rb', line 973 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.
505 506 507 |
# 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_in_score_range_in(leaderboard_name, min_score, max_score) ⇒ Object
Remove members from the named leaderboard in a given score range.
514 515 516 |
# 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_outside_rank(rank) ⇒ Object
Remove members from the leaderboard outside a given rank.
522 523 524 |
# File 'lib/leaderboard.rb', line 522 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.
531 532 533 534 535 536 537 |
# 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 |
#score_and_rank_for(member) ⇒ Object
Retrieve the score and rank for a member in the leaderboard.
475 476 477 |
# File 'lib/leaderboard.rb', line 475 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.
485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 |
# 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 |
#score_for(member) ⇒ Object
Retrieve the score for a member in the leaderboard.
436 437 438 |
# File 'lib/leaderboard.rb', line 436 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.
446 447 448 449 |
# 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 |
#score_for_percentile(percentile) ⇒ Object
Calculate the score for a given percentile value in the leaderboard.
573 574 575 |
# File 'lib/leaderboard.rb', line 573 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).
585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 |
# 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 |
#top(number, options = {}) ⇒ Object
Retrieve members from the leaderboard within a range from 1 to the number given.
863 864 865 |
# File 'lib/leaderboard.rb', line 863 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.
875 876 877 |
# File 'lib/leaderboard.rb', line 875 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 total_scores_in(@leaderboard_name) end |
#total_scores_in(leaderboard_name) ⇒ Object
Sum of scores for all members in the named leaderboard
382 383 384 |
# 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_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 |