2014-04-01 00:47:41 -06:00

156 lines
5.0 KiB
JavaScript

"use strict";
// By default fakeredis simulates a ridiculous amount of network latency
// to help you discover race-conditions when testing multi-client setups.
// Instantiate your 'clients' with a truthy .fast option,
// or set it here globally to make things go a bit faster.
exports.fast = false;
/**
TODO:
- lint negative count and offset LIMITs away,
SORT and ZRANGEBYSCORE treat them differently, so it's just confusing and a bad practice.
**/
var index = require ( "redis" ),
Backend = require ( "./lib/backend" ).Backend,
Connection = require ( "./lib/connection" ).Connection,
helpers = require ( "./lib/helpers" ),
backends = {},
RedisClient = index.RedisClient,
anon = 0;
//// Re-export redis exports.
exports.RedisClient = index.RedisClient;
exports.Multi = index.Multi;
exports.print = index.print;
//// Overriden client factory.
exports.createClient = function ( port, host, options )
{
var id = !port && !host ? 'fake_' + ( ++ anon ) : ( host || "" ) + ( port || "" ),
lat = options && options.fast || exports.fast ? 1 : null,
c = new Connection ( backends [ id ] || ( backends [ id ] = new Backend ), lat, lat ),
cl = new RedisClient ( { on : function () {} } /* , options */ ),
ns = options && options.no_sugar;
if ( options && options.verbose )
c.verbose = true;
cl.connected = true;
cl.ready = true;
cl.send_command = function ( command, args, callback )
{
//// Interpret arguments, copy-paste from mranney/redis/index.js for best compat.
if (typeof command !== "string") {
throw new Error("First argument to send_command must be the command name string, not " + typeof command);
}
if (Array.isArray(args)) {
if (typeof callback === "function") {
// probably the fastest way:
// client.command([arg1, arg2], cb); (straight passthrough)
// send_command(command, [arg1, arg2], cb);
} else if (! callback) {
// most people find this variable argument length form more convenient, but it uses arguments, which is slower
// client.command(arg1, arg2, cb); (wraps up arguments into an array)
// send_command(command, [arg1, arg2, cb]);
// client.command(arg1, arg2); (callback is optional)
// send_command(command, [arg1, arg2]);
// client.command(arg1, arg2, undefined); (callback is undefined)
// send_command(command, [arg1, arg2, undefined]);
var last_arg_type = typeof args[args.length - 1];
if (last_arg_type === "function" || last_arg_type === "undefined") {
callback = args.pop();
}
} else {
throw new Error("send_command: last argument must be a callback or undefined");
}
} else {
throw new Error("send_command: second argument must be an array");
}
// if the last argument is an array, expand it out. This allows commands like this:
// client.command(arg1, [arg2, arg3, arg4], cb);
// and converts to:
// client.command(arg1, arg2, arg3, arg4, cb);
// which is convenient for some things like sadd
if (Array.isArray(args[args.length - 1])) {
args = args.slice(0, -1).concat(args[args.length - 1]);
}
//// Lint args.
if ( !options || !options.no_lint )
{
var i, n;
n = args.length;
for ( i = 0; i < n; i ++ )
if ( typeof args [ i ] !== 'string' && typeof args [ i ] !== 'number' )
throw new Error ( "fakeredis/lint: Argument #" + i + " for " + command + " is not a String or Number: " + args [ i ] );
}
//// You can disable hash sugar with the no_sugar option.
var cb;
if ( callback && !ns && /^hgetall/i.test ( command ) )
cb = function ( err, data )
{
if ( !err && data )
data = reply_to_object ( data );
callback ( err, data );
};
else
cb = callback;
c.push ( this, command, args, cb );
};
cl.pushMessage = cl.emit.bind ( cl );
( function ()
{
var prop;
for ( prop in helpers )
cl [ prop ] = helpers [ prop ];
}
() );
return cl;
};
//// Helpers for node_redis compat.
// hgetall converts its replies to an Object. If the reply is empty, null is returned.
function reply_to_object(reply) {
var obj = {}, j, jl, key, val;
if (reply.length === 0) {
return null;
}
for (j = 0, jl = reply.length; j < jl; j += 2) {
key = reply[j].toString();
val = reply[j + 1];
obj[key] = val;
}
return obj;
}