From 91ebe64dae7b75c13ac837fa46c4cff1aefcb1c8 Mon Sep 17 00:00:00 2001 From: Tj Holowaychuk Date: Wed, 22 Sep 2010 18:38:55 -0700 Subject: [PATCH 1/2] Implemented alternate MULTI syntax Usage: client .multi .set(foo, bar) .set(counter, 1) .incr(counter) .exec(function(err, replies){}); --- index.js | 67 +++++++++++++++++++++++++++----------------------------- test.js | 49 ++++++++++++++++++++++------------------- 2 files changed, 58 insertions(+), 58 deletions(-) diff --git a/index.js b/index.js index d0cfbc2844..52dcf7636e 100644 --- a/index.js +++ b/index.js @@ -658,8 +658,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) { @@ -676,47 +677,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 748bfa32c4..6af06f70b1 100644 --- a/test.js +++ b/test.js @@ -339,30 +339,33 @@ tests.MSETNX = function () { tests.MULTI = function () { var name = "MULTI"; - client.multi([ - ["mset", ["multifoo", "10", "multibar", "20"], require_string("OK", name)], - ["set", ["foo2"], require_error(name)], - ["incr", ["multifoo"], require_number(11, name)], - ["incr", ["multibar"], require_number(21, name)] - ]); - client.multi([ - ["incr", ["multibar"], require_number(22, name)], - ["incr", ["multifoo"], require_number(12, name)] - ]); - - client.multi([ - ["mget", ["multifoo", "multibar"], function (err, res) { - assert.strictEqual(2, res.length, name); - assert.strictEqual("12", res[0].toString(), name); - assert.strictEqual("22", res[1].toString(), name); - }], - ["set", ["foo2"], require_error(name)], - ["incr", ["multifoo"], require_number(13, name)], - ["incr", ["multibar"], require_number(23, name)] - ], function (reply) { - next(name); - }); + client + .multi + .mset('some', '10', 'keys', '20') + .incr('some') + .incr('keys') + .exec(function(err, replies){ + assert.strictEqual(null, err); + assert.equal('OK', replies[0]); + assert.equal(11, replies[1]); + assert.equal(21, replies[2]); + 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 () { From 7795d68351f3167861588ed973ccde432570571e Mon Sep 17 00:00:00 2001 From: Tj Holowaychuk Date: Wed, 22 Sep 2010 20:12:37 -0700 Subject: [PATCH 2/2] Extended multi test to include multibulk --- test.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test.js b/test.js index 6af06f70b1..f4badedaac 100644 --- a/test.js +++ b/test.js @@ -345,11 +345,14 @@ tests.MULTI = function () { .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); }); };