Получилась конструкция, которая работает наподобие git'a.
Есть this._map с данными, которые записываются при загрузке модели из разных источников и this._index, в котором хранятся все изменения (diff) модели от this._map
Естественно, остальные имплементации моделей уже расширяют данный класс.
Base.js
const VError = require('verror');
const { fromComponentsSchemas } = require('../utils/schemaExtractor');
const STATES = {
UNDEFINED: 'UNDEFINED',
CREATING: 'CREATING',
LOADED: 'LOADED',
DELETED: 'DELETED'
};
/**
* Base class for our model structure, which takes the responsibility of getting and setting
*/
class ExtendableProxy {
constructor() {
return new Proxy(this, {
set: (target, property, value, receiver) => {
if (property.indexOf('_') !== 0) { // If not private property -> work with _index
if (this._state === STATES.UNDEFINED) { // Transition to CREATING state
this._state = STATES.CREATING;
}
if (!this._index[property] && this._map[property].value !== value) { // Check if not in index or not changed
this._index[property] = Object.assign({}, this._map[property]);
this._index[property].value = value;
} else if (this._index[property]) { // If exists -> needs to be updated
this._index[property].value = value;
}
return true;
}
target[property] = value;
return true;
},
get: (target, property, receiver) => {
// Check if not accessing the methods of the class
// and not the private properties
// and not symbol
// so we could validate the state of a model
if (typeof target[property] !== 'function' && typeof property !== 'symbol' && property.indexOf('_') !== 0) {
if (this._state === STATES.UNDEFINED) throw new Error('Model is in UNDEFINED state');
// TODO: Check if getter exists for this property and use it
return (this._index[property] && this._index[property].value) || (this._map[property] && this._map[property].value);
}
return target[property];
}
});
}
}
class Base extends ExtendableProxy {
/**
* Sets the source map for Model's variables
*/
constructor(deviceId, modelName) {
super();
this._state = STATES.UNDEFINED;
this._deviceId = deviceId;
this._map = fromComponentsSchemas(modelName).properties;
this._index = {}; // Index of model modifications
if (!this._map) throw new VError(`Schema ${modelName} is not found`);
}
/**
* Loads whole model from different sources, must be overrided
* @return {Promise.<T>}
*/
load() {
this._state = STATES.LOADED;
}
/**
* Sends the data from model to different sources, must be overrided
* @return {Promise.<T>}
*/
commit() {
}
/**
* Deletes the model in different sources, must be overrided
* @return {Promise.<T>}
*/
delete() {
this._state = STATES.DELETED;
}
/**
* Returns an array of names of modified variables
* @return {Array}
*/
difference() {
if (this._state === STATES.UNDEFINED) return [];
const diff = [];
for (let key in this._index) {
diff.push(key);
}
return diff;
}
}
Base.STATES = STATES;
module.exports = Base;
https://twitter.com/raulitojordan/status/1079085816209649664
https://twitter.com/raulitojordan/status/1079085816209649664
Есть this._map с данными, которые записываются при загрузке модели из разных источников и this._index, в котором хранятся все изменения (diff) модели от this._map
Естественно, остальные имплементации моделей уже расширяют данный класс.