Clean up the options for model constructor

This commit is contained in:
Raymond Feng 2014-02-10 22:38:59 -08:00
parent 1961fbeefe
commit 85232f31b3
4 changed files with 79 additions and 47 deletions

View File

@ -221,7 +221,7 @@ DataAccessObject.upsert = DataAccessObject.updateOrCreate = function upsert(data
this.getDataSource().connector.updateOrCreate(Model.modelName, inst.toObject(true), function (err, data) { this.getDataSource().connector.updateOrCreate(Model.modelName, inst.toObject(true), function (err, data) {
var obj; var obj;
if (data) { if (data) {
inst._initProperties(data, false); inst._initProperties(data);
obj = inst; obj = inst;
} else { } else {
obj = null; obj = null;
@ -318,7 +318,7 @@ DataAccessObject.findById = function find(id, cb) {
setIdValue(this, data, id); setIdValue(this, data, id);
} }
obj = new this(); obj = new this();
obj._initProperties(data, false); obj._initProperties(data);
} }
cb(err, obj); cb(err, obj);
}.bind(this)); }.bind(this));
@ -540,7 +540,7 @@ DataAccessObject.find = function find(params, cb) {
data.forEach(function (d, i) { data.forEach(function (d, i) {
var obj = new constr(); var obj = new constr();
obj._initProperties(d, false, params.fields); obj._initProperties(d, {fields: params.fields});
if (params && params.include) { if (params && params.include) {
if (params.collect) { if (params.collect) {
@ -756,7 +756,7 @@ DataAccessObject.prototype.save = function (options, callback) {
if (err) { if (err) {
return callback(err, inst); return callback(err, inst);
} }
inst._initProperties(data, false); inst._initProperties(data);
updateDone.call(inst, function () { updateDone.call(inst, function () {
saveDone.call(inst, function () { saveDone.call(inst, function () {
callback(err, inst); callback(err, inst);

View File

@ -431,11 +431,12 @@ DataSource.prototype.setupDataAccess = function (modelClass, settings) {
if (this.connector) { if (this.connector) {
// Check if the id property should be generated // Check if the id property should be generated
var idName = modelClass.definition.idName(); var idName = modelClass.definition.idName();
var idProp = modelClass.definition.properties[idName]; var idProp = modelClass.definition.rawProperties[idName];
if(idProp && idProp.generated && this.connector.getDefaultIdType) { if(idProp && idProp.generated && this.connector.getDefaultIdType) {
// Set the default id type from connector's ability // Set the default id type from connector's ability
var idType = this.connector.getDefaultIdType() || String; var idType = this.connector.getDefaultIdType() || String;
idProp.type = idType; idProp.type = idType;
modelClass.definition.properties[idName].type = idType;
} }
if (this.connector.define) { if (this.connector.define) {
// pass control to connector // pass control to connector

View File

@ -156,17 +156,14 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
// TODO: [rfeng] We need to decide what names to use for built-in models such as User. // TODO: [rfeng] We need to decide what names to use for built-in models such as User.
if (!ModelClass || !ModelClass.settings.unresolved) { if (!ModelClass || !ModelClass.settings.unresolved) {
// every class can receive hash of data as optional param // every class can receive hash of data as optional param
ModelClass = function ModelConstructor(data, dataSource) { ModelClass = function ModelConstructor(data, options) {
if (!(this instanceof ModelConstructor)) { if (!(this instanceof ModelConstructor)) {
return new ModelConstructor(data, dataSource); return new ModelConstructor(data, options);
} }
if (ModelClass.settings.unresolved) { if (ModelClass.settings.unresolved) {
throw new Error('Model ' + ModelClass.modelName + ' is not defined.'); throw new Error('Model ' + ModelClass.modelName + ' is not defined.');
} }
ModelBaseClass.apply(this, arguments); ModelBaseClass.apply(this, arguments);
if (dataSource) {
hiddenProperty(this, '__dataSource', dataSource);
}
}; };
// mix in EventEmitter (don't inherit from) // mix in EventEmitter (don't inherit from)
var events = new EventEmitter(); var events = new EventEmitter();
@ -343,16 +340,6 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
if (!DataType) { if (!DataType) {
throw new Error('Invalid type for property ' + propertyName); throw new Error('Invalid type for property ' + propertyName);
} }
if (Array.isArray(DataType) || DataType === Array) {
DataType = List;
} else if (DataType.name === 'Date') {
var OrigDate = Date;
DataType = function Date(arg) {
return new OrigDate(arg);
};
} else if (typeof DataType === 'string') {
DataType = modelBuilder.resolveType(DataType);
}
if (prop.required) { if (prop.required) {
var requiredOptions = typeof prop.required === 'object' ? prop.required : undefined; var requiredOptions = typeof prop.required === 'object' ? prop.required : undefined;
@ -368,6 +355,17 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
} }
}, },
set: function (value) { set: function (value) {
var DataType = ModelClass.definition.properties[propertyName].type;
if (Array.isArray(DataType) || DataType === Array) {
DataType = List;
} else if (DataType.name === 'Date') {
var OrigDate = Date;
DataType = function Date(arg) {
return new OrigDate(arg);
};
} else if (typeof DataType === 'string') {
DataType = modelBuilder.resolveType(DataType);
}
if (ModelClass.setter[propertyName]) { if (ModelClass.setter[propertyName]) {
ModelClass.setter[propertyName].call(this, value); // Try setter first ModelClass.setter[propertyName].call(this, value); // Try setter first
} else { } else {

View File

@ -27,8 +27,13 @@ var BASE_TYPES = ['String', 'Boolean', 'Number', 'Date', 'Text'];
* @constructor * @constructor
* @param {Object} data - initial object data * @param {Object} data - initial object data
*/ */
function ModelBaseClass(data) { function ModelBaseClass(data, options) {
this._initProperties(data, true); options = options || {};
if(!('applySetters' in options)) {
// Default to true
options.applySetters = true;
}
this._initProperties(data, options);
} }
// FIXME: [rfeng] We need to make sure the input data should not be mutated. Disabled cloning for now to get tests passing // FIXME: [rfeng] We need to make sure the input data should not be mutated. Disabled cloning for now to get tests passing
@ -42,19 +47,29 @@ function clone(data) {
*/ */
return data; return data;
} }
/** /**
* Initialize properties * Initialize the model instance with a list of properties
* @param data * @param {Object} data The data object
* @param applySetters * @param {Object} options An object to control the instantiation
* @property {Boolean} applySetters Controls if the setters will be applied
* @property {Boolean} strict Set the instance level strict mode
* @private * @private
*/ */
ModelBaseClass.prototype._initProperties = function (data, applySetters) { ModelBaseClass.prototype._initProperties = function (data, options) {
var self = this; var self = this;
var ctor = this.constructor; var ctor = this.constructor;
var properties = ctor.definition.build(); var properties = ctor.definition.build();
data = data || {}; data = data || {};
options = options || {};
var applySetters = options.applySetters;
var strict = options.strict;
if(strict === undefined) {
strict = ctor.definition.settings.strict;
}
Object.defineProperty(this, '__cachedRelations', { Object.defineProperty(this, '__cachedRelations', {
writable: true, writable: true,
enumerable: false, enumerable: false,
@ -76,15 +91,32 @@ ModelBaseClass.prototype._initProperties = function (data, applySetters) {
value: {} value: {}
}); });
/**
* Instance level data source
*/
Object.defineProperty(this, '__dataSource', {
writable: true,
enumerable: false,
configurable: true,
value: options.dataSource
});
/**
* Instance level strict mode
*/
Object.defineProperty(this, '__strict', {
writable: true,
enumerable: false,
configurable: true,
value: strict
});
if (data.__cachedRelations) { if (data.__cachedRelations) {
this.__cachedRelations = data.__cachedRelations; this.__cachedRelations = data.__cachedRelations;
} }
// Check if the strict option is set to false for the model
var strict = ctor.definition.settings.strict;
for (var i in data) { for (var i in data) {
if (i in properties) { if (i in properties && typeof data[i] !== 'function') {
this.__data[i] = this.__dataWas[i] = clone(data[i]); this.__data[i] = this.__dataWas[i] = clone(data[i]);
} else if (i in ctor.relations) { } else if (i in ctor.relations) {
this.__data[ctor.relations[i].keyFrom] = this.__dataWas[i] = data[i][ctor.relations[i].keyTo]; this.__data[ctor.relations[i].keyFrom] = this.__dataWas[i] = data[i][ctor.relations[i].keyTo];
@ -100,7 +132,7 @@ ModelBaseClass.prototype._initProperties = function (data, applySetters) {
if (applySetters === true) { if (applySetters === true) {
for (var propertyName in data) { for (var propertyName in data) {
if ((propertyName in properties) || (propertyName in ctor.relations)) { if (typeof data[propertyName] !== 'function' && ((propertyName in properties) || (propertyName in ctor.relations))) {
self[propertyName] = self.__data[propertyName] || data[propertyName]; self[propertyName] = self.__data[propertyName] || data[propertyName];
} }
} }
@ -109,7 +141,7 @@ ModelBaseClass.prototype._initProperties = function (data, applySetters) {
// Set the unknown properties as properties to the object // Set the unknown properties as properties to the object
if (strict === false) { if (strict === false) {
for (var propertyName in data) { for (var propertyName in data) {
if (!(propertyName in properties)) { if (typeof data[propertyName] !== 'function' && !(propertyName in properties)) {
self[propertyName] = self.__data[propertyName] || data[propertyName]; self[propertyName] = self.__data[propertyName] || data[propertyName];
} }
} }
@ -117,7 +149,7 @@ ModelBaseClass.prototype._initProperties = function (data, applySetters) {
ctor.forEachProperty(function (propertyName) { ctor.forEachProperty(function (propertyName) {
if ('undefined' === typeof self.__data[propertyName]) { if (undefined === self.__data[propertyName]) {
self.__data[propertyName] = self.__dataWas[propertyName] = getDefault(propertyName); self.__data[propertyName] = self.__dataWas[propertyName] = getDefault(propertyName);
} else { } else {
self.__dataWas[propertyName] = self.__data[propertyName]; self.__dataWas[propertyName] = self.__data[propertyName];
@ -160,7 +192,7 @@ ModelBaseClass.prototype._initProperties = function (data, applySetters) {
} }
this.trigger('initialize'); this.trigger('initialize');
} };
/** /**
* @param {String} prop - property name * @param {String} prop - property name
@ -197,11 +229,11 @@ ModelBaseClass.toString = function () {
}; };
/** /**
* Convert instance to Object * Convert model instance to a plain JSON object
* *
* @param {Boolean} onlySchema - restrict properties to dataSource only, default true * @param {Boolean} onlySchema - restrict properties to dataSource only,
* when onlySchema == true, only properties defined in dataSource returned, * default to false. When onlySchema is true, only properties defined in
* otherwise all enumerable properties returned * the schema are returned, otherwise all enumerable properties returned
* @returns {Object} - canonical object representation (no getters and setters) * @returns {Object} - canonical object representation (no getters and setters)
*/ */
ModelBaseClass.prototype.toObject = function (onlySchema) { ModelBaseClass.prototype.toObject = function (onlySchema) {
@ -211,8 +243,9 @@ ModelBaseClass.prototype.toObject = function (onlySchema) {
var data = {}; var data = {};
var self = this; var self = this;
var strict = this.constructor.definition.settings.strict; var strict = this.__strict;
var schemaLess = (strict === false) || !onlySchema; var schemaLess = (strict === false) || !onlySchema;
this.constructor.forEachProperty(function (propertyName) { this.constructor.forEachProperty(function (propertyName) {
if (self[propertyName] instanceof List) { if (self[propertyName] instanceof List) {
data[propertyName] = self[propertyName].toObject(!schemaLess); data[propertyName] = self[propertyName].toObject(!schemaLess);
@ -257,13 +290,8 @@ ModelBaseClass.prototype.toObject = function (onlySchema) {
return data; return data;
}; };
// ModelBaseClass.prototype.hasOwnProperty = function (prop) {
// return this.__data && this.__data.hasOwnProperty(prop) ||
// Object.getOwnPropertyNames(this).indexOf(prop) !== -1;
// };
ModelBaseClass.prototype.toJSON = function () { ModelBaseClass.prototype.toJSON = function () {
return this.toObject(); return this.toObject(false);
}; };
ModelBaseClass.prototype.fromObject = function (obj) { ModelBaseClass.prototype.fromObject = function (obj) {
@ -310,10 +338,15 @@ ModelBaseClass.mixin = function (anotherClass, options) {
ModelBaseClass.prototype.getDataSource = function () { ModelBaseClass.prototype.getDataSource = function () {
return this.__dataSource || this.constructor.dataSource; return this.__dataSource || this.constructor.dataSource;
} };
ModelBaseClass.getDataSource = function () { ModelBaseClass.getDataSource = function () {
return this.dataSource; return this.dataSource;
} };
ModelBaseClass.prototype.setStrict = function (strict) {
this.__strict = strict;
};
jutil.mixin(ModelBaseClass, Hookable); jutil.mixin(ModelBaseClass, Hookable);
jutil.mixin(ModelBaseClass, validations.Validatable); jutil.mixin(ModelBaseClass, validations.Validatable);