From 7aa2eefec43155ca2d171752599ac5937a71ff1b Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Fri, 8 Nov 2013 17:13:00 -0800 Subject: [PATCH] Remove inheritence from DataSource to ModelBuilder --- lib/datasource.js | 98 ++++++++++++++++++++++------------------ lib/model-builder.js | 8 ++++ lib/relations.js | 10 ++-- test/json.test.js | 3 +- test/loopback-dl.test.js | 6 +-- 5 files changed, 73 insertions(+), 52 deletions(-) diff --git a/lib/datasource.js b/lib/datasource.js index 254fca15..a2749adf 100644 --- a/lib/datasource.js +++ b/lib/datasource.js @@ -53,7 +53,7 @@ var slice = Array.prototype.slice; * }); * ``` */ -function DataSource(name, settings) { +function DataSource(name, settings, modelBuilder) { if (!(this instanceof DataSource)) { return new DataSource(name, settings); } @@ -74,7 +74,9 @@ function DataSource(name, settings) { settings = utils.parseSettings(settings); } - ModelBuilder.call(this, name, settings); + this.modelBuilder = modelBuilder || new ModelBuilder(); + this.models = this.modelBuilder.models; + this.definitions = this.modelBuilder.definitions; // operation metadata // Initialize it before calling setup as the connector might register operations @@ -113,8 +115,7 @@ function DataSource(name, settings) { var fn = this.DataAccessObject.prototype[name]; if(typeof fn === 'function') { - var returns = fn.returns; - + this.defineOperation(name, { prototype: true, accepts: fn.accepts, @@ -128,17 +129,12 @@ function DataSource(name, settings) { }.bind(this)); } -util.inherits(DataSource, ModelBuilder); +util.inherits(DataSource, EventEmitter); // allow child classes to supply a data access object DataSource.DataAccessObject = DataAccessObject; -// Copy over statics -for (var m in ModelBuilder) { - if (!DataSource.hasOwnProperty(m) && 'super_' !== m) { - DataSource[m] = ModelBuilder[m]; - } -} + /** * Set up the connector instance for backward compatibility with JugglingDB schema/adapter @@ -322,7 +318,7 @@ DataSource.prototype.defineRelations = function(modelClass, relations) { // Create a function for the closure in the loop var createListener = function (name, relation, targetModel, throughModel) { if (targetModel && targetModel.settings.unresolved) { - targetModel.once('defined', function (model) { + targetModel.once('dataSourceAttached', function (model) { // Check if the through model doesn't exist or resolved if (!throughModel || !throughModel.settings.unresolved) { // The target model is resolved @@ -340,7 +336,7 @@ DataSource.prototype.defineRelations = function(modelClass, relations) { } if (throughModel && throughModel.settings.unresolved) { // Set up a listener to the through model - throughModel.once('defined', function (model) { + throughModel.once('dataSourceAttached', function (model) { if (!targetModel.settings.unresolved) { // The target model is resolved var params = { @@ -360,14 +356,14 @@ DataSource.prototype.defineRelations = function(modelClass, relations) { for (var rn in relations) { var r = relations[rn]; assert(DataSource.relationTypes.indexOf(r.type) !== -1, "Invalid relation type: " + r.type); - var targetModel = isModelClass(r.model) ? r.model : this.models[r.model]; + var targetModel = isModelClass(r.model) ? r.model : this.getModel(r.model); if (!targetModel) { // The target model doesn't exist, let create a place holder for it targetModel = this.define(r.model, {}, {unresolved: true}); } var throughModel = null; if (r.through) { - throughModel = isModelClass(r.through) ? r.through : this.models[r.through]; + throughModel = isModelClass(r.through) ? r.through : this.getModel(r.through); if (!throughModel) { // The through model doesn't exist, let create a place holder for it throughModel = this.define(r.through, {}, {unresolved: true}); @@ -475,11 +471,15 @@ DataSource.prototype.createModel = DataSource.prototype.define = function define } } - var modelClass = ModelBuilder.prototype.define.call(this, className, properties, settings); + var modelClass = this.modelBuilder.define(className, properties, settings); + modelClass.dataSource = this; if(settings.unresolved) { return modelClass; } + + modelClass.emit('dataSourceAttached', modelClass); + this.setupDataAccess(modelClass, settings); return modelClass; }; @@ -526,6 +526,14 @@ DataSource.prototype.mixin = function (ModelCtor) { }); }; +DataSource.prototype.getModel = function(name) { + return this.modelBuilder.getModel(name); +}; + +DataSource.prototype.getModelDefinition = function(name) { + return this.modelBuilder.getModelDefinition(name); +}; + /** * Attach an existing model to a data source. * @@ -538,16 +546,19 @@ DataSource.prototype.attach = function (modelClass) { return; } var className = modelClass.modelName; - var properties = modelClass.dataSource.definitions[className].properties; - var settings = modelClass.dataSource.definitions[className].settings; + var modelDef = modelClass.dataSource.getModelDefinition(className); + var properties = modelDef.properties; + var settings = modelDef.settings; // redefine the dataSource modelClass.dataSource = this; // add to def - var def = new ModelDefinition(this, className, properties, settings); + var def = new ModelDefinition(this.modelBuilder, className, properties, settings); def.build(); - this.definitions[className] = def; - this.models[className] = modelClass; + this.modelBuilder.definitions[className] = def; + this.modelBuilder.models[className] = modelClass; + + modelClass.emit('dataSourceAttached', modelClass); this.setupDataAccess(modelClass, settings); @@ -562,9 +573,9 @@ DataSource.prototype.attach = function (modelClass) { * @param {Object} params - property settings */ DataSource.prototype.defineProperty = function (model, prop, params) { - ModelBuilder.prototype.defineProperty.call(this, model, prop, params); + this.modelBuilder.defineProperty(model, prop, params); - var resolvedProp = this.definitions[model].properties[prop]; + var resolvedProp = this.getModelDefinition(model).properties[prop]; if (this.connector.defineProperty) { this.connector.defineProperty(model, prop, resolvedProp); } @@ -1225,7 +1236,7 @@ DataSource.prototype.discoverAndBuildModels = function (modelName, options, cb) schemaList.push(schema); } - var models = self.buildModels(schemaList); + var models = self.modelBuilder.buildModels(schemaList); cb && cb(err, models); }); }; @@ -1252,7 +1263,7 @@ DataSource.prototype.discoverAndBuildModelsSync = function (modelName, options) schemaList.push(schema); } - var models = this.buildModels(schemaList); + var models = this.modelBuilder.buildModels(schemaList); return models; }; @@ -1305,7 +1316,7 @@ DataSource.prototype.freeze = function freeze() { * @param {String} modelName The model name */ DataSource.prototype.tableName = function (modelName) { - return this.definitions[modelName].tableName(this.connector.name); + return this.getModelDefinition(modelName).tableName(this.connector.name); }; /** @@ -1315,7 +1326,7 @@ DataSource.prototype.tableName = function (modelName) { * @returns {String} columnName */ DataSource.prototype.columnName = function (modelName, propertyName) { - return this.definitions[modelName].columnName(this.connector.name, propertyName); + return this.getModelDefinition(modelName).columnName(this.connector.name, propertyName); }; /** @@ -1325,7 +1336,7 @@ DataSource.prototype.columnName = function (modelName, propertyName) { * @returns {Object} column metadata */ DataSource.prototype.columnMetadata = function (modelName, propertyName) { - return this.definitions[modelName].columnMetadata(this.connector.name, propertyName); + return this.getModelDefinition(modelName).columnMetadata(this.connector.name, propertyName); }; /** @@ -1334,7 +1345,7 @@ DataSource.prototype.columnMetadata = function (modelName, propertyName) { * @returns {String[]} column names */ DataSource.prototype.columnNames = function (modelName) { - return this.definitions[modelName].columnNames(this.connector.name); + return this.getModelDefinition(modelName).columnNames(this.connector.name); }; /** @@ -1343,7 +1354,7 @@ DataSource.prototype.columnNames = function (modelName) { * @returns {String} columnName for ID */ DataSource.prototype.idColumnName = function(modelName) { - return this.definitions[modelName].idColumnName(this.connector.name); + return this.getModelDefinition(modelName).idColumnName(this.connector.name); }; /** @@ -1352,10 +1363,10 @@ DataSource.prototype.idColumnName = function(modelName) { * @returns {String} property name for ID */ DataSource.prototype.idName = function(modelName) { - if(!this.definitions[modelName].idName) { - console.log(this.definitions[modelName]); + if(!this.getModelDefinition(modelName).idName) { + console.error('No id name', this.getModelDefinition(modelName)); } - return this.definitions[modelName].idName(); + return this.getModelDefinition(modelName).idName(); }; /** @@ -1364,7 +1375,7 @@ DataSource.prototype.idName = function(modelName) { * @returns {String[]} property names for IDs */ DataSource.prototype.idNames = function (modelName) { - return this.definitions[modelName].idNames(); + return this.getModelDefinition(modelName).idNames(); }; @@ -1376,7 +1387,7 @@ DataSource.prototype.idNames = function (modelName) { */ DataSource.prototype.defineForeignKey = function defineForeignKey(className, key, foreignClassName) { // quit if key already defined - if (this.definitions[className].rawProperties[key]) return; + if (this.getModelDefinition(className).rawProperties[key]) return; if (this.connector.defineForeignKey) { var cb = function (err, keyType) { @@ -1428,7 +1439,7 @@ DataSource.prototype.disconnect = function disconnect(cb) { DataSource.prototype.copyModel = function copyModel(Master) { var dataSource = this; var className = Master.modelName; - var md = Master.dataSource.definitions[className]; + var md = Master.dataSource.getModelDefinition(className); var Slave = function SlaveModel() { Master.apply(this, [].slice.call(arguments)); }; @@ -1442,11 +1453,11 @@ DataSource.prototype.copyModel = function copyModel(Master) { hiddenProperty(Slave, 'modelName', className); hiddenProperty(Slave, 'relations', Master.relations); - if (!(className in dataSource.models)) { + if (!(className in dataSource.modelBuilder.models)) { // store class in model pool - dataSource.models[className] = Slave; - dataSource.definitions[className] = new ModelDefinition(dataSource, md.name, md.properties, md.settings); + dataSource.modelBuilder.models[className] = Slave; + dataSource.modelBuilder.definitions[className] = new ModelDefinition(dataSource.modelBuilder, md.name, md.properties, md.settings); if ((!dataSource.isTransaction) && dataSource.connector && dataSource.connector.define) { dataSource.connector.define({ @@ -1483,11 +1494,12 @@ DataSource.prototype.transaction = function() { transaction.connector = dataSource.connector.transaction(); // create blank models pool - transaction.models = {}; - transaction.definitions = {}; + transaction.modelBuilder = new ModelBuilder(); + transaction.models = transaction.modelBuilder.models; + transaction.definitions = transaction.modelBuilder.definitions; - for (var i in dataSource.models) { - dataSource.copyModel.call(transaction, dataSource.models[i]); + for (var i in dataSource.modelBuilder.models) { + dataSource.copyModel.call(transaction, dataSource.modelBuilder.models[i]); } transaction.exec = function(cb) { diff --git a/lib/model-builder.js b/lib/model-builder.js index a0914db5..9ab831ad 100644 --- a/lib/model-builder.js +++ b/lib/model-builder.js @@ -55,6 +55,14 @@ function isModelClass(cls) { return cls.prototype instanceof DefaultModelBaseClass; } +ModelBuilder.prototype.getModel = function(name) { + return this.models[name]; +}; + +ModelBuilder.prototype.getModelDefinition = function(name) { + return this.definitions[name]; +}; + /** * Define a model class * diff --git a/lib/relations.js b/lib/relations.js index c437468f..e5b8b317 100644 --- a/lib/relations.js +++ b/lib/relations.js @@ -34,9 +34,9 @@ Relation.hasMany = function hasMany(anotherClass, params) { anotherClass = params.model; } else { var anotherClassName = i8n.singularize(anotherClass).toLowerCase(); - for(var name in this.dataSource.models) { + for(var name in this.dataSource.modelBuilder.models) { if (name.toLowerCase() === anotherClassName) { - anotherClass = this.dataSource.models[name]; + anotherClass = this.dataSource.modelBuilder.models[name]; } } } @@ -187,9 +187,9 @@ Relation.belongsTo = function (anotherClass, params) { anotherClass = params.model; } else { var anotherClassName = anotherClass.toLowerCase(); - for(var name in this.dataSource.models) { + for(var name in this.dataSource.modelBuilder.models) { if (name.toLowerCase() === anotherClassName) { - anotherClass = this.dataSource.models[name]; + anotherClass = this.dataSource.modelBuilder.models[name]; } } } @@ -271,7 +271,7 @@ Relation.belongsTo = function (anotherClass, params) { */ Relation.hasAndBelongsToMany = function hasAndBelongsToMany(anotherClass, params) { params = params || {}; - var models = this.dataSource.models; + var models = this.dataSource.modelBuilder.models; if ('string' === typeof anotherClass) { params.as = anotherClass; diff --git a/test/json.test.js b/test/json.test.js index f7df9773..93b73180 100644 --- a/test/json.test.js +++ b/test/json.test.js @@ -2,13 +2,14 @@ var should = require('./init.js'); var Schema = require('../').Schema; +var ModelBuilder = require('../').ModelBuilder; describe('JSON property', function() { var dataSource, Model; it('should be defined', function() { dataSource = getSchema(); - Model = dataSource.define('Model', {propertyName: Schema.JSON}); + Model = dataSource.define('Model', {propertyName: ModelBuilder.JSON}); var m = new Model; (new Boolean('propertyName' in m)).should.eql(true); should.not.exist(m.propertyName); diff --git a/test/loopback-dl.test.js b/test/loopback-dl.test.js index 9a3e8bc7..fdad6fbb 100644 --- a/test/loopback-dl.test.js +++ b/test/loopback-dl.test.js @@ -200,7 +200,7 @@ describe('DataSource define model', function () { // define models var Post = ds.define('Post', { title: { type: String, length: 255 }, - content: { type: DataSource.Text }, + content: { type: ModelBuilder.Text }, date: { type: Date, default: function () { return new Date(); } }, @@ -211,7 +211,7 @@ describe('DataSource define model', function () { // simpler way to describe model var User = ds.define('User', { name: String, - bio: DataSource.Text, + bio: ModelBuilder.Text, approved: Boolean, joinedAt: Date, age: Number @@ -592,7 +592,7 @@ describe('Load models from json', function () { // Read the dataSource JSON file var schemas = JSON.parse(fs.readFileSync(schemaFile)); - return dataSource.buildModels(schemas); + return dataSource.modelBuilder.buildModels(schemas); }