/** * @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;