typescript-memoize
Advanced tools
Comparing version 1.0.1 to 1.1.0
@@ -1,2 +0,9 @@ | ||
export declare function Memoize(autoHashOrHashFn?: boolean | ((...args: any[]) => any)): (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>) => void; | ||
export declare function MemoizeExpiring(duration: number, autoHashOrHashFn?: boolean | ((...args: any[]) => any)): (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>) => void; | ||
interface MemoizeArgs { | ||
expiring?: number; | ||
hashFunction?: boolean | ((...args: any[]) => any); | ||
tags?: string[]; | ||
} | ||
export declare function Memoize(args?: MemoizeArgs | MemoizeArgs['hashFunction']): (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>) => void; | ||
export declare function MemoizeExpiring(expiring: number, hashFunction?: MemoizeArgs['hashFunction']): (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>) => void; | ||
export declare function clear(tags: string[]): number; | ||
export {}; |
@@ -1,8 +0,19 @@ | ||
export function Memoize(autoHashOrHashFn) { | ||
export function Memoize(args) { | ||
let hashFunction; | ||
let duration; | ||
let tags; | ||
if (typeof args === 'object') { | ||
hashFunction = args.hashFunction; | ||
duration = args.expiring; | ||
tags = args.tags; | ||
} | ||
else { | ||
hashFunction = args; | ||
} | ||
return (target, propertyKey, descriptor) => { | ||
if (descriptor.value != null) { | ||
descriptor.value = getNewFunction(descriptor.value, autoHashOrHashFn); | ||
descriptor.value = getNewFunction(descriptor.value, hashFunction, duration, tags); | ||
} | ||
else if (descriptor.get != null) { | ||
descriptor.get = getNewFunction(descriptor.get, autoHashOrHashFn); | ||
descriptor.get = getNewFunction(descriptor.get, hashFunction, duration, tags); | ||
} | ||
@@ -14,38 +25,54 @@ else { | ||
} | ||
export function MemoizeExpiring(duration, autoHashOrHashFn) { | ||
return (target, propertyKey, descriptor) => { | ||
if (descriptor.value != null) { | ||
descriptor.value = getNewFunction(descriptor.value, autoHashOrHashFn, duration); | ||
export function MemoizeExpiring(expiring, hashFunction) { | ||
return Memoize({ | ||
expiring, | ||
hashFunction | ||
}); | ||
} | ||
const clearCacheTagsMap = new Map(); | ||
export function clear(tags) { | ||
const cleared = new Set(); | ||
for (const tag of tags) { | ||
const maps = clearCacheTagsMap.get(tag); | ||
if (maps) { | ||
for (const mp of maps) { | ||
if (!cleared.has(mp)) { | ||
mp.clear(); | ||
cleared.add(mp); | ||
} | ||
} | ||
} | ||
else if (descriptor.get != null) { | ||
descriptor.get = getNewFunction(descriptor.get, autoHashOrHashFn, duration); | ||
} | ||
else { | ||
throw 'Only put a Memoize() decorator on a method or get accessor.'; | ||
} | ||
}; | ||
} | ||
return cleared.size; | ||
} | ||
let counter = 0; | ||
function getNewFunction(originalMethod, autoHashOrHashFn, duration = 0) { | ||
const identifier = ++counter; | ||
function getNewFunction(originalMethod, hashFunction, duration = 0, tags) { | ||
const propMapName = Symbol(`__memoized_map__`); | ||
return function (...args) { | ||
const propValName = `__memoized_value_${identifier}`; | ||
const propMapName = `__memoized_map_${identifier}`; | ||
let returnedValue; | ||
if (autoHashOrHashFn || args.length > 0 || duration > 0) { | ||
if (!this.hasOwnProperty(propMapName)) { | ||
Object.defineProperty(this, propMapName, { | ||
configurable: false, | ||
enumerable: false, | ||
writable: false, | ||
value: new Map() | ||
}); | ||
if (!this.hasOwnProperty(propMapName)) { | ||
Object.defineProperty(this, propMapName, { | ||
configurable: false, | ||
enumerable: false, | ||
writable: false, | ||
value: new Map() | ||
}); | ||
} | ||
let myMap = this[propMapName]; | ||
if (Array.isArray(tags)) { | ||
for (const tag of tags) { | ||
if (clearCacheTagsMap.has(tag)) { | ||
clearCacheTagsMap.get(tag).push(myMap); | ||
} | ||
else { | ||
clearCacheTagsMap.set(tag, [myMap]); | ||
} | ||
} | ||
let myMap = this[propMapName]; | ||
} | ||
if (hashFunction || args.length > 0 || duration > 0) { | ||
let hashKey; | ||
if (autoHashOrHashFn === true) { | ||
if (hashFunction === true) { | ||
hashKey = args.map(a => a.toString()).join('!'); | ||
} | ||
else if (autoHashOrHashFn) { | ||
hashKey = autoHashOrHashFn.apply(this, args); | ||
else if (hashFunction) { | ||
hashKey = hashFunction.apply(this, args); | ||
} | ||
@@ -78,13 +105,9 @@ else { | ||
else { | ||
if (this.hasOwnProperty(propValName)) { | ||
returnedValue = this[propValName]; | ||
const hashKey = this; | ||
if (myMap.has(hashKey)) { | ||
returnedValue = myMap.get(hashKey); | ||
} | ||
else { | ||
returnedValue = originalMethod.apply(this, args); | ||
Object.defineProperty(this, propValName, { | ||
configurable: false, | ||
enumerable: false, | ||
writable: false, | ||
value: returnedValue | ||
}); | ||
myMap.set(hashKey, returnedValue); | ||
} | ||
@@ -91,0 +114,0 @@ } |
@@ -1,2 +0,9 @@ | ||
export declare function Memoize(autoHashOrHashFn?: boolean | ((...args: any[]) => any)): (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>) => void; | ||
export declare function MemoizeExpiring(duration: number, autoHashOrHashFn?: boolean | ((...args: any[]) => any)): (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>) => void; | ||
interface MemoizeArgs { | ||
expiring?: number; | ||
hashFunction?: boolean | ((...args: any[]) => any); | ||
tags?: string[]; | ||
} | ||
export declare function Memoize(args?: MemoizeArgs | MemoizeArgs['hashFunction']): (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>) => void; | ||
export declare function MemoizeExpiring(expiring: number, hashFunction?: MemoizeArgs['hashFunction']): (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>) => void; | ||
export declare function clear(tags: string[]): number; | ||
export {}; |
@@ -12,10 +12,21 @@ (function (factory) { | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.MemoizeExpiring = exports.Memoize = void 0; | ||
function Memoize(autoHashOrHashFn) { | ||
exports.clear = exports.MemoizeExpiring = exports.Memoize = void 0; | ||
function Memoize(args) { | ||
var hashFunction; | ||
var duration; | ||
var tags; | ||
if (typeof args === 'object') { | ||
hashFunction = args.hashFunction; | ||
duration = args.expiring; | ||
tags = args.tags; | ||
} | ||
else { | ||
hashFunction = args; | ||
} | ||
return function (target, propertyKey, descriptor) { | ||
if (descriptor.value != null) { | ||
descriptor.value = getNewFunction(descriptor.value, autoHashOrHashFn); | ||
descriptor.value = getNewFunction(descriptor.value, hashFunction, duration, tags); | ||
} | ||
else if (descriptor.get != null) { | ||
descriptor.get = getNewFunction(descriptor.get, autoHashOrHashFn); | ||
descriptor.get = getNewFunction(descriptor.get, hashFunction, duration, tags); | ||
} | ||
@@ -28,20 +39,31 @@ else { | ||
exports.Memoize = Memoize; | ||
function MemoizeExpiring(duration, autoHashOrHashFn) { | ||
return function (target, propertyKey, descriptor) { | ||
if (descriptor.value != null) { | ||
descriptor.value = getNewFunction(descriptor.value, autoHashOrHashFn, duration); | ||
function MemoizeExpiring(expiring, hashFunction) { | ||
return Memoize({ | ||
expiring: expiring, | ||
hashFunction: hashFunction | ||
}); | ||
} | ||
exports.MemoizeExpiring = MemoizeExpiring; | ||
var clearCacheTagsMap = new Map(); | ||
function clear(tags) { | ||
var cleared = new Set(); | ||
for (var _i = 0, tags_1 = tags; _i < tags_1.length; _i++) { | ||
var tag = tags_1[_i]; | ||
var maps = clearCacheTagsMap.get(tag); | ||
if (maps) { | ||
for (var _a = 0, maps_1 = maps; _a < maps_1.length; _a++) { | ||
var mp = maps_1[_a]; | ||
if (!cleared.has(mp)) { | ||
mp.clear(); | ||
cleared.add(mp); | ||
} | ||
} | ||
} | ||
else if (descriptor.get != null) { | ||
descriptor.get = getNewFunction(descriptor.get, autoHashOrHashFn, duration); | ||
} | ||
else { | ||
throw 'Only put a Memoize() decorator on a method or get accessor.'; | ||
} | ||
}; | ||
} | ||
return cleared.size; | ||
} | ||
exports.MemoizeExpiring = MemoizeExpiring; | ||
var counter = 0; | ||
function getNewFunction(originalMethod, autoHashOrHashFn, duration) { | ||
exports.clear = clear; | ||
function getNewFunction(originalMethod, hashFunction, duration, tags) { | ||
if (duration === void 0) { duration = 0; } | ||
var identifier = ++counter; | ||
var propMapName = Symbol("__memoized_map__"); | ||
return function () { | ||
@@ -52,21 +74,30 @@ var args = []; | ||
} | ||
var propValName = "__memoized_value_" + identifier; | ||
var propMapName = "__memoized_map_" + identifier; | ||
var returnedValue; | ||
if (autoHashOrHashFn || args.length > 0 || duration > 0) { | ||
if (!this.hasOwnProperty(propMapName)) { | ||
Object.defineProperty(this, propMapName, { | ||
configurable: false, | ||
enumerable: false, | ||
writable: false, | ||
value: new Map() | ||
}); | ||
if (!this.hasOwnProperty(propMapName)) { | ||
Object.defineProperty(this, propMapName, { | ||
configurable: false, | ||
enumerable: false, | ||
writable: false, | ||
value: new Map() | ||
}); | ||
} | ||
var myMap = this[propMapName]; | ||
if (Array.isArray(tags)) { | ||
for (var _a = 0, tags_2 = tags; _a < tags_2.length; _a++) { | ||
var tag = tags_2[_a]; | ||
if (clearCacheTagsMap.has(tag)) { | ||
clearCacheTagsMap.get(tag).push(myMap); | ||
} | ||
else { | ||
clearCacheTagsMap.set(tag, [myMap]); | ||
} | ||
} | ||
var myMap = this[propMapName]; | ||
} | ||
if (hashFunction || args.length > 0 || duration > 0) { | ||
var hashKey = void 0; | ||
if (autoHashOrHashFn === true) { | ||
if (hashFunction === true) { | ||
hashKey = args.map(function (a) { return a.toString(); }).join('!'); | ||
} | ||
else if (autoHashOrHashFn) { | ||
hashKey = autoHashOrHashFn.apply(this, args); | ||
else if (hashFunction) { | ||
hashKey = hashFunction.apply(this, args); | ||
} | ||
@@ -99,13 +130,9 @@ else { | ||
else { | ||
if (this.hasOwnProperty(propValName)) { | ||
returnedValue = this[propValName]; | ||
var hashKey = this; | ||
if (myMap.has(hashKey)) { | ||
returnedValue = myMap.get(hashKey); | ||
} | ||
else { | ||
returnedValue = originalMethod.apply(this, args); | ||
Object.defineProperty(this, propValName, { | ||
configurable: false, | ||
enumerable: false, | ||
writable: false, | ||
value: returnedValue | ||
}); | ||
myMap.set(hashKey, returnedValue); | ||
} | ||
@@ -112,0 +139,0 @@ } |
{ | ||
"name": "typescript-memoize", | ||
"version": "1.0.1", | ||
"version": "1.1.0", | ||
"description": "Memoize decorator for Typescript", | ||
@@ -5,0 +5,0 @@ "main": "./dist/memoize-decorator.js", |
@@ -166,2 +166,13 @@ # typescript-memoize | ||
// Memoize also accepts parameters via a single object argument | ||
@Memoize({ | ||
expiring: 10000, | ||
hashFunction: (name: string, planet: string) => { | ||
return name + ';' + string; | ||
} | ||
}) | ||
public getSameBetterGreeting(name: string, planet: string) { | ||
return 'Hello, ' + name + '! Welcome to ' + planet; | ||
} | ||
} | ||
@@ -179,3 +190,3 @@ ``` | ||
// 'Hello, Darryl! Welcome to Mars' | ||
let greeeterVal2 = moreComplicatedFoo.getBetterGreeting('Darryl', 'Mars'); | ||
let greeterVal2 = moreComplicatedFoo.getBetterGreeting('Darryl', 'Mars'); | ||
@@ -186,1 +197,73 @@ // Fill up the computer with useless greetings: | ||
``` | ||
## Memoize accepts one or more "tag" strings that allow the cache to be invalidated on command | ||
Passing an array with one or more "tag" strings these will allow you to later clear the cache of results associated with methods or the `get`accessors using the `clear()` function. | ||
The `clear()` function also requires an array of "tag" strings. | ||
```typescript | ||
import {Memoize} from 'typescript-memoize'; | ||
class ClearableFoo { | ||
// Memoize accepts tags | ||
@Memoize({ tags: ["foo", "bar"] }) | ||
public getClearableGreeting(name: string, planet: string) { | ||
return 'Hello, ' + name + '! Welcome to ' + planet; | ||
} | ||
// Memoize accepts tags | ||
@Memoize({ tags: ["bar"] }) | ||
public getClearableSum(a: number, b: number) { | ||
return a + b; | ||
} | ||
} | ||
``` | ||
We call these methods from somewhere else in our code. | ||
```typescript | ||
import {clear} from 'typescript-memoize'; | ||
let clearableFoo = new ClearableFoo(); | ||
// 'Hello, Darryl! Welcome to Earth' | ||
let greeterVal1 = clearableFoo.getClearableGreeting('Darryl', 'Earth'); | ||
// Ignores the second parameter, and returns memoized value | ||
// 'Hello, Darryl! Welcome to Earth' | ||
let greeterVal2 = clearableFoo.getClearableGreeting('Darryl', 'Mars'); | ||
// '3' | ||
let sum1 = clearableFoo.getClearableSum(2, 1); | ||
// Ignores the second parameter, and returns memoized value | ||
// '3' | ||
let sum2 = clearableFoo.getClearableSum(2, 2); | ||
clear(["foo"]); | ||
// The memoized values are cleared, return a new value | ||
// 'Hello, Darryl! Welcome to Mars' | ||
let greeterVal3 = clearableFoo.getClearableGreeting('Darryl', 'Mars'); | ||
// The memoized value is not associated with 'foo' tag, returns memoized value | ||
// '3' | ||
let sum3 = clearableFoo.getClearableSum(2, 2); | ||
clear(["bar"]); | ||
// The memoized values are cleared, return a new value | ||
// 'Hello, Darryl! Welcome to Earth' | ||
let greeterVal4 = clearableFoo.getClearableGreeting('Darryl', 'Earth'); | ||
// The memoized values are cleared, return a new value | ||
// '4' | ||
let sum4 = clearableFoo.getClearableSum(2, 2); | ||
``` |
@@ -1,60 +0,92 @@ | ||
export function Memoize(autoHashOrHashFn?: boolean | ((...args: any[]) => any)) { | ||
interface MemoizeArgs { | ||
expiring?: number; | ||
hashFunction?: boolean | ((...args: any[]) => any); | ||
tags?: string[]; | ||
} | ||
export function Memoize(args?: MemoizeArgs | MemoizeArgs['hashFunction']) { | ||
let hashFunction: MemoizeArgs['hashFunction']; | ||
let duration: MemoizeArgs['expiring']; | ||
let tags: MemoizeArgs['tags']; | ||
if (typeof args === 'object') { | ||
hashFunction = args.hashFunction; | ||
duration = args.expiring; | ||
tags = args.tags; | ||
} else { | ||
hashFunction = args; | ||
} | ||
return (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>) => { | ||
if (descriptor.value != null) { | ||
descriptor.value = getNewFunction(descriptor.value, autoHashOrHashFn); | ||
descriptor.value = getNewFunction(descriptor.value, hashFunction, duration, tags); | ||
} else if (descriptor.get != null) { | ||
descriptor.get = getNewFunction(descriptor.get, autoHashOrHashFn); | ||
descriptor.get = getNewFunction(descriptor.get, hashFunction, duration, tags); | ||
} else { | ||
throw 'Only put a Memoize() decorator on a method or get accessor.'; | ||
} | ||
}; | ||
} | ||
export function MemoizeExpiring(duration: number, autoHashOrHashFn?: boolean | ((...args: any[]) => any)) { | ||
return (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>) => { | ||
export function MemoizeExpiring(expiring: number, hashFunction?: MemoizeArgs['hashFunction']) { | ||
return Memoize({ | ||
expiring, | ||
hashFunction | ||
}); | ||
} | ||
if (descriptor.value != null) { | ||
descriptor.value = getNewFunction(descriptor.value, autoHashOrHashFn, duration); | ||
} else if (descriptor.get != null) { | ||
descriptor.get = getNewFunction(descriptor.get, autoHashOrHashFn, duration); | ||
} else { | ||
throw 'Only put a Memoize() decorator on a method or get accessor.'; | ||
const clearCacheTagsMap: Map<string, Map<any, any>[]> = new Map(); | ||
export function clear (tags: string[]): number { | ||
const cleared: Set<Map<any, any>> = new Set(); | ||
for (const tag of tags) { | ||
const maps = clearCacheTagsMap.get(tag); | ||
if (maps) { | ||
for (const mp of maps) { | ||
if (!cleared.has(mp)) { | ||
mp.clear(); | ||
cleared.add(mp); | ||
} | ||
} | ||
} | ||
}; | ||
} | ||
return cleared.size; | ||
} | ||
let counter = 0; | ||
function getNewFunction(originalMethod: () => void, autoHashOrHashFn?: boolean | ((...args: any[]) => any), duration: number = 0) { | ||
const identifier = ++counter; | ||
function getNewFunction(originalMethod: () => void, hashFunction?: MemoizeArgs['hashFunction'], duration: number = 0, tags?: MemoizeArgs['tags']) { | ||
const propMapName = Symbol(`__memoized_map__`); | ||
// The function returned here gets called instead of originalMethod. | ||
return function (...args: any[]) { | ||
const propValName = `__memoized_value_${identifier}`; | ||
const propMapName = `__memoized_map_${identifier}`; | ||
let returnedValue: any; | ||
if (autoHashOrHashFn || args.length > 0 || duration > 0) { | ||
// Get or create map | ||
if (!this.hasOwnProperty(propMapName)) { | ||
Object.defineProperty(this, propMapName, { | ||
configurable: false, | ||
enumerable: false, | ||
writable: false, | ||
value: new Map<any, any>() | ||
}); | ||
} | ||
let myMap: Map<any, any> = this[propMapName]; | ||
// Get or create map | ||
if (!this.hasOwnProperty(propMapName)) { | ||
Object.defineProperty(this, propMapName, { | ||
configurable: false, | ||
enumerable: false, | ||
writable: false, | ||
value: new Map<any, any>() | ||
}); | ||
if (Array.isArray(tags)) { | ||
for (const tag of tags) { | ||
if (clearCacheTagsMap.has(tag)) { | ||
clearCacheTagsMap.get(tag).push(myMap); | ||
} else { | ||
clearCacheTagsMap.set(tag, [myMap]); | ||
} | ||
} | ||
let myMap: Map<any, any> = this[propMapName]; | ||
} | ||
if (hashFunction || args.length > 0 || duration > 0) { | ||
let hashKey: any; | ||
// If true is passed as first parameter, will automatically use every argument, passed to string | ||
if (autoHashOrHashFn === true) { | ||
if (hashFunction === true) { | ||
hashKey = args.map(a => a.toString()).join('!'); | ||
} else if (autoHashOrHashFn) { | ||
hashKey = autoHashOrHashFn.apply(this, args); | ||
} else if (hashFunction) { | ||
hashKey = hashFunction.apply(this, args); | ||
} else { | ||
@@ -87,13 +119,8 @@ hashKey = args[0]; | ||
} else { | ||
if (this.hasOwnProperty(propValName)) { | ||
returnedValue = this[propValName]; | ||
const hashKey = this; | ||
if (myMap.has(hashKey)) { | ||
returnedValue = myMap.get(hashKey); | ||
} else { | ||
returnedValue = originalMethod.apply(this, args); | ||
Object.defineProperty(this, propValName, { | ||
configurable: false, | ||
enumerable: false, | ||
writable: false, | ||
value: returnedValue | ||
}); | ||
myMap.set(hashKey, returnedValue); | ||
} | ||
@@ -100,0 +127,0 @@ } |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
29883
386
267