Rename adl to be ModelBuilder

This commit is contained in:
Raymond Feng 2013-05-23 22:20:20 -07:00
parent a014fddd8a
commit dea07f3a02
7 changed files with 187 additions and 197 deletions

View File

@ -1,24 +1,24 @@
var ADL = require('../../jugglingdb').ADL; var ModelBuilder = require('../../jugglingdb').ModelBuilder;
var adl = new ADL(); var modelBuilder = new ModelBuilder();
// define models // define models
var Post = adl.define('Post', { var Post = modelBuilder.define('Post', {
title: { type: String, length: 255 }, title: { type: String, length: 255 },
content: { type: ADL.Text }, content: { type: ModelBuilder.Text },
date: { type: Date, default: function () { return new Date;} }, date: { type: Date, default: function () { return new Date;} },
timestamp: { type: Number, default: Date.now }, timestamp: { type: Number, default: Date.now },
published: { type: Boolean, default: false, index: true } published: { type: Boolean, default: false, index: true }
}); });
// simplier way to describe model // simplier way to describe model
var User = adl.define('User', { var User = modelBuilder.define('User', {
name: String, name: String,
bio: ADL.Text, bio: ModelBuilder.Text,
approved: Boolean, approved: Boolean,
joinedAt: Date, joinedAt: Date,
age: Number age: Number
}); });
var Group = adl.define('Group', {name: String}); var Group = modelBuilder.define('Group', {name: String});
// define any custom method // define any custom method
User.prototype.getNameAndAge = function () { User.prototype.getNameAndAge = function () {
@ -28,8 +28,8 @@ User.prototype.getNameAndAge = function () {
var user = new User({name: 'Joe'}); var user = new User({name: 'Joe'});
console.log(user); console.log(user);
console.log(adl.models); console.log(modelBuilder.models);
console.log(adl.definitions); console.log(modelBuilder.definitions);

View File

@ -1,5 +1,5 @@
var DataSource = require('../../jugglingdb').DataSource; var DataSource = require('../../jugglingdb').DataSource;
var ADL = require('../../jugglingdb').ADL; var ModelBuilder = require('../../jugglingdb').ModelBuilder;
var ds = new DataSource('memory'); var ds = new DataSource('memory');
// define models // define models
@ -93,9 +93,9 @@ Article.create(function(e, article) {
}); });
// should be able to attach a data source to an existing model // should be able to attach a data source to an existing model
var adl = new ADL(); var modelBuilder = new ModelBuilder();
Color = adl.define('Color', { Color = modelBuilder.define('Color', {
name: String name: String
}); });

View File

@ -1,19 +1,34 @@
var path = require('path'); var path = require('path'),
fs = require('fs'),
DataSource = require('../lib/datasource').DataSource;
var loadSchemasSync = require('../lib/adl-loader').loadSchemasSync; /**
* Load ADL schemas from a json doc
* @param schemaFile The schema 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 schema JSON file
var schemas = JSON.parse(fs.readFileSync(schemaFile));
return DataSource.buildModels(dataSource, schemas);
}
var models = loadSchemasSync(path.join(__dirname, 'jdb-schemas.json')); var models = loadSchemasSync(path.join(__dirname, 'jdb-schemas.json'));
for (var s in models) { for (var s in models) {
var m = models[s]; var m = models[s];
// console.dir(m); console.log(m.modelName, new m());
console.log(new m());
} }
models = loadSchemasSync(path.join(__dirname, 'schemas.json')); models = loadSchemasSync(path.join(__dirname, 'schemas.json'));
for (var s in models) { for (var s in models) {
var m = models[s]; var m = models[s];
// console.dir(m); console.log(m.modelName, new m());
console.log(new m());
} }

View File

@ -1,9 +1,8 @@
var fs = require('fs'); var fs = require('fs');
var path = require('path'); var path = require('path');
exports.ADL = require('./lib/adl').Schema; exports.ModelBuilder = exports.ADL = require('./lib/model-builder').ModelBuilder;
exports.DataSource = require('./lib/datasource').DataSource; exports.DataSource = exports.Schema = require('./lib/datasource').DataSource;
exports.Schema = exports.DataSource; // require('./lib/schema').Schema;
exports.ModelBaseClass = require('./lib/model.js'); exports.ModelBaseClass = require('./lib/model.js');
var baseSQL = './lib/sql'; var baseSQL = './lib/sql';

View File

@ -1,138 +0,0 @@
var fs = require('fs')
, DataSource = require('./datasource').DataSource;
// Built-in data types
var builtinTypes = {
'string': String,
'number': Number,
'date': Date,
'buffer': Buffer,
'binary': Buffer,
'boolean': Boolean,
'any': DataSource.Any,
'array': Array
}
/**
* Resolve the type string to be a function, for example, 'String' to String
* @param type The type string, such as 'number', 'Number', 'boolean', or 'String'. It's case insensitive
* @returns {Function} if the type is resolved
*/
function getSchemaType(type) {
if (!type) {
return type;
}
if (Array.isArray(type) && type.length > 0) {
// For array types, the first item should be the type string
var itemType = getSchemaType(type[0]);
if (typeof itemType === 'function') {
return [itemType];
}
else return itemType; // Not resolved, return the type string
}
if (typeof type === 'string') {
var schemaType = builtinTypes[type.toLowerCase()];
if (schemaType) {
return schemaType;
} else {
return type;
}
} else if (type.constructor.name == 'Object') {
// We also support the syntax {type: 'string', ...}
if (type.type) {
return getSchemaType(type.type);
} else {
throw new Error('Missing type property');
}
}
}
/**
* Build a schema
* @param name The name of the schema
* @param properties The properties of the schema
* @param associations An array of associations between models
* @returns {*}
*/
function buildSchema(name, properties, associations) {
for (var p in properties) {
// console.log(name + "." + p + ": " + properties[p]);
var type = getSchemaType(properties[p]);
if (typeof type === 'string') {
// console.log('Association: ' + type);
associations.push({
source: name,
target: type,
relation: Array.isArray(properties[p]) ? 'hasMany' : 'belongsTo',
as: p
});
delete properties[p];
} else {
properties[p] = type;
}
}
return properties;
}
/**
* Load ADL schemas from a json doc
* @param schemaFile The schema 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 schema JSON file
var schemas = JSON.parse(fs.readFileSync(schemaFile));
return parseSchemas(dataSource, schemas);
}
function parseSchemas(dataSource, schemas) {
var models = {};
if (Array.isArray(schemas)) {
// An array already
} else if (schemas.properties && schemas.name) {
// Only one item
schemas = [schemas];
} else {
// Anonymous schema
schemas = [
{
name: 'Anonymous',
properties: schemas
}
];
}
var associations = [];
for (var s in schemas) {
var name = schemas[s].name;
// console.log('Loading ' + name);
var jdbSchema = buildSchema(name, schemas[s].properties, associations);
// console.dir(jdbSchema);
var model = dataSource.define(name, jdbSchema);
console.dir(model);
models[name.toLowerCase()] = model;
}
// Connect the models based on the associations
for (var i = 0; i < associations.length; i++) {
var association = associations[i];
var sourceModel = models[association.source.toLowerCase()];
var targetModel = models[association.target.toLowerCase()];
if (sourceModel && targetModel) {
sourceModel[association.relation](targetModel, {as: association.as});
}
}
return models;
}
exports.loadSchemasSync = loadSchemasSync;
exports.parseSchemas = parseSchemas;

View File

@ -1,7 +1,7 @@
/** /**
* Module dependencies * Module dependencies
*/ */
var ADL = require('./adl.js').Schema; var ModelBuilder = require('./model-builder.js').ModelBuilder;
var ModelBaseClass = require('./model.js'); var ModelBaseClass = require('./model.js');
var DataAccessObject = require('./dao.js'); var DataAccessObject = require('./dao.js');
var List = require('./list.js'); var List = require('./list.js');
@ -53,22 +53,22 @@ function DataSource(name, settings) {
if (!(this instanceof DataSource)) { if (!(this instanceof DataSource)) {
return new DataSource(name, settings); return new DataSource(name, settings);
} }
ADL.call(this); ModelBuilder.call(this);
this.setup(name, settings); this.setup(name, settings);
// default DataAccessObject // default DataAccessObject
this.DataAccessObject = this.constructor.DataAccessObject; this.DataAccessObject = this.constructor.DataAccessObject;
}; };
util.inherits(DataSource, ADL); util.inherits(DataSource, ModelBuilder);
// allow child classes to supply a data access object // allow child classes to supply a data access object
DataSource.DataAccessObject = DataAccessObject; DataSource.DataAccessObject = DataAccessObject;
// Copy over statics // Copy over statics
for (var m in ADL) { for (var m in ModelBuilder) {
if (!DataSource.hasOwnProperty(m) && 'super_' !== m) { if (!DataSource.hasOwnProperty(m) && 'super_' !== m) {
DataSource[m] = ADL[m]; DataSource[m] = ModelBuilder[m];
} }
} }
@ -202,7 +202,7 @@ DataSource.prototype.define = function defineClass(className, properties, settin
properties = properties || {}; properties = properties || {};
settings = settings || {}; settings = settings || {};
var NewClass = ADL.prototype.define.call(this, className, properties, settings); var NewClass = ModelBuilder.prototype.define.call(this, className, properties, settings);
// add data access objects // add data access objects
this.mixin(NewClass); this.mixin(NewClass);
@ -275,7 +275,7 @@ DataSource.prototype.attach = function (ModelCtor) {
* @param {Object} params - property settings * @param {Object} params - property settings
*/ */
DataSource.prototype.defineProperty = function (model, prop, params) { DataSource.prototype.defineProperty = function (model, prop, params) {
ADL.prototype.defineProperty.call(this, model, prop, params); ModelBuilder.prototype.defineProperty.call(this, model, prop, params);
if (this.adapter.defineProperty) { if (this.adapter.defineProperty) {
this.adapter.defineProperty(model, prop, params); this.adapter.defineProperty(model, prop, params);
@ -416,7 +416,7 @@ function fromDBName(dbName, camelCase) {
} }
/** /**
* Discover ADL schema from a given table/view * Discover schema from a given table/view
* @param owner * @param owner
* @param table * @param table
* @param cb * @param cb

View File

@ -2,19 +2,14 @@
* Module dependencies * Module dependencies
*/ */
var ModelBaseClass = require('./model.js'); var ModelBaseClass = require('./model.js');
// var DataAccessObject = require('./dao.js');
var List = require('./list.js'); var List = require('./list.js');
var EventEmitter = require('events').EventEmitter; var EventEmitter = require('events').EventEmitter;
var util = require('util'); var util = require('util');
var path = require('path');
var fs = require('fs');
var existsSync = fs.existsSync || path.existsSync;
/** /**
* Export public API * Export public API
*/ */
exports.Schema = Schema; exports.Schema = exports.ModelBuilder = ModelBuilder;
// exports.ModelBaseClass = ModelBaseClass; // exports.ModelBaseClass = ModelBaseClass;
@ -23,37 +18,44 @@ exports.Schema = Schema;
*/ */
var slice = Array.prototype.slice; var slice = Array.prototype.slice;
Schema.Text = function Text() {}; /**
Schema.JSON = function JSON() {}; * Schema types
Schema.Any = function Any() {}; */
ModelBuilder.Text = function Text() {}; // Text type
ModelBuilder.JSON = function JSON() {}; // JSON Object
ModelBuilder.Any = function Any() {}; // Any Type
Schema.types = {}; ModelBuilder.schemaTypes = {};
Schema.registerType = function (type) { ModelBuilder.registerType = function (type, names) {
this.types[type.name] = type; names = names || [];
names = names.concat([type.name]);
for (var n = 0; n < names.length; n++) {
this.schemaTypes[names[n].toLowerCase()] = type;
}
}; };
Schema.registerType(Schema.Text); ModelBuilder.registerType(ModelBuilder.Text);
Schema.registerType(Schema.JSON); ModelBuilder.registerType(ModelBuilder.JSON);
Schema.registerType(Schema.Any); ModelBuilder.registerType(ModelBuilder.Any);
Schema.registerType(String); ModelBuilder.registerType(String);
Schema.registerType(Number); ModelBuilder.registerType(Number);
Schema.registerType(Boolean); ModelBuilder.registerType(Boolean);
Schema.registerType(Date); ModelBuilder.registerType(Date);
Schema.registerType(Buffer); ModelBuilder.registerType(Buffer, ['Binary']);
Schema.registerType(Array); ModelBuilder.registerType(Array);
/** /**
* Schema - Data Model Definition * ModelBuilder - Data Model Definition
*/ */
function Schema() { function ModelBuilder() {
// create blank models pool // create blank models pool
this.models = {}; this.models = {};
this.definitions = {}; this.definitions = {};
}; };
util.inherits(Schema, EventEmitter); util.inherits(ModelBuilder, EventEmitter);
/** /**
@ -87,7 +89,7 @@ util.inherits(Schema, EventEmitter);
* }); * });
* ``` * ```
*/ */
Schema.prototype.define = function defineClass(className, properties, settings) { ModelBuilder.prototype.define = function defineClass(className, properties, settings) {
var schema = this; var schema = this;
var args = slice.call(arguments); var args = slice.call(arguments);
@ -177,7 +179,7 @@ Schema.prototype.define = function defineClass(className, properties, settings)
DataType = function JSON(s) { DataType = function JSON(s) {
return s; return s;
}; };
} else if (DataType.name === 'Text' || DataType === Schema.Text) { } else if (DataType.name === 'Text' || DataType === ModelBuilder.Text) {
DataType = function Text(s) { DataType = function Text(s) {
return s; return s;
}; };
@ -251,7 +253,7 @@ function standartize(properties, settings) {
* @param {String} prop - name of propery * @param {String} prop - name of propery
* @param {Object} params - property settings * @param {Object} params - property settings
*/ */
Schema.prototype.defineProperty = function (model, prop, params) { ModelBuilder.prototype.defineProperty = function (model, prop, params) {
this.definitions[model].properties[prop] = params; this.definitions[model].properties[prop] = params;
this.models[model].registerProperty(prop); this.models[model].registerProperty(prop);
}; };
@ -279,7 +281,7 @@ Schema.prototype.defineProperty = function (model, prop, params) {
* isExpired: { type: Boolean, index: true } * isExpired: { type: Boolean, index: true }
* }); * });
*/ */
Schema.prototype.extendModel = function (model, props) { ModelBuilder.prototype.extendModel = function (model, props) {
var t = this; var t = this;
standartize(props, {}); standartize(props, {});
Object.keys(props).forEach(function (propName) { Object.keys(props).forEach(function (propName) {
@ -290,7 +292,7 @@ Schema.prototype.extendModel = function (model, props) {
Schema.prototype.copyModel = function copyModel(Master) { ModelBuilder.prototype.copyModel = function copyModel(Master) {
var schema = this; var schema = this;
var className = Master.modelName; var className = Master.modelName;
var md = Master.schema.definitions[className]; var md = Master.schema.definitions[className];
@ -350,4 +352,116 @@ function defineReadonlyProp(obj, key, value) {
}); });
} }
exports.ADL = new Schema(); /**
* Resolve the type string to be a function, for example, 'String' to String
* @param type The type string, such as 'number', 'Number', 'boolean', or 'String'. It's case insensitive
* @returns {Function} if the type is resolved
*/
function getSchemaType(type) {
if (!type) {
return type;
}
if (Array.isArray(type) && type.length > 0) {
// For array types, the first item should be the type string
var itemType = getSchemaType(type[0]);
if (typeof itemType === 'function') {
return [itemType];
}
else return itemType; // Not resolved, return the type string
}
if (typeof type === 'string') {
var schemaType = ModelBuilder.schemaTypes[type.toLowerCase()];
if (schemaType) {
return schemaType;
} else {
return type;
}
} else if (type.constructor.name == 'Object') {
// We also support the syntax {type: 'string', ...}
if (type.type) {
return getSchemaType(type.type);
} else {
throw new Error('Missing type property');
}
}
}
/**
* Build a schema
* @param name The name of the schema
* @param properties The properties of the schema
* @param associations An array of associations between models
* @returns {*}
*/
function buildSchema(name, properties, associations) {
for (var p in properties) {
// console.log(name + "." + p + ": " + properties[p]);
var type = getSchemaType(properties[p]);
if (typeof type === 'string') {
// console.log('Association: ' + type);
associations.push({
source: name,
target: type,
relation: Array.isArray(properties[p]) ? 'hasMany' : 'belongsTo',
as: p
});
delete properties[p];
} else {
properties[p] = type;
}
}
return properties;
}
/**
* Build models from schema definitions
* @param modelBuilder The model builder
* @param schemas The schemas can be one of the following three formats:
* 1. An array of named schema definition JSON objects
* 2. A schema definition JSON object
* 3. A list of property definitions (anonymous)
* @returns {Object} A map of model constructors keyed by model name
*/
function buildModels(modelBuilder, schemas) {
var models = {};
if (Array.isArray(schemas)) {
// An array already
} else if (schemas.properties && schemas.name) {
// Only one item
schemas = [schemas];
} else {
// Anonymous schema
schemas = [
{
name: 'Anonymous',
properties: schemas
}
];
}
var associations = [];
for (var s in schemas) {
var name = schemas[s].name;
var schema = buildSchema(name, schemas[s].properties, associations);
var model = modelBuilder.define(name, schema);
models[name.toLowerCase()] = model;
}
// Connect the models based on the associations
for (var i = 0; i < associations.length; i++) {
var association = associations[i];
var sourceModel = models[association.source.toLowerCase()];
var targetModel = models[association.target.toLowerCase()];
if (sourceModel && targetModel) {
if(typeof sourceModel[association.relation] === 'function') {
sourceModel[association.relation](targetModel, {as: association.as});
}
}
}
return models;
}
ModelBuilder.buildModels = buildModels;