diff --git a/index.js b/index.js index af7b8fc2ab..97a160cbc5 100644 --- a/index.js +++ b/index.js @@ -662,8 +662,9 @@ exports.commands = [ "INFO", "MONITOR", "SLAVEOF", "CONFIG", // Publish/Subscribe "PUBLISH", "SUBSCRIBE", "PSUBSCRIBE", "UNSUBSCRIBE", "PUNSUBSCRIBE", + "MULTI", "EXEC", // Undocumented commands - "PING" + "PING", ]; exports.commands.forEach(function (command) { @@ -680,47 +681,43 @@ exports.commands.forEach(function (command) { }; }); -// Transactions - "DISCARD", "WATCH", "UNWATCH", +function Multi(client) { + this.client = client; + this.queue = []; + this.queue.push(['MULTI']); +} -RedisClient.prototype.multi = function (commands, callback) { - var self = this; +exports.commands.forEach(function (command) { + Multi.prototype[command.toLowerCase()] = function () { + var args = to_array(arguments); + args.unshift(command); // put command at the beginning + this.queue.push(args); + return this; + }; +}); - this.send_command("MULTI", function (err, reply) { - if (err) { - console.warn("Error starting MULTI request: " + err.stack); - } - }); - commands.forEach(function (args, command_num) { - self.send_command(args[0], args[1], function (err, reply) { +Multi.prototype.exec = function(fn){ + var done; + this.queue.push(['EXEC']); + this.queue.forEach(function(args){ + var command = args.shift(); + this.client[command](args, function(err, reply){ + if (done) return; if (err) { - args[2](err); - commands.splice(command_num, 1); // what if this runs before all commands are sent? - } else { - if (reply !== "QUEUED") { - console.warn("Unexpected MULTI reply: " + reply + " instead of 'QUEUED'"); - } + done = true; + fn(new Error(err)); + } else if ('EXEC' == command) { + done = true; + fn(null, reply); } }); - }); - this.send_command("EXEC", function (err, replies) { - replies.forEach(function (reply, reply_num) { - if (typeof commands[reply_num][2] === "function") { - commands[reply_num][2](null, reply); - } else { - if (exports.debug_mode) { - console.log("no callback for multi response " + reply_num + ", skipping."); - } - } - }); - if (typeof callback === "function") { - callback(replies); - } - }); -}; -RedisClient.prototype.MULTI = function (commands) { - return this.multi(commands); + }, this); }; +RedisClient.prototype.__defineGetter__('multi', function(){ + return new Multi(this); +}); + exports.createClient = function (port_arg, host_arg, options) { var port = port_arg || default_port, host = host_arg || default_host, diff --git a/test.js b/test.js index 87965b4bed..5a7f2ae2bc 100644 --- a/test.js +++ b/test.js @@ -343,8 +343,6 @@ tests.SETEX = function () { client.ttl(["setex key"], last(name, require_number_pos(name))); }; -// plenty of tests of MSET already - tests.MSETNX = function () { var name = "MSETNX"; client.mset(["mset1", "val1", "mset2", "val2", "mset3", "val3"], require_string("OK", name)); @@ -355,6 +353,39 @@ tests.MSETNX = function () { client.exists(["mset4"], last(name, require_number(1, name))); }; +tests.MULTI_4 = function () { + var name = "MULTI_4"; + + client.multi + .mset('some', '10', 'keys', '20') + .incr('some') + .incr('keys') + .mget('some', 'keys') + .exec(function(err, replies){ + assert.strictEqual(null, err); + assert.equal('OK', replies[0]); + assert.equal(11, replies[1]); + assert.equal(21, replies[2]); + assert.equal(11, replies[3][0].toString()); + assert.equal(21, replies[3][1].toString()); + next(name); + }); +}; + +tests.MULTI_ERROR = function () { + var name = "MULTI_ERROR"; + + client + .multi + .set('something', 'amazing') + .set('invalid') + .exec(function(err, replies){ + assert.equal("ERR wrong number of arguments for 'set' command", err.message); + assert.strictEqual(undefined, replies); + next(name); + }); +}; + tests.HGETALL = function () { var name = "HGETALL"; client.hmset(["hosts", "mjr", "1", "another", "23", "home", "1234"], require_string("OK", name));