Permalink
Browse files

add redis store

  • Loading branch information...
1 parent d955946 commit ad6603c8272cf526032fd13b60b6fb6b4b15435c Carlos Rodriguez committed Oct 13, 2012
Showing with 138 additions and 20 deletions.
  1. +1 −1 Makefile
  2. +7 −1 index.js
  3. +4 −2 package.json
  4. +1 −7 stores/memory.js
  5. +0 −1 stores/mysql.js
  6. +118 −0 stores/redis.js
  7. +6 −0 test/basic.js
  8. +1 −8 test/common.js
View
@@ -1,7 +1,7 @@
all: build
build:
- ./node_modules/.bin/pegjs parser.pegjs
+ @./node_modules/.bin/pegjs parser.pegjs
test: build
@./node_modules/.bin/mocha --reporter spec --timeout 2s --require test/common.js --bail
View
@@ -96,7 +96,8 @@ relations._queue = [];
relations.stores = {
memory: require('./stores/memory'),
- mysql: require('./stores/mysql')
+ mysql: require('./stores/mysql'),
+ redis: require('./stores/redis')
};
relations.use = function (store, options) {
@@ -108,6 +109,11 @@ relations.use = function (store, options) {
});
};
+relations.tearDown = function (cb) {
+ if (relations.store.tearDown) return relations.store.tearDown(cb);
+ else cb();
+};
+
(function doQueue () {
process.nextTick(function () {
if (!relations.store) {
View
@@ -5,11 +5,13 @@
"main": "index.js",
"dependencies": {
"eventflow": "0.0.6",
- "pegjs": "~0.7.0"
+ "pegjs": "~0.7.0",
+ "async": "~0.1.22"
},
"devDependencies": {
"mocha": "*",
- "mysql": "2.0.0-alpha3"
+ "mysql": "2.0.0-alpha3",
+ "redis": "~0.8.1"
},
"scripts": {
"test": "make test"
View
@@ -1,5 +1,4 @@
var store = module.exports = require('eventflow')();
-
var contexts = {};
store.on('init', function (options, cb) {
@@ -75,9 +74,4 @@ store.on('role-request', function (cmd, cb) {
cb(null, Object.keys(subject.objects).filter(function (k) {
return subject.objects[k][cmd.role];
}));
-});
-
-store.tearDown = function (cb) {
- contexts = {};
- cb();
-};
+});
View
@@ -1,5 +1,4 @@
var store = module.exports = require('eventflow')();
-
var client;
store.on('init', function (options, cb) {
View
@@ -0,0 +1,118 @@
+var store = module.exports = require('eventflow')();
+var client;
+var async = require('async');
+
+store.on('init', function (options, cb) {
+ if (!options.client) {
+ return cb(new Error('must pass a node_redis client in options.client to use redis store'));
+ }
+ client = store._client = options.client;
+ if (client.connected) return cb();
+ else client.once('ready', cb);
+});
+
+function getKey (cmd) {
+ return [
+ 'relations',
+ cmd.ctx.name,
+ cmd.subject,
+ cmd.object || all
+ ].join(':');
+}
+
+var all = '__all__';
+
+store.on('declaration', function (cmd, cb) {
+ client.SADD(getKey(cmd), cmd.role, cb);
+});
+
+store.on('revocation', function (cmd, cb) {
+ client.SREM(getKey(cmd), cmd.role, cb);
+})
+
+store.on('verb-question', function (cmd, cb) {
+ (function doCheck () {
+ client.SMEMBERS(getKey(cmd), function (err, roles) {
+ if (err) return cb(err);
+ if (!roles) roles = [];
+ var can = roles.some(function (role) {
+ return ~cmd.ctx.verbs[cmd.verb].indexOf(role);
+ });
+ if (can || !cmd.object) return cb(null, can);
+ else {
+ delete cmd.object;
+ doCheck();
+ }
+ });
+ })();
+});
+
+store.on('role-question', function (cmd, cb) {
+ (function doCheck () {
+ client.SMEMBERS(getKey(cmd), function (err, roles) {
+ if (err) return cb(err);
+ if (!roles) roles = [];
+ var can = !!~roles.indexOf(cmd.role);
+ if (can || !cmd.object) return cb(null, can);
+ else {
+ delete cmd.object;
+ doCheck();
+ }
+ });
+ })();
+});
+
+store.on('verb-request', function (cmd, cb) {
+ client.KEYS([
+ 'relations',
+ cmd.ctx.name,
+ cmd.subject,
+ '*'
+ ].join(':'), function (err, keys) {
+ if (err || !keys) return cb(err, []);
+ async.map(keys, function (key, cb_) {
+ client.SMEMBERS(key, function (err, roles) {
+ var object = key.split(':').pop();
+ if (err || !roles || object == all) return cb_(err);
+ cb_(null, roles.some(function (role) {
+ return ~cmd.ctx.verbs[cmd.verb].indexOf(role);
+ }) ? object : null);
+ });
+ }, function (err, objects) {
+ if (err) return cb(err);
+ cb(null, objects.filter(function (object) {
+ return !!object;
+ }));
+ });
+ });
+});
+
+store.on('role-request', function (cmd, cb) {
+ client.KEYS([
+ 'relations',
+ cmd.ctx.name,
+ cmd.subject,
+ '*'
+ ].join(':'), function (err, keys) {
+ if (err || !keys) return cb(err, []);
+ async.map(keys, function (key, cb_) {
+ client.SMEMBERS(key, function (err, roles) {
+ var object = key.split(':').pop();
+ if (err || !roles || object === all) return cb_(err);
+ cb_(null, ~roles.indexOf(cmd.role) ? object : null);
+ });
+ }, function (err, objects) {
+ if (err) return cb(err);
+ cb(null, objects.filter(function (object) {
+ return !!object;
+ }));
+ });
+ });
+});
+
+store.tearDown = function (cb) {
+ client.KEYS('relations:*', function (err, keys) {
+ if (err || !keys) return cb(err);
+ async.map(keys, client.DEL.bind(client), cb);
+ });
+};
View
@@ -1,7 +1,13 @@
describe('basic test', function () {
doBasicTest();
});
+
describe('mysql', function () {
var mysql = require('mysql');
doBasicTest('mysql', {client: mysql.createConnection({user: 'root', database: 'test'})});
+});
+
+describe('redis', function () {
+ var redis = require('redis');
+ doBasicTest('redis', {client: redis.createClient()});
});
View
@@ -31,14 +31,7 @@ doBasicTest = function (store, options) {
relations.repos('%s is a watcher', sagar);
});
- after(function (done) {
- if (store) {
- relations.store.tearDown(done);
- }
- else {
- done();
- }
- });
+ after(relations.tearDown);
it('can brian administrate views', function (done) {
relations.repos('can :user administrate :repo?', {user: brian, repo: views}, function (err, can) {

0 comments on commit ad6603c

Please sign in to comment.