UNPKG

patio

Version:
1,111 lines (980 loc) 37.8 kB
var comb = require("comb"), asyncArray = comb.async.array, isFunction = comb.isFunction, isUndefined = comb.isUndefined, isDefined = comb.isDefined, isBoolean = comb.isBoolean, isString = comb.isString, argsToArray = comb.argsToArray, isInstanceOf = comb.isInstanceOf, isHash = comb.isHash, when = comb.when, merge = comb.merge, toArray = comb.array.toArray, ModelError = require("./errors").ModelError, plugins = require("./plugins"), isUndefinedOrNull = comb.isUndefinedOrNull, AssociationPlugin = plugins.AssociationPlugin, QueryPlugin = plugins.QueryPlugin, Promise = comb.Promise, PromiseList = comb.PromiseList, HashTable = comb.collections.HashTable, hitch = comb.hitch, Middleware = comb.plugins.Middleware, EventEmitter = require("events").EventEmitter, util = require("util"), define = comb.define, patio; var MODELS = new HashTable(); var applyColumnTransformMethod = function (val, meth) { return !isUndefinedOrNull(meth) ? isFunction(val[meth]) ? val[meth] : isFunction(comb[meth]) ? comb[meth](val) : val : val; }; var Model = define([QueryPlugin, Middleware], { instance: { /** * @lends patio.Model.prototype */ __ignore: false, __changed: null, __values: null, /** * patio - read only * * @type patio */ patio: null, /** * The database type such as mysql * * @type String * * */ type: null, /** * Whether or not this model is new * */ __isNew: true, /** * Signifies if the model has changed * */ __isChanged: false, /** * Base class for all models. * <p>This is used through {@link patio.addModel}, <b>NOT directly.</b></p> * * @constructs * @augments comb.plugins.Middleware * * @param {Object} columnValues values of each column to be used by this Model. * * @property {patio.Dataset} dataset a dataset to use to retrieve models from the database. The dataset * has the {@link patio.Dataset#rowCb} set to create instances of this model. * @property {String[]} columns a list of columns this models table contains. * @property {Object} schema the schema of this models table. * @property {String} tableName the table name of this models table. * @property {*} primaryKeyValue the value of this models primaryKey * @property {Boolean} isNew true if this model is new and does not exist in the database. * @property {Boolean} isChanged true if the model has been changed and not saved. * * @borrows patio.Dataset#all as all * @borrows patio.Dataset#one as one * @borrows patio.Dataset#avg as avg * @borrows patio.Dataset#count as count * @borrows patio.Dataset#columns as columns * @borrows patio.Dataset#forEach as forEach * @borrows patio.Dataset#isEmpty as empty * @borrows patio.Dataset#first as first * @borrows patio.Dataset#get as get * @borrows patio.Dataset#import as import * @borrows patio.Dataset#insert as insert * @borrows patio.Dataset#insertMultiple as insertMultiple * @borrows patio.Dataset#saveMultiple as saveMultiple * @borrows patio.Dataset#interval as interval * @borrows patio.Dataset#last as last * @borrows patio.Dataset#map as map * @borrows patio.Dataset#max as max * @borrows patio.Dataset#min as min * @borrows patio.Dataset#multiInsert as multiInsert * @borrows patio.Dataset#range as range * @borrows patio.Dataset#selectHash as selectHash * @borrows patio.Dataset#selectMap as selectMap * @borrows patio.Dataset#selectOrderMap as selectOrderMap * @borrows patio.Dataset#set as set * @borrows patio.Dataset#singleRecord as singleRecord * @borrows patio.Dataset#singleValue as singleValue * @borrows patio.Dataset#sum as sum * @borrows patio.Dataset#toCsv as toCsv * @borrows patio.Dataset#toHash as toHash * @borrows patio.Dataset#truncate as truncate * @borrows patio.Dataset#addGraphAliases as addGraphAliases * @borrows patio.Dataset#and as and * @borrows patio.Dataset#distinct as distinct * @borrows patio.Dataset#except as except * @borrows patio.Dataset#exclude as exclude * @borrows patio.Dataset#is as is * @borrows patio.Dataset#isNot as isNot * @borrows patio.Dataset#eq as eq * @borrows patio.Dataset#neq as neq * @borrows patio.Dataset#lt as lt * @borrows patio.Dataset#lte as lte * @borrows patio.Dataset#gt as gt * @borrows patio.Dataset#gte as gte * @borrows patio.Dataset#forUpdate as forUpdate * @borrows patio.Dataset#from as from * @borrows patio.Dataset#fromSelf as fromSelf * @borrows patio.Dataset#graph as graph * @borrows patio.Dataset#grep as grep * @borrows patio.Dataset#group as group * @borrows patio.Dataset#groupAndCount as groupAndCount * @borrows patio.Dataset#groupBy as groupBy * @borrows patio.Dataset#having as having * @borrows patio.Dataset#intersect as intersect * @borrows patio.Dataset#invert as invert * @borrows patio.Dataset#limit as limit * @borrows patio.Dataset#lockStyle as lockStyle * @borrows patio.Dataset#naked as naked * @borrows patio.Dataset#or as or * @borrows patio.Dataset#order as order * @borrows patio.Dataset#orderAppend as orderAppend * @borrows patio.Dataset#orderBy as orderBy * @borrows patio.Dataset#orderMore as orderMore * @borrows patio.Dataset#orderPrepend as orderPrepend * @borrows patio.Dataset#qualify as qualify * @borrows patio.Dataset#reverse as reverse * @borrows patio.Dataset#reverseOrder as reverseOrder * @borrows patio.Dataset#select as select * @borrows patio.Dataset#selectAll as selectAll * @borrows patio.Dataset#selectAppend as selectAppend * @borrows patio.Dataset#selectMore as selectMore * @borrows patio.Dataset#setDefaults as setDefaults * @borrows patio.Dataset#setGraphAliases as setGraphAliases * @borrows patio.Dataset#setOverrides as setOverrides * @borrows patio.Dataset#unfiltered as unfiltered * @borrows patio.Dataset#ungraphed as ungraphed * @borrows patio.Dataset#ungrouped as ungrouped * @borrows patio.Dataset#union as union * @borrows patio.Dataset#unlimited as unlimited * @borrows patio.Dataset#unordered as unordered * @borrows patio.Dataset#where as where * @borrows patio.Dataset#with as with * @borrows patio.Dataset#withRecursive as withRecursive * @borrows patio.Dataset#withSql as withSql * @borrows patio.Dataset#naturalJoin as naturalJoin * @borrows patio.Dataset#naturalLeftJoin as naturalLeftJoin * @borrows patio.Dataset#naturalRightJoin as naturalRightJoin * @borrows patio.Dataset#naturalFullJoin as naturalFullJoin * @borrows patio.Dataset#crossJoin as crossJoin * @borrows patio.Dataset#innerJoin as innerJoin * @borrows patio.Dataset#fullOuterJoin as fullOuterJoin * @borrows patio.Dataset#rightOuterJoin as rightOuterJoin * @borrows patio.Dataset#leftOuterJoin as leftOuterJoin * @borrows patio.Dataset#fullJoin as fullJoin * @borrows patio.Dataset#rightJoin as rightJoin * @borrows patio.Dataset#leftJoin as leftJoin * */ constructor: function (options, fromDb) { if (this.synced) { this.__emitter = new EventEmitter(); this._super(arguments); this.patio = patio || require("./index"); fromDb = isBoolean(fromDb) ? fromDb : false; this.__changed = {}; this.__values = {}; if (fromDb) { this._hook("pre", "load"); this.__isNew = false; this.__setFromDb(options, true); if (this._static.emitOnLoad) { this.emit("load", this); this._static.emit("load", this); } } else { this.__isNew = true; this.__set(options); } } else { throw new ModelError("Model " + this.tableName + " has not been synced"); } }, __set: function (values, ignore) { values = values || {}; this.__ignore = ignore === true; Object.keys(values).forEach(function (attribute) { var value = values[attribute]; //check if the column is a constrained value and is allowed to be set !ignore && this._checkIfColumnIsConstrained(attribute); this[attribute] = value; }, this); this.__ignore = false; }, __setFromDb: function (values, ignore) { values = values || {}; this.__ignore = ignore === true; var schema = this.schema; Object.keys(values).forEach(function (column) { var value = values[column]; // Typecast value retrieved from db if (schema.hasOwnProperty(column)) { this.__values[column] = this._typeCastValue(column, value, ignore); } else { this[column] = value; } }, this); this.__ignore = false; }, /** * Set multiple values at once. Useful if you have a hash of properties that you want to set. * * <b>NOTE:</b> This method will use the static restrictedColumns property of the model. * * @example * * myModel.setValues({firstName : "Bob", lastName : "yukon"}); * * //this will throw an error by default, assuming id is a pk. * myModel.setValues({id : 1, firstName : "Bob", lastName : "yukon"}); * * @param {Object} values value to set on the model. * * @return {patio.Model} return this for chaining. */ setValues: function (values) { this.__set(values, false); return this; }, _toObject: function () { if (this.synced) { var columns = this._static.columns, ret = {}; for (var i in columns) { var col = columns[i]; var val = this.__values[col]; if (!isUndefined(val)) { ret[col] = val; } } return ret; } else { throw new ModelError("Model " + this.tableName + " has not been synced"); } }, _addColumnToIsChanged: function (name, val) { if (!this.isNew && !this.__ignore) { this.__isChanged = true; this.__changed[name] = val; } }, _checkIfColumnIsConstrained: function (name) { if (this.synced && !this.__ignore) { var col = this.schema[name], restrictedCols = this._static.restrictedColumns || []; if (!isUndefined(col) && (col.primaryKey && this._static.isRestrictedPrimaryKey) || restrictedCols.indexOf(name) !== -1) { throw new ModelError("Cannot set primary key of model " + this._static.tableName); } } }, _getColumnValue: function (name) { var val = this.__values[name]; var getterFunc = this["_get" + name.charAt(0).toUpperCase() + name.substr(1)]; var columnValue = isFunction(getterFunc) ? getterFunc.call(this, val) : val; return columnValue; }, _setColumnValue: function (name, val) { var ignore = this.__ignore; val = this._typeCastValue(name, val, ignore); var setterFunc = this["_set" + name.charAt(0).toUpperCase() + name.substr(1)]; var columnValue = isFunction(setterFunc) ? setterFunc.call(this, val, ignore) : val; this._addColumnToIsChanged(name, columnValue); this.__values[name] = columnValue; if (this._static.emitOnColumnSet) { this.emit("column", name, columnValue, ignore); } }, //Typecast the value to the column's type if typecasting. Calls the database's //typecast_value method, so database adapters can override/augment the handling //for database specific column types. _typeCastValue: function (column, value, fromDatabase) { var colSchema, clazz = this._static; if (((fromDatabase && clazz.typecastOnLoad) || (!fromDatabase && clazz.typecastOnAssignment)) && !isUndefinedOrNull(this.schema) && !isUndefinedOrNull((colSchema = this.schema[column]))) { var type = colSchema.type; if (value === "" && clazz.typecastEmptyStringToNull === true && !isUndefinedOrNull(type) && ["string", "blob", "text"].indexOf(type) === -1) { value = null; } var raiseOnError = clazz.raiseOnTypecastError; if (raiseOnError === true && isUndefinedOrNull(value) && colSchema.allowNull === false) { throw new ModelError("null is not allowed for the " + column + " column on model " + clazz.tableName); } try { value = clazz.db.typecastValue(type, value); } catch (e) { if (raiseOnError === true) { throw e; } } } return value; }, /** * Convert this model to an object, containing column, value pairs. * * @return {Object} the object version of this model. **/ toObject: function () { return this._toObject(false); }, /** * Convert this model to JSON, containing column, value pairs. * * @return {JSON} the JSON version of this model. **/ toJSON: function () { return this.toObject(); }, /** * Convert this model to a string, containing column, value pairs. * * @return {String} the string version of this model. **/ toString: function () { return JSON.stringify(this.toObject(), null, 4); }, /** * Convert this model to a string, containing column, value pairs. * * @return {String} the string version of this model. **/ valueOf: function () { return this.toObject(); }, _checkTransaction: function (options, cb) { return this._static._checkTransaction(options, cb); }, addListener: function () { var emitter = this.__emitter; return emitter.addListener.apply(emitter, arguments); }, on: function () { var emitter = this.__emitter; return emitter.on.apply(emitter, arguments); }, once: function () { var emitter = this.__emitter; return emitter.once.apply(emitter, arguments); }, removeListener: function () { var emitter = this.__emitter; return emitter.removeListener.apply(emitter, arguments); }, removeAllListeners: function () { var emitter = this.__emitter; return emitter.removeAllListeners.apply(emitter, arguments); }, setMaxListeners: function () { var emitter = this.__emitter; return emitter.setMaxListeners.apply(emitter, arguments); }, listeners: function () { var emitter = this.__emitter; return emitter.listeners.apply(emitter, arguments); }, emit: function () { var emitter = this.__emitter; return emitter.emit.apply(emitter, arguments); }, getters: { /**@lends patio.Model.prototype*/ /*Returns my actual primary key value*/ primaryKeyValue: function () { return this[this.primaryKey]; }, /*Return if Im a new object*/ isNew: function () { return this.__isNew; }, /*Return if Im changed*/ isChanged: function () { return this.__isChanged; }, /**@lends patio.Model.prototype*/ primaryKey: function () { return this._static.primaryKey; }, tableName: function () { return this._static.tableName; }, dataset: function () { return this.__dataset || (this.__dataset = this._static.dataset); }, removeDataset: function () { return this.__removeDataset || (this.__removeDataset = this._static.removeDataset); }, queryDataset: function () { return this.__queryDataset || (this.__queryDataset = this._static.queryDataset); }, updateDataset: function () { return this.__updateDataset || (this.__updateDataset = this._static.updateDataset); }, insertDataset: function () { return this.__insertDataset || (this.__insertDataset = this._static.insertDataset); }, db: function () { return this._static.db; }, schema: function () { return this._static.schema; }, columns: function () { return this._static.columns; }, synced: function () { return this._static.synced; } } }, static: { /** * @lends patio.Model */ synced: false, /** * Set to false to prevent the emitting of an event on load * @default true */ emitOnLoad: true, /** * Set to false to prevent the emitting of an event on the setting of a column value * @default true */ emitOnColumnSet: true, /** * Set to false to prevent empty strings from being type casted to null * @default true */ typecastEmptyStringToNull: true, /** * Set to false to prevent properties from being type casted when loaded from the database. * See {@link patio.Database#typecastValue} * @default true */ typecastOnLoad: true, /** * Set to false to prevent properties from being type casted when manually set. * See {@link patio.Database#typecastValue} * @default true */ typecastOnAssignment: true, /** * Set to false to prevent errors thrown while type casting a value from being propogated. * @default true */ raiseOnTypecastError: true, /** * Set to false to allow the setting of primary keys. * @default false */ isRestrictedPrimaryKey: true, /** * Set to false to prevent models from using transactions when saving, deleting, or updating. * This applies to the model associations also. */ useTransactions: true, /** * See {@link patio.Dataset#identifierOutputMethod} * @default null */ identifierOutputMethod: null, /** * See {@link patio.Dataset#identifierInputMethod} * @default null */ identifierInputMethod: null, /** * Set to false to prevent the reload of a model after saving. * @default true */ reloadOnSave: true, /** * Columns that should be restriced when setting values through the {@link patio.Model#set} method. * */ restrictedColumns: null, /** * Set to false to prevent the reload of a model after updating. * @default true */ reloadOnUpdate: true, __camelize: false, __underscore: false, __columns: null, __schema: null, __primaryKey: null, __dataset: null, __db: null, __tableName: null, /** * The table that this Model represents. * <b>READ ONLY</b> */ table: null, init: function () { var emitter = new EventEmitter(); ["addListener", "on", "once", "removeListener", "removeAllListeners", "setMaxListeners", "listeners", "emit"].forEach(function (name) { this[name] = hitch(emitter, emitter[name]); }, this); if (this.__tableName) { this._setTableName(this.__tableName); } if (this.__db) { this._setDb(this.__db); } }, sync: function (cb) { var ret = new Promise(); if (!this.synced) { var db = this.db, tableName = this.tableName, supers = this.__supers, self = this; ret = db.schema(tableName).chain(function (schema) { if (!self.synced && schema) { self._setSchema(schema); if (supers && supers.length) { return when(supers.map(function (sup) { return sup.sync(); })).chain(function () { self.synced = true; supers.forEach(self.inherits, self); return self; }); } else { self.synced = true; return self; } } else { var error = new ModelError("Unable to find schema for " + tableName); self.emit("error", error); throw error; } }); } else { ret.callback(this); } if (isFunction(cb)) { ret.classic(cb); } return ret.promise(); }, /** * Stub for plugins to notified of model inheritance * * @param {patio.Model} model a model class to inherit from */ inherits: function (model) { }, /** * Create a new model initialized with the specified values. * * @param {Object} values the values to initialize the model with. * * @returns {Model} instantiated model initialized with the values passed in. */ create: function (values) { //load an object from an object var Self = this; return new Self(values, false); }, load: function (vals) { var Self = this, ret; if (!this.synced) { //sync our model ret = this.sync().chain(function () { var m = new Self(vals, true); //call the hooks! return m._hook("post", "load").chain(function () { return m; }); }); } else { var m = new Self(vals, true); //call the hooks! ret = m._hook("post", "load").chain(function () { return m; }); } return ret; }, _checkTransaction: function (opts, cb) { if (isFunction(opts)) { cb = opts; opts = {}; } else { opts = opts || {}; } var retVal = null, errored = false, self = this; return this.sync().chain(function () { if (self.useTransaction(opts)) { return self.db.transaction(opts, function () { return when(cb()).chain(function (val) { retVal = val; }, function (err) { retVal = err; errored = true; }); }).chain(function () { if (errored) { throw retVal; } else { return retVal; } }, function (err) { if (errored) { throw retVal; } else { throw err; } }); } else { return when(cb()); } }); }, /** * @private * Returns a boolean indicating whether or not to use a transaction. * @param {Object} [opts] set a transaction property to override the {@link patio.Model#useTransaction}. */ useTransaction: function (opts) { opts = opts || {}; return isBoolean(opts.transaction) ? opts.transaction === true : this.useTransactions === true; }, _setDataset: function (ds) { this.__dataset = ds; if (ds.db) { this._setDb(ds.db); } }, _setDb: function (db) { this.__db = db; }, _setTableName: function (name) { this.__tableName = name; }, _setColumns: function (cols) { var proto = this.prototype; if (this.__columns) { this.__columns.forEach(function (name) { delete proto[name]; }); } this.__columns = cols; cols.forEach(function (name) { this._defineColumnSetter(name); this._defineColumnGetter(name); }, this); }, _setPrimaryKey: function (pks) { this.__primaryKey = pks || []; }, _setSchema: function (schema) { var columns = []; var pks = []; for (var i in schema) { var col = schema[i]; var name = applyColumnTransformMethod(i, this.identifierOutputMethod); schema[name] = col; columns.push(name); col.primaryKey && pks.push(name); } this.__schema = schema; this._setPrimaryKey(pks); this._setColumns(columns); }, _defineColumnSetter: function (name) { /*Adds a setter to an object*/ this.prototype["__defineSetter__"](name, function (val) { this._setColumnValue(name, val); }); }, _defineColumnGetter: function (name) { this.prototype["__defineGetter__"](name, function () { return this._getColumnValue(name); }); }, _getDataset: function () { var ds = this.__dataset, self = this; if (!ds) { ds = this.db.from(this.tableName); ds.rowCb = function (vals) { return self.load(vals); }; this.identifierInputMethod && (ds.identifierInputMethod = this.identifierInputMethod); this.identifierOutputMethod && (ds.identifierOutputMethod = this.identifierOutputMethod); this.__dataset = ds; } else if (!ds.rowCb) { ds.rowCb = function rowCb(vals) { return self.load(vals); }; } return ds; }, _getQueryDataset: function () { return this._getDataset(); }, _getUpdateDataset: function () { return this._getDataset(); }, _getRemoveDataset: function () { return this._getDataset(); }, _getInsertDataset: function () { return this._getDataset(); }, /** * @ignore */ getters: { /**@lends patio.Model*/ /** * Set to true if this models column names should be use the "underscore" method when sending * keys to the database and to "camelize" method on columns returned from the database. If set to false see * {@link patio.Model#underscore}. * @field * @default false * @type {Boolean} */ camelize: function (camelize) { return this.__camelize; }, /** * Set to true if this models column names should be use the "camelize" method when sending * keys to the database and to "underscore" method on columns returned from the database. If set to false see * {@link patio.Model#underscore}. * @field * @default false * @type {Boolean} */ underscore: function (underscore) { return this.__underscore; }, /**@lends patio.Model*/ /** * The name of the table all instances of the this {@link patio.Model} use. * @field * @ignoreCode * @type String */ tableName: function () { return this.__tableName; }, /** * The database all instances of this {@link patio.Model} use. * @field * @ignoreCode * @type patio.Database */ db: function () { var db = this.__db; if (!db) { db = this.__db = patio.defaultDatabase; } if (!db) { throw new ModelError("patio has not been connected to a database"); } return db; }, /** * A dataset to use to retrieve instances of this {@link patio.Model{ from the database. The dataset * has the {@link patio.Dataset#rowCb} set to create instances of this model. * @field * @ignoreCode * @type patio.Dataset */ dataset: function () { return this._getDataset(); }, removeDataset: function () { return this._getRemoveDataset(); }, queryDataset: function () { return this._getQueryDataset(); }, updateDataset: function () { return this._getUpdateDataset(); }, insertDataset: function () { return this._getInsertDataset(); }, /** * A list of columns this models table contains. * @field * @ignoreCode * @type String[] */ columns: function () { return this.__columns; }, /** * The schema of this {@link patio.Model}'s table. See {@link patio.Database#schema} for details * on the schema object. * @field * @ignoreCode * @type Object */ schema: function () { if (this.synced) { return this.__schema; } else { throw new ModelError("Model has not been synced yet"); } }, /** * The primaryKey column/s of this {@link patio.Model} * @field * @ignoreCode */ primaryKey: function () { if (this.synced) { return this.__primaryKey.slice(0); } else { throw new ModelError("Model has not been synced yet"); } }, /** * A reference to the global {@link patio}. * @field * @ignoreCode */ patio: function () { return patio || require("./index"); } }, /**@ignore*/ setters: { /**@lends patio.Model*/ /**@ignore*/ camelize: function (camelize) { camelize = camelize === true; if (camelize) { this.identifierOutputMethod = "camelize"; this.identifierInputMethod = "underscore"; } this.__camelize = camelize; this.__underscore = !camelize; }, /**@ignore*/ underscore: function (underscore) { underscore = underscore === true; if (underscore) { this.identifierOutputMethod = "underscore"; this.identifierInputMethod = "camelize"; } this.__underscore = underscore; this.__camelize = !underscore; } } } }).as(exports, "Model"); function checkAndAddDBToTable(db, table) { if (!table.contains(db)) { table.set(db, new HashTable()); } } var allModels = []; /**@ignore*/ exports.create = function (name, supers, modelOptions) { if (!patio) { (patio = require("./index")); patio.on("disconnect", function () { allModels.length = 0; MODELS.clear(); }); } var db, ds, tableName, modelKey; if (isString(name)) { tableName = name; db = patio.defaultDatabase || "default"; } else if (isInstanceOf(name, patio.Dataset)) { ds = name; tableName = ds.firstSourceAlias.toString(); db = ds.db; } var hasSuper = false; if (isHash(supers) || isUndefinedOrNull(supers)) { modelOptions = supers; supers = [Model]; } else { supers = toArray(supers); supers = supers.map(function (sup) { return exports.getModel(sup, db); }); hasSuper = true; } var model; checkAndAddDBToTable(db, MODELS); var DEFAULT_PROTO = {instance: {}, "static": {}}; modelOptions = merge(DEFAULT_PROTO, modelOptions || {}); modelOptions.instance._hooks = ["save", "update", "remove", "load"]; modelOptions.instance.__hooks = {pre: {}, post: {}}; //Mixin the column setter/getters modelOptions["static"].synced = false; modelOptions["static"].__tableName = tableName; modelOptions["static"].__db = (db === "default" ? null : db); modelOptions["static"].__supers = hasSuper ? supers : []; modelOptions["static"].__dataset = ds; model = define(supers.concat(modelOptions.plugins || []).concat([AssociationPlugin]), modelOptions); ["pre", "post"].forEach(function (op) { var optionsOp = modelOptions[op]; if (optionsOp) { for (var i in optionsOp) { model[op](i, optionsOp[i]); } } }); allModels.push(model); if (!(MODELS.get(db).contains(tableName))) { MODELS.get(db).set(tableName, model); } return model; }; exports.syncModels = function (cb) { // is this syncing correctly? return asyncArray(allModels).forEach(function (model) { return model.sync(); }, 1).classic(cb).promise(); }; var checkAndGetModel = function (db, name) { var ret; if (MODELS.contains(db)) { ret = MODELS.get(db).get(name); } return ret; }; exports.getModel = function (name, db) { var ret = null; if (isDefined(name)) { !patio && (patio = require("./index")); if (isFunction(name)) { ret = name; } else { if (!db && isInstanceOf(name, patio.Dataset)) { db = name.db; name = name.firstSourceAlias.toString(); } var defaultDb = patio.defaultDatabase; if (db) { ret = checkAndGetModel(db, name); if (!ret && db === defaultDb) { ret = checkAndGetModel("default", name); } } else { db = patio.defaultDatabase; ret = checkAndGetModel(db, name); if (!ret) { ret = checkAndGetModel("default", name); } } } } else { ret = name; } if (isUndefinedOrNull(ret)) { throw new ModelError("Model " + name + " has not been registered with patio"); } return ret; };