diff --git a/lib/leaderboard.js b/lib/leaderboard.js index 28e00a3..547ea54 100644 --- a/lib/leaderboard.js +++ b/lib/leaderboard.js @@ -22,7 +22,11 @@ DEFAULT_OPTIONS = { 'pageSize': Leaderboard.DEFAULT_PAGE_SIZE, - 'reverse': false + 'reverse': false, + 'memberKey': 'member', + 'rankKey': 'rank', + 'scoreKey': 'score', + 'memberDataKey': 'member_data' }; /* @@ -72,6 +76,10 @@ if (this.pageSize === null || this.pageSize < 1) { this.pageSize = Leaderboard.DEFAULT_PAGE_SIZE; } + this.memberKeyOption = options['memberKey'] || 'member'; + this.rankKeyOption = options['rankKey'] || 'rank'; + this.scoreKeyOption = options['scoreKey'] || 'score'; + this.memberDataKeyOption = options['memberDataKey'] || 'member_data'; this.redisConnection = redisOptions['redis_connection']; if (this.redisConnection != null) { delete redisOptions['redis_connection']; @@ -681,7 +689,8 @@ Leaderboard.prototype.scoreAndRankForIn = function(leaderboardName, member, callback) { - var transaction; + var transaction, + _this = this; transaction = this.redisConnection.multi(); transaction.zscore(leaderboardName, member); if (this.reverse) { @@ -694,16 +703,16 @@ if (replies) { scoreAndRankData = {}; if (replies[0] != null) { - scoreAndRankData['score'] = parseFloat(replies[0]); + scoreAndRankData[_this.scoreKeyOption] = parseFloat(replies[0]); } else { - scoreAndRankData['score'] = null; + scoreAndRankData[_this.scoreKeyOption] = null; } if (replies[1] != null) { - scoreAndRankData['rank'] = replies[1] + 1; + scoreAndRankData[_this.rankKeyOption] = replies[1] + 1; } else { - scoreAndRankData['rank'] = null; + scoreAndRankData[_this.rankKeyOption] = null; } - scoreAndRankData['member'] = member; + scoreAndRankData[_this.memberKeyOption] = member; return callback(scoreAndRankData); } }); @@ -1072,19 +1081,19 @@ _results.push((function(member) { var data; data = {}; - data['member'] = member; + data[_this.memberKeyOption] = member; if (!options['members_only']) { - data['rank'] = replies[index * 2] + 1; + data[_this.rankKeyOption] = replies[index * 2] + 1; if (replies[index * 2 + 1]) { - data['score'] = parseFloat(replies[index * 2 + 1]); + data[_this.scoreKeyOption] = parseFloat(replies[index * 2 + 1]); } else { - data['score'] = null; - data['rank'] = null; + data[_this.scoreKeyOption] = null; + data[_this.rankKeyOption] = null; } } if (options['with_member_data']) { return _this.memberDataForIn(leaderboardName, member, function(memberdata) { - data['member_data'] = memberdata; + data[_this.memberDataKeyOption] = memberdata; ranksForMembers.push(data); if (ranksForMembers.length === members.length) { switch (options['sort_by']) { diff --git a/spec/leaderboard_spec.coffee b/spec/leaderboard_spec.coffee index 5dfc6e8..ce46708 100644 --- a/spec/leaderboard_spec.coffee +++ b/spec/leaderboard_spec.coffee @@ -3,7 +3,7 @@ describe 'Leaderboard', -> @redisConnection = redis.createClient(6379, 'localhost') beforeEach -> - @leaderboard = new Leaderboard('highscores') + @leaderboard = new Leaderboard('highscores', Leaderboard.DEFAULT_OPTIONS) afterEach -> @redisConnection.flushdb() @@ -27,6 +27,29 @@ describe 'Leaderboard', -> done() + it 'should allow you to set custom keys for member, score, rank and member_data', (done) -> + options = + 'pageSize': Leaderboard.DEFAULT_PAGE_SIZE + 'reverse': false + 'memberKey': 'member_custom' + 'rankKey': 'rank_custom' + 'scoreKey': 'score_custom' + 'memberDataKey': 'member_data_custom' + + + @leaderboard = new Leaderboard('highscores', options) + + for index in [0...Leaderboard.DEFAULT_PAGE_SIZE + 1] + @leaderboard.rankMember("member_#{index}", index, "member_data_#{index}", (reply) -> ) + + @leaderboard.leaders(1, {'with_member_data': true}, (reply) -> + reply.length.should.equal(25) + reply[0]['member_custom'].should.equal('member_25') + reply[0]['score_custom'].should.equal(25) + reply[0]['rank_custom'].should.equal(1) + reply[0]['member_data_custom'].should.equal('member_data_25') + done()) + it 'should allow you to disconnect the Redis connection', (done) -> @leaderboard.disconnect() done() @@ -540,9 +563,9 @@ describe 'Leaderboard', -> done() it 'should allow you to merge leaderboards', (done) -> - foo = new Leaderboard('foo') - bar = new Leaderboard('bar') - foobar = new Leaderboard('foobar') + foo = new Leaderboard('foo', Leaderboard.DEFAULT_OPTIONS) + bar = new Leaderboard('bar', Leaderboard.DEFAULT_OPTIONS) + foobar = new Leaderboard('foobar', Leaderboard.DEFAULT_OPTIONS) foo.rankMember('foo_1', 1, null, (reply) -> foo.rankMember('foo_2', 2, null, (reply) -> @@ -556,9 +579,9 @@ describe 'Leaderboard', -> done()))))))) it 'should allow you to intersect leaderboards', (done) -> - foo = new Leaderboard('foo') - bar = new Leaderboard('bar') - foobar = new Leaderboard('foobar') + foo = new Leaderboard('foo', Leaderboard.DEFAULT_OPTIONS) + bar = new Leaderboard('bar', Leaderboard.DEFAULT_OPTIONS) + foobar = new Leaderboard('foobar', Leaderboard.DEFAULT_OPTIONS) foo.rankMember('foo_1', 1, null, (reply) -> foo.rankMember('foo_2', 2, null, (reply) -> diff --git a/src/leaderboard.coffee b/src/leaderboard.coffee index 81cb3d2..10b6fba 100644 --- a/src/leaderboard.coffee +++ b/src/leaderboard.coffee @@ -14,6 +14,10 @@ class Leaderboard DEFAULT_OPTIONS = 'pageSize': @DEFAULT_PAGE_SIZE 'reverse': false + 'memberKey': 'member' + 'rankKey': 'rank' + 'scoreKey': 'score' + 'memberDataKey': 'member_data' ### # Default Redis host: localhost @@ -45,6 +49,10 @@ class Leaderboard @pageSize = options['pageSize'] if @pageSize == null || @pageSize < 1 @pageSize = Leaderboard.DEFAULT_PAGE_SIZE + @memberKeyOption = options['memberKey'] || 'member' + @rankKeyOption = options['rankKey'] || 'rank' + @scoreKeyOption = options['scoreKey'] || 'score' + @memberDataKeyOption = options['memberDataKey'] || 'member_data' @redisConnection = redisOptions['redis_connection'] @@ -487,18 +495,19 @@ class Leaderboard transaction.zrank(leaderboardName, member) else transaction.zrevrank(leaderboardName, member) - transaction.exec((err, replies) -> + + transaction.exec((err, replies) => if replies scoreAndRankData = {} if replies[0]? - scoreAndRankData['score'] = parseFloat(replies[0]) + scoreAndRankData[@scoreKeyOption] = parseFloat(replies[0]) else - scoreAndRankData['score'] = null + scoreAndRankData[@scoreKeyOption] = null if replies[1]? - scoreAndRankData['rank'] = replies[1] + 1 + scoreAndRankData[@rankKeyOption] = replies[1] + 1 else - scoreAndRankData['rank'] = null - scoreAndRankData['member'] = member + scoreAndRankData[@rankKeyOption] = null + scoreAndRankData[@memberKeyOption] = member callback(scoreAndRankData)) ### @@ -764,19 +773,19 @@ class Leaderboard for member, index in members do (member) => data = {} - data['member'] = member + data[@memberKeyOption] = member unless options['members_only'] - data['rank'] = replies[index * 2] + 1 + data[@rankKeyOption] = replies[index * 2] + 1 if replies[index * 2 + 1] - data['score'] = parseFloat(replies[index * 2 + 1]) + data[@scoreKeyOption] = parseFloat(replies[index * 2 + 1]) else - data['score'] = null - data['rank'] = null + data[@scoreKeyOption] = null + data[@rankKeyOption] = null # Retrieve optional member data based on options['with_member_data'] if options['with_member_data'] this.memberDataForIn leaderboardName, member, (memberdata) => - data['member_data'] = memberdata + data[@memberDataKeyOption] = memberdata ranksForMembers.push(data) # Sort if options['sort_by'] if ranksForMembers.length == members.length