Permalink
Browse files

Delete child models when parent is removed. Add JSHint configuration …

…files. Update tests.
  • Loading branch information...
1 parent a5c40de commit febc74eedb977f78c3ab999488d3bffcddaf5cc8 Karthik Viswanathan committed Jun 26, 2012
Showing with 235 additions and 56 deletions.
  1. +1 −0 .jshintignore
  2. +6 −0 .jshintrc
  3. +3 −11 lib/components.js
  4. +2 −2 lib/crud.js
  5. +3 −11 lib/elements.js
  6. +3 −10 lib/projects.js
  7. +99 −11 lib/scaffold.js
  8. +3 −10 lib/screens.js
  9. +41 −0 test/test.components.js
  10. +34 −0 test/test.projects.js
  11. +40 −1 test/test.screens.js
View
@@ -0,0 +1 @@
+node_modules
View
@@ -0,0 +1,6 @@
+{
+ "node": true,
+ "strict": false,
+ "es5": true,
+ "esnext": true
+}
View
@@ -1,19 +1,11 @@
+/*globals exports:true*/
const ALLOWED_FIELDS = {
layout: undefined,
action: undefined
};
var scaffold = require('./scaffold');
-/* Get Components Key
- * Requires: web request
- * Returns: database key for components hash
- */
-function getComponentsKey(req) {
- return req.session.email + ':project:' + req.params.project_id + 'screen:' +
- req.params.screen_id + ':components';
-}
-
/* Get Default Component
* Requires: web request
* Returns: default component data
@@ -26,5 +18,5 @@ function getDefaultComponent(req) {
};
}
-exports = module.exports = scaffold.generate(getComponentsKey,
- getDefaultComponent, ALLOWED_FIELDS);
+exports = module.exports = scaffold.generate(['projects', 'screens'],
+ ['elements'], 'components', getDefaultComponent, ALLOWED_FIELDS);
View
@@ -8,7 +8,7 @@ var utils = require('./utils');
* error - null if object list was retrieved or an error otherwise
* objectList - if error is null, the list of objects
*/
-exports.list = function(key, allowedFields, db, callback) {
+exports.list = function(key, db, callback) {
db.hvals(key, function(err, objectList) {
if (err) {
callback(err);
@@ -55,7 +55,7 @@ exports.add = function(req, key, defaultValues, db, callback) {
callback(err);
} else {
var object = defaultValues;
- object.id = length + 1;
+ object.id = length + 1; // ids are 1-indexed
var value = JSON.stringify(object);
db.hset(key, String(object.id), value, function(err, status) {
View
@@ -1,3 +1,4 @@
+/*globals exports:true*/
const ALLOWED_FIELDS = {
title: undefined,
identifier: undefined,
@@ -8,15 +9,6 @@ const ALLOWED_FIELDS = {
var scaffold = require('./scaffold');
-/* Get Elements Key
- * Requires: web request
- * Returns: database key for elements hash
- */
-function getElementsKey(req) {
- return req.session.email + ':project:' + req.params.project_id + ':screen:' +
- req.params.screen_id + ':component:' + req.params.component_id + ':elements';
-}
-
/* Get Default Element
* Requires: web request
* Returns: default element data
@@ -31,5 +23,5 @@ function getDefaultElement(req) {
};
}
-exports = module.exports = scaffold.generate(getElementsKey,
- getDefaultElement, ALLOWED_FIELDS);
+exports = module.exports = scaffold.generate(['projects', 'screens',
+ 'components'], [], 'elements', getDefaultElement, ALLOWED_FIELDS);
View
@@ -1,18 +1,11 @@
+/*globals exports:true*/
const ALLOWED_FIELDS = {
title: undefined,
author: undefined
};
var scaffold = require('./scaffold');
-/* Get Projects Key
- * Requires: web request
- * Returns: database key for project hash
- */
-function getProjectsKey(req) {
- return req.session.email + ':projects';
-}
-
/* Get Default Project
* Requires: web request
* Returns: default project data
@@ -34,5 +27,5 @@ function getDefaultProject(req) {
* can continue to use `exports.someProperty = someValue;` and have
* someProperty exported by node.
*/
-exports = module.exports = scaffold.generate(getProjectsKey,
- getDefaultProject, ALLOWED_FIELDS);
+exports = module.exports = scaffold.generate([], ['screens'],
+ 'projects', getDefaultProject, ALLOWED_FIELDS);
View
@@ -4,8 +4,12 @@ var utils = require('./utils');
/* Generate a CRUD scaffold for an object
*
* Requires:
- * getKey - function(req) which returns a key to the object's hash based on
- * a request
+ * parents - the scaffolds that are parents of this scaffold
+ * i.e. ['projects', 'screens']
+ * children - the scaffolds that are direct children of this scaffold
+ * i.e. ['elements']
+ * name - the name of this scaffold
+ * i.e. 'components'
* getDefault - function(req) which returns a default object based on
* a request
* allowedFields - fields that are allowed to be modified in the form of
@@ -18,18 +22,37 @@ var utils = require('./utils');
* update(req, db, identifier, callback) - update an object
* remove(req, db, identifier, callback) - remove an object
*/
-exports.generate = function(getKey, getDefault, allowedFields) {
+exports.generate = function(parents, children, name, getDefault, allowedFields) {
var scaffold = {};
+ /* Get Hash Key
+ * Requires: web request
+ * Returns: database key for objects hash
+ */
+ function getHashKey(req) {
+ var key = req.session.email + ':';
+
+ var parentKeys = parents.map(function(parentName) {
+ var singularName = parentName.substring(0, parentName.length - 1);
+ return singularName + ':' + req.params[singularName + '_id'];
+ });
+
+ if (parentKeys.length > 0) {
+ parentKeys[parentKeys.length - 1] += ':';
+ }
+
+ return key + parentKeys.join(':') + name;
+ }
+
/* Object List
* Requires: web request, db connection, callback
* Calls: callback(error, objectList):
* error - null if object list was retrieved or an error otherwise
* objectList - if error is null, the list of objects
*/
scaffold.list = function(req, db, callback) {
- var key = getKey(req);
- crud.list(key, allowedFields, db, function(err, objectList) {
+ var key = getHashKey(req);
+ crud.list(key, db, function(err, objectList) {
if (err) {
callback(err);
} else {
@@ -45,7 +68,7 @@ exports.generate = function(getKey, getDefault, allowedFields) {
* object - if error is null, the object
*/
scaffold.get = function(req, db, id, callback) {
- var key = getKey(req);
+ var key = getHashKey(req);
crud.get(key, id, db, function(err, object) {
if (err) {
callback(err);
@@ -63,7 +86,7 @@ exports.generate = function(getKey, getDefault, allowedFields) {
*/
scaffold.add = function(req, db, callback) {
var defaultValues = getDefault(req);
- var key = getKey(req);
+ var key = getHashKey(req);
callback = callback || utils.noop;
crud.add(req, key, defaultValues, db, function(err, object) {
@@ -82,7 +105,7 @@ exports.generate = function(getKey, getDefault, allowedFields) {
* object - if error is null, the object that was updated
*/
scaffold.update = function(req, db, id, callback) {
- var key = getKey(req);
+ var key = getHashKey(req);
callback = callback || utils.noop;
crud.update(req, key, id, allowedFields, db,
@@ -101,11 +124,76 @@ exports.generate = function(getKey, getDefault, allowedFields) {
* error - null if the object was removed or an error otherwise
*/
scaffold.remove = function(req, db, id, callback) {
- var key = getKey(req);
+ var key = getHashKey(req);
callback = callback || utils.noop;
+ id = String(id);
+
+ // set up the request to remove child scaffolds
+ var childReq = req;
+ var singularName = name.substring(0, name.length - 1);
+
+ if (!childReq.params) {
+ childReq.params = {};
+ }
+
+ // set up the id of the current object being removed for access by child
+ // scaffolds
+ childReq.params[singularName + '_id'] = id;
+
+ // statistics for determining when deletion is complete
+ var objectsRemoved = 0;
+ var numChildrenProcessed = 0;
+ var numObjects = 0;
+
+ // remove handler for deleting child scaffold objects
+ function removeHandler(err) {
+ if (err) {
+ callback(err);
+ } else {
+ objectsRemoved++;
+ if (numChildrenProcessed === children.length &&
+ objectsRemoved === numObjects) {
+ crud.remove(key, id, db, function(err, status) {
+ callback(err);
+ });
+ }
+ }
+ }
+
+ // if there is nothing to remove, immediately call the remove handler
+ if (children.length === 0) {
+ // set to one so that the handler thinks it has removed all objects
+ numObjects = 1;
+ removeHandler(null);
+ }
+
+ // remove objects in child scaffolds that correspond to this object
+ children.forEach(function(childName) {
+ var childScaffold = require('./' + childName);
+
+ // key is in the form project:screen:components; want to get it in
+ // the form project:screen:component:elements
+ var childKey = key.substr(0, key.length - 1);
+ childKey += ':' + id + ':' + childName;
+
+ db.hlen(childKey, function(err, length) {
+ numObjects += length;
+ numChildrenProcessed++;
- crud.remove(key, id, db, function(err, status) {
- callback(err);
+ // there are no children to remove
+ if (numChildrenProcessed === children.length && numObjects === 0) {
+ numObjects = 1;
+ removeHandler(null);
+ }
+
+ if (err) {
+ callback(err);
+ } else {
+ for (var i = 1; i <= length; i++) {
+ childScaffold.remove(childReq, db, i, removeHandler);
+ }
+ }
+ });
});
};
View
@@ -1,3 +1,4 @@
+/*globals exports:true*/
const ALLOWED_FIELDS = {
title: undefined,
is_start: undefined,
@@ -6,14 +7,6 @@ const ALLOWED_FIELDS = {
var scaffold = require('./scaffold');
-/* Get Screens Key
- * Requires: web request
- * Returns: database key for screen hash
- */
-function getScreensKey(req) {
- return req.session.email + ':project:' + req.params.project_id + ':screens';
-}
-
/* Get Default Screen
* Requires: web request
* Returns: default screen data
@@ -26,5 +19,5 @@ function getDefaultScreen(req) {
};
}
-exports = module.exports = scaffold.generate(getScreensKey,
- getDefaultScreen, ALLOWED_FIELDS);
+exports = module.exports = scaffold.generate(['projects'], ['components'],
+ 'screens', getDefaultScreen, ALLOWED_FIELDS);
View
@@ -14,6 +14,7 @@ var db = redis.redisConnect(settings);
var screens = require('../lib/screens');
var projects = require('../lib/projects');
var components = require('../lib/components');
+var elements = require('../lib/elements');
var projectReq = {
session: {
@@ -53,6 +54,23 @@ var componentReq = {
}
};
+var elementReq = {
+ session: {
+ email: 'test@test.org'
+ },
+ body: {
+ type: 'input_text',
+ layout: 'row1',
+ required: true,
+ src: ''
+ },
+ params: {
+ project_id: 1,
+ screen_id: 1,
+ component_id: 1
+ }
+};
+
describe('component', function() {
before(function(done) {
var req = projectReq;
@@ -102,6 +120,9 @@ describe('component', function() {
componentList[0].type.should.equal(req.body.type);
componentList[0].layout.should.equal(req.body.layout);
componentList[0].action.should.equal(req.body.action);
+ componentList[1].type.should.equal(req.body.type);
+ componentList[1].layout.should.equal(req.body.layout);
+ componentList[1].action.should.equal(req.body.action);
done();
});
});
@@ -173,5 +194,25 @@ describe('component', function() {
});
}, 10);
});
+
+ it('deletes an element associated with a component', function(done) {
+ var req = componentReq;
+
+ components.add(req, db, function(err, component) {
+ req = elementReq;
+
+ elements.add(req, db, function(err, element) {
+ req = componentReq;
+
+ components.remove(req, db, 1, function(err) {
+ should.not.exist(err);
+ elements.list(req, db, function(err, elementList) {
+ elementList.should.eql([]);
+ done();
+ });
+ });
+ });
+ });
+ });
});
});
Oops, something went wrong.

0 comments on commit febc74e

Please sign in to comment.