From dea07f3a02ceedf15fa8da56b9c2821ee17172d6 Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Thu, 23 May 2013 22:20:20 -0700 Subject: [PATCH] Rename adl to be ModelBuilder --- examples/app.js | 18 ++-- examples/datasource-app.js | 6 +- examples/load-schemas.js | 27 +++-- index.js | 5 +- lib/adl-loader.js | 138 ------------------------ lib/datasource.js | 16 +-- lib/{adl.js => model-builder.js} | 174 +++++++++++++++++++++++++------ 7 files changed, 187 insertions(+), 197 deletions(-) delete mode 100644 lib/adl-loader.js rename lib/{adl.js => model-builder.js} (63%) diff --git a/examples/app.js b/examples/app.js index 3096f9e1..4ef430d5 100644 --- a/examples/app.js +++ b/examples/app.js @@ -1,24 +1,24 @@ -var ADL = require('../../jugglingdb').ADL; -var adl = new ADL(); +var ModelBuilder = require('../../jugglingdb').ModelBuilder; +var modelBuilder = new ModelBuilder(); // define models -var Post = adl.define('Post', { +var Post = modelBuilder.define('Post', { title: { type: String, length: 255 }, - content: { type: ADL.Text }, + content: { type: ModelBuilder.Text }, date: { type: Date, default: function () { return new Date;} }, timestamp: { type: Number, default: Date.now }, published: { type: Boolean, default: false, index: true } }); // simplier way to describe model -var User = adl.define('User', { +var User = modelBuilder.define('User', { name: String, - bio: ADL.Text, + bio: ModelBuilder.Text, approved: Boolean, joinedAt: Date, age: Number }); -var Group = adl.define('Group', {name: String}); +var Group = modelBuilder.define('Group', {name: String}); // define any custom method User.prototype.getNameAndAge = function () { @@ -28,8 +28,8 @@ User.prototype.getNameAndAge = function () { var user = new User({name: 'Joe'}); console.log(user); -console.log(adl.models); -console.log(adl.definitions); +console.log(modelBuilder.models); +console.log(modelBuilder.definitions); diff --git a/examples/datasource-app.js b/examples/datasource-app.js index b61f20d1..429f6605 100644 --- a/examples/datasource-app.js +++ b/examples/datasource-app.js @@ -1,5 +1,5 @@ var DataSource = require('../../jugglingdb').DataSource; -var ADL = require('../../jugglingdb').ADL; +var ModelBuilder = require('../../jugglingdb').ModelBuilder; var ds = new DataSource('memory'); // define models @@ -93,9 +93,9 @@ Article.create(function(e, article) { }); // 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 }); diff --git a/examples/load-schemas.js b/examples/load-schemas.js index 09313246..9b1b83bb 100644 --- a/examples/load-schemas.js +++ b/examples/load-schemas.js @@ -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')); for (var s in models) { var m = models[s]; - // console.dir(m); - console.log(new m()); + console.log(m.modelName, new m()); } models = loadSchemasSync(path.join(__dirname, 'schemas.json')); for (var s in models) { var m = models[s]; - // console.dir(m); - console.log(new m()); + console.log(m.modelName, new m()); } diff --git a/index.js b/index.js index c249caee..833f25ad 100644 --- a/index.js +++ b/index.js @@ -1,9 +1,8 @@ var fs = require('fs'); var path = require('path'); -exports.ADL = require('./lib/adl').Schema; -exports.DataSource = require('./lib/datasource').DataSource; -exports.Schema = exports.DataSource; // require('./lib/schema').Schema; +exports.ModelBuilder = exports.ADL = require('./lib/model-builder').ModelBuilder; +exports.DataSource = exports.Schema = require('./lib/datasource').DataSource; exports.ModelBaseClass = require('./lib/model.js'); var baseSQL = './lib/sql'; diff --git a/lib/adl-loader.js b/lib/adl-loader.js deleted file mode 100644 index 824adb16..00000000 --- a/lib/adl-loader.js +++ /dev/null @@ -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; diff --git a/lib/datasource.js b/lib/datasource.js index 4210d104..046194e7 100644 --- a/lib/datasource.js +++ b/lib/datasource.js @@ -1,7 +1,7 @@ /** * Module dependencies */ -var ADL = require('./adl.js').Schema; +var ModelBuilder = require('./model-builder.js').ModelBuilder; var ModelBaseClass = require('./model.js'); var DataAccessObject = require('./dao.js'); var List = require('./list.js'); @@ -53,22 +53,22 @@ function DataSource(name, settings) { if (!(this instanceof DataSource)) { return new DataSource(name, settings); } - ADL.call(this); + ModelBuilder.call(this); this.setup(name, settings); // default DataAccessObject this.DataAccessObject = this.constructor.DataAccessObject; }; -util.inherits(DataSource, ADL); +util.inherits(DataSource, ModelBuilder); // allow child classes to supply a data access object DataSource.DataAccessObject = DataAccessObject; // Copy over statics -for (var m in ADL) { +for (var m in ModelBuilder) { 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 || {}; 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 this.mixin(NewClass); @@ -275,7 +275,7 @@ DataSource.prototype.attach = function (ModelCtor) { * @param {Object} params - property settings */ 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) { 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 table * @param cb diff --git a/lib/adl.js b/lib/model-builder.js similarity index 63% rename from lib/adl.js rename to lib/model-builder.js index decb9529..25385372 100644 --- a/lib/adl.js +++ b/lib/model-builder.js @@ -2,19 +2,14 @@ * Module dependencies */ var ModelBaseClass = require('./model.js'); -// var DataAccessObject = require('./dao.js'); var List = require('./list.js'); var EventEmitter = require('events').EventEmitter; var util = require('util'); -var path = require('path'); -var fs = require('fs'); - -var existsSync = fs.existsSync || path.existsSync; /** * Export public API */ -exports.Schema = Schema; +exports.Schema = exports.ModelBuilder = ModelBuilder; // exports.ModelBaseClass = ModelBaseClass; @@ -23,37 +18,44 @@ exports.Schema = Schema; */ var slice = Array.prototype.slice; -Schema.Text = function Text() {}; -Schema.JSON = function JSON() {}; -Schema.Any = function Any() {}; +/** + * Schema types + */ +ModelBuilder.Text = function Text() {}; // Text type +ModelBuilder.JSON = function JSON() {}; // JSON Object +ModelBuilder.Any = function Any() {}; // Any Type -Schema.types = {}; -Schema.registerType = function (type) { - this.types[type.name] = type; +ModelBuilder.schemaTypes = {}; +ModelBuilder.registerType = function (type, names) { + 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); -Schema.registerType(Schema.JSON); -Schema.registerType(Schema.Any); +ModelBuilder.registerType(ModelBuilder.Text); +ModelBuilder.registerType(ModelBuilder.JSON); +ModelBuilder.registerType(ModelBuilder.Any); -Schema.registerType(String); -Schema.registerType(Number); -Schema.registerType(Boolean); -Schema.registerType(Date); -Schema.registerType(Buffer); -Schema.registerType(Array); +ModelBuilder.registerType(String); +ModelBuilder.registerType(Number); +ModelBuilder.registerType(Boolean); +ModelBuilder.registerType(Date); +ModelBuilder.registerType(Buffer, ['Binary']); +ModelBuilder.registerType(Array); /** - * Schema - Data Model Definition + * ModelBuilder - Data Model Definition */ -function Schema() { +function ModelBuilder() { // create blank models pool this.models = {}; 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 args = slice.call(arguments); @@ -177,7 +179,7 @@ Schema.prototype.define = function defineClass(className, properties, settings) DataType = function JSON(s) { return s; }; - } else if (DataType.name === 'Text' || DataType === Schema.Text) { + } else if (DataType.name === 'Text' || DataType === ModelBuilder.Text) { DataType = function Text(s) { return s; }; @@ -251,7 +253,7 @@ function standartize(properties, settings) { * @param {String} prop - name of propery * @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.models[model].registerProperty(prop); }; @@ -279,7 +281,7 @@ Schema.prototype.defineProperty = function (model, prop, params) { * isExpired: { type: Boolean, index: true } * }); */ -Schema.prototype.extendModel = function (model, props) { +ModelBuilder.prototype.extendModel = function (model, props) { var t = this; standartize(props, {}); 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 className = Master.modelName; var md = Master.schema.definitions[className]; @@ -350,4 +352,116 @@ function defineReadonlyProp(obj, key, value) { }); } -exports.ADL = new Schema(); \ No newline at end of file +/** + * 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; +