nope-js-browser
Version:
NoPE Runtime for the Browser. For nodejs please use nope-js-node
251 lines (250 loc) • 9.89 kB
JavaScript
/**
* @author Martin Karkowski
* @email m.karkowski@zema.de
*/
import { deepClone } from "../helpers/objectMethods";
import { getNopeLogger } from "../logger/getLogger";
import { NopeBaseModule } from "./BaseModule";
function _invertMode(options) {
const _ret = deepClone(options);
if (Array.isArray(_ret.mode)) {
if (_ret.mode.includes("subscribe")) {
_ret.mode = ["publish", "subscribe"];
}
else {
_ret.mode = ["subscribe"];
}
}
else if (_ret.mode === "subscribe") {
_ret.mode = ["publish", "subscribe"];
}
else {
_ret.mode = ["subscribe"];
}
return _ret;
}
/**
* A Generic Wrapper
*
* @author M.Karkowski
* @export
* @class NopeGenericModule
* @extends {NopeBaseModule}
*/
export class NopeGenericWrapper extends NopeBaseModule {
/**
*
* Return the Class Identifier.
*/
get type() {
return this._type;
}
/**
* Return the Class Identifier.
*/
set type(value) {
this._type = value;
}
/**
* Function, used to add the Attributes based on the Description.
*
* @param {INopeModuleDescription} description
* @param {('overwrite' | 'add')} [mode='overwrite']
* @memberof NopeGenericModule
*/
async fromDescription(description, mode = "overwrite") {
const _this = this;
if (mode === "overwrite") {
await this.dispose();
this.author = description.author;
this.description = description.description;
this.type = description.type;
this.identifier = description.identifier;
}
if (this.author == null) {
this.author = description.author;
}
if (this.description == null) {
this.description = description.description;
}
if (this.version == null) {
this.version = description.version;
}
if (this.identifier == null) {
this.identifier = description.identifier;
this._logger = getNopeLogger("generic-wrapper-" + this.identifier, "debug");
}
for (const name in description.methods) {
this._logger.debug('Create function interface for "' + name + '"');
const options = description.methods[name];
const func = (...args) => {
return _this._core.rpcManager.performCall(options.id, args, options);
};
const funcWithCustomOptions = (_options, ...args) => {
return _this._core.rpcManager.performCall(options.id, args, Object.assign({}, options, _options));
};
if (this.dynamicInstanceMethods[name]) {
throw Error("Name alread used. Not able to use the name twice");
}
this.dynamicInstanceMethods[name] = func;
if (this.dynamicInstanceMethodsWithOptions[name]) {
throw Error("Name alread used. Not able to use the name twice");
}
this.dynamicInstanceMethodsWithOptions[name] =
funcWithCustomOptions;
// If the Function isnt dynamic, register it on the Object itself.
if (!options.isDynamic) {
if (this[name]) {
throw Error("Name alread used. Not able to use the name twice");
}
this[name] = func;
}
this._registeredMethods.set(name, {
method: func,
options,
});
}
if (this._linkProperties) {
for (const name in description.properties) {
this._logger.debug('Create property interface for "' + name + '"');
const options = description.properties[name];
// Add only elements, that are subscribed.
// Properties, which are only publishing
// should throw an error, if data is published
// in a remote. This is done to maintain
// consistency.
// let mode = prop.mode;
if (this.dynamicInstanceProperties[name]) {
throw Error("Name alread used. Not able to use the name twice");
}
// Make shure it isnt published.
// options.preventSendingToRegistery = true;
// Register the Observable:
this.dynamicInstanceProperties[name] =
this._core.dataDistributor.register(
// Assign a new Observable.
this._observableFactory(),
// Use the provided Properties:
_invertMode(options));
if (!options.isDynamic) {
if (this[name]) {
throw Error("Name alread used. Not able to use the name twice");
}
// Use the Same Element.
this[name] = this.dynamicInstanceProperties[name];
}
this._logger.debug('Register Property "' + name + '"', options);
this._registeredProperties.set(name, {
observable: this.dynamicInstanceProperties[name],
options,
});
}
}
if (this._linkEvents) {
for (const name in description.events) {
this._logger.debug('Create property interface for "' + name + '"');
const options = description.events[name];
// Add only elements, that are subscribed.
// Properties, which are only publishing
// should throw an error, if data is published
// in a remote. This is done to maintain
// consistency.
// let mode = prop.mode;
if (this.dynamicInstanceEvents[name]) {
throw Error("Name alread used. Not able to use the name twice");
}
// Make shure it isnt published.
// options.preventSendingToRegistery = true;
// Register the Observable:
this.dynamicInstanceEvents[name] = this._core.eventDistributor.register(
// Assign a new Observable.
this._emitterFactory(),
// Use the provided Properties:
_invertMode(options));
if (!options.isDynamic) {
if (this[name]) {
throw Error("Name alread used. Not able to use the name twice");
}
// Use the Same Element.
this[name] = this.dynamicInstanceEvents[name];
}
this._logger.debug('Register Property "' + name + '"', options);
this._registeredEvents.set(name, {
emitter: this.dynamicInstanceEvents[name],
options,
});
}
}
}
/**
* Creates an instance of NopeGenericModule.
* @param {INopeDispatcher} _core
* @param {() => INopeObservable<any>} _observableFactory
* @memberof NopeGenericModule
*/
constructor(_core, _emitterFactory, _observableFactory, _linkProperties = true, _linkEvents = true) {
super(_core);
this._emitterFactory = _emitterFactory;
this._observableFactory = _observableFactory;
this._linkProperties = _linkProperties;
this._linkEvents = _linkEvents;
this._type = "";
this.dynamicInstanceMethods = {};
this.dynamicInstanceProperties = {};
this.dynamicInstanceEvents = {};
this.dynamicInstanceMethodsWithOptions = {};
}
async listMethods() {
const _this = this;
return Object.getOwnPropertyNames(this.dynamicInstanceMethods).map((name) => {
return {
method: _this.dynamicInstanceMethods[name],
options: null,
};
});
}
async registerProperty(name, observable, options) {
throw Error("Function Should not be called on remote!");
}
async registerMethod(name, func, options) {
throw Error("Function Should not be called on remote!");
}
async unregisterFunction(name) {
throw Error("Function Should not be called on remote!");
}
async unregisterProperty(name) {
throw Error("Function Should not be called on remote!");
}
async init() {
try {
await super.init();
}
catch (e) {
throw Error("Call fromDescription before using");
}
}
async dispose() {
for (const name in this.dynamicInstanceProperties) {
// Unregister all Properties.
this._core.dataDistributor.unregister(this[name]);
this.dynamicInstanceProperties[name].dispose();
// Remove Reference
delete this[name];
}
for (const name in this.dynamicInstanceEvents) {
// Unregister all Properties.
this._core.eventDistributor.unregister(this[name]);
this.dynamicInstanceEvents[name].dispose();
// Remove Reference
delete this[name];
}
this.dynamicInstanceProperties = {};
for (const name in this.dynamicInstanceMethods) {
delete this[name];
delete this.dynamicInstanceMethods[name];
}
this._registeredProperties.clear();
this._registeredEvents.clear();
this._registeredMethods.clear();
}
}