From 66810cbd87289042a7d46492f06f2440349e8732 Mon Sep 17 00:00:00 2001 From: Jelle De Loecker Date: Mon, 29 Jan 2024 16:24:01 +0100 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Add=20namespace=20support?= =?UTF-8?q?=20to=20models?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/app/helper_field/schema_field.js | 14 +- lib/app/helper_model/document.js | 42 +++-- lib/app/helper_model/field_set.js | 2 +- lib/app/helper_model/model.js | 120 +++++++++---- lib/app/model/05-system_model.js | 27 +++ ...ion_model.js => system_migration_model.js} | 4 +- ...tting_model.js => system_setting_model.js} | 12 +- ..._model.js => system_task_history_model.js} | 8 +- ...emy_task_model.js => system_task_model.js} | 12 +- lib/class/datasource.js | 49 +++-- lib/class/document.js | 79 +++----- lib/class/field.js | 10 +- lib/class/migration.js | 2 +- lib/class/model.js | 170 +++++++++--------- lib/class/plugin.js | 6 +- lib/class/schema.js | 2 +- lib/class/schema_client.js | 20 ++- lib/class/task.js | 2 +- lib/class/task_service.js | 6 +- lib/core/alchemy_functions.js | 6 +- lib/core/alchemy_load_functions.js | 6 +- lib/core/base.js | 14 +- lib/core/client_base.js | 27 +-- lib/stages/20-settings.js | 2 +- 24 files changed, 342 insertions(+), 300 deletions(-) create mode 100644 lib/app/model/05-system_model.js rename lib/app/model/{alchemy_migration_model.js => system_migration_model.js} (79%) rename lib/app/model/{alchemy_setting_model.js => system_setting_model.js} (87%) rename lib/app/model/{alchemy_task_history_model.js => system_task_history_model.js} (90%) rename lib/app/model/{alchemy_task_model.js => system_task_model.js} (90%) diff --git a/lib/app/helper_field/schema_field.js b/lib/app/helper_field/schema_field.js index 9cd51209..b47f436a 100644 --- a/lib/app/helper_field/schema_field.js +++ b/lib/app/helper_field/schema_field.js @@ -367,7 +367,7 @@ SchemaField.setMethod(function _toDatasourceFromValue(value, holder, datasource, * * @author Jelle De Loecker * @since 0.2.0 - * @version 1.3.16 + * @version 1.4.0 * * @param {Mixed} value * @param {Function} callback @@ -388,15 +388,21 @@ SchemaField.setMethod(function _toApp(query, options, value, callback) { }); } - Function.parallel(tasks, (err, result) => { + Pledge.Swift.all(tasks).done((err, result) => { if (err) { return callback(err); } - callback(null, this.cast(result)); - }); + try { + result = this.cast(result) + } catch (err) { + callback(err); + return; + } + callback(null, result); + }); }); /** diff --git a/lib/app/helper_model/document.js b/lib/app/helper_model/document.js index 7b4397cb..49069829 100644 --- a/lib/app/helper_model/document.js +++ b/lib/app/helper_model/document.js @@ -12,7 +12,7 @@ if (Blast.isBrowser) { key; for (config of hawkejs.scene.exposed.model_info) { - DocClass = Document.getDocumentClass(config.name); + DocClass = Document.getDocumentClass(config.model_name); for (key in config.schema.dict) { DocClass.setFieldGetter(key); @@ -240,35 +240,43 @@ Document.setStatic(function getClassForUndry(class_name) { * * @author Jelle De Loecker * @since 1.0.0 - * @version 1.3.1 + * @version 1.4.0 * - * @param {Object|string} model + * @param {Function|Object|string} model_param */ -Document.setStatic(function getDocumentClass(model) { +Document.setStatic(function getDocumentClass(model_param) { - if (!model) { + if (!model_param) { throw new Error('Can not get Hawkejs.Document class for non-existing model'); } - let model_name; + let model = typeof model_param == 'function' ? model_param : alchemy.getModel(model_param, false); - if (typeof model == 'function') { - model_name = model.name; - } else if (typeof model == 'string') { - model_name = model; - } else { - model_name = model.model_name; + if (!model) { + throw new Error('There is no model named "' + model_param + '"'); } - // Construct the name of the document class - let document_name = model_name; + if (model.is_namespace) { + return; + } - if (class_cache.has(document_name)) { - return class_cache.get(document_name); + let model_name = model.model_name; + + if (class_cache.has(model_name)) { + return class_cache.get(model_name); } + // Construct the name of the document class constructor + let document_name = model.name; + // Construct the path to this class - let doc_path = 'Alchemy.Client.Document.' + document_name; + let doc_path = 'Alchemy.Client.Document.'; + + if (model.model_namespace) { + doc_path += model.model_namespace + '.'; + } + + doc_path += document_name; // Get the class let DocClass = Object.path(Blast.Classes, doc_path); diff --git a/lib/app/helper_model/field_set.js b/lib/app/helper_model/field_set.js index e8d4232b..d609cb64 100644 --- a/lib/app/helper_model/field_set.js +++ b/lib/app/helper_model/field_set.js @@ -15,7 +15,7 @@ const FieldSet = Fn.inherits('Alchemy.Base', 'Alchemy.Criteria', function FieldS this.name = name; if (typeof model == 'function') { - model = model.name; + model = model.model_name; } this.model = model; diff --git a/lib/app/helper_model/model.js b/lib/app/helper_model/model.js index a2d17fdb..62c2fe23 100644 --- a/lib/app/helper_model/model.js +++ b/lib/app/helper_model/model.js @@ -10,7 +10,7 @@ if (Blast.isBrowser) { for (config of hawkejs.scene.exposed.model_info) { // First get or create the client-side Model class, // then set some extra configuration coming from the server-side - Model.getClass(config.name).setModelConfig(config); + Model.getClass(config.model_name).setModelConfig(config); } }); } @@ -36,12 +36,44 @@ const Model = Function.inherits('Alchemy.Client.Base', 'Alchemy.Client.Model', f * * @author Jelle De Loecker * @since 1.1.0 - * @version 1.1.0 + * @version 1.4.0 */ -Model.constitute(function setModelName() { - this.model_name = this.name; - this.setProperty('model_name', this.model_name); - this.table = this.model_name.tableize(); +Model.postInherit(function setModelName() { + + let base_name = this.name; + let model_name = base_name; + let table_prefix = this.table_prefix; + let namespace = this.namespace; + + if (namespace.startsWith('Alchemy.Model.') || namespace == 'Alchemy.Model') { + namespace = namespace.slice(14); + } else if (namespace.startsWith('Alchemy.Client.Model.') || namespace == 'Alchemy.Client.Model') { + namespace = namespace.slice(21); + } + + this.setStatic('model_namespace', namespace, false); + + let ns = namespace.replaceAll('.', '_'); + + if (!table_prefix && ns) { + table_prefix = ns.tableize(); + } + + if (ns) { + model_name = ns + '_' + model_name; + } + + // The simple name of the model + this.model_name = model_name; + this.setProperty('model_name', model_name); + + let table_name = base_name.tableize(); + + if (table_prefix) { + table_name = table_prefix + '_' + table_name; + } + + this.table = table_name; }); /** @@ -221,7 +253,7 @@ Model.prepareStaticProperty('Document', function getDocumentClass() { * * @author Jelle De Loecker * @since 1.0.0 - * @version 1.3.1 + * @version 1.4.0 * * @param {string} model_name * @param {boolean} allow_create @@ -229,15 +261,26 @@ Model.prepareStaticProperty('Document', function getDocumentClass() { */ Model.setStatic(function getClass(model_name, allow_create, parent) { - if (class_cache.has(model_name)) { - return class_cache.get(model_name); + let cache_key = model_name; + + if (class_cache.has(cache_key)) { + return class_cache.get(cache_key); } + // Get the model path as an array + let model_path = Blast.parseClassPath(model_name); + + // Ensure it is the correct model_name + model_name = model_path.join('_'); + // Construct the name of the class - let class_name = model_name; + let class_name = model_path.last(); - // Construct the path to this class - let class_path = 'Alchemy.Client.Model.' + class_name; + // The root path + const root_path = 'Alchemy.Client.Model'; + + // Construct the expected path to this class + let class_path = root_path + '.' + model_path.join('.'); // Get the class let ModelClass = Object.path(Blast.Classes, class_path); @@ -247,39 +290,39 @@ Model.setStatic(function getClass(model_name, allow_create, parent) { } if (ModelClass == null && allow_create) { - let config; + + let namespace = root_path, + config; + + if (model_path.length > 1) { + namespace += '.' + model_path.slice(0, -1).join('.'); + } let model_constructor = Function.create(class_name, function ModelConstructor(record, options) { ModelConstructor.wrapper.super.call(this, record, options); }); - // @TODO: inherit from parents - let parent_path = 'Alchemy.Client.Model'; - if (Blast.isBrowser) { - // No longer needed - } else if (Blast.isNode) { - config = alchemy.getModel(model_name, false); - if (config && config.super) { - config = { - parent : config.super.name, - primary_key : config.prototype.primary_key, - display_field : config.prototype.display_field, - }; + if (parent) { + let parent_model_path = Blast.parseClassPath(parent); + parent = root_path + '.' + parent_model_path.join('.'); + } else { + parent = root_path; } - } - if (config && config.parent) { - parent = config.parent; - } + } else if (Blast.isNode) { + let server_class = alchemy.getModel(model_name, false); - if (parent) { - getClass(parent); - parent_path += '.' + parent; + parent = getClass(server_class.super.model_name); + + config = { + primary_key : server_class.prototype.primary_key, + display_field : server_class.prototype.display_field, + }; } - ModelClass = Function.inherits(parent_path, model_constructor); + ModelClass = Function.inherits(parent, namespace, model_constructor); ModelClass.setProperty('$model_name', model_name); @@ -298,7 +341,7 @@ Model.setStatic(function getClass(model_name, allow_create, parent) { } } - class_cache.set(model_name, ModelClass); + class_cache.set(cache_key, ModelClass); return ModelClass; }); @@ -1560,7 +1603,8 @@ Model.setMethod(function hasServerAction(action) { */ Model.setProperty(function model_info() { - let name = this.constructor.name, + let class_name = this.constructor.name, + model_name = this.constructor.model_name, data; if (Blast.isBrowser) { @@ -1573,17 +1617,17 @@ Model.setProperty(function model_info() { let config; for (config of hawkejs.scene.exposed.model_info) { - if (config.name === name) { + if (config.model_name === model_name) { data = config; break; } } } else { - let MainClass = Blast.Classes.Alchemy.Model[name]; + let MainClass = Blast.Classes.Alchemy.Model.Model.get(model_name, false); if (!MainClass) { - throw new Error('Unable to find main class for "' + name + '", unable to get client config!'); + throw new Error('Unable to find main class for "' + model_name + '", unable to get client config!'); } data = MainClass.getClientConfig(); diff --git a/lib/app/model/05-system_model.js b/lib/app/model/05-system_model.js new file mode 100644 index 00000000..c6c03c03 --- /dev/null +++ b/lib/app/model/05-system_model.js @@ -0,0 +1,27 @@ +/** + * The base System model class. + * Models meant for the Alchemy system. + * + * @author Jelle De Loecker + * @since 1.4.0 + * @version 1.4.0 + */ +const System = Function.inherits('Alchemy.Model.App', 'Alchemy.Model.System', 'System'); + +/** + * Mark this class as being abstract + * + * @author Jelle De Loecker + * @since 1.4.0 + * @version 1.4.0 + */ +System.makeAbstractClass(); + +/** + * Use the `alchemy` prefix for the table name + * + * @author Jelle De Loecker + * @since 1.4.0 + * @version 1.4.0 + */ +System.setTablePrefix('alchemy'); \ No newline at end of file diff --git a/lib/app/model/alchemy_migration_model.js b/lib/app/model/system_migration_model.js similarity index 79% rename from lib/app/model/alchemy_migration_model.js rename to lib/app/model/system_migration_model.js index 0811d879..1a9cde16 100644 --- a/lib/app/model/alchemy_migration_model.js +++ b/lib/app/model/system_migration_model.js @@ -7,7 +7,7 @@ * @since 1.2.0 * @version 1.2.0 */ -const AlchemyMigration = Function.inherits('Alchemy.Model.App', 'AlchemyMigration'); +const Migration = Function.inherits('Alchemy.Model.System', 'Migration'); /** * Constitute the class wide schema @@ -16,7 +16,7 @@ const AlchemyMigration = Function.inherits('Alchemy.Model.App', 'AlchemyMigratio * @since 1.2.0 * @version 1.2.0 */ -AlchemyMigration.constitute(function addTaskFields() { +Migration.constitute(function addTaskFields() { // The name of the migration this.addField('name', 'String'); diff --git a/lib/app/model/alchemy_setting_model.js b/lib/app/model/system_setting_model.js similarity index 87% rename from lib/app/model/alchemy_setting_model.js rename to lib/app/model/system_setting_model.js index bdc4437a..c6e6f446 100644 --- a/lib/app/model/alchemy_setting_model.js +++ b/lib/app/model/system_setting_model.js @@ -8,7 +8,7 @@ * @since 1.4.0 * @version 1.4.0 */ -const AlchemySetting = Function.inherits('Alchemy.Model.App', 'AlchemySetting'); +const SystemSetting = Function.inherits('Alchemy.Model.System', 'Setting'); /** * Constitute the class wide schema @@ -17,7 +17,7 @@ const AlchemySetting = Function.inherits('Alchemy.Model.App', 'AlchemySetting'); * @since 1.4.0 * @version 1.4.0 */ -AlchemySetting.constitute(function addTaskFields() { +SystemSetting.constitute(function addTaskFields() { this.addField('setting_id', 'Enum', { description: 'The setting id', @@ -37,7 +37,7 @@ AlchemySetting.constitute(function addTaskFields() { * @since 1.4.0 * @version 1.4.0 */ -AlchemySetting.constitute(function chimeraConfig() { +SystemSetting.constitute(function chimeraConfig() { if (!this.chimera) { return; @@ -65,7 +65,7 @@ AlchemySetting.constitute(function chimeraConfig() { * @param {Object} changes * @param {Conduit} permission_context */ -AlchemySetting.setMethod(async function saveChanges(changes, permission_context) { +SystemSetting.setMethod(async function saveChanges(changes, permission_context) { if (!changes) { return; @@ -115,7 +115,7 @@ AlchemySetting.setMethod(async function saveChanges(changes, permission_context) * * @param {Document.AlchemyTask} doc */ -AlchemySetting.setMethod(function beforeSave(doc) { +SystemSetting.setMethod(function beforeSave(doc) { return doc.applySetting(); }); @@ -128,7 +128,7 @@ AlchemySetting.setMethod(function beforeSave(doc) { * * @param {boolean} do_actions Should the setting actions be executed */ -AlchemySetting.setDocumentMethod(function applySetting(do_actions = true) { +SystemSetting.setDocumentMethod(function applySetting(do_actions = true) { if (!this.setting_id) { return; diff --git a/lib/app/model/alchemy_task_history_model.js b/lib/app/model/system_task_history_model.js similarity index 90% rename from lib/app/model/alchemy_task_history_model.js rename to lib/app/model/system_task_history_model.js index 1fad7800..f189a073 100644 --- a/lib/app/model/alchemy_task_history_model.js +++ b/lib/app/model/system_task_history_model.js @@ -8,7 +8,7 @@ * @since 0.5.0 * @version 0.5.0 */ -const AlchemyTaskHistory = Function.inherits('Alchemy.Model.App', 'AlchemyTaskHistory'); +const SystemTaskHistory = Function.inherits('Alchemy.Model.System', 'TaskHistory'); /** * Constitute the class wide schema @@ -17,9 +17,9 @@ const AlchemyTaskHistory = Function.inherits('Alchemy.Model.App', 'AlchemyTaskHi * @since 0.5.0 * @version 1.3.17 */ -AlchemyTaskHistory.constitute(function addTaskFields() { +SystemTaskHistory.constitute(function addTaskFields() { - this.belongsTo('AlchemyTask', { + this.belongsTo('System.Task', { description : 'The original AlchemyTask document it belonged to', }); @@ -79,7 +79,7 @@ AlchemyTaskHistory.constitute(function addTaskFields() { * @since 1.3.17 * @version 1.3.17 */ -AlchemyTaskHistory.constitute(function chimeraConfig() { +SystemTaskHistory.constitute(function chimeraConfig() { if (!this.chimera) { return; diff --git a/lib/app/model/alchemy_task_model.js b/lib/app/model/system_task_model.js similarity index 90% rename from lib/app/model/alchemy_task_model.js rename to lib/app/model/system_task_model.js index 54db45d8..155264f3 100644 --- a/lib/app/model/alchemy_task_model.js +++ b/lib/app/model/system_task_model.js @@ -8,7 +8,7 @@ * @since 1.3.17 * @version 1.3.17 */ -const AlchemyTask = Function.inherits('Alchemy.Model.App', 'AlchemyTask'); +const SystemTask = Function.inherits('Alchemy.Model.System', 'Task'); /** * Constitute the class wide schema @@ -17,7 +17,7 @@ const AlchemyTask = Function.inherits('Alchemy.Model.App', 'AlchemyTask'); * @since 1.3.17 * @version 1.3.17 */ -AlchemyTask.constitute(function addTaskFields() { +SystemTask.constitute(function addTaskFields() { this.addField('title', 'String', { description : 'The title of the task', @@ -72,7 +72,7 @@ AlchemyTask.constitute(function addTaskFields() { * @since 1.3.17 * @version 1.3.17 */ -AlchemyTask.constitute(function chimeraConfig() { +SystemTask.constitute(function chimeraConfig() { if (!this.chimera) { return; @@ -105,9 +105,9 @@ AlchemyTask.constitute(function chimeraConfig() { * @since 1.3.17 * @version 1.3.17 * - * @param {Document.AlchemyTask} doc + * @param {Document.SystemTask} doc */ -AlchemyTask.setMethod(function beforeSave(doc) { +SystemTask.setMethod(function beforeSave(doc) { if (!doc.schedule_type) { doc.schedule_type = 'user'; @@ -143,7 +143,7 @@ AlchemyTask.setMethod(function beforeSave(doc) { * @param {Object} main * @param {Object} info */ -AlchemyTask.setMethod(function afterSave(main, info) { +SystemTask.setMethod(function afterSave(main, info) { if (!main.type) { return; diff --git a/lib/class/datasource.js b/lib/class/datasource.js index acb75475..2db7433c 100644 --- a/lib/class/datasource.js +++ b/lib/class/datasource.js @@ -338,7 +338,7 @@ Datasource.setMethod(function toDatasource(schema, data, callback) { * * @author Jelle De Loecker * @since 0.2.0 - * @version 1.3.1 + * @version 1.4.0 * * @param {Schema|Model} schema * @param {Object} query @@ -349,16 +349,13 @@ Datasource.setMethod(function toDatasource(schema, data, callback) { */ Datasource.setMethod(function toApp(schema, query, options, data, callback) { - var pledge; - if (!data) { - pledge = Pledge.reject(new Error('Unable to convert data: no data given')); + let pledge = Pledge.reject(new Error('Unable to convert data: no data given')); pledge.done(callback); return pledge; } - let that = this, - tasks; + let that = this; schema = this.getSchema(schema); @@ -377,6 +374,8 @@ Datasource.setMethod(function toApp(schema, query, options, data, callback) { options._root_data = data; } + let tasks; + if (data[schema.name]) { tasks = {}; @@ -430,34 +429,32 @@ Datasource.setMethod(function toApp(schema, query, options, data, callback) { data = Object.assign({}, data); tasks = {}; - Object.each(data, function eachField(value, fieldName) { + for (let entry of schema.getSortedItems()) { - var field = schema.get(fieldName); + let field_name = entry.key, + field = entry.value, + value = data[field_name]; if (field != null) { - tasks[fieldName] = function doToDatasource(next) { + tasks[field_name] = function doToDatasource(next) { that.valueToApp(field, query, options, value, next); }; - } else { - let allow_field = false; + } + } - if (options.extraneous) { - allow_field = true; - } else if (Blast.isBrowser && fieldName[0] == '_' && fieldName[1] == '$') { + if (Blast.isBrowser) { + for (let key in data) { + if (key[0] == '_' && key[1] == '$') { // Certain extra data is stored on the browser as // properties starting with "_$" - allow_field = true; - } - - if (allow_field) { - tasks[fieldName] = function addExtraneous(next) { - next(null, value); + tasks[key] = function addExtraneous(next) { + next(null, data[key]); }; } } - }); + } - pledge = Function.parallel(tasks, callback); + let pledge = Function.parallel(tasks, callback); return pledge; }); @@ -513,9 +510,7 @@ Datasource.setMethod(function valueToApp(field, query, options, value, callback) * @version 1.1.0 */ Datasource.setMethod(function _valueToDatasource(field, value, data, callback) { - Blast.setImmediate(function immediateDelay() { - callback(null, value); - }); + callback(null, value); }); /** @@ -527,9 +522,7 @@ Datasource.setMethod(function _valueToDatasource(field, value, data, callback) { * @version 1.1.0 */ Datasource.setMethod(function _valueToApp(field, query, options, value, callback) { - Blast.setImmediate(function immediateDelay() { - callback(null, value); - }); + callback(null, value); }); /** diff --git a/lib/class/document.js b/lib/class/document.js index a7e8aa9c..4522a59c 100644 --- a/lib/class/document.js +++ b/lib/class/document.js @@ -27,68 +27,55 @@ var Document = Function.inherits('Alchemy.Base', 'Alchemy.Document', function Do * * @author Jelle De Loecker * @since 0.2.0 - * @version 1.0.6 + * @version 1.4.0 * * @param {Model|string} model_param */ Document.setStatic(function getDocumentClass(model_param) { - var doc_constructor, - document_name, - parent_path, - model_name, - namespace, - doc_path, - DocClass, - model, - alias; - if (!model_param) { throw new Error('No model name was given, can not get Document class'); } - if (typeof model_param == 'function') { - model = model_param; - } else { - model = Model.get(model_param, false); - } + let model = typeof model_param == 'function' ? model_param : Model.get(model_param, false); if (!model) { throw new Error('There is no model named "' + model_param + '"'); } - // Unfortunately we need the model_name right now, - // though sometimes it's set in the future. - // This happens when we need to do a setDocumentMethod - if (model.prototype.name) { - model_name = model.prototype.name; - } else { - model_name = model.name; - } + // Model name like "System_Task" or "User" + let model_name = model.model_name || model.name; - // Make sure we got a model name if (!model_name) { throw new Error('Tried to get nameless document class'); } - // Get the namespace - namespace = 'Alchemy.Document'; + // Get the Document namespace + let namespace = 'Alchemy.Document'; - // Get the name of the document class - document_name = model.name; + let model_namespace = model.model_namespace; - doc_path = namespace + '.' + document_name; + if (model_namespace) { + namespace += '.' + model_namespace; + } + + // The name of the document class will be the same as the name of the model + let document_name = model.name; + + let doc_path = namespace + '.' + document_name; // Get the document class - DocClass = Object.path(Classes, doc_path); + let DocClass = Object.path(Classes, doc_path); if (DocClass == null) { // Create the document constructor function - doc_constructor = Function.create(document_name, function med(data, options) { + let doc_constructor = Function.create(document_name, function med(data, options) { med.wrapper.super.call(this, data, options); }); + let parent_path; + if (!model.super || model.super.name == 'Model' || !model.super.Document) { parent_path = 'Alchemy.Document.Document'; } else { @@ -101,6 +88,9 @@ Document.setStatic(function getDocumentClass(model_param) { DocClass = Function.inherits(parent_path, doc_constructor); } + // Set a reference to the Model class + DocClass.Model = model; + // Set the getter for this document alias itself DocClass.setAliasGetter(model_name); @@ -110,13 +100,12 @@ Document.setStatic(function getDocumentClass(model_param) { // Set the path to this document DocClass.setProperty('path_to_class', doc_path); + let alias; + // Set association getters for (alias in model.schema.associations) { DocClass.setAliasGetter(alias); } - - // Set a reference to the Model class - DocClass.Model = model; } return DocClass; @@ -127,17 +116,10 @@ Document.setStatic(function getDocumentClass(model_param) { * * @author Jelle De Loecker * @since 1.0.0 - * @version 1.0.0 - * - * @param {string} model_name + * @version 1.4.0 */ -Document.setStatic(function getClientDocumentClass(model_name) { - - if (!model_name) { - model_name = this.prototype.$model_name; - } - - return Classes.Alchemy.Client.Document.Document.getDocumentClass(model_name); +Document.setStatic(function getClientDocumentClass() { + return Classes.Alchemy.Client.Document.Document.getDocumentClass(this.Model); }); /** @@ -154,14 +136,11 @@ Document.setStatic(function getClientDocumentClass(model_name) { */ Document.setStatic(function unDry(obj, cloned) { - var DocClass, - result; - // Get the document class - DocClass = this.getDocumentClass(obj.$model_name); + const DocClass = this.getDocumentClass(obj.$model_name); // Create a new instance, without constructing it yet - result = Object.create(DocClass.prototype); + const result = Object.create(DocClass.prototype); // Restore the attributes object if there is one if (obj.$_attributes) { diff --git a/lib/class/field.js b/lib/class/field.js index dbc3065d..903da29a 100644 --- a/lib/class/field.js +++ b/lib/class/field.js @@ -855,7 +855,7 @@ Field.setMethod(function translatableToDatasource(value, data, datasource, callb * * @author Jelle De Loecker * @since 0.2.0 - * @version 0.2.0 + * @version 1.4.0 * * @param {Object} query The original query * @param {Object} options The original query options @@ -863,14 +863,6 @@ Field.setMethod(function translatableToDatasource(value, data, datasource, callb * @param {Function} callback */ Field.setMethod(function toApp(query, options, value, callback) { - - var that = this, - tasks; - - if (value == null) { - return callback(null, null); - } - if (this.is_translatable) { this.fromTranslatableToApp(query, options, value, callback); } else { diff --git a/lib/class/migration.js b/lib/class/migration.js index a0ae656a..a660c6c6 100644 --- a/lib/class/migration.js +++ b/lib/class/migration.js @@ -24,7 +24,7 @@ const Migration = Function.inherits('Alchemy.Base', function Migration(document) */ Migration.setStatic(async function start() { - const AlchemyMigration = Model.get('AlchemyMigration'); + const AlchemyMigration = Model.get('System.Migration'); console.log('Starting migration task...'); diff --git a/lib/class/model.js b/lib/class/model.js index c270b765..ef0db774 100644 --- a/lib/class/model.js +++ b/lib/class/model.js @@ -13,24 +13,68 @@ var nameCache = {}, * @since 0.0.1 * @version 1.1.0 */ -var Model = Function.inherits('Alchemy.Base', 'Alchemy.Model', function Model(options) { +const Model = Function.inherits('Alchemy.Base', 'Alchemy.Model', function Model(options) { this.init(options); }); +/** + * This is a wrapper class + */ +Model.makeAbstractClass(); + +/** + * This wrapper class starts a new group + */ +Model.startNewGroup(); + /** * Set the modelName property after class creation * * @author Jelle De Loecker * @since 0.2.0 - * @version 1.1.0 + * @version 1.4.0 */ -Model.constitute(function setModelName() { - this.model_name = this.name; - this.setProperty('model_name', this.model_name); +Model.postInherit(function setModelName() { + + let base_name = this.name; + let model_name = base_name; + let table_prefix = this.table_prefix; + let namespace = this.namespace; + + if (namespace.startsWith('Alchemy.Model.') || namespace == 'Alchemy.Model') { + namespace = namespace.slice(14); + } else if (namespace.startsWith('Alchemy.Client.Model.') || namespace == 'Alchemy.Client.Model') { + namespace = namespace.slice(21); + } + + this.setStatic('model_namespace', namespace, false); + + let ns = namespace.replaceAll('.', '_'); + + if (!table_prefix && ns) { + table_prefix = ns.tableize(); + } - if (!this.table) { - this.table = this.model_name.tableize(); + if (ns) { + model_name = ns + '_' + model_name; } + + if (model_name[0] == '_') { + console.log(ns, model_name) + throw new Error('KAK') + } + + // The simple name of the model + this.model_name = model_name; + this.setProperty('model_name', model_name); + + let table_name = base_name.tableize(); + + if (table_prefix) { + table_name = table_prefix + '_' + table_name; + } + + this.table = table_name; }); /** @@ -130,7 +174,6 @@ Model.setStaticProperty(function is_abstract() { return field_count < 1; }); - /** * Get the document class constructor * @@ -171,7 +214,7 @@ Model.staticCompose('schema', function createSchema(doNext) { schema.setModel(model); // Set the schema name - schema.setName(model.name); + schema.setName(model.model_name); if (model.prototype.add_basic_fields !== false) { @@ -285,16 +328,6 @@ Model.setProperty(function is_abstract() { return this.constructor.is_abstract; }); -/** - * This is a wrapper class - */ -Model.makeAbstractClass(); - -/** - * This wrapper class starts a new group - */ -Model.startNewGroup(); - /** * The connection * @@ -423,6 +456,16 @@ Model.setStatic(function addField(name, type, options) { return field; }); +/** + * Set the wanted table prefix + * + * @author Jelle De Loecker + * @since 1.4.0 + * @version 1.4.0 + */ +Model.setStatic(function setTablePrefix(prefix) { + this.setStatic('table_prefix', prefix); +}); /** * Add a behaviour to this model @@ -539,15 +582,17 @@ Model.setStatic(function getField(name) { Model.setStatic(function getClientConfig() { const result = { - name : this.model_name, - schema : this.schema, - primary_key : this.prototype.primary_key, - display_field : this.prototype.display_field, - ancestors : 0, + class_name : this.name, + model_name : this.model_name, + model_namespace : this.model_namespace || undefined, + schema : this.schema, + primary_key : this.prototype.primary_key, + display_field : this.prototype.display_field, + ancestors : 0, }; if (this.super.name != 'Model') { - result.parent = this.super.name; + result.parent = this.super.model_name; let ancestors = 0, ancestor = this.super; @@ -1730,7 +1775,7 @@ Model.setMethod(function importFromStream(input, options) { * * @author Jelle De Loecker * @since 0.0.1 - * @version 1.1.0 + * @version 1.4.0 * * @param {string} name * @param {boolean} init @@ -1739,79 +1784,24 @@ Model.setMethod(function importFromStream(input, options) { */ Model.get = function get(name, init, options) { - var constructor, - namespace, - pieces, - path, - obj; - - if (typeof name == 'function') { - name = name.name; - } - - if (!name) { - throw new TypeError('Model name should be a non-empty string'); - } - - if (init && typeof init == 'object') { + if (typeof init != 'boolean') { options = init; init = true; } - if (!options) { - options = {}; - } + let path = Blast.parseClassPath(name); - if (nameCache[name] && options.cache !== false) { - if (init === false) { - return nameCache[name]; - } else { - return new nameCache[name]; - } - } - - pieces = name.split('.'); - - if (pieces.length > 1) { - // The first part is the namespace - namespace = pieces.shift(); - - // The rest should be the path - path = pieces.join('.'); - - obj = Classes[namespace]; + let constructor = Object.path(Blast.Classes.Alchemy.Model, path) || Object.path(Blast.Classes, path); - if (!obj) { - if (init === false) { - return null; - } - - throw new TypeError('Namespace "' + namespace + '" could not be found'); - } - } else { - path = name; - obj = Classes.Alchemy.Model; + if (!constructor) { + throw new Error('Model "' + name + '" could not be found'); } - constructor = Object.path(obj, path) || obj[String(path).modelName()]; - - if (constructor == null) { - if (init === false) { - return null; - } - - throw new Error('Could not find model "' + name + '"'); - } - - // Store this name in the cache, - // so we don't have to perform the expensive #modelName() method again - nameCache[name] = constructor; - - if (init === false) { + if (!init) { return constructor; - } else { - return new constructor; } + + return new constructor(options); }; /** @@ -1823,4 +1813,4 @@ Model.get = function get(name, init, options) { * * @type {Object} */ -global.Model = Model; \ No newline at end of file +DEFINE('Model', Model); \ No newline at end of file diff --git a/lib/class/plugin.js b/lib/class/plugin.js index f89acd01..c95a1d24 100644 --- a/lib/class/plugin.js +++ b/lib/class/plugin.js @@ -258,6 +258,10 @@ Plugin.setMethod(function useFile(path, options) { options.arguments = argument_configuration; } + if (typeof path == 'string' && path[0] != '/') { + path = libpath.resolve(this.path, path); + } + return alchemy.useOnce(path, options); }); @@ -305,4 +309,4 @@ Plugin.setMethod(function addRoute(config) { */ Plugin.setMethod(Symbol.for('janeway_arg_right'), function janewayInstanceInfo() { return this.name; -}); \ No newline at end of file +}); diff --git a/lib/class/schema.js b/lib/class/schema.js index 51d521a0..db8608b7 100644 --- a/lib/class/schema.js +++ b/lib/class/schema.js @@ -280,7 +280,7 @@ Schema.setMethod(function addAssociation(type, relation_config, alias, modelName modelName = args.modelName; options = args.options; options.singular = is_singular; - className = this.model_name; + className = this.model_class_name; if (this.namespace) { path = this.namespace + '.' + className; diff --git a/lib/class/schema_client.js b/lib/class/schema_client.js index 280ba553..f61c65d9 100644 --- a/lib/class/schema_client.js +++ b/lib/class/schema_client.js @@ -239,7 +239,7 @@ Schema.setMethod(function addAssociation(relation_type, relation_config, alias, * * @author Jelle De Loecker * @since 0.2.0 - * @version 1.3.21 + * @version 1.4.0 * * @param {boolean} is_internal Is this internal (A remote record's id is stored inside this record) * @param {string} alias @@ -257,6 +257,7 @@ Schema.setMethod(function getAssociationArguments(is_internal, alias, model_name if (typeof model_name === 'undefined') { model_name = alias; + alias = null; } let local_key = options.local_key || options.localKey || false, @@ -270,6 +271,16 @@ Schema.setMethod(function getAssociationArguments(is_internal, alias, model_name throw new Error('Foreign key for ' + alias + ' association can not be an object'); } + // Get the actual model + let model = this.getModel(model_name); + + // And get the correct model_name + model_name = model.model_name; + + if (!alias) { + alias = model_name; + } + if (is_internal) { if (!local_key) { @@ -277,13 +288,11 @@ Schema.setMethod(function getAssociationArguments(is_internal, alias, model_name } if (!foreign_key) { - let model = this.getModel(model_name); foreign_key = model.primary_key || '_id'; } } else { if (!local_key) { - let model = this.getModel(model_name); local_key = model.primary_key || '_id'; } @@ -516,7 +525,7 @@ Schema.setMethod(function setModel(model) { if (typeof model == 'string') { path = model; } else { - path = model.name; + path = model.model_name; // See if this passed model is a constructor if (model.staticChain) { @@ -541,9 +550,10 @@ Schema.setMethod(function setModel(model) { } } - model_name = constructor.name; + model_name = constructor.model_name; namespace = constructor.namespace; + this.model_class_name = constructor.name; this.model_name = model_name; this.model_class = constructor; this.namespace = namespace; diff --git a/lib/class/task.js b/lib/class/task.js index 785084be..673f78f3 100644 --- a/lib/class/task.js +++ b/lib/class/task.js @@ -329,7 +329,7 @@ Task.setMethod(async function start(payload) { this.setPayload(payload); } - const History = Model.get('AlchemyTaskHistory'); + const History = Model.get('System.TaskHistory'); this[STATUS] = STARTED; this[RUNNING_PLEDGE] = new Pledge(); diff --git a/lib/class/task_service.js b/lib/class/task_service.js index d45ad041..47fd87fc 100644 --- a/lib/class/task_service.js +++ b/lib/class/task_service.js @@ -19,7 +19,7 @@ const Service = Function.inherits('Alchemy.Base', 'Alchemy.Task', function TaskS } // Get the AlchemyTask model - this.AlchemyTask = Model.get('AlchemyTask'); + this.AlchemyTask = Model.get('System.Task'); // Keep track of all running tasks this.running_tasks = []; @@ -385,7 +385,7 @@ Service.setMethod(async function rescheduleTasksOfType(task_class) { schedules.clearAll(); - const Task = Model.get('AlchemyTask'); + const Task = Model.get('System.Task'); const crit = Task.find(); crit.where('type').equals(task_class.type_path); const records = await Task.find('all', crit); @@ -693,7 +693,7 @@ class TaskSchedule { */ async getHistoryDocumentForDate(date) { - const TaskHistory = Model.get('AlchemyTaskHistory'); + const TaskHistory = Model.get('System.TaskHistory'); const crit = TaskHistory.find(); crit.where('scheduled_at').equals(date); diff --git a/lib/core/alchemy_functions.js b/lib/core/alchemy_functions.js index 0b191efb..a37bfccf 100644 --- a/lib/core/alchemy_functions.js +++ b/lib/core/alchemy_functions.js @@ -1454,12 +1454,12 @@ Alchemy.setMethod(function exposeDefaultStaticVariables() { // Sort the models by their ancestor count model_info.sortByPath(1, 'ancestors'); - model_code = `function inheritModel(parent, child) { - return Classes.Hawkejs.Model.getClass(child, true, parent); + model_code = `function inheritModel(parent_model_name, child_model_name) { + return Classes.Hawkejs.Model.getClass(child_model_name, true, parent_model_name); }\n`; for (let info of model_info) { - model_code += 'inheritModel(' + JSON.stringify(info.parent) + ', ' + JSON.stringify(info.name) + ')\n'; + model_code += 'inheritModel(' + JSON.stringify(info.parent) + ', ' + JSON.stringify(info.model_name) + ')\n'; } // Expose the model configuration diff --git a/lib/core/alchemy_load_functions.js b/lib/core/alchemy_load_functions.js index 9cefceec..6a49aaea 100644 --- a/lib/core/alchemy_load_functions.js +++ b/lib/core/alchemy_load_functions.js @@ -508,6 +508,8 @@ Alchemy.setMethod(function addViewDirectory(dirPath, weight) { * * @param {string} name The name of the plugin (which is its path) * @param {Object} options Options to pass to the plugin + * + * @return {Alchemy.Plugin} */ Alchemy.setMethod(function usePlugin(name, options) { @@ -575,7 +577,9 @@ Alchemy.setMethod(function usePlugin(name, options) { // Set the given options alchemy.plugins[name] = instance; - return instance.doPreload(); + instance.doPreload(); + + return instance; }); /** diff --git a/lib/core/base.js b/lib/core/base.js index 1988a87d..1ee902c3 100644 --- a/lib/core/base.js +++ b/lib/core/base.js @@ -574,17 +574,23 @@ Base.setMethod(function attachConduit(conduit) { */ Base.setMethod(function getModel(name, init, options) { - var instance; - if (typeof init != 'boolean') { options = init; init = true; } + let constructor = Model.get(name, false); + + if (!constructor) { + throw new Error('Model "' + name + '" could not be found'); + } + if (!init) { - return Model.get(name, false, options); + return constructor; } + let instance; + if (!options) { options = {}; } @@ -605,7 +611,7 @@ Base.setMethod(function getModel(name, init, options) { return instance; } - instance = Model.get(name, options); + instance = new constructor(options); if (conduit) { instance.attachConduit(conduit); diff --git a/lib/core/client_base.js b/lib/core/client_base.js index 1ce39092..82970fae 100644 --- a/lib/core/client_base.js +++ b/lib/core/client_base.js @@ -231,7 +231,7 @@ ClientBase.setStatic(function mapEventToMethod(event_name, method_name) { * * @author Jelle De Loecker * @since 1.0.0 - * @version 1.2.4 + * @version 1.4.0 * * @param {string} name * @param {Object} options @@ -240,33 +240,12 @@ ClientBase.setStatic(function mapEventToMethod(event_name, method_name) { */ ClientBase.setMethod(function getModel(name, init, options) { - var constructor, - instance; - if (typeof init != 'boolean') { options = init; init = true; } - if (!options || !options.strict_name) { - name = name.modelName(); - } - - if (Blast.isBrowser) { - - if (name == '') { - constructor = Hawkejs.Model; - } else { - constructor = Hawkejs.Model.getClass(name, false); - } - } else { - - if (name == '') { - constructor = Blast.Classes.Hawkejs.Model; - } else { - constructor = Blast.Classes.Hawkejs.Model.getClass(name); - } - } + let constructor = Classes.Alchemy.Client.Model.Model.getClass(name); if (!constructor) { throw new Error('Model "' + name + '" could not be found'); @@ -276,7 +255,7 @@ ClientBase.setMethod(function getModel(name, init, options) { return constructor; } - instance = new constructor(options); + let instance = new constructor(options); let conduit = this.conduit; diff --git a/lib/stages/20-settings.js b/lib/stages/20-settings.js index 43bb6f2a..5eb96730 100644 --- a/lib/stages/20-settings.js +++ b/lib/stages/20-settings.js @@ -27,7 +27,7 @@ settings.dependsOn('datasource.connect'); */ const load = settings.createStage('load', async () => { - let records = await Model.get('AlchemySetting').find('all'); + let records = await Model.get('System.Setting').find('all'); if (!records.length) { return;