models/store/model-store.js

import { MissingImplementation, IncorrectSyncType } from "../../errors.js";

/**
 * <p>
 * This is the base class for model stores, which are used to give the
 * {@link Models} management class a data backend to work with, allowing
 * you to save and load {@link Model} instances.
 * </p>
 *
 * <p>
 * For an example of a ModelStore implementation, see {@link FileSystemStore}.
 * </p>
 *
 * @hideconstructor
 */
export class ModelStore {
  constructor() {
    const prot = ModelStore.prototype;

    const subcon = this.constructor;
    const subprot = subcon.prototype;

    const names = Object.getOwnPropertyNames(prot);
    names.splice(names.indexOf(`constructor`), 1);
    names.forEach((name) => {
      const fn = prot[name];
      const fnstr = fn.toString();
      const argstr = fnstr.substring(fnstr.indexOf(`(`), fnstr.indexOf(`)`));

      const sfn = subprot[name] === prot[name] ? undefined : subprot[name];
      if (!sfn) {
        console.log(`${subcon.name} is missing ${name}?`, subprot);
        throw new MissingImplementation(
          `${this.constructor.name}.${name}(${argstr})`
        );
      }

      const syncType = fn[Symbol.toStringTag];
      if (sfn[Symbol.toStringTag] !== syncType) {
        throw new IncorrectSyncType(
          `${this.constructor.name}.${name}(${argstr})`,
          syncType
        );
      }
    });
  }

  /**
   * @return {boolean} whether this store is ready for use or not.
   */
  ready() {
    return false;
  }

  /**
   * This method must load a data object, keyed to both the indicated
   * <code>recordName</code> and schema. How your model store implementation
   * does that is up to you, and schemas could map to directories, or
   * database tables, or even database files, or completely different
   * disks or domains - all that matters is that given a schema and a
   * recordName, this method yields the data previously stored for that tuple.
   *
   * @param {schema} schema - The schema that this record conforms to.
   * @param {string} recordName - The key with which the record was previously saved.
   */
  async loadRecord(schema, recordName) {
    throw new MissingImplementation(
      `${this.__proto__.constructor.name}.loadRecord(schema, recordName)`
    );
  }

  /**
   * This method must save a data object, keyed to both the indicated
   * <code>recordName</code> and schema. How your model store implementation
   * does that is up to you, and schemas could map to directories, or
   * database tables, or even database files, or completely different
   * disks or domains - all that matters is that given a schema and a
   * recordName, this method saves data in a way that it can be loaded later.
   *
   * @param {schema} schema - The schema that this record conforms to.
   * @param {object} instance - The schema-conformant data that is to be saved.
   * @param {string} recordName - The key with which this data should be saved.
   */
  async saveRecord(schema, instance, recordName) {
    throw new MissingImplementation(
      `${this.__proto__.constructor.name}.saveRecord(schema, instance, recordName)`
    );
  }

  /**
   * This method must delete a data record, keyed to both the indicated
   * <code>recordName</code> and schema.
   *
   * @param {schema} schema - The schema that this record conforms to.
   * @param {string} recordName - The key for this data.
   */
  async deleteRecord(schema, recordName) {
    throw new MissingImplementation(
      `${this.__proto__.constructor.name}.saveRecord(schema, instance, recordName)`
    );
  }

  /**
   *
   * @param {*} schema
   */
  async loadSchema(schema) {
    throw new MissingImplementation(
      `${this.__proto__.constructor.name}.loadSchema(schema)`
    );
  }

  /**
   *
   * @param {*} schema
   */
  async saveSchema(schema) {
    throw new MissingImplementation(
      `${this.__proto__.constructor.name}.saveSchema(schem)`
    );
  }

  /**
   *
   * @param {*} schema1
   * @param {*} schema2
   * @param {*} migration
   */
  async saveMigration(schema1, schema2, migration) {
    throw new MissingImplementation(
      `${this.__proto__.constructor.name}.saveMigration(schema1, schema2, migration)`
    );
  }
}