Source: cache/index.js

/**
 * @module Cache
 * @author Vivek Kumar <vivek.kumar26@live.com>
 * @overview Cache Store Implementation.
 * @copyright Vivek Kumar 2018
 * @license MIT
 */
import EventEmitter from 'events';
import { pick, isNumeric } from '../utils';

/* eslint-disable arrow-parens */
/* eslint-disable consistent-return */
/* eslint-disable no-plusplus */

/** Cache Store Class */
class CacheStore extends EventEmitter {
  /**
   * Creates an instance of CacheStore.
   * @param {number} [ttl] TTL value for cache items in minutes.
   * @param {object} [config] Config / Options object.
   * @param {number} [config.maxSize] Maximum number of items to be stored in cache.
   * @memberof CacheStore
   */
  constructor(ttl, config) {
    super();
    /* Convert `ttl` from minutes to milliseconds. */
    this.ttl = (isNumeric(ttl) ? parseFloat(ttl) : 1) * 60 * 1000;
    this.cache = Object.create(null);
    this.queue = Object.create(null);
    this.timeoutIds = Object.create(null);
    this.maxSize = pick(config, ['maxSize'])
      ? Math.floor(config.maxSize) || 1000
      : 1000;
    this.cacheSize = 0;
    this.flush = false;
  }

  /**
   * Check whether `key` is present in cache object.
   * @param {string} key
   * @returns {boolean}
   * @memberof CacheStore
   */
  has(key) {
    return key in this.cache;
  }

  /**
   * Get `key` from cache object.
   * @param {string} key
   * @returns {any}
   * @memberof CacheStore
   */
  get(key) {
    return this.cache[key];
  }

  /**
   * Set `key` with `value` on cache object.
   * Cache Expiry is also handled in this method.
   * @param {string} key
   * @param {any} value
   * @memberof CacheStore
   */
  set(key, value) {
    this.cache[key] = value;

    /* Handle cache expiry */
    this.timeoutIds[key] = setTimeout(() => {
      delete this.cache[key];
      delete this.timeoutIds[key];
      this.cacheSize -= 1;
    }, this.ttl);
  }

  /**
   * Get the number of elements present in cache object.
   * Useful for tests.
   * @returns {number} Number of elements in cache.
   * @memberof CacheStore
   */
  getSize() {
    return this.cacheSize;
  }

  /**
   * Get the number of elements present in queue.
   * Useful for tests.
   * @returns {number} Number of elements in queue.
   * @memberof CacheStore
   */
  getQueueSize() {
    return Object.keys(this.queue).length;
  }

  /**
   * Clear / Reset the cache and timers.
   * Emits `flush-complete` event with stats upon completion.
   * @fires CacheStore#flush-complete
   * @memberof CacheStore
   */
  flushCache() {
    /* If items are present in queue, and flush is set, delay flushing */
    if (this.flush && this.getQueueSize()) {
      return setImmediate(() => this.flushCache());
    }

    /* Clear all timeouts */
    const timerKeys = Object.keys(this.timeoutIds);
    let i = timerKeys.length;
    let j = 0;
    while (i--) {
      clearTimeout(this.timeoutIds[timerKeys[j++]]);
    }
    /* For tests */
    const nKeys = this.cacheSize;

    /* Delete old objects */
    delete this.cache;
    delete this.queue;
    delete this.timeoutIds;

    /* Reset cache */
    this.cache = Object.create(null);
    this.queue = Object.create(null);
    this.timeoutIds = Object.create(null);
    this.cacheSize = 0;
    this.flush = false;

    /**
     * Flush Complete Event, good for tests.
     * @event CacheStore#flush-complete
     * @type {object}
     * @property {boolean} status Indicates status.
     * @property {number} nTimeouts Number of timeouts cleared.
     * @property {number} nKeys Number of cache items cleared.
     */
    this.emit('flush-complete', { status: true, nTimeouts: j, nKeys });
  }
}

/**
 * Function to create a new instance of Cache Store.
 * @param {number} [ttl] TTL value for cache items in minutes.
 * @param {object} [config] Config / Options object.
 * @param {number} [config.maxSize] Maximum number of items to be stored in cache.
 * @returns {object} Instance of CacheStore class.
 */
const createCacheStore = (ttl, config) => new CacheStore(ttl, config);

export default createCacheStore;