Merge pull request #20 from strongloop/juggler-tidyup
Add more tests and fix toJSON
This commit is contained in:
commit
5518039d84
|
@ -117,7 +117,7 @@ Memory.prototype.fromDb = function(model, data) {
|
|||
if (!data) return null;
|
||||
data = JSON.parse(data);
|
||||
var props = this._models[model].properties;
|
||||
Object.keys(data).forEach(function (key) {
|
||||
for(var key in data) {
|
||||
var val = data[key];
|
||||
if (typeof val === 'undefined' || val === null) {
|
||||
return;
|
||||
|
@ -136,7 +136,7 @@ Memory.prototype.fromDb = function(model, data) {
|
|||
}
|
||||
}
|
||||
data[key] = val;
|
||||
});
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
|
|
16
lib/dao.js
16
lib/dao.js
|
@ -58,14 +58,14 @@ DataAccessObject._forDB = function (data) {
|
|||
return data;
|
||||
}
|
||||
var res = {};
|
||||
Object.keys(data).forEach(function (propName) {
|
||||
for(var propName in data) {
|
||||
var type = this.getPropertyType(propName);
|
||||
if (type === 'JSON' || type === 'Any' || type === 'Object' || data[propName] instanceof Array) {
|
||||
res[propName] = JSON.stringify(data[propName]);
|
||||
} else {
|
||||
res[propName] = data[propName];
|
||||
}
|
||||
}.bind(this));
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
|
@ -700,9 +700,9 @@ DataAccessObject.prototype.updateAttributes = function updateAttributes(data, cb
|
|||
}
|
||||
|
||||
// update instance's properties
|
||||
Object.keys(data).forEach(function (key) {
|
||||
for(var key in data) {
|
||||
inst[key] = data[key];
|
||||
});
|
||||
}
|
||||
|
||||
inst.isValid(function (valid) {
|
||||
if (!valid) {
|
||||
|
@ -713,16 +713,16 @@ DataAccessObject.prototype.updateAttributes = function updateAttributes(data, cb
|
|||
inst.trigger('save', function (saveDone) {
|
||||
inst.trigger('update', function (done) {
|
||||
|
||||
Object.keys(data).forEach(function (key) {
|
||||
for(var key in data) {
|
||||
inst[key] = data[key];
|
||||
});
|
||||
}
|
||||
|
||||
inst._adapter().updateAttributes(model, getIdValue(inst.constructor, inst), inst.constructor._forDB(data), function (err) {
|
||||
if (!err) {
|
||||
// update $was attrs
|
||||
Object.keys(data).forEach(function (key) {
|
||||
for(var key in data) {
|
||||
inst.__dataWas[key] = inst.__data[key];
|
||||
});
|
||||
};
|
||||
}
|
||||
done.call(inst, function () {
|
||||
saveDone.call(inst, function () {
|
||||
|
|
|
@ -217,18 +217,30 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
|
|||
|
||||
subclassProperties = subclassProperties || {};
|
||||
subclassSettings = subclassSettings || {};
|
||||
|
||||
|
||||
// Check if subclass redefines the ids
|
||||
var idFound = false;
|
||||
for(var k in subclassProperties) {
|
||||
if(subclassProperties[k].id) {
|
||||
idFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Merging the properties
|
||||
Object.keys(properties).forEach(function (key) {
|
||||
// don't inherit the id property
|
||||
if(key !== 'id' && typeof subclassProperties[key] === 'undefined') {
|
||||
if(idFound && properties[key].id) {
|
||||
// don't inherit id properties
|
||||
return;
|
||||
}
|
||||
if(subclassProperties[key] === undefined) {
|
||||
subclassProperties[key] = properties[key];
|
||||
}
|
||||
});
|
||||
|
||||
// Merge the settings
|
||||
Object.keys(settings).forEach(function (key) {
|
||||
if(typeof subclassSettings[key] === 'undefined') {
|
||||
if(subclassSettings[key] === undefined) {
|
||||
subclassSettings[key] = settings[key];
|
||||
}
|
||||
});
|
||||
|
@ -376,7 +388,6 @@ ModelBuilder.prototype.extendModel = function (model, props) {
|
|||
var definition = props[propName];
|
||||
t.defineProperty(model, propName, definition);
|
||||
});
|
||||
t.definitions[model].build(true);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ require('./types')(ModelDefinition);
|
|||
ModelDefinition.prototype.tableName = function (connectorType) {
|
||||
var settings = this.settings;
|
||||
if(settings[connectorType]) {
|
||||
return settings[connectorType].table || this.name;
|
||||
return settings[connectorType].table || settings[connectorType].tableName || this.name;
|
||||
} else {
|
||||
return this.name;
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ ModelDefinition.prototype.columnName = function (connectorType, propertyName) {
|
|||
this.build();
|
||||
var property = this.properties[propertyName];
|
||||
if(property && property[connectorType]) {
|
||||
return property[connectorType].columnName || propertyName;
|
||||
return property[connectorType].column || property[connectorType].columnName || propertyName;
|
||||
} else {
|
||||
return propertyName;
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ ModelDefinition.prototype.columnNames = function (connectorType) {
|
|||
var cols = [];
|
||||
for(var p in props) {
|
||||
if(props[p][connectorType]) {
|
||||
cols.push(props[p][connectorType].columnName || p);
|
||||
cols.push(property[connectorType].column || props[p][connectorType].columnName || p);
|
||||
} else {
|
||||
cols.push(p);
|
||||
}
|
||||
|
@ -220,7 +220,7 @@ ModelDefinition.prototype.resolveType = function(type) {
|
|||
}
|
||||
}
|
||||
if (typeof type === 'string') {
|
||||
var schemaType = ModelDefinition.schemaTypes[type.toLowerCase()];
|
||||
var schemaType = ModelDefinition.schemaTypes[type.toLowerCase()] || this.modelBuilder.models[type];
|
||||
if (schemaType) {
|
||||
return schemaType;
|
||||
} else {
|
||||
|
@ -231,16 +231,8 @@ ModelDefinition.prototype.resolveType = function(type) {
|
|||
if (type.type) {
|
||||
return this.resolveType(type.type);
|
||||
} else {
|
||||
if(!this.anonymousTypesCount) {
|
||||
this.anonymousTypesCount = 0;
|
||||
}
|
||||
this.anonymousTypesCount++;
|
||||
return this.modelBuilder.define('AnonymousType' + this.anonymousTypesCount,
|
||||
return this.modelBuilder.define(this.modelBuilder.getSchemaName(null),
|
||||
type, {anonymous: true, idInjection: false});
|
||||
/*
|
||||
console.error(type);
|
||||
throw new Error('Missing type property');
|
||||
*/
|
||||
}
|
||||
} else if('function' === typeof type ) {
|
||||
return type;
|
||||
|
@ -263,25 +255,25 @@ ModelDefinition.prototype.build = function (forceRebuild) {
|
|||
}
|
||||
this.properties = {};
|
||||
for (var p in this.rawProperties) {
|
||||
var type = this.resolveType(this.rawProperties[p]);
|
||||
var prop = this.rawProperties[p];
|
||||
var type = this.resolveType(prop);
|
||||
if (typeof type === 'string') {
|
||||
if (Array.isArray(this.associations)) {
|
||||
this.associations.push({
|
||||
source: this.name,
|
||||
target: type,
|
||||
relation: Array.isArray(this.rawProperties[p]) ? 'hasMany' : 'belongsTo',
|
||||
as: p
|
||||
});
|
||||
// delete this.rawProperties[p];
|
||||
}
|
||||
this.associations.push({
|
||||
source: this.name,
|
||||
target: type,
|
||||
relation: Array.isArray(prop) ? 'hasMany' : 'belongsTo',
|
||||
as: p
|
||||
});
|
||||
} else {
|
||||
var typeDef = {
|
||||
type: type
|
||||
};
|
||||
for (var a in this.rawProperties[p]) {
|
||||
// Skip the type property but don't delete it Model.extend() shares same instances of the properties from the base class
|
||||
if (a !== 'type') {
|
||||
typeDef[a] = this.rawProperties[p][a];
|
||||
if (typeof prop === 'object' && prop !== null) {
|
||||
for (var a in prop) {
|
||||
// Skip the type property but don't delete it Model.extend() shares same instances of the properties from the base class
|
||||
if (a !== 'type') {
|
||||
typeDef[a] = prop[a];
|
||||
}
|
||||
}
|
||||
}
|
||||
this.properties[p] = typeDef;
|
||||
|
@ -302,15 +294,10 @@ ModelDefinition.prototype.defineProperty = function (propertyName, propertyDefin
|
|||
|
||||
|
||||
function isModelClass(cls) {
|
||||
while (true) {
|
||||
if (!cls) {
|
||||
return false;
|
||||
}
|
||||
if (ModelBaseClass === cls) {
|
||||
return true;
|
||||
}
|
||||
cls = cls.prototype;
|
||||
if(!cls) {
|
||||
return false;
|
||||
}
|
||||
return cls.prototype instanceof ModelBaseClass;
|
||||
}
|
||||
|
||||
ModelDefinition.prototype.toJSON = function(forceRebuild) {
|
||||
|
@ -338,7 +325,7 @@ ModelDefinition.prototype.toJSON = function(forceRebuild) {
|
|||
if('function' === typeof val) {
|
||||
if(isModelClass(val)) {
|
||||
if(val.settings && val.settings.anonymous) {
|
||||
return val.definition && val.definition.toJSON();
|
||||
return val.definition && val.definition.toJSON().properties;
|
||||
} else {
|
||||
return val.modelName;
|
||||
}
|
||||
|
|
20
lib/model.js
20
lib/model.js
|
@ -99,20 +99,20 @@ ModelBaseClass.prototype._initProperties = function (data, applySetters) {
|
|||
}
|
||||
|
||||
if (applySetters === true) {
|
||||
Object.keys(data).forEach(function (propertyName) {
|
||||
for(var propertyName in data) {
|
||||
if((propertyName in properties) || (propertyName in ctor.relations)) {
|
||||
self[propertyName] = self.__data[propertyName] || data[propertyName];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Set the unknown properties as properties to the object
|
||||
if(strict === false) {
|
||||
Object.keys(data).forEach(function (propertyName) {
|
||||
for(var propertyName in data) {
|
||||
if(!(propertyName in properties)) {
|
||||
self[propertyName] = self.__data[propertyName] || data[propertyName];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ctor.forEachProperty(function (propertyName) {
|
||||
|
@ -224,7 +224,7 @@ ModelBaseClass.prototype.toObject = function (onlySchema) {
|
|||
});
|
||||
|
||||
if (schemaLess) {
|
||||
Object.keys(self.__data).forEach(function (propertyName) {
|
||||
for(var propertyName in self.__data) {
|
||||
if (!data.hasOwnProperty(propertyName)) {
|
||||
var val = self.hasOwnProperty(propertyName) ? self[propertyName] : self.__data[propertyName];
|
||||
if(val !== undefined && val!== null && val.toObject) {
|
||||
|
@ -233,7 +233,7 @@ ModelBaseClass.prototype.toObject = function (onlySchema) {
|
|||
data[propertyName] = val;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
@ -248,9 +248,9 @@ ModelBaseClass.prototype.toJSON = function () {
|
|||
};
|
||||
|
||||
ModelBaseClass.prototype.fromObject = function (obj) {
|
||||
Object.keys(obj).forEach(function (key) {
|
||||
for(var key in obj) {
|
||||
this[key] = obj[key];
|
||||
}.bind(this));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -271,14 +271,14 @@ ModelBaseClass.prototype.propertyChanged = function propertyChanged(propertyName
|
|||
*/
|
||||
ModelBaseClass.prototype.reset = function () {
|
||||
var obj = this;
|
||||
Object.keys(obj).forEach(function (k) {
|
||||
for(var k in obj) {
|
||||
if (k !== 'id' && !obj.constructor.dataSource.definitions[obj.constructor.modelName].properties[k]) {
|
||||
delete obj[k];
|
||||
}
|
||||
if (obj.propertyChanged(k)) {
|
||||
obj[k] = obj[k + '$was'];
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
ModelBaseClass.prototype.inspect = function () {
|
||||
|
|
|
@ -429,5 +429,40 @@ describe('Load models from json', function () {
|
|||
assert(new m());
|
||||
}
|
||||
});
|
||||
|
||||
it('should be able to extend models', function (done) {
|
||||
var modelBuilder = new ModelBuilder();
|
||||
|
||||
var User = modelBuilder.define('User', {
|
||||
name: String,
|
||||
bio: ModelBuilder.Text,
|
||||
approved: Boolean,
|
||||
joinedAt: Date,
|
||||
age: Number
|
||||
});
|
||||
|
||||
var Customer = User.extend('Customer', {customerId: {type: String, id: true}});
|
||||
|
||||
var customer = new Customer({name: 'Joe', age: 20, customerId: 'c01'});
|
||||
|
||||
customer.should.be.a('object').and.have.property('name', 'Joe');
|
||||
customer.should.have.property('name', 'Joe');
|
||||
customer.should.have.property('age', 20);
|
||||
customer.should.have.property('customerId', 'c01');
|
||||
customer.should.not.have.property('bio');
|
||||
|
||||
// The properties are defined at prototype level
|
||||
assert.equal(Object.keys(customer).length, 0);
|
||||
var count = 0;
|
||||
for(var p in customer) {
|
||||
if(typeof customer[p] !== 'function') {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
assert.equal(count, 7); // Please note there is an injected id from User prototype
|
||||
assert.equal(Object.keys(customer.toObject()).length, 6);
|
||||
|
||||
done(null, customer);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -14,15 +14,55 @@ describe('ModelDefinition class', function () {
|
|||
var modelBuilder = new ModelBuilder();
|
||||
|
||||
var User = new ModelDefinition(modelBuilder, 'User', {
|
||||
name: String,
|
||||
name: "string",
|
||||
bio: ModelBuilder.Text,
|
||||
approved: Boolean,
|
||||
joinedAt: Date,
|
||||
age: Number
|
||||
age: "number"
|
||||
});
|
||||
|
||||
// console.log(User.toJSON());
|
||||
User.build();
|
||||
assert.equal(User.properties.name.type, String);
|
||||
assert.equal(User.properties.bio.type, ModelBuilder.Text);
|
||||
assert.equal(User.properties.approved.type, Boolean);
|
||||
assert.equal(User.properties.joinedAt.type, Date);
|
||||
assert.equal(User.properties.age.type, Number);
|
||||
|
||||
|
||||
var json = User.toJSON();
|
||||
assert.equal(json.name, "User");
|
||||
assert.equal(json.properties.name.type, "String");
|
||||
assert.equal(json.properties.bio.type, "Text");
|
||||
assert.equal(json.properties.approved.type, "Boolean");
|
||||
assert.equal(json.properties.joinedAt.type, "Date");
|
||||
assert.equal(json.properties.age.type, "Number");
|
||||
|
||||
done();
|
||||
|
||||
|
||||
});
|
||||
|
||||
it('should be able to define additional properties', function (done) {
|
||||
var modelBuilder = new ModelBuilder();
|
||||
|
||||
var User = new ModelDefinition(modelBuilder, 'User', {
|
||||
name: "string",
|
||||
bio: ModelBuilder.Text,
|
||||
approved: Boolean,
|
||||
joinedAt: Date,
|
||||
age: "number"
|
||||
});
|
||||
|
||||
User.build();
|
||||
|
||||
User.defineProperty("id", {type: "number", id: true});
|
||||
assert.equal(User.properties.name.type, String);
|
||||
assert.equal(User.properties.bio.type, ModelBuilder.Text);
|
||||
assert.equal(User.properties.approved.type, Boolean);
|
||||
assert.equal(User.properties.joinedAt.type, Date);
|
||||
assert.equal(User.properties.age.type, Number);
|
||||
|
||||
assert.equal(User.properties.id.type, Number);
|
||||
done();
|
||||
|
||||
|
||||
|
@ -46,11 +86,29 @@ describe('ModelDefinition class', function () {
|
|||
}
|
||||
});
|
||||
|
||||
// console.log(JSON.stringify(User.toJSON(), null, ' '));
|
||||
User.build();
|
||||
assert.equal(User.properties.name.type, String);
|
||||
assert.equal(User.properties.bio.type, ModelBuilder.Text);
|
||||
assert.equal(User.properties.approved.type, Boolean);
|
||||
assert.equal(User.properties.joinedAt.type, Date);
|
||||
assert.equal(User.properties.age.type, Number);
|
||||
assert.equal(typeof User.properties.address.type, 'function');
|
||||
|
||||
var json = User.toJSON();
|
||||
assert.equal(json.name, "User");
|
||||
assert.equal(json.properties.name.type, "String");
|
||||
assert.equal(json.properties.bio.type, "Text");
|
||||
assert.equal(json.properties.approved.type, "Boolean");
|
||||
assert.equal(json.properties.joinedAt.type, "Date");
|
||||
assert.equal(json.properties.age.type, "Number");
|
||||
|
||||
assert.deepEqual(json.properties.address.type, { street: { type: 'String' },
|
||||
city: { type: 'String' },
|
||||
zipCode: { type: 'String' },
|
||||
state: { type: 'String' } });
|
||||
|
||||
done();
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
@ -73,11 +131,102 @@ describe('ModelDefinition class', function () {
|
|||
|
||||
});
|
||||
|
||||
// console.log(JSON.stringify(User.toJSON(), null, ' '));
|
||||
User.build();
|
||||
assert.equal(User.properties.name.type, String);
|
||||
assert.equal(User.properties.bio.type, ModelBuilder.Text);
|
||||
assert.equal(User.properties.approved.type, Boolean);
|
||||
assert.equal(User.properties.joinedAt.type, Date);
|
||||
assert.equal(User.properties.age.type, Number);
|
||||
assert.equal(User.properties.address.type, Address);
|
||||
|
||||
|
||||
var json = User.toJSON();
|
||||
assert.equal(json.name, "User");
|
||||
assert.equal(json.properties.name.type, "String");
|
||||
assert.equal(json.properties.bio.type, "Text");
|
||||
assert.equal(json.properties.approved.type, "Boolean");
|
||||
assert.equal(json.properties.joinedAt.type, "Date");
|
||||
assert.equal(json.properties.age.type, "Number");
|
||||
|
||||
assert.equal(json.properties.address.type, 'Address');
|
||||
|
||||
done();
|
||||
|
||||
});
|
||||
|
||||
it('should be able to define referencing models by name', function (done) {
|
||||
var modelBuilder = new ModelBuilder();
|
||||
|
||||
var Address = modelBuilder.define('Address', {
|
||||
street: String,
|
||||
city: String,
|
||||
zipCode: String,
|
||||
state: String
|
||||
});
|
||||
var User = new ModelDefinition(modelBuilder, 'User', {
|
||||
name: String,
|
||||
bio: ModelBuilder.Text,
|
||||
approved: Boolean,
|
||||
joinedAt: Date,
|
||||
age: Number,
|
||||
address: 'Address'
|
||||
|
||||
});
|
||||
|
||||
User.build();
|
||||
assert.equal(User.properties.name.type, String);
|
||||
assert.equal(User.properties.bio.type, ModelBuilder.Text);
|
||||
assert.equal(User.properties.approved.type, Boolean);
|
||||
assert.equal(User.properties.joinedAt.type, Date);
|
||||
assert.equal(User.properties.age.type, Number);
|
||||
assert.equal(User.properties.address.type, Address);
|
||||
|
||||
|
||||
var json = User.toJSON();
|
||||
assert.equal(json.name, "User");
|
||||
assert.equal(json.properties.name.type, "String");
|
||||
assert.equal(json.properties.bio.type, "Text");
|
||||
assert.equal(json.properties.approved.type, "Boolean");
|
||||
assert.equal(json.properties.joinedAt.type, "Date");
|
||||
assert.equal(json.properties.age.type, "Number");
|
||||
|
||||
assert.equal(json.properties.address.type, 'Address');
|
||||
|
||||
done();
|
||||
|
||||
});
|
||||
|
||||
it('should report correct id names', function (done) {
|
||||
var modelBuilder = new ModelBuilder();
|
||||
|
||||
var User = new ModelDefinition(modelBuilder, 'User', {
|
||||
userId: {type: String, id: true},
|
||||
name: "string",
|
||||
bio: ModelBuilder.Text,
|
||||
approved: Boolean,
|
||||
joinedAt: Date,
|
||||
age: "number"
|
||||
});
|
||||
|
||||
assert.equal(User.idName(), 'userId');
|
||||
assert.deepEqual(User.idNames(), ['userId']);
|
||||
done();
|
||||
});
|
||||
|
||||
it('should report correct table/column names', function (done) {
|
||||
var modelBuilder = new ModelBuilder();
|
||||
|
||||
var User = new ModelDefinition(modelBuilder, 'User', {
|
||||
userId: {type: String, id: true, oracle: {column: 'ID'}},
|
||||
name: "string"
|
||||
}, {oracle: {table: 'USER'}});
|
||||
|
||||
assert.equal(User.tableName('oracle'), 'USER');
|
||||
assert.equal(User.tableName('mysql'), 'User');
|
||||
assert.equal(User.columnName('oracle', 'userId'), 'ID');
|
||||
assert.equal(User.columnName('mysql', 'userId'), 'userId');
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue