Skip to content

Commit

Permalink
feat: add stringNumbers option to return numbers as JavaScript strings (
Browse files Browse the repository at this point in the history
#282)

Closes #273
  • Loading branch information
luin committed Apr 10, 2016
1 parent 3ca30d8 commit 2a33fc7
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 26 deletions.
83 changes: 58 additions & 25 deletions API.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
## Classes

<dl>
<dt><a href="#Redis">Redis</a> ⇐ <code>[EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter)</code></dt>
<dd></dd>
Expand All @@ -7,7 +8,9 @@
<dt><a href="#Commander">Commander</a></dt>
<dd></dd>
</dl>

## Members

<dl>
<dt><a href="#defaultOptions">defaultOptions</a></dt>
<dd><p>Default options</p>
Expand All @@ -16,26 +19,29 @@
<dd><p>Default options</p>
</dd>
</dl>

<a name="Redis"></a>

## Redis ⇐ <code>[EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter)</code>
**Kind**: global class
**Extends:** <code>[EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter)</code>, <code>[Commander](#Commander)</code>

* [Redis](#Redis) ⇐ <code>[EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter)</code>
* [new Redis([port], [host], [options])](#new_Redis_new)
* _instance_
* [.connect(callback)](#Redis+connect) ⇒ <code>Promise</code>
* [.disconnect()](#Redis+disconnect)
* ~~[.end()](#Redis+end)~~
* [.duplicate()](#Redis+duplicate)
* [.monitor([callback])](#Redis+monitor)
* [.getBuiltinCommands()](#Commander+getBuiltinCommands) ⇒ <code>Array.&lt;string&gt;</code>
* [.createBuiltinCommand(commandName)](#Commander+createBuiltinCommand) ⇒ <code>object</code>
* [.defineCommand(name, definition)](#Commander+defineCommand)
* _static_
* ~~[.createClient()](#Redis.createClient)~~
* [new Redis([port], [host], [options])](#new_Redis_new)
* _instance_
* [.connect(callback)](#Redis+connect) ⇒ <code>Promise</code>
* [.disconnect()](#Redis+disconnect)
* ~~[.end()](#Redis+end)~~
* [.duplicate()](#Redis+duplicate)
* [.monitor([callback])](#Redis+monitor)
* [.getBuiltinCommands()](#Commander+getBuiltinCommands) ⇒ <code>Array.&lt;string&gt;</code>
* [.createBuiltinCommand(commandName)](#Commander+createBuiltinCommand) ⇒ <code>object</code>
* [.defineCommand(name, definition)](#Commander+defineCommand)
* _static_
* ~~[.createClient()](#Redis.createClient)~~

<a name="new_Redis_new"></a>

### new Redis([port], [host], [options])
Creates a Redis instance

Expand Down Expand Up @@ -63,6 +69,7 @@ Creates a Redis instance
| [options.retryStrategy] | <code>function</code> | | See "Quick Start" section |
| [options.reconnectOnError] | <code>function</code> | | See "Quick Start" section |
| [options.readOnly] | <code>boolean</code> | <code>false</code> | Enable READONLY mode for the connection. Only available for cluster mode. |
| [options.stringNumbers] | <code>boolean</code> | <code>false</code> | Force numbers to be always returned as JavaScript strings. This option is necessary when dealing with big numbers (exceed the [-2^53, +2^53] range). Notice that when this option is enabled, the JavaScript parser will be used even "hiredis" is specified because only JavaScript parser supports this feature for the time being. |

**Example**
```js
Expand All @@ -80,6 +87,7 @@ var urlRedis2 = new Redis('//localhost:6379');
var authedRedis = new Redis(6380, '192.168.100.1', { password: 'password' });
```
<a name="Redis+connect"></a>

### redis.connect(callback) ⇒ <code>Promise</code>
Create a connection to Redis.
This method will be invoked automatically when creating a new Redis instance.
Expand All @@ -92,6 +100,7 @@ This method will be invoked automatically when creating a new Redis instance.
| callback | <code>function</code> |

<a name="Redis+disconnect"></a>

### redis.disconnect()
Disconnect from Redis.

Expand All @@ -102,13 +111,15 @@ If you want to wait for the pending replies, use Redis#quit instead.
**Kind**: instance method of <code>[Redis](#Redis)</code>
**Access:** public
<a name="Redis+end"></a>

### ~~redis.end()~~
***Deprecated***

Disconnect from Redis.

**Kind**: instance method of <code>[Redis](#Redis)</code>
<a name="Redis+duplicate"></a>

### redis.duplicate()
Create a new instance with the same options as the current one.

Expand All @@ -120,6 +131,7 @@ var redis = new Redis(6380);
var anotherRedis = redis.duplicate();
```
<a name="Redis+monitor"></a>

### redis.monitor([callback])
Listen for all requests received by the server in real time.

Expand Down Expand Up @@ -152,13 +164,15 @@ redis.monitor().then(function (monitor) {
});
```
<a name="Commander+getBuiltinCommands"></a>

### redis.getBuiltinCommands() ⇒ <code>Array.&lt;string&gt;</code>
Return supported builtin commands

**Kind**: instance method of <code>[Redis](#Redis)</code>
**Returns**: <code>Array.&lt;string&gt;</code> - command list
**Access:** public
<a name="Commander+createBuiltinCommand"></a>

### redis.createBuiltinCommand(commandName) ⇒ <code>object</code>
Create a builtin command

Expand All @@ -171,6 +185,7 @@ Create a builtin command
| commandName | <code>string</code> | command name |

<a name="Commander+defineCommand"></a>

### redis.defineCommand(name, definition)
Define a custom command using lua script

Expand All @@ -184,28 +199,31 @@ Define a custom command using lua script
| [definition.numberOfKeys] | <code>number</code> | <code></code> | the number of keys. If omit, you have to pass the number of keys as the first argument every time you invoke the command |

<a name="Redis.createClient"></a>

### ~~Redis.createClient()~~
***Deprecated***

Create a Redis instance

**Kind**: static method of <code>[Redis](#Redis)</code>
<a name="Cluster"></a>

## Cluster ⇐ <code>[EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter)</code>
**Kind**: global class
**Extends:** <code>[EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter)</code>, <code>[Commander](#Commander)</code>

* [Cluster](#Cluster) ⇐ <code>[EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter)</code>
* [new Cluster(startupNodes, options)](#new_Cluster_new)
* [.connect()](#Cluster+connect) ⇒ <code>Promise</code>
* [.disconnect()](#Cluster+disconnect)
* [.nodes([role])](#Cluster+nodes) ⇒ <code>[Array.&lt;Redis&gt;](#Redis)</code>
* [.getBuiltinCommands()](#Commander+getBuiltinCommands) ⇒ <code>Array.&lt;string&gt;</code>
* [.createBuiltinCommand(commandName)](#Commander+createBuiltinCommand) ⇒ <code>object</code>
* [.defineCommand(name, definition)](#Commander+defineCommand)
* *[.sendCommand()](#Commander+sendCommand)*
* [new Cluster(startupNodes, options)](#new_Cluster_new)
* [.connect()](#Cluster+connect) ⇒ <code>Promise</code>
* [.disconnect()](#Cluster+disconnect)
* [.nodes([role])](#Cluster+nodes) ⇒ <code>[Array.&lt;Redis&gt;](#Redis)</code>
* [.getBuiltinCommands()](#Commander+getBuiltinCommands) ⇒ <code>Array.&lt;string&gt;</code>
* [.createBuiltinCommand(commandName)](#Commander+createBuiltinCommand) ⇒ <code>object</code>
* [.defineCommand(name, definition)](#Commander+defineCommand)
* *[.sendCommand()](#Commander+sendCommand)*

<a name="new_Cluster_new"></a>

### new Cluster(startupNodes, options)
Creates a Redis Cluster instance

Expand All @@ -225,18 +243,21 @@ Creates a Redis Cluster instance
| [options.redisOptions] | <code>Object</code> | | Passed to the constructor of `Redis`. |

<a name="Cluster+connect"></a>

### cluster.connect() ⇒ <code>Promise</code>
Connect to a cluster

**Kind**: instance method of <code>[Cluster](#Cluster)</code>
**Access:** public
<a name="Cluster+disconnect"></a>

### cluster.disconnect()
Disconnect from every node in the cluster.

**Kind**: instance method of <code>[Cluster](#Cluster)</code>
**Access:** public
<a name="Cluster+nodes"></a>

### cluster.nodes([role]) ⇒ <code>[Array.&lt;Redis&gt;](#Redis)</code>
Get nodes with the specified role

Expand All @@ -249,13 +270,15 @@ Get nodes with the specified role
| [role] | <code>string</code> | <code>&quot;all&quot;</code> | role, "master", "slave" or "all" |

<a name="Commander+getBuiltinCommands"></a>

### cluster.getBuiltinCommands() ⇒ <code>Array.&lt;string&gt;</code>
Return supported builtin commands

**Kind**: instance method of <code>[Cluster](#Cluster)</code>
**Returns**: <code>Array.&lt;string&gt;</code> - command list
**Access:** public
<a name="Commander+createBuiltinCommand"></a>

### cluster.createBuiltinCommand(commandName) ⇒ <code>object</code>
Create a builtin command

Expand All @@ -268,6 +291,7 @@ Create a builtin command
| commandName | <code>string</code> | command name |

<a name="Commander+defineCommand"></a>

### cluster.defineCommand(name, definition)
Define a custom command using lua script

Expand All @@ -281,24 +305,27 @@ Define a custom command using lua script
| [definition.numberOfKeys] | <code>number</code> | <code></code> | the number of keys. If omit, you have to pass the number of keys as the first argument every time you invoke the command |

<a name="Commander+sendCommand"></a>

### *cluster.sendCommand()*
Send a command

**Kind**: instance abstract method of <code>[Cluster](#Cluster)</code>
**Overrides:** <code>[sendCommand](#Commander+sendCommand)</code>
**Access:** public
<a name="Commander"></a>

## Commander
**Kind**: global class

* [Commander](#Commander)
* [new Commander()](#new_Commander_new)
* [.getBuiltinCommands()](#Commander+getBuiltinCommands) ⇒ <code>Array.&lt;string&gt;</code>
* [.createBuiltinCommand(commandName)](#Commander+createBuiltinCommand) ⇒ <code>object</code>
* [.defineCommand(name, definition)](#Commander+defineCommand)
* *[.sendCommand()](#Commander+sendCommand)*
* [new Commander()](#new_Commander_new)
* [.getBuiltinCommands()](#Commander+getBuiltinCommands) ⇒ <code>Array.&lt;string&gt;</code>
* [.createBuiltinCommand(commandName)](#Commander+createBuiltinCommand) ⇒ <code>object</code>
* [.defineCommand(name, definition)](#Commander+defineCommand)
* *[.sendCommand()](#Commander+sendCommand)*

<a name="new_Commander_new"></a>

### new Commander()
Commander

Expand All @@ -310,13 +337,15 @@ This is the base class of Redis, Redis.Cluster and Pipeline
| [options.showFriendlyErrorStack] | <code>boolean</code> | <code>false</code> | Whether to show a friendly error stack. Will decrease the performance significantly. |

<a name="Commander+getBuiltinCommands"></a>

### commander.getBuiltinCommands() ⇒ <code>Array.&lt;string&gt;</code>
Return supported builtin commands

**Kind**: instance method of <code>[Commander](#Commander)</code>
**Returns**: <code>Array.&lt;string&gt;</code> - command list
**Access:** public
<a name="Commander+createBuiltinCommand"></a>

### commander.createBuiltinCommand(commandName) ⇒ <code>object</code>
Create a builtin command

Expand All @@ -329,6 +358,7 @@ Create a builtin command
| commandName | <code>string</code> | command name |

<a name="Commander+defineCommand"></a>

### commander.defineCommand(name, definition)
Define a custom command using lua script

Expand All @@ -342,18 +372,21 @@ Define a custom command using lua script
| [definition.numberOfKeys] | <code>number</code> | <code></code> | the number of keys. If omit, you have to pass the number of keys as the first argument every time you invoke the command |

<a name="Commander+sendCommand"></a>

### *commander.sendCommand()*
Send a command

**Kind**: instance abstract method of <code>[Commander](#Commander)</code>
**Access:** public
<a name="defaultOptions"></a>

## defaultOptions
Default options

**Kind**: global variable
**Access:** protected
<a name="defaultOptions"></a>

## defaultOptions
Default options

Expand Down
7 changes: 6 additions & 1 deletion lib/redis.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ var ScanStream = require('./scan_stream');
* @param {function} [options.reconnectOnError] - See "Quick Start" section
* @param {boolean} [options.readOnly=false] - Enable READONLY mode for the connection.
* Only available for cluster mode.
* @param {boolean} [options.stringNumbers=false] - Force numbers to be always returned as JavaScript
* strings. This option is necessary when dealing with big numbers (exceed the [-2^53, +2^53] range).
* Notice that when this option is enabled, the JavaScript parser will be used even "hiredis" is specified
* because only JavaScript parser supports this feature for the time being.
* @extends [EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter)
* @extends Commander
* @example
Expand Down Expand Up @@ -169,7 +173,8 @@ Redis.defaultOptions = {
lazyConnect: false,
keyPrefix: '',
reconnectOnError: null,
readOnly: false
readOnly: false,
stringNumbers: false
};

Redis.prototype.resetCommandQueue = function () {
Expand Down
1 change: 1 addition & 0 deletions lib/redis/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ exports.initParser = function () {

this.replyParser = new Parser({
name: this.options.parser,
stringNumbers: this.options.stringNumbers,
returnBuffers: true,
returnError: function (err) {
_this.returnError(new ReplyError(err.message));
Expand Down
52 changes: 52 additions & 0 deletions test/functional/string_numbers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
'use strict';

var MAX_NUMBER = 9007199254740991; // Number.MAX_SAFE_INTEGER

describe('stringNumbers', function () {
context('enabled', function () {
it('returns numbers as strings', function (done) {
var redis = new Redis({
stringNumbers: true
});

var pending = 0;

redis.set('foo', MAX_NUMBER);
redis.incr('foo', check('9007199254740992'));
redis.incr('foo', check('9007199254740993'));
redis.incr('foo', check('9007199254740994'));

// also works for small interger
redis.set('foo', 123);
redis.incr('foo', check('124'));

// and floats
redis.set('foo', 123.23);
redis.incrbyfloat('foo', 1.2, check('124.43'));

function check(expected) {
pending += 1;
return function (err, res) {
expect(res).to.eql(expected);
if (!--pending) {
redis.disconnect();
done();
}
};
}
});
});

context('disabled', function () {
it('returns numbers', function (done) {
var redis = new Redis();

redis.set('foo', '123');
redis.incr('foo', function (err, res) {
expect(res).to.eql(124);
redis.disconnect();
done();
});
});
});
});

0 comments on commit 2a33fc7

Please sign in to comment.