Merge pull request #20 from strongloop/juggler-tidyup

Add more tests and fix toJSON
This commit is contained in:
Raymond Feng 2013-10-07 14:55:09 -07:00
commit 5518039d84
7 changed files with 249 additions and 67 deletions

View File

@ -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;
};

View File

@ -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 () {

View File

@ -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);
};

View File

@ -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;
}

View File

@ -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 () {

View File

@ -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);
});
});

View File

@ -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();
});
});