Merge pull request #301 from strongloop/feature/allow-base-for-discovery

Tidy up model building from data sources
This commit is contained in:
Raymond Feng 2014-09-23 16:38:17 -07:00
commit 43e0f0f6fa
5 changed files with 125 additions and 51 deletions

View File

@ -1427,6 +1427,7 @@ DataSource.prototype.discoverSchemasSync = function (modelName, options) {
*/ */
DataSource.prototype.discoverAndBuildModels = function (modelName, options, cb) { DataSource.prototype.discoverAndBuildModels = function (modelName, options, cb) {
var self = this; var self = this;
options = options || {};
this.discoverSchemas(modelName, options, function (err, schemas) { this.discoverSchemas(modelName, options, function (err, schemas) {
if (err) { if (err) {
cb && cb(err, schemas); cb && cb(err, schemas);
@ -1436,14 +1437,16 @@ DataSource.prototype.discoverAndBuildModels = function (modelName, options, cb)
var schemaList = []; var schemaList = [];
for (var s in schemas) { for (var s in schemas) {
var schema = schemas[s]; var schema = schemas[s];
if (options.base) {
schema.options = schema.options || {};
schema.options.base = options.base;
}
schemaList.push(schema); schemaList.push(schema);
} }
var models = self.modelBuilder.buildModels(schemaList); var models = self.modelBuilder.buildModels(schemaList,
// Now attach the models to the data source self.createModel.bind(self));
for (var m in models) {
models[m].attachTo(self);
}
cb && cb(err, models); cb && cb(err, models);
}); });
}; };
@ -1462,18 +1465,41 @@ DataSource.prototype.discoverAndBuildModels = function (modelName, options, cb)
* @param {Object} [options] The options * @param {Object} [options] The options
*/ */
DataSource.prototype.discoverAndBuildModelsSync = function (modelName, options) { DataSource.prototype.discoverAndBuildModelsSync = function (modelName, options) {
options = options || {};
var schemas = this.discoverSchemasSync(modelName, options); var schemas = this.discoverSchemasSync(modelName, options);
var schemaList = []; var schemaList = [];
for (var s in schemas) { for (var s in schemas) {
var schema = schemas[s]; var schema = schemas[s];
if (options.base) {
schema.options = schema.options || {};
schema.options.base = options.base;
}
schemaList.push(schema); schemaList.push(schema);
} }
var models = this.modelBuilder.buildModels(schemaList); var models = this.modelBuilder.buildModels(schemaList,
this.createModel.bind(this));
return models; return models;
}; };
/**
* Introspect a JSON object and build a model class
* @param {String} name Name of the model
* @param {Object} json The json object representing a model instance
* @param {Object} options Options
* @returns {*}
*/
DataSource.prototype.buildModelFromInstance = function (name, json, options) {
// Introspect the JSON document to generate a schema
var schema = ModelBuilder.introspect(json);
// Create a model for the generated schema
return this.createModel(name, schema, options);
};
/** /**
* Check whether migrations needed * Check whether migrations needed
* This method applies only to SQL connectors. * This method applies only to SQL connectors.

View File

@ -24,12 +24,13 @@ module.exports = function getIntrospector(ModelBuilder) {
return 'date'; return 'date';
} }
var itemType;
if (Array.isArray(value)) { if (Array.isArray(value)) {
for (var i = 0; i < value.length; i++) { for (var i = 0; i < value.length; i++) {
if (value[i] === null || value[i] === undefined) { if (value[i] === null || value[i] === undefined) {
continue; continue;
} }
var itemType = introspectType(value[i]); itemType = introspectType(value[i]);
if (itemType) { if (itemType) {
return [itemType]; return [itemType];
} }
@ -43,7 +44,7 @@ module.exports = function getIntrospector(ModelBuilder) {
var properties = {}; var properties = {};
for (var p in value) { for (var p in value) {
var itemType = introspectType(value[p]); itemType = introspectType(value[p]);
if (itemType) { if (itemType) {
properties[p] = itemType; properties[p] = itemType;
} }
@ -54,6 +55,7 @@ module.exports = function getIntrospector(ModelBuilder) {
return properties; return properties;
} }
ModelBuilder.introspect = introspectType;
return introspectType; return introspectType;
} }

View File

@ -632,7 +632,7 @@ ModelBuilder.prototype.resolveType = function (type) {
* @param {*} schemas The schemas * @param {*} schemas The schemas
* @returns {Object} A map of model constructors keyed by model name * @returns {Object} A map of model constructors keyed by model name
*/ */
ModelBuilder.prototype.buildModels = function (schemas) { ModelBuilder.prototype.buildModels = function (schemas, createModel) {
var models = {}; var models = {};
// Normalize the schemas to be an array of the schema objects {name: <name>, properties: {}, options: {}} // Normalize the schemas to be an array of the schema objects {name: <name>, properties: {}, options: {}}
@ -656,7 +656,12 @@ ModelBuilder.prototype.buildModels = function (schemas) {
for (var s in schemas) { for (var s in schemas) {
var name = this.getSchemaName(schemas[s].name); var name = this.getSchemaName(schemas[s].name);
schemas[s].name = name; schemas[s].name = name;
var model = this.define(schemas[s].name, schemas[s].properties, schemas[s].options); var model;
if(typeof createModel === 'function') {
model = createModel(schemas[s].name, schemas[s].properties, schemas[s].options);
} else {
model = this.define(schemas[s].name, schemas[s].properties, schemas[s].options);
}
models[name] = model; models[name] = model;
relations = relations.concat(model.definition.relations); relations = relations.concat(model.definition.relations);
} }

View File

@ -1,8 +1,29 @@
var assert = require('assert'); var assert = require('assert');
var ModelBuilder = require('../lib/model-builder').ModelBuilder; var ModelBuilder = require('..').ModelBuilder;
var DataSource = require('../').DataSource;
var introspectType = require('../lib/introspection')(ModelBuilder); var introspectType = require('../lib/introspection')(ModelBuilder);
var traverse = require('traverse'); var traverse = require('traverse');
var json = {
name: 'Joe',
age: 30,
birthday: new Date(),
vip: true,
address: {
street: '1 Main St',
city: 'San Jose',
state: 'CA',
zipcode: '95131',
country: 'US'
},
friends: ['John', 'Mary'],
emails: [
{label: 'work', id: 'x@sample.com'},
{label: 'home', id: 'x@home.com'}
],
tags: []
};
describe('Introspection of model definitions from JSON', function () { describe('Introspection of model definitions from JSON', function () {
it('should handle simple types', function () { it('should handle simple types', function () {
@ -61,27 +82,6 @@ describe('Introspection of model definitions from JSON', function () {
}); });
it('should build a model from the introspected schema', function (done) { it('should build a model from the introspected schema', function (done) {
var json = {
name: 'Joe',
age: 30,
birthday: new Date(),
vip: true,
address: {
street: '1 Main St',
city: 'San Jose',
state: 'CA',
zipcode: '95131',
country: 'US'
},
friends: ['John', 'Mary'],
emails: [
{label: 'work', id: 'x@sample.com'},
{label: 'home', id: 'x@home.com'}
],
tags: []
};
var copy = traverse(json).clone(); var copy = traverse(json).clone();
var schema = introspectType(json); var schema = introspectType(json);
@ -97,5 +97,33 @@ describe('Introspection of model definitions from JSON', function () {
assert.deepEqual(obj, copy); assert.deepEqual(obj, copy);
done(); done();
}); });
it('should build a model using buildModelFromInstance', function (done) {
var copy = traverse(json).clone();
var builder = new ModelBuilder();
var Model = builder.buildModelFromInstance('MyModel', copy, {idInjection: false});
var obj = new Model(json);
obj = obj.toObject();
assert.deepEqual(obj, copy);
done();
});
it('should build a model using DataSource.buildModelFromInstance', function (done) {
var copy = traverse(json).clone();
var builder = new DataSource('memory');
var Model = builder.buildModelFromInstance('MyModel', copy,
{idInjection: false});
assert.equal(Model.dataSource, builder);
var obj = new Model(json);
obj = obj.toObject();
assert.deepEqual(obj, copy);
done();
});
}); });

View File

@ -1418,28 +1418,31 @@ describe('DataAccessObject', function () {
}); });
describe('Load models from json', function () { describe('Load models from json', function () {
it('should be able to define models from json', function () { var path = require('path'),
var path = require('path'), fs = require('fs');
fs = require('fs');
/**
* Load LDL schemas from a json doc
* @param schemaFile The dataSource json file
* @returns A map of schemas keyed by name
*/
function loadSchemasSync(schemaFile, dataSource) {
// Set up the data source
if (!dataSource) {
dataSource = new DataSource('memory');
}
// Read the dataSource JSON file
var schemas = JSON.parse(fs.readFileSync(schemaFile));
return dataSource.modelBuilder.buildModels(schemas);
/**
* Load LDL schemas from a json doc
* @param schemaFile The dataSource json file
* @returns A map of schemas keyed by name
*/
function loadSchemasSync(schemaFile, dataSource) {
var modelBuilder, createModel;
// Set up the data source
if (!dataSource) {
modelBuilder = new ModelBuilder();
} else {
modelBuilder = dataSource.modelBuilder;
createModel = dataSource.createModel.bind(dataSource);
} }
// Read the dataSource JSON file
var schemas = JSON.parse(fs.readFileSync(schemaFile));
return modelBuilder.buildModels(schemas, createModel);
}
it('should be able to define models from json', function () {
var models = loadSchemasSync(path.join(__dirname, 'test1-schemas.json')); var models = loadSchemasSync(path.join(__dirname, 'test1-schemas.json'));
models.should.have.property('AnonymousModel_0'); models.should.have.property('AnonymousModel_0');
@ -1459,6 +1462,16 @@ describe('Load models from json', function () {
} }
}); });
it('should be able to define models from json using dataSource', function() {
var ds = new DataSource('memory');
var models = loadSchemasSync(path.join(__dirname, 'test2-schemas.json'), ds);
models.should.have.property('Address');
models.should.have.property('Account');
models.should.have.property('Customer');
assert.equal(models.Address.dataSource, ds);
});
it('should allow customization of default model base class', function () { it('should allow customization of default model base class', function () {
var modelBuilder = new ModelBuilder(); var modelBuilder = new ModelBuilder();