This commit is contained in:
Ritchie 2013-07-25 11:07:54 -07:00
commit 3190298aac
7 changed files with 142 additions and 49 deletions

View File

@ -85,7 +85,7 @@ DataAccessObject.create = function (data, callback) {
data = {};
}
if (data instanceof Array) {
if (Array.isArray(data)) {
var instances = [];
var errors = Array(data.length);
var gotError = false;
@ -117,7 +117,7 @@ DataAccessObject.create = function (data, callback) {
var obj;
// if we come from save
if (data instanceof Model && !data.id) {
if (data instanceof Model && !getIdValue(this, data)) {
obj = data;
} else {
obj = new Model(data);
@ -182,6 +182,16 @@ function stillConnecting(dataSource, obj, args) {
return true;
};
function getIdValue(m, data) {
return data && data[m.dataSource.idName(m.modelName)];
}
function setIdValue(m, data, value) {
if(data) {
data[m.dataSource.idName(m.modelName)] = value;
}
}
/**
* Update or insert
*/
@ -189,7 +199,7 @@ DataAccessObject.upsert = DataAccessObject.updateOrCreate = function upsert(data
if (stillConnecting(this.dataSource, this, arguments)) return;
var Model = this;
if (!data.id) return this.create(data, callback);
if (!getIdValue(this, data)) return this.create(data, callback);
if (this.dataSource.connector.updateOrCreate) {
var inst = new Model(data);
this.dataSource.connector.updateOrCreate(Model.modelName, inst.toObject(true), function (err, data) {
@ -203,7 +213,7 @@ DataAccessObject.upsert = DataAccessObject.updateOrCreate = function upsert(data
callback(err, obj);
});
} else {
this.findById(data.id, function (err, inst) {
this.findById(getIdValue(this, data), function (err, inst) {
if (err) return callback(err);
if (inst) {
inst.updateAttributes(data, callback);
@ -276,8 +286,8 @@ DataAccessObject.findById = function find(id, cb) {
this.dataSource.connector.find(this.modelName, id, function (err, data) {
var obj = null;
if (data) {
if (!data.id) {
data.id = id;
if (!getIdValue(this, data)) {
setIdValue(this, data, id);
}
obj = new this();
obj._initProperties(data, false);

View File

@ -132,6 +132,7 @@ DataSource.prototype._setupConnector = function () {
// Set up the dataSource if the connector doesn't do so
this.connector.dataSource = this;
}
var dataSource = this;
this.connector.log = function (query, start) {
dataSource.log(query, start);
};

View File

@ -7,7 +7,6 @@ var ModelBaseClass = require('./model.js');
var List = require('./list.js');
var EventEmitter = require('events').EventEmitter;
var util = require('util');
var GeoPoint = require('./geo').GeoPoint;
/**
* Export public API
@ -21,36 +20,6 @@ exports.Schema = exports.ModelBuilder = ModelBuilder;
*/
var slice = Array.prototype.slice;
/**
* Schema types
*/
ModelBuilder.Text = function Text() {}; // Text type
ModelBuilder.JSON = function JSON() {}; // JSON Object
ModelBuilder.Any = function Any() {}; // Any Type
ModelBuilder.schemaTypes = {};
ModelBuilder.registerType = function (type, names) {
names = names || [];
names = names.concat([type.name]);
for (var n = 0; n < names.length; n++) {
this.schemaTypes[names[n].toLowerCase()] = type;
}
};
ModelBuilder.registerType(ModelBuilder.Text);
ModelBuilder.registerType(ModelBuilder.JSON);
ModelBuilder.registerType(ModelBuilder.Any);
ModelBuilder.registerType(String);
ModelBuilder.registerType(Number);
ModelBuilder.registerType(Boolean);
ModelBuilder.registerType(Date);
ModelBuilder.registerType(Buffer, ['Binary']);
ModelBuilder.registerType(Array);
ModelBuilder.registerType(GeoPoint);
ModelBuilder.registerType(Object);
/**
* ModelBuilder - Data Model Definition
*/
@ -62,6 +31,8 @@ function ModelBuilder() {
util.inherits(ModelBuilder, EventEmitter);
// Set up types
require('./types')(ModelBuilder);
/**
* Define class
@ -221,7 +192,7 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
if(!DataType) {
throw new Error('Invalid type for property ' + attr);
}
if (DataType instanceof Array) {
if (Array.isArray(DataType)) {
DataType = List;
} else if (DataType.name === 'Date') {
var OrigDate = Date;
@ -261,6 +232,8 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
if(DataType === List) {
this.__data[attr] = DataType(value, properties[attr].type, this.__data);
} else {
// Assume the type constructor handles Constructor() call
// If not, we should call new DataType(value).valueOf();
this.__data[attr] = DataType(value);
}
}

View File

@ -34,12 +34,7 @@ ModelBaseClass.prototype._initProperties = function (data, applySetters) {
var self = this;
var ctor = this.constructor;
var ds = {
properties: ctor.properties,
setters: ctor.settings
};
var properties = ds.properties;
var properties = ctor.properties;
data = data || {};
Object.defineProperty(this, '__cachedRelations', {
@ -67,18 +62,29 @@ ModelBaseClass.prototype._initProperties = function (data, applySetters) {
this.__cachedRelations = data['__cachedRelations'];
}
// Check if the strict option is set to false for the model
var strict = ctor.settings.strict;
for (var i in data) {
if (i in properties) {
this.__data[i] = this.__dataWas[i] = data[i];
} else if (i in ctor.relations) {
this.__data[ctor.relations[i].keyFrom] = this.__dataWas[i] = data[i][ctor.relations[i].keyTo];
this.__cachedRelations[i] = data[i];
} else {
if(strict === false) {
this.__data[i] = this.__dataWas[i] = data[i];
} else if(strict === 'throw') {
throw new Error('Unknown property: ' + i);
}
}
}
if (applySetters === true) {
Object.keys(data).forEach(function (attr) {
self[attr] = data[attr];
if((attr in properties) || (attr in ctor.relations) || strict === false) {
self[attr] = data[attr];
}
});
}
@ -170,12 +176,13 @@ ModelBaseClass.prototype.toObject = function (onlySchema) {
var properties = ds.properties;
var self = this;
var schemaless = this.constructor.settings.strict === false || !onlySchema;
this.constructor.forEachProperty(function (attr) {
if (self[attr] instanceof List) {
data[attr] = self[attr].toObject(onlySchema);
data[attr] = self[attr].toObject(!schemaless);
} else if (self.__data.hasOwnProperty(attr)) {
if(self[attr] !== undefined && self[attr]!== null && self[attr].toObject) {
data[attr] = self[attr].toObject(onlySchema);
data[attr] = self[attr].toObject(!schemaless);
} else {
data[attr] = self[attr];
}
@ -184,10 +191,14 @@ ModelBaseClass.prototype.toObject = function (onlySchema) {
}
});
if (!onlySchema) {
if (schemaless) {
Object.keys(self).forEach(function (attr) {
if (!data.hasOwnProperty(attr)) {
data[attr] = this[attr];
if(self[attr] !== undefined && self[attr]!== null && self[attr].toObject) {
data[attr] = self[attr].toObject(!schemaless);
} else {
data[attr] = self[attr];
}
}
});
}

61
lib/types.js Normal file
View File

@ -0,0 +1,61 @@
module.exports = function (Types) {
var List = require('./list.js');
var GeoPoint = require('./geo').GeoPoint;
/**
* Schema types
*/
Types.Text = function Text(value) {
if (!(this instanceof Text)) {
return new Text(value);
}
this.value = value;
}; // Text type
Types.Text.prototype.toObject = Types.Text.prototype.toJSON = function () {
return this.value;
};
Types.JSON = function JSON() {
if (!(this instanceof JSON)) {
return new JSON(value);
}
this.value = value;
}; // JSON Object
Types.JSON.prototype.toObject = Types.JSON.prototype.toJSON = function () {
return this.value;
};
Types.Any = function Any() {
if (!(this instanceof Any)) {
return new Any(value);
}
this.value = value;
}; // Any Type
Types.Any.prototype.toObject = Types.Any.prototype.toJSON = function () {
return this.value;
};
Types.schemaTypes = {};
Types.registerType = function (type, names) {
names = names || [];
names = names.concat([type.name]);
for (var n = 0; n < names.length; n++) {
this.schemaTypes[names[n].toLowerCase()] = type;
}
};
Types.registerType(Types.Text);
Types.registerType(Types.JSON);
Types.registerType(Types.Any);
Types.registerType(String);
Types.registerType(Number);
Types.registerType(Boolean);
Types.registerType(Date);
Types.registerType(Buffer, ['Binary']);
Types.registerType(Array);
Types.registerType(GeoPoint);
Types.registerType(Object);
}

View File

@ -39,6 +39,43 @@ describe('ModelBuilder define model', function () {
done(null, User);
});
it('should not take unknown properties in strict mode', function (done) {
var modelBuilder = new ModelBuilder();
// simplier way to describe model
var User = modelBuilder.define('User', {name: String, bio: String}, {strict: true});
var user = new User({name: 'Joe', age: 20});
// console.log(user);
User.modelName.should.equal('User');
user.should.be.a('object');
// console.log(user);
assert(user.name === 'Joe');
assert(user.age === undefined);
assert(user.toObject().age === undefined);
assert(user.toObject(true).age === undefined);
assert(user.bio === undefined);
done(null, User);
});
it('should be able to define open models', function (done) {
var modelBuilder = new ModelBuilder();
// simplier way to describe model
var User = modelBuilder.define('User', {}, {strict: false});
var user = new User({name: 'Joe', age: 20});
// console.log(user);
User.modelName.should.equal('User');
user.should.be.a('object').and.have.property('name', 'Joe');
user.should.have.property('name', 'Joe');
user.should.have.property('age', 20);
user.should.not.have.property('bio');
done(null, User);
});
it('should be able to define nesting models', function (done) {
var modelBuilder = new ModelBuilder();