Comparing version 0.2.0 to 0.3.0
@@ -40,5 +40,5 @@ var _ = require('lodash'); | ||
_this.name = name.toLowerCase(); | ||
_this.name = name; | ||
_this.args = args ? _.flatten(args) : []; | ||
var transformer = Command.transformer.argument[_this.name]; | ||
var transformer = Command._transformer.argument[_this.name]; | ||
if (transformer) { | ||
@@ -114,3 +114,3 @@ _this.args = transformer(_this.args); | ||
} | ||
transformer = Command.transformer.reply[_this.name]; | ||
transformer = Command._transformer.reply[_this.name]; | ||
if (transformer) { | ||
@@ -140,3 +140,3 @@ result = transformer(result); | ||
Command.transformer = { | ||
Command._transformer = { | ||
argument: {}, | ||
@@ -146,2 +146,10 @@ reply: {} | ||
Command.setArgumentTransformer = function (name, func) { | ||
Command._transformer.argument[name] = func; | ||
}; | ||
Command.setReplyTransformer = function (name, func) { | ||
Command._transformer.reply[name] = func; | ||
}; | ||
module.exports = Command; |
@@ -101,3 +101,3 @@ var _ = require('lodash'); | ||
return promise.then(function (a) { | ||
return promise.then(function () { | ||
var data = ''; | ||
@@ -104,0 +104,0 @@ var writePending = _this.replyPending = _this._queue.length; |
@@ -66,3 +66,5 @@ var _ = require('lodash'); | ||
function Redis(port, host, options) { | ||
if (!(this instanceof Redis)) return new Redis(port, host, options); | ||
if (!(this instanceof Redis)) { | ||
return new Redis(port, host, options); | ||
} | ||
@@ -144,2 +146,28 @@ EventEmitter.call(this); | ||
/** | ||
* Default options | ||
* | ||
* @var _defaultOptions | ||
* @memberOf Redis | ||
* @private | ||
*/ | ||
Redis._defaultOptions = { | ||
port: 6379, | ||
host: 'localhost', | ||
family: 4, | ||
enableOfflineQueue: true, | ||
enableReadyCheck: true, | ||
retryStrategy: function (times) { | ||
var delay = Math.min(times * 2, 2000); | ||
return delay; | ||
}, | ||
password: null, | ||
db: 0, | ||
role: 'master', | ||
sentinel: null, | ||
roleRetryDelay: 500, | ||
name: null | ||
}; | ||
/** | ||
* Create a connection to Redis. | ||
@@ -267,3 +295,3 @@ * This method will be invoked automatically when creating a new Redis instance. | ||
} else { | ||
var retryTime = (obj.loading_eta_seconds || 1) * 1000; | ||
var retryTime = (info.loading_eta_seconds || 1) * 1000; | ||
debug('Redis server still loading, trying again in ' + retryTime + 'ms'); | ||
@@ -348,3 +376,3 @@ setTimeout(function () { | ||
return new Promise(function (resolve, reject) { | ||
return new Promise(function (resolve) { | ||
monitorInstance.once('monitoring', function () { | ||
@@ -486,4 +514,2 @@ resolve(monitorInstance); | ||
_.assign(Redis, require('./redis/default_options')); | ||
_.assign(Redis.prototype, require('./redis/prototype/parser')); | ||
@@ -493,2 +519,41 @@ | ||
Redis.Command = Command; | ||
Redis.Command.setArgumentTransformer('mset', function (args) { | ||
if (args.length === 1) { | ||
var pos = 1; | ||
if (typeof Map !== 'undefined' && args[0] instanceof Map) { | ||
return utils.convertMapToArray(args[0]); | ||
} | ||
if ( typeof args[0] === 'object' && args[0] !== null) { | ||
return utils.convertObjectToArray(args[0]); | ||
} | ||
} | ||
return args; | ||
}); | ||
Redis.Command.setArgumentTransformer('hmset', function (args) { | ||
if (args.length === 2) { | ||
var pos = 1; | ||
if (typeof Map !== 'undefined' && args[1] instanceof Map) { | ||
return [args[0]].concat(utils.convertMapToArray(args[1])); | ||
} | ||
if ( typeof args[1] === 'object' && args[1] !== null) { | ||
return [args[0]].concat(utils.convertObjectToArray(args[1])); | ||
} | ||
} | ||
return args; | ||
}); | ||
Redis.Command.setReplyTransformer('hgetall', function (result) { | ||
if (Array.isArray(result)) { | ||
var obj = {}; | ||
for (var i = 0; i < result.length; i += 2) { | ||
obj[result[i]] = result[i + 1]; | ||
} | ||
return obj; | ||
} | ||
return result; | ||
}); | ||
var Sentinel = require('./sentinel'); |
@@ -148,3 +148,3 @@ var Queue = require('fastqueue'); | ||
while (offlineQueue.length > 0) { | ||
item = offlineQueue.shift(); | ||
var item = offlineQueue.shift(); | ||
if (item.select !== self.condition.select && item.command.name !== 'select') { | ||
@@ -151,0 +151,0 @@ self.sendCommand(new Command('select', [item.select])); |
@@ -18,3 +18,5 @@ /** Test if two buffers are equal | ||
for (var i = 0; i < a.length; ++i) { | ||
if (a[i] !== b[i]) return false; | ||
if (a[i] !== b[i]) { | ||
return false; | ||
} | ||
} | ||
@@ -99,3 +101,2 @@ return true; | ||
var isValue = false; | ||
for (var i = 1; i < length; i += 2) { | ||
@@ -120,1 +121,25 @@ result[array[i - 1]] = array[i]; | ||
}; | ||
exports.convertObjectToArray = function (obj) { | ||
var result = []; | ||
var pos = 0; | ||
for (var key in obj) { | ||
if (obj.hasOwnProperty(key)) { | ||
result[pos] = key; | ||
result[pos + 1] = obj[key]; | ||
} | ||
pos += 2; | ||
} | ||
return result; | ||
}; | ||
exports.convertMapToArray = function (map) { | ||
var result = []; | ||
var pos = 0; | ||
map.forEach(function (value, key) { | ||
result[pos] = key; | ||
result[pos + 1] = value; | ||
pos += 2; | ||
}); | ||
return result; | ||
}; |
{ | ||
"name": "ioredis", | ||
"version": "0.2.0", | ||
"version": "0.3.0", | ||
"description": "A delightful, performance-focused Redis client for Node and io.js", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "mocha", | ||
"test": "NODE_ENV=test mocha", | ||
"test:debug": "NODE_ENV=test DEBUG=ioredis:* mocha", | ||
"test:cov": "NODE_ENV=test node ./node_modules/istanbul/lib/cli.js cover --preserve-comments ./node_modules/mocha/bin/_mocha -- -R spec", | ||
"generate-docs": "jsdoc --configure .jsdoc.json --verbose" | ||
@@ -31,2 +33,4 @@ }, | ||
"chai": "^2.2.0", | ||
"codeclimate-test-reporter": "0.0.4", | ||
"istanbul": "^0.3.13", | ||
"jsdoc": "^3.3.0-beta3", | ||
@@ -33,0 +37,0 @@ "mocha": "^2.2.1", |
# ioredis | ||
[![Build Status](https://travis-ci.org/luin/ioredis.png?branch=master)](https://travis-ci.org/luin/ioredis) | ||
[![Test Coverage](https://codeclimate.com/github/luin/ioredis/badges/coverage.svg)](https://codeclimate.com/github/luin/ioredis) | ||
[![Dependency Status](https://david-dm.org/luin/ioredis.svg)](https://david-dm.org/luin/ioredis) | ||
@@ -13,12 +14,12 @@ [![Join the chat at https://gitter.im/luin/ioredis](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/luin/ioredis?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) | ||
ioredis is a robust, full-featured Redis client | ||
used in the biggest online commerce company [Alibaba](http://www.alibaba.com/). | ||
used in the world's biggest online commerce company [Alibaba](http://www.alibaba.com/). | ||
0. Full-featured. It supports [Cluster](http://redis.io/topics/cluster-tutorial), [Sentinel](redis.io/topics/sentinel), [Pipelining](http://redis.io/topics/pipelining) and of course [Lua scripting](http://redis.io/commands/eval) & [Pub/Sub](http://redis.io/topics/pubsub)(with the support of binary messages). | ||
0. Full-featured. It supports [Cluster](http://redis.io/topics/cluster-tutorial)(WIP), [Sentinel](redis.io/topics/sentinel), [Pipelining](http://redis.io/topics/pipelining) and of course [Lua scripting](http://redis.io/commands/eval) & [Pub/Sub](http://redis.io/topics/pubsub)(with the support of binary messages). | ||
0. High performance. | ||
0. Delightful API. Supports both Node-style callbacks and promises. | ||
0. Supports Redis commands transform. | ||
0. Supports command arguments and replies transform. | ||
0. Abstraction for Lua scripting, allowing you to define custom commands. | ||
0. Support for binary data. | ||
0. Support for both TCP/IP and UNIX domain sockets. | ||
0. Flexible system for defining custom command and registering command plugins. | ||
0. Flexible system for registering command wrapper. | ||
0. Supports offline queue and ready checking. | ||
@@ -130,2 +131,66 @@ 0. Supports ES6 types such as `Map` and `Set`. | ||
## Arguments & Replies Transform | ||
Most Redis commands take one or more Strings as arguments, | ||
and replies are sent back as a single String or an Array of Strings. However sometimes | ||
you may want something different: For instance it would be more convenient if HGETALL | ||
command returns a hash (e.g. `{key: val1, key2: v2}`) rather than an array of key values (e.g. `[key1,val1,key2,val2]`). | ||
ioredis has a flexible system for transforming arguments and replies. There are two types | ||
of transformers, argument transform and reply transformer: | ||
```javascript | ||
var Redis = require('ioredis'); | ||
// define a argument transformer that convert | ||
// hmset('key', { k1: 'v1', k2: 'v2' }) | ||
// or | ||
// hmset('key', new Map([['k1', 'v1'], ['k2', 'v2']])) | ||
// into | ||
// hmset('key', 'k1', 'v1', 'k2', 'v2') | ||
Redis.Command.setArgumentTransformer('hmset', function (args) { | ||
if (args.length === 2) { | ||
var pos = 1; | ||
if (typeof Map !== 'undefined' && args[1] instanceof Map) { | ||
return [args[0]].concat(utils.convertMapToArray(args[1])); | ||
} | ||
if ( typeof args[1] === 'object' && args[1] !== null) { | ||
return [args[0]].concat(utils.convertObjectToArray(args[1])); | ||
} | ||
} | ||
return args; | ||
}); | ||
// define a reply transformer that convert the reply | ||
// ['k1', 'v1', 'k2', 'v2'] | ||
// into | ||
// { k1: 'v1', 'k2': 'v2' } | ||
Redis.Command.setReplyTransformer('hgetall', function (result) { | ||
if (Array.isArray(result)) { | ||
var obj = {}; | ||
for (var i = 0; i < result.length; i += 2) { | ||
obj[result[i]] = result[i + 1]; | ||
} | ||
return obj; | ||
} | ||
return result; | ||
}); | ||
``` | ||
There are three built-in transformers, two argument transformer for `hmset` & `mset` and | ||
a reply transformer for `hgetall`. Transformers for `hmset` and `hgetall` has been mentioned | ||
above, and the transformer for `mset` is similar to the one for `hmset`: | ||
``` | ||
redis.mset({ k1: 'v1', k2: 'v2' }); | ||
redis.get('k1', function (err, result) { | ||
// result === 'v1'; | ||
}); | ||
redis.mset(new Map([['k3', 'v3'], ['k4', 'v4']])); | ||
redis.get('k3', function (err, result) { | ||
// result === 'v3'; | ||
}); | ||
``` | ||
## Pipelining | ||
@@ -335,5 +400,5 @@ If you want to send a batch of commands(e.g. > 100), you can use pipelining to queue | ||
ioredis **guarantees** that the node you connected to always be a master even after a failover. When a failover happens, instead of trying to reconnect with the failed node(which will be demoted to slave when it's available again), ioredis will ask sentinels for the new master node and connect to it. All commands sent during the failover are queued and will be executed when the new connection is established so that none of the commands will be lost. | ||
ioredis **guarantees** that the node you connected with is always a master even after a failover. When a failover happens, instead of trying to reconnect with the failed node(which will be demoted to slave when it's available again), ioredis will ask sentinels for the new master node and connect to it. All commands sent during the failover are queued and will be executed when the new connection is established so that none of the commands will be lost. | ||
It's possible to connect to a slave instead of a master by specifying a option `role` with the value of `slave`, and ioredis will try to connect to a random slave of the specified master, with the guarantee of the connected node is always a slave. If the current node is promoted to master because of a failover, ioredis will disconnect with it and ask sentinels for another slave node to connect to. | ||
It's possible to connect to a slave instead of a master by specifying the option `role` with the value of `slave`, and ioredis will try to connect to a random slave of the specified master, with the guarantee that the connected node is always a slave. If the current node is promoted to master owing to a failover, ioredis will disconnect with it and ask sentinels for another slave node to connect to. | ||
@@ -359,3 +424,3 @@ <hr> | ||
client.on('message', function (msg) { | ||
// Will print "Hello world", although no `publish` in invoked. | ||
// Will print "Hello world", although no `publish` is invoked. | ||
console.log('received ', msg); | ||
@@ -366,1 +431,9 @@ }); | ||
I submitted some pull requests but sadly none of them has been merged, so here's ioredis. | ||
# Acknowledge | ||
The JavaScript and hiredis parsers are modified from [node_redis](https://github.com/mranney/node_redis) (MIT License, Copyright (c) 2010 Matthew Ranney, http://ranney.com/). | ||
# License | ||
MIT |
@@ -14,10 +14,10 @@ GLOBAL.expect = require('chai').expect; | ||
// afterEach(function (done) { | ||
// var redis = new Redis(); | ||
// redis.flushall(function () { | ||
// redis.script('flush', function () { | ||
// redis.disconnect(); | ||
// done(); | ||
// }); | ||
// }); | ||
// }); | ||
afterEach(function (done) { | ||
var redis = new Redis(); | ||
redis.flushall(function () { | ||
redis.script('flush', function () { | ||
redis.disconnect(); | ||
done(); | ||
}); | ||
}); | ||
}); |
@@ -5,9 +5,2 @@ var Command = require('../../lib/command'); | ||
describe('constructor()', function () { | ||
it('should reject when the command isnt a string', function () { | ||
var command = new Command(123); | ||
return command.promise.catch(function (err) { | ||
expect(err).to.be.instanceof(Error); | ||
}); | ||
}); | ||
it('should flatten the args', function () { | ||
@@ -14,0 +7,0 @@ var command = new Command('get', ['foo', ['bar', ['zoo']]]); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
728774
3908
435
7