Skip to content
This repository has been archived by the owner on Dec 1, 2017. It is now read-only.

Commit

Permalink
Allow projects and screens to be added/edited/deleted via backbone.js…
Browse files Browse the repository at this point in the history
… and REST APIs. Fix bug in setting CRUD object IDs.
  • Loading branch information
Karthik Viswanathan committed Jul 4, 2012
1 parent 8f04f9b commit 6e87257
Show file tree
Hide file tree
Showing 29 changed files with 1,617 additions and 376 deletions.
4 changes: 4 additions & 0 deletions .jshintignore
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
node_modules
public/javascripts/backbone.min.js
public/javascripts/underscore.min.js
public/javascripts/bootstrap.min.js
public/javascripts/json2.js
2 changes: 1 addition & 1 deletion app.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ db.select(settings.set('napkin'), function(errDb, res) {
nconf.argv().env().file({ file: 'local.json' });

// routes
require("./routes")(app);
require('./routes')(app, nconf, db);
require('./routes/auth')(app, nconf);

app.listen(process.env.PORT || nconf.get('port'));
2 changes: 1 addition & 1 deletion lib/components.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const ALLOWED_FIELDS = {

var scaffold = require('./scaffold');

/* Get Default Component
/* Get default component
* Requires: web request
* Returns: default component data
*/
Expand Down
4 changes: 2 additions & 2 deletions lib/crud.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,12 @@ exports.get = function(key, id, db, callback) {
*/
exports.add = function(req, key, defaultValues, db, callback) {
callback = callback || utils.noop;
db.hlen(key, function(err, length) {
db.incr(key + ':id', function(err, id) {
if (err) {
callback(err);
} else {
var object = defaultValues;
object.id = length + 1; // ids are 1-indexed
object.id = id; // ids are 1-indexed

var value = JSON.stringify(object);
db.hset(key, String(object.id), value, function(err, status) {
Expand Down
2 changes: 1 addition & 1 deletion lib/elements.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const ALLOWED_FIELDS = {

var scaffold = require('./scaffold');

/* Get Default Element
/* Get default element
* Requires: web request
* Returns: default element data
*/
Expand Down
23 changes: 21 additions & 2 deletions lib/projects.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const ALLOWED_FIELDS = {

var scaffold = require('./scaffold');

/* Get Default Project
/* Get default project
* Requires: web request
* Returns: default project data
*/
Expand All @@ -18,6 +18,25 @@ function getDefaultProject(req) {
};
}

/* Validate project
* Requires:
* req - web request,
* beingCreated - true if this project is being created for the first time and
* false otherwise
* Returns: true if the request is valid to create/update a project
* or an error message otherwise
*/
function validateProject(req, beingCreated) {
var title = req.body.title;

if (!title || title.length === 0) {
return 'Project must have a title.';
} else if (title.length > 25) {
return 'Project must have a title less than 25 characters long.';
}
return true;
}

/*
* Directly assigning `exports` alone won't do anything, as node only exports
* the properties of `exports` and not a reassigned object. Assigning
Expand All @@ -28,4 +47,4 @@ function getDefaultProject(req) {
* someProperty exported by node.
*/
exports = module.exports = scaffold.generate([], ['screens'],
'projects', getDefaultProject, ALLOWED_FIELDS);
'projects', getDefaultProject, ALLOWED_FIELDS, validateProject);
112 changes: 104 additions & 8 deletions lib/scaffold.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ var utils = require('./utils');
* a request
* allowedFields - fields that are allowed to be modified in the form of
* a map
* validateRequest - function(req, beingCreated) which determines whether the
* POST values in the given request are valid for creating or updating this
* object. beingCreated will be true if the object is being created or
* false if it is being updated. validateRequest should return true if the
* request is valid or return an error message otherwise.
*
* Returns: the scaffold in the form of an object with the following methods:
* list(req, db, callback) - get a list of the scaffolded objects
Expand All @@ -22,10 +27,11 @@ var utils = require('./utils');
* update(req, db, identifier, callback) - update an object
* remove(req, db, identifier, callback) - remove an object
*/
exports.generate = function(parents, children, name, getDefault, allowedFields) {
exports.generate = function(parents, children, name, getDefault,
allowedFields, validateRequest) {
var scaffold = {};

/* Get Hash Key
/* Get hash key
* Requires: web request
* Returns: database key for objects hash
*/
Expand All @@ -34,7 +40,7 @@ exports.generate = function(parents, children, name, getDefault, allowedFields)

var parentKeys = parents.map(function(parentName) {
var singularName = parentName.substring(0, parentName.length - 1);
return singularName + ':' + req.params[singularName + '_id'];
return parentName + ':' + req.params[singularName + '_id'];
});

if (parentKeys.length > 0) {
Expand All @@ -44,7 +50,7 @@ exports.generate = function(parents, children, name, getDefault, allowedFields)
return key + parentKeys.join(':') + name;
}

/* Object List
/* Object list
* Requires: web request, db connection, callback
* Calls: callback(error, objectList):
* error - null if object list was retrieved or an error otherwise
Expand All @@ -61,7 +67,7 @@ exports.generate = function(parents, children, name, getDefault, allowedFields)
});
};

/* Object Get
/* Object get
* Requires: web request, db connection, id, callback
* Calls: callback(error, object):
* error - null if object was retrieved or an error otherwise
Expand All @@ -78,7 +84,7 @@ exports.generate = function(parents, children, name, getDefault, allowedFields)
});
};

/* Object Add
/* Object add
* Requires: web request, db connection, callback
* Calls: callback(error, object):
* error - null if the object was added or an error otherwise
Expand All @@ -98,7 +104,7 @@ exports.generate = function(parents, children, name, getDefault, allowedFields)
});
};

/* Object Update
/* Object update
* Requires: web request, db connection, id, callback
* Calls: callback(error, object):
* error - null if object was updated or error otherwise
Expand All @@ -118,7 +124,7 @@ exports.generate = function(parents, children, name, getDefault, allowedFields)
});
};

/* Object Remove
/* Object remove
* Requires: web request, db connection, id, callback
* Calls: callback(error):
* error - null if the object was removed or an error otherwise
Expand Down Expand Up @@ -197,5 +203,95 @@ exports.generate = function(parents, children, name, getDefault, allowedFields)
});
};

/* Generate REST routes for this scaffold
* Requires: application, db connection
*/
scaffold.generateRESTRoutes = function(app, db) {
var baseRoute = '/';

// construct base route to be in the form /project/:project_id/screens
var parentKeys = parents.map(function(parentName) {
var singularName = parentName.substring(0, parentName.length - 1);
return parentName + '/:' + singularName + '_id';
});

if (parentKeys.length > 0) {
parentKeys[parentKeys.length - 1] += '/';
}

baseRoute += parentKeys.join('/') + name;
var that = this;

// GET should read
app.get(baseRoute + '/:id?', function(req, res) {
if (!req.params.id) {
// if no id is specified, then return a list of all objects
that.list(req, db, function(err, objectList) {
if (err) {
// TODO: how to handle error?
throw err;
} else {
res.send(objectList);
}
});
} else {
// otherwise return a specific object
that.get(req, db, req.params.id, function(err, object) {
if (err) {
throw err;
} else {
res.send(object);
}
});
}
});

// POST should create
app.post(baseRoute, function(req, res) {
var result = validateRequest(req);
if (result !== true) {
res.send(result, 400);
return;
}

that.add(req, db, function(err, object) {
if (err) {
throw err;
} else {
res.send(object);
}
});
});

// PUT should update
app.put(baseRoute + '/:id', function(req, res) {
var result = validateRequest(req);
if (result !== true) {
res.send(result, 400);
return;
}

that.update(req, db, req.params.id, function(err, object) {
if (err) {
throw err;
} else {
res.send(object);
}
});
});

// DELETE should remove
app.del(baseRoute + '/:id', function(req, res) {
that.remove(req, db, req.params.id, function(err) {
if (err) {
throw err;
} else {
// sends a "204 No Content" status code
res.send();
}
});
});
};

return scaffold;
};
24 changes: 22 additions & 2 deletions lib/screens.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const ALLOWED_FIELDS = {

var scaffold = require('./scaffold');

/* Get Default Screen
/* Get default screen
* Requires: web request
* Returns: default screen data
*/
Expand All @@ -19,5 +19,25 @@ function getDefaultScreen(req) {
};
}

/* Validate screen
* Requires:
* req - web request,
* beingCreated - true if this screen is being created for the first time and
* false otherwise
* Returns: true if the request is valid to create/update a screen
* or an error message otherwise
*/
function validateScreen(req, beingCreated) {
var title = req.body.title;

/* TODO: validate is_start and layout */
if (!title || title.length === 0) {
return 'Screen must have a title.';
} else if (title.length > 25) {
return 'Screen must have a title less than 25 characters long.';
}
return true;
}

exports = module.exports = scaffold.generate(['projects'], ['components'],
'screens', getDefaultScreen, ALLOWED_FIELDS);
'screens', getDefaultScreen, ALLOWED_FIELDS, validateScreen);
Loading

0 comments on commit 6e87257

Please sign in to comment.