Home Reference Source

src/EntityStore.js

import * as R from 'ramda';

import Rx from 'rxjs/Rx';
import EntitySubject from './entity/EntitySubject';


/**
 * @class {EntityStore}
 */
export default class EntityStore {

    /**
     * Constructs entity store
     * @param {Array} models - array of defined models
     */
    constructor(models) {
        /**
         * Saved array of models for resetting purposes
         * @member {Array}
         **/
        this._modelsArray = models;
        /**
         * Object with models where key is model name and value is model instance
         * @member {Object}
         **/
        this.models = {};
        /**
         * Object with entities where key is [model name][entity id] and value is entity state
         * @member {Object}
         **/
        this.heap = {};
        /**
         * Object with subjects where key is [model name][entity id] and value is entity state
         * @member {Object}
         **/
        this.observables = {};
        this._setupModelsAndHeap(models);
    }

    /**
     * Resolve all entities of model
     * @param {string} model - name of model
     * @returns {Array} - all kept entities as subject array
     */
    allEntities(model) {
        return Object.keys(this.heap[model]).map(key => {
            return this.getOrCreateEntitySubject(model, key);
        })
    }

    /**
     * Notifies all subscribers entity is updated
     * @param {string} model - name of model
     * @param {*} id - id of entity
     * @param {Object} value - result state
     * @param {EntitySubject} source - EntitySubject which caused update
     * @param {string} message - optional message for debugging purposes
     */
    updateEntity(model, id, value, source, message = '') {
        this.heap[model][id] = value;
        this.entityUpdated(model, id, source);
    }

    /**
     * Clears internal objects
     * @param {*} models - array of defined models
     */
    _setupModelsAndHeap(models) {
        models.forEach(model => {
            this.models[model.name] = model;
            this.heap[model.name] = {};
            this.observables[model.name] = {};
            model.attachStore(this);
        });
    }

    /**
     * Clears heap and observables
     */
    clear() {
        this._setupModelsAndHeap(this._modelsArray);
    }

    /**
     * Put or replace state of entity
     * @param {string} model - name of model
     * @param {*} id - entity id 
     * @param {Object} value - result state
     */
    putEntity(model, id, value) {
        this.heap[model][id] = value;
        this.getOrCreateEntitySubject(model, id).updateInterface();
    }

    /**
     * Delete entity from heap
     * @param {string} model - name of model
     * @param {*} id - entity id
     */
    deleteEntity(model, id) {
        delete this.heap[model][id];
        this.getOrCreateEntitySubject(model, id).updateInterface();
    }

    /**
     * Lazy EntitySubject evaluation
     * @see {@link EntitySubject}
     * @param {string} name - name of model
     * @param {*} id - entity id
     */
    getOrCreateEntitySubject(name, id) {
        const observable = this.observables[name][id];
        if (observable)
            return observable;
        else
            return this._createEntitySubject(name, id);
    }

    /**
     * Create and return subject
     * @see {@link EntityStore.getOrCreateEntitySubject}
     * @param {string} model - name of model
     * @param {*} id - entity id
     */
    _createEntitySubject(model, id) {
        this.observables[model][id] = new EntitySubject(model, id, new Rx.Subject(), this.models[model], this);
        return this.observables[model][id];
    }


    /**
     * Called when need to force update entity
     * @param {string} model - name of model
     * @param {*} id - entity id
     * @param {EntitySubject} source - cause of update
     */
    entityUpdated(model, id, source) {
        this.getOrCreateEntitySubject(model, id).observable.next({
            payload: this.heap[model][id],
            source
        });
    }
}