Skip to content

Commit d2b8f2f

Browse files
author
Ruben Bridgewater
committed
Add support for camelCase
Fixes missing `EXEC_BATCH` on multi
1 parent dfd493f commit d2b8f2f

File tree

11 files changed

+154
-34
lines changed

11 files changed

+154
-34
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ This will display:
4949
mjr:~/work/node_redis (master)$
5050

5151
Note that the API is entirely asynchronous. To get data back from the server, you'll need to use a callback.
52+
From v.2.6 on the API supports camelCase and snack_case and all options / variables / events etc. can be used either way.
53+
It is recommended to use camelCase as this is the default for the Node.js landscape.
5254

5355
### Promises
5456

@@ -109,8 +111,6 @@ client.get("missingkey", function(err, reply) {
109111

110112
For a list of Redis commands, see [Redis Command Reference](http://redis.io/commands)
111113

112-
The commands can be specified in uppercase or lowercase for convenience. `client.get()` is the same as `client.GET()`.
113-
114114
Minimal parsing is done on the replies. Commands that return a integer return JavaScript Numbers, arrays return JavaScript Array. `HGETALL` returns an Object keyed by the hash keys. All strings will either be returned as string or as buffer depending on your setting.
115115
Please be aware that sending null, undefined and Boolean values will result in the value coerced to a string!
116116

index.js

Lines changed: 88 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -479,13 +479,19 @@ RedisClient.prototype.send_offline_queue = function () {
479479
var retry_connection = function (self, error) {
480480
debug('Retrying connection...');
481481

482-
self.emit('reconnecting', {
482+
var reconnect_params = {
483483
delay: self.retry_delay,
484484
attempt: self.attempts,
485-
error: error,
486-
times_connected: self.times_connected,
487-
total_retry_time: self.retry_totaltime
488-
});
485+
error: error
486+
};
487+
if (self.options.camel_case) {
488+
reconnect_params.totalRetryTime = self.retry_totaltime;
489+
reconnect_params.timesConnected = self.times_connected;
490+
} else {
491+
reconnect_params.total_retry_time = self.retry_totaltime;
492+
reconnect_params.times_connected = self.times_connected;
493+
}
494+
self.emit('reconnecting', reconnect_params);
489495

490496
self.retry_totaltime += self.retry_delay;
491497
self.attempts += 1;
@@ -529,12 +535,18 @@ RedisClient.prototype.connection_gone = function (why, error) {
529535
}
530536

531537
if (typeof this.options.retry_strategy === 'function') {
532-
this.retry_delay = this.options.retry_strategy({
538+
var retry_params = {
533539
attempt: this.attempts,
534-
error: error,
535-
total_retry_time: this.retry_totaltime,
536-
times_connected: this.times_connected
537-
});
540+
error: error
541+
};
542+
if (this.options.camel_case) {
543+
retry_params.totalRetryTime = this.retry_totaltime;
544+
retry_params.timesConnected = this.times_connected;
545+
} else {
546+
retry_params.total_retry_time = this.retry_totaltime;
547+
retry_params.times_connected = this.times_connected;
548+
}
549+
this.retry_delay = this.options.retry_strategy(retry_params);
538550
if (typeof this.retry_delay !== 'number') {
539551
// Pass individual error through
540552
if (this.retry_delay instanceof Error) {
@@ -902,6 +914,72 @@ RedisClient.prototype.write = function (data) {
902914
return;
903915
};
904916

917+
Object.defineProperty(exports, 'debugMode', {
918+
get: function () {
919+
return this.debug_mode;
920+
},
921+
set: function (val) {
922+
this.debug_mode = val;
923+
}
924+
});
925+
926+
// Don't officially expose the command_queue directly but only the length as read only variable
927+
Object.defineProperty(RedisClient.prototype, 'command_queue_length', {
928+
get: function () {
929+
return this.command_queue.length;
930+
}
931+
});
932+
933+
Object.defineProperty(RedisClient.prototype, 'offline_queue_length', {
934+
get: function () {
935+
return this.offline_queue.length;
936+
}
937+
});
938+
939+
// Add support for camelCase by adding read only properties to the client
940+
// All known exposed snack_case variables are added here
941+
Object.defineProperty(RedisClient.prototype, 'retryDelay', {
942+
get: function () {
943+
return this.retry_delay;
944+
}
945+
});
946+
947+
Object.defineProperty(RedisClient.prototype, 'retryBackoff', {
948+
get: function () {
949+
return this.retry_backoff;
950+
}
951+
});
952+
953+
Object.defineProperty(RedisClient.prototype, 'commandQueueLength', {
954+
get: function () {
955+
return this.command_queue.length;
956+
}
957+
});
958+
959+
Object.defineProperty(RedisClient.prototype, 'offlineQueueLength', {
960+
get: function () {
961+
return this.offline_queue.length;
962+
}
963+
});
964+
965+
Object.defineProperty(RedisClient.prototype, 'shouldBuffer', {
966+
get: function () {
967+
return this.should_buffer;
968+
}
969+
});
970+
971+
Object.defineProperty(RedisClient.prototype, 'connectionId', {
972+
get: function () {
973+
return this.connection_id;
974+
}
975+
});
976+
977+
Object.defineProperty(RedisClient.prototype, 'serverInfo', {
978+
get: function () {
979+
return this.server_info;
980+
}
981+
});
982+
905983
exports.createClient = function () {
906984
return new RedisClient(unifyOptions.apply(null, arguments));
907985
};

lib/extendedApi.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ All documented and exposed API belongs in here
1010
**********************************************/
1111

1212
// Redirect calls to the appropriate function and use to send arbitrary / not supported commands
13-
RedisClient.prototype.send_command = function (command, args, callback) {
13+
RedisClient.prototype.send_command = RedisClient.prototype.sendCommand = function (command, args, callback) {
1414
// Throw to fail early instead of relying in order in this case
1515
if (typeof command !== 'string') {
1616
throw new Error('Wrong input type "' + (command !== null && command !== undefined ? command.constructor.name : command) + '" for command name');

lib/multi.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ function pipeline_transaction_command (self, command, args, index, cb) {
7070
});
7171
}
7272

73-
Multi.prototype.exec_atomic = function exec_atomic (callback) {
73+
Multi.prototype.exec_atomic = Multi.prototype.EXEC_ATOMIC = Multi.prototype.execAtomic = function exec_atomic (callback) {
7474
if (this.queue.length < 2) {
7575
return this.exec_batch(callback);
7676
}

lib/utils.js

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,10 @@ function print (err, reply) {
4141
}
4242
}
4343

44+
var camelCase;
4445
// Deep clone arbitrary objects with arrays. Can't handle cyclic structures (results in a range error)
4546
// Any attribute with a non primitive value besides object and array will be passed by reference (e.g. Buffers, Maps, Functions)
47+
// All capital letters are going to be replaced with a lower case letter and a underscore infront of it
4648
function clone (obj) {
4749
var copy;
4850
if (Array.isArray(obj)) {
@@ -57,15 +59,27 @@ function clone (obj) {
5759
var elems = Object.keys(obj);
5860
var elem;
5961
while (elem = elems.pop()) {
60-
copy[elem] = clone(obj[elem]);
62+
// Accept camelCase options and convert them to snack_case
63+
var snack_case = elem.replace(/[A-Z][^A-Z]/g, '_$&').toLowerCase();
64+
// If camelCase is detected, pass it to the client, so all variables are going to be camelCased
65+
// There are no deep nested options objects yet, but let's handle this future proof
66+
if (snack_case !== elem.toLowerCase()) {
67+
camelCase = true;
68+
}
69+
copy[snack_case] = clone(obj[elem]);
6170
}
6271
return copy;
6372
}
6473
return obj;
6574
}
6675

6776
function convenienceClone (obj) {
68-
return clone(obj) || {};
77+
camelCase = false;
78+
obj = clone(obj) || {};
79+
if (camelCase) {
80+
obj.camel_case = true;
81+
}
82+
return obj;
6983
}
7084

7185
function callbackOrEmit (self, callback, err, res) {

test/auth.spec.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,18 @@ describe('client authentication', function () {
166166
client = redis.createClient.apply(null, args);
167167
client.auth(auth);
168168
client.on('ready', function () {
169-
if (this.times_connected === 1) {
170-
client.stream.destroy();
169+
if (this.times_connected < 3) {
170+
var interval = setInterval(function () {
171+
if (client.commandQueueLength !== 0) {
172+
return;
173+
}
174+
clearInterval(interval);
175+
interval = null;
176+
client.stream.destroy();
177+
client.set('foo', 'bar');
178+
client.get('foo'); // Errors would bubble
179+
assert.strictEqual(client.offlineQueueLength, 2);
180+
}, 1);
171181
} else {
172182
done();
173183
}

test/commands/info.spec.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,16 @@ describe("The 'info' method", function () {
2323
client.end(true);
2424
});
2525

26-
it('update server_info after a info command', function (done) {
26+
it('update serverInfo after a info command', function (done) {
2727
client.set('foo', 'bar');
2828
client.info();
2929
client.select(2, function () {
30-
assert.strictEqual(client.server_info.db2, undefined);
30+
assert.strictEqual(client.serverInfo.db2, undefined);
3131
});
3232
client.set('foo', 'bar');
3333
client.info();
3434
setTimeout(function () {
35-
assert.strictEqual(typeof client.server_info.db2, 'object');
35+
assert.strictEqual(typeof client.serverInfo.db2, 'object');
3636
done();
3737
}, 30);
3838
});

test/connection.spec.js

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -132,12 +132,14 @@ describe('connection tests', function () {
132132

133133
describe('on lost connection', function () {
134134
it('emit an error after max retry attempts and do not try to reconnect afterwards', function (done) {
135-
var max_attempts = 3;
135+
var maxAttempts = 3;
136136
var options = {
137137
parser: parser,
138-
max_attempts: max_attempts
138+
maxAttempts: maxAttempts
139139
};
140140
client = redis.createClient(options);
141+
assert.strictEqual(client.retryBackoff, 1.7);
142+
assert.strictEqual(client.retryDelay, 200);
141143
assert.strictEqual(Object.keys(options).length, 2);
142144
var calls = 0;
143145

@@ -152,7 +154,7 @@ describe('connection tests', function () {
152154
client.on('error', function (err) {
153155
if (/Redis connection in broken state: maximum connection attempts.*?exceeded./.test(err.message)) {
154156
process.nextTick(function () { // End is called after the error got emitted
155-
assert.strictEqual(calls, max_attempts - 1);
157+
assert.strictEqual(calls, maxAttempts - 1);
156158
assert.strictEqual(client.emitted_end, true);
157159
assert.strictEqual(client.connected, false);
158160
assert.strictEqual(client.ready, false);
@@ -248,16 +250,16 @@ describe('connection tests', function () {
248250
});
249251
});
250252

251-
it('retry_strategy used to reconnect with individual error', function (done) {
253+
it('retryStrategy used to reconnect with individual error', function (done) {
252254
var text = '';
253255
var unhookIntercept = intercept(function (data) {
254256
text += data;
255257
return '';
256258
});
257259
var end = helper.callFuncAfter(done, 2);
258260
client = redis.createClient({
259-
retry_strategy: function (options) {
260-
if (options.total_retry_time > 150) {
261+
retryStrategy: function (options) {
262+
if (options.totalRetryTime > 150) {
261263
client.set('foo', 'bar', function (err, res) {
262264
assert.strictEqual(err.message, 'Connection timeout');
263265
end();
@@ -267,8 +269,8 @@ describe('connection tests', function () {
267269
}
268270
return Math.min(options.attempt * 25, 200);
269271
},
270-
max_attempts: 5,
271-
retry_max_delay: 123,
272+
maxAttempts: 5,
273+
retryMaxDelay: 123,
272274
port: 9999
273275
});
274276

test/multi.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -571,7 +571,7 @@ describe("The 'multi' method", function () {
571571
test = true;
572572
};
573573
multi.set('baz', 'binary');
574-
multi.exec_atomic();
574+
multi.EXEC_ATOMIC();
575575
assert(test);
576576
});
577577

test/node_redis.spec.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ describe('The node_redis client', function () {
3030
it('check if all options got copied properly', function (done) {
3131
client.selected_db = 2;
3232
var client2 = client.duplicate();
33+
assert.strictEqual(client.connectionId + 1, client2.connection_id);
3334
assert.strictEqual(client2.selected_db, 2);
3435
assert(client.connected);
3536
assert(!client2.connected);
@@ -360,7 +361,7 @@ describe('The node_redis client', function () {
360361
client.on('error', function (err) {
361362
assert.strictEqual(err.message, 'SET can\'t be processed. The connection has already been closed.');
362363
assert.strictEqual(err.command, 'SET');
363-
assert.strictEqual(client.offline_queue.length, 0);
364+
assert.strictEqual(client.offline_queue_length, 0);
364365
done();
365366
});
366367
setTimeout(function () {
@@ -966,7 +967,7 @@ describe('The node_redis client', function () {
966967
multi.set('foo' + (i + 2), 'bar' + (i + 2));
967968
}
968969
multi.exec();
969-
assert.equal(client.command_queue.length, 15);
970+
assert.equal(client.command_queue_length, 15);
970971
helper.killConnection(client);
971972
});
972973

0 commit comments

Comments
 (0)