@depup/fakeredis 2.0.0-depup.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.codeclimate.yml +15 -0
- package/.editorconfig +16 -0
- package/.jshintrc +6 -0
- package/.travis.yml +14 -0
- package/Changelog.md +53 -0
- package/README.md +31 -0
- package/changes.json +10 -0
- package/covertest +16 -0
- package/lib/backend.js +1951 -0
- package/lib/connection.js +474 -0
- package/lib/helpers.js +172 -0
- package/main.js +268 -0
- package/package.json +56 -0
- package/redis.test.js +2352 -0
- package/runtest +10 -0
- package/test.js +1025 -0
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
exports.Connection = function(backend, minLatency, maxLatency) {
|
|
5
|
+
var connection = this
|
|
6
|
+
, db = 0
|
|
7
|
+
|
|
8
|
+
, queue, watch, block
|
|
9
|
+
|
|
10
|
+
, timeout = 0
|
|
11
|
+
, state = NORMAL
|
|
12
|
+
, subs = 0;
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
this.push = function(client, command, args, callback) {
|
|
16
|
+
state(client, prep(command, args, callback));
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
// Push a command to a normal connection.
|
|
21
|
+
|
|
22
|
+
function NORMAL(client, entry) {
|
|
23
|
+
var i, n, matches;
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
// Transactions.
|
|
27
|
+
|
|
28
|
+
if (entry.command === "WATCH") {
|
|
29
|
+
entry.override = function() {
|
|
30
|
+
var i, n = entry.args.length;
|
|
31
|
+
if (!watch)
|
|
32
|
+
watch = {};
|
|
33
|
+
for (i = 0; i < n; i++)
|
|
34
|
+
if (!(entry.args[i]in watch))
|
|
35
|
+
watch[entry.args[i]] = backend.getRevision(entry.args[i]);
|
|
36
|
+
|
|
37
|
+
return "OK";
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
else if (entry.command === "UNWATCH") {
|
|
42
|
+
entry.override = function() {
|
|
43
|
+
watch = null;
|
|
44
|
+
return "OK";
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
else if (entry.command === "DISCARD") {
|
|
49
|
+
if (queue) {
|
|
50
|
+
if (!timeout)
|
|
51
|
+
timeout = setTimeout(exec, randLat());
|
|
52
|
+
|
|
53
|
+
for (i = 0; i < queue.length; i++)
|
|
54
|
+
if (queue[i].command === "MULTI") {
|
|
55
|
+
queue.splice(i, queue.length);
|
|
56
|
+
|
|
57
|
+
// This will substitute the DISCARD command with an UNWATCH,
|
|
58
|
+
// hence the recursive call to this.push.
|
|
59
|
+
return this.push(["UNWATCH"], entry.callback);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
entry.override = function() { return "OK"; };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
else if (entry.command === "MULTI") {
|
|
67
|
+
/*jshint loopfunc:true*/
|
|
68
|
+
entry.override = function(queue) {
|
|
69
|
+
if (!queue) throw new Error('WOOT! no queue.');
|
|
70
|
+
var w = watch, key, entry;
|
|
71
|
+
watch = null;
|
|
72
|
+
if (w) for (key in w)
|
|
73
|
+
if (backend.getRevision(key) !== w[key]) {
|
|
74
|
+
|
|
75
|
+
// Abort because of a change in the watched keyspace.
|
|
76
|
+
n = 0;
|
|
77
|
+
while ((entry = queue.shift())) {
|
|
78
|
+
if (entry.command === "EXEC") {
|
|
79
|
+
entry.override = function() {
|
|
80
|
+
var i, out = [];
|
|
81
|
+
for (i = 0; i < n; i++)
|
|
82
|
+
out[i] = null;
|
|
83
|
+
|
|
84
|
+
return out;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
queue.unshift(entry);
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
n++;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return "OK";
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
var replies = [];
|
|
98
|
+
var i, m = queue.length, cb = pushReply.bind(replies);
|
|
99
|
+
for (i = 0; i < m; i++) {
|
|
100
|
+
entry = queue[i];
|
|
101
|
+
if (entry.command !== "EXEC") {
|
|
102
|
+
|
|
103
|
+
// Collect replies for the EXEC output.
|
|
104
|
+
entry.callback = cb;
|
|
105
|
+
|
|
106
|
+
// Prevent blocking within a transaction.
|
|
107
|
+
delete entry.block;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
else {
|
|
111
|
+
|
|
112
|
+
// Exec calls back with the entire reply list.
|
|
113
|
+
entry.override = entry.override.bind(replies);
|
|
114
|
+
return "OK";
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
throw new Error("WOOT! Can't find the EXEC command in the queue.");
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// Prevent flushing before the exec.
|
|
122
|
+
if (timeout) {
|
|
123
|
+
clearTimeout(timeout);
|
|
124
|
+
timeout = 0;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (queue)
|
|
128
|
+
queue.push(entry);
|
|
129
|
+
else
|
|
130
|
+
queue = [entry];
|
|
131
|
+
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
else if (entry.command === "EXEC") {
|
|
136
|
+
entry.override = function() {
|
|
137
|
+
return this.join ? this : null;
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
if (queue && !timeout)
|
|
141
|
+
timeout = setTimeout(exec, randLat());
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
// Pubsub.
|
|
146
|
+
|
|
147
|
+
if ((matches = /^(P)?(UN)?SUBSCRIBE$/.exec(entry.command))) {
|
|
148
|
+
if (!client.$PUSHDELAY)
|
|
149
|
+
client.$PUSHDELAY = new Delay(client, 'pushMessage', minLatency);
|
|
150
|
+
|
|
151
|
+
entry.override = function() {
|
|
152
|
+
var i, n = entry.args.length;
|
|
153
|
+
|
|
154
|
+
if (n) for (i = 0; i < n; i++) {
|
|
155
|
+
|
|
156
|
+
// Unsubscribe.
|
|
157
|
+
if (matches[2])
|
|
158
|
+
subs = backend.unsub(matches[1]? true : false, entry.args[i], client.$PUSHDELAY);
|
|
159
|
+
|
|
160
|
+
// Subscribe.
|
|
161
|
+
else
|
|
162
|
+
subs = backend.sub(matches[1]? true : false, entry.args[i], client.$PUSHDELAY);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
else if (matches[2]) {
|
|
166
|
+
|
|
167
|
+
// Unsubscribe from all.
|
|
168
|
+
subs = backend.unsub(matches[1]? true : false, null, client.$PUSHDELAY);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
else
|
|
172
|
+
return new Error('Wrong number of arguments for \'' + matches[0] + '\' command');
|
|
173
|
+
|
|
174
|
+
if (!subs)
|
|
175
|
+
state = NORMAL;
|
|
176
|
+
|
|
177
|
+
return "OK";
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
if (!matches[2])
|
|
181
|
+
state = SUBSCRIBED;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
// Connection.
|
|
186
|
+
|
|
187
|
+
if (entry.command === 'QUIT') {
|
|
188
|
+
entry.override = function() {
|
|
189
|
+
if (client.$PUSHDELAY) {
|
|
190
|
+
|
|
191
|
+
// Unsubscribe.
|
|
192
|
+
backend.unsub(true, null, client.$PUSHDELAY);
|
|
193
|
+
backend.unsub(false, null, client.$PUSHDELAY);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return "OK";
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
state = CLOSED;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
else if (entry.command === 'SELECT') {
|
|
203
|
+
entry.override = function() {
|
|
204
|
+
var n = entry.args.length;
|
|
205
|
+
if (n !== 1)
|
|
206
|
+
return new Error("Wrong number of arguments for 'SELECT' command.");
|
|
207
|
+
var id = Number(entry.args[0]);
|
|
208
|
+
if ((!id && id !== 0) || id % 1 !== 0 || id < 0)
|
|
209
|
+
return new Error("invalid DB index");
|
|
210
|
+
|
|
211
|
+
db = id;
|
|
212
|
+
backend.selectDB(db);
|
|
213
|
+
return "OK";
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Regular commands.
|
|
218
|
+
|
|
219
|
+
if (queue)
|
|
220
|
+
queue.push(entry);
|
|
221
|
+
|
|
222
|
+
else {
|
|
223
|
+
queue = [entry];
|
|
224
|
+
timeout = setTimeout(exec, randLat());
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
// Push a command to a subscribed connection.
|
|
230
|
+
|
|
231
|
+
function SUBSCRIBED(client, entry) {
|
|
232
|
+
|
|
233
|
+
// Allow commands that modify the subscription set.
|
|
234
|
+
if (/SUBSCRIBE|^QUIT/.test(entry.command))
|
|
235
|
+
NORMAL(client, entry);
|
|
236
|
+
else
|
|
237
|
+
throw new Error("fakeredis: Connection is in pub/sub mode (" + subs + " subscriptions).");
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
// Closed connection.
|
|
242
|
+
|
|
243
|
+
function CLOSED(client, entry) {
|
|
244
|
+
throw new Error("fakeredis: You've closed this connection with QUIT, cannot " + entry.command);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
// Blocked connection.
|
|
249
|
+
|
|
250
|
+
function BLOCKED(client, entry) {
|
|
251
|
+
if (!block)
|
|
252
|
+
block = [client, entry];
|
|
253
|
+
else
|
|
254
|
+
block.push(client, entry);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
// Execute everything in the queue sequentially.
|
|
259
|
+
|
|
260
|
+
function exec() {
|
|
261
|
+
timeout = 0;
|
|
262
|
+
var q = queue, entry, func, out, err, data, resp = [];
|
|
263
|
+
queue = null;
|
|
264
|
+
|
|
265
|
+
if (connection.verbose)
|
|
266
|
+
console.log('\n');
|
|
267
|
+
|
|
268
|
+
backend.selectDB(db);
|
|
269
|
+
|
|
270
|
+
if (q) while ((entry = q.shift())) {
|
|
271
|
+
if (entry === 'SKIP')
|
|
272
|
+
continue;
|
|
273
|
+
|
|
274
|
+
func = backend[entry.command];
|
|
275
|
+
out = null;
|
|
276
|
+
|
|
277
|
+
if (connection.verbose)
|
|
278
|
+
console.log("fakeredis>", entry.command, entry.args.join(' '));
|
|
279
|
+
|
|
280
|
+
if (entry.override) {
|
|
281
|
+
out = entry.override(q);
|
|
282
|
+
err = out instanceof Error ? out : null;
|
|
283
|
+
data = out instanceof Error ? null : out;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
else if (!func || typeof func !== 'function')
|
|
287
|
+
throw new Error('WOOT! Wierd queue entry : ' + JSON.stringify(entry) + ' / ' + JSON.stringify(q));
|
|
288
|
+
|
|
289
|
+
else if (func.length && func.length !== entry.args.length) {
|
|
290
|
+
err = new Error('Wrong number of arguments for \'' + entry.command.toLowerCase() + '\' command');
|
|
291
|
+
data = null;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
else {
|
|
295
|
+
out = func.apply(backend, entry.args);
|
|
296
|
+
err = ((out && out.getError) || null) && new Error(out.getError());
|
|
297
|
+
data = err ? null : (out && out.getStatus && out.getStatus()) || out;
|
|
298
|
+
|
|
299
|
+
// Block if necessary.
|
|
300
|
+
if (entry.block && err === null && data === null) {
|
|
301
|
+
if (resp.length)
|
|
302
|
+
flush(resp);
|
|
303
|
+
|
|
304
|
+
q.unshift(entry);
|
|
305
|
+
queue = q;
|
|
306
|
+
state = BLOCKED;
|
|
307
|
+
backend.sub(false, backend.UPDATE, connection);
|
|
308
|
+
|
|
309
|
+
if (entry.block && typeof entry.block === 'number')
|
|
310
|
+
setTimeout(unblock.bind(null, entry), entry.block * 1000);
|
|
311
|
+
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (!err && !data && typeof out === "undefined")
|
|
317
|
+
throw new Error("WOOT! Backend returned undefined.");
|
|
318
|
+
if (out && out.rev)
|
|
319
|
+
throw new Error("WOOT! Returning the whole keyspace entry.");
|
|
320
|
+
|
|
321
|
+
if (data === true)
|
|
322
|
+
throw new Error("TRUE THAT! " + JSON.stringify(entry));
|
|
323
|
+
|
|
324
|
+
data = fdata(data);
|
|
325
|
+
if (entry.callback)
|
|
326
|
+
resp.push(entry.callback.bind(null, err, data));
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (connection.verbose)
|
|
330
|
+
console.log('\n');
|
|
331
|
+
|
|
332
|
+
if (resp.length)
|
|
333
|
+
flush(resp);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function flush(resp) {
|
|
337
|
+
setTimeout(
|
|
338
|
+
function() {
|
|
339
|
+
var i, n;
|
|
340
|
+
|
|
341
|
+
n = resp.length;
|
|
342
|
+
for (i = 0; i < n; i++)
|
|
343
|
+
resp[i]();
|
|
344
|
+
},
|
|
345
|
+
minLatency
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function unblock(entry) {
|
|
350
|
+
if (entry)
|
|
351
|
+
delete entry.block;
|
|
352
|
+
|
|
353
|
+
state = NORMAL;
|
|
354
|
+
exec();
|
|
355
|
+
|
|
356
|
+
if (state === NORMAL) {
|
|
357
|
+
backend.unsub(false, backend.UPDATE, connection);
|
|
358
|
+
|
|
359
|
+
var a = block, i, n = a && a.length;
|
|
360
|
+
block = null;
|
|
361
|
+
for (i = 0; i < n; i += 2)
|
|
362
|
+
NORMAL(a[i], a[i + 1]);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
this.pushMessage = function(/* type, channel, message */) {
|
|
367
|
+
|
|
368
|
+
// Attempt to unblock on backend keyspace change.
|
|
369
|
+
unblock();
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
// Format data the way it comes out of node_redis.
|
|
374
|
+
|
|
375
|
+
function fdata(data) {
|
|
376
|
+
if (typeof data !== 'object' && typeof data !== 'number' && typeof data !== 'string')
|
|
377
|
+
throw new Error('WOOT! Data is not an object/string/number : ' + data);
|
|
378
|
+
|
|
379
|
+
if (data) {
|
|
380
|
+
if (typeof data === 'string' && !isNaN(data))
|
|
381
|
+
data = Number(data);
|
|
382
|
+
|
|
383
|
+
else if (data.length && data.map)
|
|
384
|
+
data = data.map(finnerdata);
|
|
385
|
+
|
|
386
|
+
else if (typeof data === 'object' && !data.map)
|
|
387
|
+
throw new Error('WOOT! Illegal object in data : ' + data);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return data;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function finnerdata(data) {
|
|
394
|
+
if (typeof data !== 'object' && typeof data !== 'number' && typeof data !== 'string')
|
|
395
|
+
throw new Error('WOOT! Data is not an object/string/number : ' + data);
|
|
396
|
+
|
|
397
|
+
if (data || data === 0) {
|
|
398
|
+
if (typeof data === 'number')
|
|
399
|
+
data = String(data);
|
|
400
|
+
|
|
401
|
+
else if (data.length && data.map)
|
|
402
|
+
data = data.map(finnerdata);
|
|
403
|
+
|
|
404
|
+
else if (typeof data === 'object' && !data.map)
|
|
405
|
+
throw new Error('WOOT! Illegal object in data : ' + data);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return data;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
// Prepare command.
|
|
413
|
+
|
|
414
|
+
function prep(command, args, callback) {
|
|
415
|
+
args = args.map(function(arg) { return String(arg); });
|
|
416
|
+
command = command.toUpperCase();
|
|
417
|
+
var block = false;
|
|
418
|
+
if (/^B[LR]POP/.test(command) && args.length) // Backend will validate the timeout param more robustly.
|
|
419
|
+
block = parseInt(args[args.length - 1], 10) || true;
|
|
420
|
+
|
|
421
|
+
if (!backend[command])
|
|
422
|
+
throw new Error("fakeredis: " + command + " is not implemented in fakeredis. Let me know if you need it.");
|
|
423
|
+
|
|
424
|
+
return { command: command, args: args, callback: callback, block: block };
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
// Helper to push replies onto the replies list.
|
|
429
|
+
|
|
430
|
+
function pushReply(err, data) {
|
|
431
|
+
/*jshint validthis:true*/
|
|
432
|
+
this.push(err ||fdata(data));
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
// Immitate latency.
|
|
437
|
+
|
|
438
|
+
minLatency = Math.ceil(minLatency || 15);
|
|
439
|
+
maxLatency = Math.ceil(maxLatency || minLatency * 3);
|
|
440
|
+
|
|
441
|
+
if (maxLatency < minLatency || minLatency < 0)
|
|
442
|
+
throw new Error("Bad min/max latency settings.");
|
|
443
|
+
|
|
444
|
+
function randLat() {
|
|
445
|
+
return Math.ceil((maxLatency - minLatency)* Math.random() + minLatency);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
function Delay(object, method, delay) {
|
|
452
|
+
var queue
|
|
453
|
+
, flush;
|
|
454
|
+
|
|
455
|
+
this[method] = function() {
|
|
456
|
+
if (!queue) {
|
|
457
|
+
queue = [arguments];
|
|
458
|
+
setTimeout(flush, delay);
|
|
459
|
+
}
|
|
460
|
+
else
|
|
461
|
+
queue.push(arguments);
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
flush = function() {
|
|
465
|
+
var q = queue, i, n = q.length;
|
|
466
|
+
queue = null;
|
|
467
|
+
|
|
468
|
+
for (i = 0; i < n; i++)
|
|
469
|
+
object[method].apply(object, q[i]);
|
|
470
|
+
};
|
|
471
|
+
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
|
package/lib/helpers.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
// Stylize a string alla vows
|
|
5
|
+
|
|
6
|
+
var stylize;
|
|
7
|
+
(function() {
|
|
8
|
+
var styles = {
|
|
9
|
+
bold: '1'
|
|
10
|
+
, italic: '3'
|
|
11
|
+
, underline: '4'
|
|
12
|
+
, grey: '90'
|
|
13
|
+
, red: '1;31'
|
|
14
|
+
, green: '1;32'
|
|
15
|
+
, yellow: '1;33'
|
|
16
|
+
, blue: '1;34'
|
|
17
|
+
, magenta: '1;35'
|
|
18
|
+
, cyan: '1;36'
|
|
19
|
+
, white: '1;37'
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
stylize = function(str, style) {
|
|
23
|
+
return '\x1B[' + styles[style] + 'm' + str + '\x1B[0m';
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
());
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
// Prettyprint a subset of the keyspace of the fakeredis instance.
|
|
30
|
+
|
|
31
|
+
exports.pretty = function(options) {
|
|
32
|
+
var pattern, wrap, label;
|
|
33
|
+
|
|
34
|
+
if (typeof options === 'string')
|
|
35
|
+
options = { pattern: options };
|
|
36
|
+
|
|
37
|
+
pattern = (options && options.pattern) || "*";
|
|
38
|
+
wrap = (options && options.wrap) || 4;
|
|
39
|
+
label = (options && options.label) || "keyspace " + pattern;
|
|
40
|
+
|
|
41
|
+
this.send_command("FAKE_DUMP", [pattern || "*"], function(err, dump) {
|
|
42
|
+
var i, n = dump && dump.length, style, key, ttl, type, value;
|
|
43
|
+
|
|
44
|
+
if (err)
|
|
45
|
+
throw err;
|
|
46
|
+
if (label)
|
|
47
|
+
process.stdout.write('\n' + stylize(label, 'bold') + ':\n\n');
|
|
48
|
+
else
|
|
49
|
+
process.stdout.write('\n');
|
|
50
|
+
|
|
51
|
+
for (i = 0; i < n; i += 4) {
|
|
52
|
+
key = dump[i];
|
|
53
|
+
ttl = dump[i + 1];
|
|
54
|
+
type = dump[i + 2];
|
|
55
|
+
value = dump[i + 3];
|
|
56
|
+
|
|
57
|
+
style = 'white';
|
|
58
|
+
if (type === 'list')
|
|
59
|
+
style = 'green';
|
|
60
|
+
else if (type === 'hash')
|
|
61
|
+
style = 'yellow';
|
|
62
|
+
else if (type === 'set')
|
|
63
|
+
style = 'cyan';
|
|
64
|
+
else if (type === 'zset')
|
|
65
|
+
style = 'red';
|
|
66
|
+
|
|
67
|
+
/*jshint loopfunc:true*/
|
|
68
|
+
process.stdout.write(
|
|
69
|
+
stylize(type, 'bold') + '\t' + stylize(key, 'bold') + '\n' + stylize(ttl, ttl >= 0 ? 'italic' : 'grey') + '\t' + (
|
|
70
|
+
value.map
|
|
71
|
+
|
|
72
|
+
? value.map(function(member, index) {
|
|
73
|
+
/*jshint -W018*/
|
|
74
|
+
return (wrap && index && !((index) % wrap)? '\n\t' : '') + stylize(member, style);
|
|
75
|
+
})
|
|
76
|
+
.join(', \t')
|
|
77
|
+
|
|
78
|
+
: stylize(value, style)
|
|
79
|
+
|
|
80
|
+
) + '\n\n'
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
// Get a subset of the keyspace of the fakeredis instance.
|
|
88
|
+
|
|
89
|
+
exports.getKeyspace = function(options, callback) {
|
|
90
|
+
var cb;
|
|
91
|
+
|
|
92
|
+
if (!callback && typeof options === 'function') {
|
|
93
|
+
callback = options;
|
|
94
|
+
options = null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (typeof options === 'string')
|
|
98
|
+
options = { pattern: options };
|
|
99
|
+
if (!callback || typeof callback !== 'function')
|
|
100
|
+
throw new Error("You didn't provide a valid callback.");
|
|
101
|
+
|
|
102
|
+
// By default respond with an array of [ key, ttl, type, value, key2, ttl2, type2, value2, ... ]
|
|
103
|
+
cb = callback;
|
|
104
|
+
|
|
105
|
+
// Respond with a key-value map.
|
|
106
|
+
if (options && options.map)
|
|
107
|
+
cb = function(err, data) {
|
|
108
|
+
var out, i, n;
|
|
109
|
+
if (data) {
|
|
110
|
+
out = {};
|
|
111
|
+
n = data.length;
|
|
112
|
+
for (i = 0; i < n; i += 4)
|
|
113
|
+
out[data[i]] = data[i + 3];
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
callback(err, out);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// Respond with an array of arrays.
|
|
120
|
+
else if (options && options.group)
|
|
121
|
+
cb = function(err, data) {
|
|
122
|
+
var out, i, n;
|
|
123
|
+
if (data) {
|
|
124
|
+
out = [];
|
|
125
|
+
n = data.length;
|
|
126
|
+
for (i = 0; i < n; i += 4)
|
|
127
|
+
out.push(data.slice(i, 4));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
callback(err, out);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
this.send_command("FAKE_DUMP", [options && options.pattern || "*"], cb);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
// Serve getKeyspace() as JSON from localhost:[port]/keyspace.json
|
|
138
|
+
|
|
139
|
+
exports.serveKeyspace = function(port) {
|
|
140
|
+
var self = this
|
|
141
|
+
, url = require("url");
|
|
142
|
+
|
|
143
|
+
require("http").createServer(
|
|
144
|
+
function(req, res) {
|
|
145
|
+
var data = url.parse(req.url, true);
|
|
146
|
+
|
|
147
|
+
if (data.pathname !== '/keyspace.json') {
|
|
148
|
+
res.statusCode = 404;
|
|
149
|
+
res.end("Not found.");
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (req.method !== 'GET') {
|
|
154
|
+
res.statusCode = 405;
|
|
155
|
+
res.end("Method not supported.");
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
self.getKeyspace(data.query, function(err, data) {
|
|
160
|
+
if (err) {
|
|
161
|
+
res.statusCode = 500;
|
|
162
|
+
res.end(err);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
res.setHeader("Content-Type", "application/json");
|
|
167
|
+
res.end(JSON.stringify(data));
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
)
|
|
171
|
+
.listen(port);
|
|
172
|
+
};
|