ram-manager/index.js

const DatabaseManager = require('../database-manager')
const {
  ExtendedMap
} = require('../../util')

class RAMManager extends DatabaseManager {
  /**
   * Class representing a database manager.
   * @extends {DatabaseManager}
   */
  constructor (options) {
    super(options)
    /**
     * @type    {Map<String, ExtendedMap<String, DatabaseObject>>}
     * @private
     */
    this._store = new Map()
    /**
     * @type    {Map<String, Number>}
     * @private
     */
    this._idCount = new Map()
  }

  /**
   * Add a new data store if needed.
   * @private
   * @param   {string} type     The type of DatabaseObject.
   * @param   {any}    [object] Optional object to insert.
   * @returns {void}
   */
  _addStoreIfNeeded (type, object) {
    if (!this._store.get(type)) {
      this._store.set(type, new ExtendedMap())
      this._idCount.set(type, 0)
    }

    if (object) {
      this._store.get(type).set(object.id, object)
    }
  }

  _passingValue (record, query) {
    return Object.keys(query.conditions).every((condition) => {
      switch (query.conditions[condition].type) {
        case 'equalTo':
          return record[condition] === query.conditions[condition].value
        case 'notEqualTo':
          return record[condition] !== query.conditions[condition].value
        case 'lessThan':
          return record[condition] < query.conditions[condition].value
        case 'greaterThan':
          return record[condition] > query.conditions[condition].value
        default: throw Error('messed up query')
      }
    })
  }

  _filterRecord (record, query) {
    let passing = this._passingValue(record, query)

    for (const subQuery of query.subQueries) {
      switch (subQuery.type) {
        case 'and':
          if (passing) {
            passing = subQuery.query.every((q) => this._filterRecord(record, q))
          }
          break
        case 'or':
          if (!passing) {
            passing = subQuery.query.some((q) => this._filterRecord(record, q))
          }
          break
      }
    }

    return passing
  }

  async get (query) {
    this._addStoreIfNeeded(query.type)
    return this._store.get(query.type)
      .find((i) => i[query.getKey] === query.getValue)
  }

  async find (query) {
    this._addStoreIfNeeded(query.type)
    return [ ...this._store.get(query.type).values() ]
      .filter((record) => this._filterRecord(record, query))
  }

  async add (type, data) {
    this._addStoreIfNeeded(type)
    const id = this._idCount.get(type) + 1
    this._idCount.set(type, id)

    this._store.get(type).set(id, data)
    return this._store.get(type).get(id)
  }

  async delete (object) {
    this._addStoreIfNeeded(object.type, object)
    this._store.get(object.type).delete(object.id)
  }

  async update (object) {
    this._addStoreIfNeeded(object.type, object)
    if (!this._store.get(object.type).get(object.id)) {
      throw Error('entry does not exist')
    }
    this._store.get(object.type).set(object.id, object._data)
    return this._store.get(object.type).get(object.id)
  }
}

module.exports = RAMManager