Created
January 26, 2019 09:04
-
-
Save BluSyn/2033c1f74765d0fbdd91571e4518bd15 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* cache.js - es6 proxy cacher | |
* Copyright (c) 2019, Steven Bower (MIT License). | |
*/ | |
'use strict'; | |
/** | |
* Cache for Clients and Wallets | |
* Use with ES6 Proxy: | |
* eg, new Proxy(WalletClient, new Cache()); | |
* | |
* Cache key is serialized as: 'fuction__'+args.join('') | |
* example: client.getAccount('myaccount') | |
* is stored in Key "getaccountmyaccount" | |
* | |
* Note: You can force reset cache by calling __method, | |
* example: client.__getAccount() | |
* | |
* TODO: cache timestamp and auto-reload stale data? | |
*/ | |
class Cache { | |
constructor(options) { | |
options = options || {}; | |
this.cache = {}; | |
this.timer = {}; | |
// Cache time for individual keys (in ms) | |
// 0 or false to disable | |
this.timeout = Number(options.timeout) || false; | |
// Object depth limit when generating cache keys | |
this.keyDepth = Number(options.keyDepth) || 4; | |
} | |
clear() { | |
this.cache = {}; | |
} | |
get(target, prop, receiver) { | |
if (typeof target[prop] !== 'function') | |
return target[prop]; | |
let reset = (prop.substr(0, 2) === '__'); | |
prop = reset ? prop.substr(2) : prop; | |
return async (...args) => { | |
const key = prop + '__' + this.genKey(args); | |
const timed = this.timeout && | |
(new Date().getTime() - this.timer[key]) >= this.timeout; | |
if (!reset && !timed && this.cache[key]) { | |
return this.clone(this.cache[key]); | |
} | |
const data = await target[prop](...args); | |
this.cache[key] = data; | |
this.timer[key] = new Date().getTime(); | |
return this.clone(data); | |
}; | |
} | |
// cached results should return clone of data | |
// so data can't be accidentally mutated | |
clone(obj) { | |
if (!obj || typeof obj !== 'object') | |
return obj; | |
return Array.isArray(obj) ? | |
[...obj] : JSON.parse(JSON.stringify(obj)); | |
} | |
genKey(obj, depth) { | |
// "falsey" values should still return differently | |
if (!obj) | |
return obj; | |
depth = depth ? depth + 1 : 1; | |
if (depth > this.keyDepth) | |
return ''; | |
if (Array.isArray(obj)) { | |
return (obj.map(o => this.genKey(o, depth))).join(''); | |
} | |
switch (typeof obj) { | |
case 'function': | |
return 'fn'; | |
case 'object': | |
return Object.keys(obj) | |
.map((key) => { | |
return key + this.genKey(obj[key], depth); | |
}).join(''); | |
default: | |
return obj; | |
} | |
} | |
} | |
module.exports = Cache; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment