Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

updating models, v0.1.2

  • Loading branch information...
commit aac493741301f6d94a14ed24e05db30eb23c041b 1 parent 4a0f5a6
@tehsenaus authored
Showing with 219 additions and 102 deletions.
  1. +9 −2 client.js
  2. +63 −54 lib/resource.js
  3. +2 −2 package.json
  4. +145 −44 server.js
View
11 client.js
@@ -1,9 +1,16 @@
-var resource = module.exports = require('resource');
+var resource = module.exports = require('./lib/resource');
+
+var ServerDataStore = resource.ServerDataStore = resource.Resource.derived({
+
+});
resource.Resource.implement({
dataStoreFactory: function (type, resource) {
- return null;
+ return new ServerDataStore();
}
});
+resource.export = function (resources) {
+ return resources;
+}
View
117 lib/resource.js
@@ -7,10 +7,6 @@ var exports = module.exports = {};
var Resource = exports.Resource = new coop.Class([coop.Options], {
initialize: function(options) {
this.super.apply(this, arguments);
- },
-
- list: function (callback) {
- callback(null, []);
}
});
var DelegateResource = exports.DelegateResource = Resource.derived({
@@ -25,7 +21,7 @@ var DelegateResource = exports.DelegateResource = Resource.derived({
read: function () {
return this.resource.read.apply(this.resource, arguments);
},
- create: function (data) {
+ create: function () {
return this.resource.create.apply(this.resource, arguments);
},
update: function () {
@@ -57,7 +53,7 @@ var ChildResource = exports.ChildResource = DataStoreResource.derived({
read: function () {
return this.resource.read.apply(this.resource, arguments);
},
- create: function (data) {
+ create: function () {
return this.resource.create.apply(this.resource, arguments);
},
update: function () {
@@ -70,7 +66,7 @@ var ChildResource = exports.ChildResource = DataStoreResource.derived({
var Model = exports.Model = new coop.Class({
- initialize: function (data) {
+ initialize: function (context, data) {
this.update(data);
},
update: function (data) {
@@ -80,8 +76,28 @@ var Model = exports.Model = new coop.Class({
return {};
},
+ validate: function (context) {},
+ validateCreate: function (context) {
+ return this.validate(context);
+ },
+ save: function (context) {
+ var me = this;
+ context = context || exports.globalContext;
+ return promise.when(this.validate(context), function () {
+ return me.resource.update(context, me.getModelQuery(), me);
+ });
+ },
+
+ // wrapper for dynamic model fields.
field: function (value) {
- return value;
+ var value;
+ return function () {
+ if(arguments.length > 0) {
+ value = arguments[0];
+ } else {
+ return value;
+ }
+ }
}
});
// Creates a concrete subclass of this model class, bound
@@ -112,49 +128,57 @@ var ModelResource = exports.ModelResource = new coop.Class({
this.Model = Model.instantiate.call(this.Model, this);
},
- create: function (data) {
- for(var n in this.Model.prototype) {
+ list: function (context) {
+ var me = this;
+ return this.super.apply(this, arguments).then(function (data) {
+ return data.map(me.ctor.bind(me, context));
+ });
+ },
+
+ create: function (context, data) {
+ var me = this,
+ model = this.ctor(context, data),
+ args = [].slice.call(arguments);
+
+ return promise.when(model.validateCreate(context), function () {
+ args[1] = model;
+ return ModelResource.super(me, 'create').apply(me, args);
+ });
+
+ /*for(var n in this.Model.prototype) {
var child = this.Model.prototype[n];
if(ChildResource.isinstance(child)) {
- console.log("child", n, child.name, child);
data[child.name] = [];
}
- }
- return this.super.apply(this, arguments);
+ }*/
+ },
+ update: function (context, query, data) {
+ var me = this;
+ if(this.Model.isinstance(data)) {
+ return this.super(context, query, data);
+ } else return this.list(context, query).then(function (objects) {
+ if(objects.length == 1) {
+ var model = objects[0];
+ model.update(data);
+ return model.save(context);
+ } else {
+ return promise.defer().reject("Query returned " + objects.length + " results, required 1");
+ }
+ })
},
// Creates an instance of the model from baked data
- ctor: function (data) {
- return new this.Model(data);
+ ctor: function (context, data) {
+ return new this.Model(context, data);
}
});
-// API generation
-
-
-var RESOURCES = {};
-
-function serialize (x) {
- return typeof x.bake === "function" ? x.bake() : x;
+exports.serialize = function (x, context) {
+ return typeof x.bake === "function" ? x.bake(context) : x;
}
-/**
- * This is the join point between the server and client.
- **/
-var ServerPublicResource = new coop.Class([DelegateResource], {
- options: {
- name: "ServerPublicResource"
- },
- initialize: function (resourceURL) {
- this.super.apply(this, coop.pop(arguments));
- },
- list: function (query) {
- return this.super(query).then(function (items) {
- return items.map(serialize);
- })
- }
-});
+
var ClientPublicResource = new coop.Class([Resource], {
initialize: function (resourceURL, resource) {
this.resource = resource;
@@ -186,21 +210,6 @@ var ClientPublicResource = new coop.Class([Resource], {
}
});
-exports.export = function (resources, baseURL) {
- console.log("export", resources);
- baseURL = baseURL || '/api/';
- RESOURCES = {};
- for(var n in resources) {
- RESOURCES[n] = new ServerPublicResource(baseURL + n, resources[n]);
- };
-}
-exports.createAPI = function () {
- var api = {};
- for(var n in RESOURCES) {
- api[n] = RESOURCES[n].createAPI();
- };
- console.log("API", api);
- return api;
-}
+
View
4 package.json
@@ -2,12 +2,12 @@
"author": "Sean Micklethwaite <sean@sdmworld.co.uk> (sdmworld.co.uk)",
"name": "resource",
"description": "A RESTful API generator",
- "version": "0.1.1",
+ "version": "0.1.2",
"repository": {
"url": ""
},
"dependencies": {
- "coop": ">=0.0.1",
+ "coop": ">=0.1.5",
"dispatch": ">=0.2.0",
"node-promise": ">=0.5.1",
"event-queue": "https://github.com/tehsenaus/reactor-commonjs/tarball/master",
View
189 server.js
@@ -1,6 +1,10 @@
var resource = module.exports = require('./lib/resource');
var connect = require("connect");
+var coop = require("coop");
+var promise = require("node-promise");
+
+// Data stores
resource.Resource.serverDataStores = {};
resource.Resource.implement({
@@ -19,91 +23,176 @@ resource.Resource.implement({
}
});
+
+
+// Server / Client bridge
+
+function serialize (context, x) {
+ return typeof x.bake === "function" ? x.bake(context) : x;
+}
+
+/**
+ * This is the join point between the server and client.
+ **/
+var ServerPublicResource = resource.DelegateResource.derived({
+ options: {
+ name: "ServerPublicResource"
+ },
+
+ initialize: function (resourceURL) {
+ this.super.apply(this, coop.pop(arguments));
+ },
+ list: function (context) {
+ return this.super.call(this, arguments).then(function (items) {
+ return items.map(serialize.bind(this, context));
+ })
+ }
+});
+
+resource.export = function (resources, baseURL) {
+ baseURL = baseURL || '/api/';
+ var rs = resource.resources = {};
+ for(var n in resources) {
+ rs[n] = new ServerPublicResource(baseURL + n, resources[n]);
+ console.log(n, rs[n], resources[n]);
+ };
+ return resources;
+}
+
+
+
+// API generation
+
resource.DelegateResource.implement({
createAPI: function () {
return this.resource.createAPI.apply(this.resource, arguments);
+ },
+ createAPIMiddleware: function () {
+ return this.resource.createAPIMiddleware.apply(this.resource, arguments);
+ },
+ createContextFromRequest: function (req) {
+ return this.resource.createContextFromRequest.apply(this.resource, arguments);
+ },
+ createQueryFromSlug: function () {
+ return this.resource.createQueryFromSlug.apply(this.resource, arguments);
}
});
+function handler(fn) {
+ return function (req, res) {
+ //try {
+ fn.apply(this, arguments);
+ //} catch (e) {
+ // res.error(e.toString());
+ //}
+ }
+}
+function readHandler (r, fn) {
+ return r.createAPIMiddleware()
+ .use(handler(function (req,res) {
+ var me = this, args = [].slice.call(arguments);
+ return promise.when(r.createContextFromRequest(req), function (context) {
+ return fn.apply(me, [context].concat(args));
+ });
+ }));
+}
+function modificationHandler (r, fn) {
+ return r.createAPIMiddleware()
+ .use(connect.bodyParser())
+ .use(handler(function (req,res) {
+ var me = this, args = [].slice.call(arguments);
+ return promise.when(r.createContextFromRequest(req), function (context) {
+ return fn.apply(me, [context].concat(args));
+ });
+ }));
+}
+
+
resource.DataStoreResource.implement({
+ createContextFromRequest: function (req) {
+ return this.createDefaultContextFromRequest(req);
+ },
+ createDefaultContextFromRequest: function (req) {
+ return { isServer: true };
+ },
createQueryFromSlug: function (slug) {
return {
- test: slug
+ id: slug
};
},
- createChildAPI: function (queryStack) {
- var r = this;
+ createChildAPI: function (r) {
return {
- GET: function (req,res,slug) {
- r.list(this.createQueryFromSlug(slug)).then(function (data) {
- res.json({
- objects: data
- });
+ GET: readHandler(r, function (context,req,res,slug) {
+ r.list(context, r.createQueryFromSlug(slug)).then(function (data) {
+ res.json(data[0]);
})
- }
+ })
}
},
- createAPI: function (queryStack) {
- var r = this;
+ createAPI: function (r) {
+ var me = this;
return {
- "/:id": this.createChildAPI(),
- GET: function (req,res) {
- r.list({}).then(function (data) {
+ "/:id": this.createChildAPI(r),
+ GET: readHandler(r, function (context,req,res) {
+ r.list(context, {}).then(function (data) {
res.json({
objects: data
});
})
- },
- POST: connect(
- connect.bodyParser(),
- function (req, res) {
- //res.end("data: " + req.body.test);
- r.create(req.body).then(function (item) {
- res.json(item);
- }, function (error) {
- res.error(error);
- });
- }
- )
+ }),
+ POST: modificationHandler(r, function (context, req, res) {
+ r.create(context, req.body).then(function (item) {
+ res.created().json(item);
+ }, function (error) {
+ res.error(error.toString());
+ });
+ })
}
+ },
+ createAPIMiddleware: function () {
+ return this.createDefaultAPIMiddleware();
+ },
+ createDefaultAPIMiddleware: function () {
+ return connect().use(connect.query());
}
});
resource.ChildResource.implement({
- createAPI: function (modelQueryAccessor) {
- var r = this;
- var api = this.super();
+ createAPI: function (r, modelQueryAccessor) {
+ var me = this;
+ var api = this.super(r);
+
api.POST = function (req, res, next) {
var args = Array.prototype.slice.call(arguments, 3);
- return (connect(
- connect.bodyParser(),
- function (req, res, next) {
+ return (me.createAPIMiddleware()
+ .use(connect.bodyParser())
+ .use(handler(function (req, res, next) {
var modelQuery = modelQueryAccessor(args);
- //res.end("data: " + req.body.test);
- r.create(modelQuery, req.body).then(function (item) {
- res.json(item);
- }, function (error) {
- res.error(error && error.toString());
+
+ promise.when(me.createContextFromRequest(req), function (context) {
+ r.create(context, modelQuery, req.body).then(function (item) {
+ res.created().json(item);
+ }, function (error) {
+ res.error(error && error.toString());
+ });
});
- }
- ))(req, res, next);
+ }))
+ )(req, res, next);
}
return api;
}
});
resource.ModelResource.implement({
- createChildAPI: function (parentModelQueryAccessor) {
- var me = this, api = this.super();
-
- console.log("createChildAPI", this.Model);
+ createChildAPI: function (r, parentModelQueryAccessor) {
+ var me = this, api = this.super.apply(this, arguments);
for(var n in this.Model.prototype) {
var child = this.Model.prototype[n];
if(resource.ChildResource.isinstance(child)) {
// found child resource. Create its API, and define a model query
// accessor
- api["/" + n] = child.createAPI(function (args, i, modelQuery) {
+ api["/" + n] = child.createAPI(child, function (args, i, modelQuery) {
// top of args stack is model slug
i = i || 0;
@@ -128,3 +217,15 @@ resource.ModelResource.implement({
return api;
}
});
+
+
+resource.createAPI = function () {
+ var api = {};
+ for(var n in resource.resources) {
+ var r = resource.resources[n];
+ api[n] = r.createAPI(r);
+ };
+ console.log("API", api);
+ return api;
+}
+
Please sign in to comment.
Something went wrong with that request. Please try again.