Skip to content

Commit

Permalink
Merge pull request #97 from angelnikolov/Modifiable-cache-experimenta…
Browse files Browse the repository at this point in the history
…tion-

RFC: Modifiable cache experimentation.
  • Loading branch information
angelnikolov authored Jan 11, 2021
2 parents 78cf9ff + 4bc712e commit 5561ed9
Show file tree
Hide file tree
Showing 72 changed files with 541 additions and 116 deletions.
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,13 @@ export interface ICacheConfig {
* @description should cache be resolved asynchronously? - helps with declarative forms and two-way databinding via ngModel
*/
async?: boolean;

/**
* Emit a callback which changes the caches and returns it.
* Whatever is returned from the callback will be automatically stored against the chosen cacheKey in the
* storage of the storage strategy you've chosen.
*/
cacheModifier?: Subject<(cachePairs: ICachePair<Observable<any>>[]) => ICachePair<Observable<any>>[]>;
}
```

Expand Down Expand Up @@ -190,6 +197,35 @@ and then provide it to your decorators as in the example above.
For the `PCacheable` decorator, we also support async promise-based caching strategies. Just extend the `IAsyncStorageStrategy` and provide it to your promise decorators and voila. You can use that to implement Redis caching, IndexDB or whatever async caching you might desire.

**IMPORTANT: Right now, we only support async strategies as a configuration for our promise decorators**

### Changing, accessing and mutating cache

You can get access to the cached data and change it by providing a `cacheModifier` subject to your decorator like this:
```ts
const cacheModifier = new Subject<any>();
@Cacheable({
cacheModifier
})
getMutableData(parameter: string) {
return this.getData(parameter);
}
```

Then, say you want to change the cached data somewhere in your code.
You can emit a callback through the `cacheModifier` subject, which will be called upon all your cached data for this decorator, by:
```ts
cacheModifier.next((data: any[]) => {
data.find(p => p.parameters[0] === 'test').response.payload = 'test_modified';
return data;
});
```
What happens here is that we look for the cache under the `'test'` parameter here and modify it to a different string.
`data` is all the caches for this method so you can change it in whatever way you want.

Now, if this method is called with the same parameter as before, it will still return cached data, but this time modified.
You can also delete and add more data to the cache.


## Running the tests

Just run `npm test`.
Expand Down
13 changes: 8 additions & 5 deletions cacheable.decorator.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { empty, merge, Observable, of, Subject } from 'rxjs';
import { delay, finalize, tap, publishReplay, refCount } from 'rxjs/operators';
import { DEFAULT_CACHE_RESOLVER, ICacheable, GlobalCacheConfig, IStorageStrategy, DEFAULT_HASHER } from './common';
import { IObservableCacheConfig } from './common/IObservableCacheConfig';
import { ICachePair } from './common/ICachePair';
import {empty, merge, Observable, of, Subject} from 'rxjs';
import {delay, finalize, tap, publishReplay, refCount} from 'rxjs/operators';
import {DEFAULT_CACHE_RESOLVER, ICacheable, GlobalCacheConfig, IStorageStrategy, DEFAULT_HASHER} from './common';
import {IObservableCacheConfig} from './common/IObservableCacheConfig';
import {ICachePair} from './common/ICachePair';
export const globalCacheBusterNotifier = new Subject<void>();

export function Cacheable(cacheConfig: IObservableCacheConfig = {}) {
Expand All @@ -18,6 +18,9 @@ export function Cacheable(cacheConfig: IObservableCacheConfig = {}) {
? new GlobalCacheConfig.storageStrategy() as IStorageStrategy
: new cacheConfig.storageStrategy();
const pendingCachePairs: Array<ICachePair<Observable<any>>> = [];
if (cacheConfig.cacheModifier) {
cacheConfig.cacheModifier.subscribe(callback => storageStrategy.addMany(callback(storageStrategy.getAll(cacheKey)), cacheKey))
}
/**
* subscribe to the globalCacheBuster
* if a custom cacheBusterObserver is passed, subscribe to it as well
Expand Down
10 changes: 10 additions & 0 deletions common/DOMStorageStrategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ export class DOMStorageStrategy extends IStorageStrategy {
this.storeRawData(allCachedData);
};


addMany(cachePairs: ICachePair<any>[], cacheKey: string) {
const allCachedData = this.getRawData();
if (!allCachedData[cacheKey]) {
allCachedData[cacheKey] = [];
}
allCachedData[cacheKey] = cachePairs;
this.storeRawData(allCachedData);
};

getAll(cacheKey: string) {
return this.getRawData()[cacheKey] || [];
};
Expand Down
1 change: 1 addition & 0 deletions common/IAsyncStorageStrategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ export abstract class IAsyncStorageStrategy {
abstract updateAtIndex(index: number, entity: ICachePair<any>, cacheKey: string): void | Promise<void>;
abstract removeAtIndex(index: number, cacheKey: string): void | Promise<void>;
abstract removeAll(cacheKey: string): void | Promise<void>;
abstract addMany(entities: ICachePair<any>[], cacheKey: string): Promise<void>;
}
15 changes: 11 additions & 4 deletions common/ICacheConfig.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Observable } from 'rxjs';
import { ICacheResolver, IShouldCacheDecider, ICacheHasher } from './index';
import { IStorageStrategy } from './IStorageStrategy';
import { IAsyncStorageStrategy } from './IAsyncStorageStrategy';
import {Observable, Subject} from 'rxjs';
import {ICacheResolver, IShouldCacheDecider, ICacheHasher, ICachePair} from './index';
import {IStorageStrategy} from './IStorageStrategy';
import {IAsyncStorageStrategy} from './IAsyncStorageStrategy';
export interface ICacheConfig {
/**
* @description cache resolver which will get old and new paramaters passed to and based on those
Expand Down Expand Up @@ -47,4 +47,11 @@ export interface ICacheConfig {
* if not provided a combination of class name + method name will be used
*/
cacheKey?: string;

/**
* Emit a callback which changes the caches and returns it.
* Whatever is returned from the callback will be automatically stored against the chosen cacheKey in the
* storage of the storage strategy you've chosen.
*/
cacheModifier?: Subject<(cachePairs: ICachePair<Observable<any>>[]) => ICachePair<Observable<any>>[]>;
}
1 change: 1 addition & 0 deletions common/IStorageStrategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ export abstract class IStorageStrategy {
abstract updateAtIndex(index: number, entity: ICachePair<any>, cacheKey: string): void;
abstract removeAtIndex(index: number, cacheKey: string): void;
abstract removeAll(cacheKey: string): void;
abstract addMany(entities: ICachePair<any>[], cacheKey: string): void;
}
8 changes: 6 additions & 2 deletions common/InMemoryStorageStrategy.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { IStorageStrategy } from './IStorageStrategy';
import { ICachePair } from '.';
import {IStorageStrategy} from './IStorageStrategy';
import {ICachePair} from '.';

export class InMemoryStorageStrategy extends IStorageStrategy {
private cachePairs: Array<ICachePair<any>> = [];

add(cachePair: ICachePair<any>) {
this.cachePairs.push(cachePair)
};

addMany(cachePairs: ICachePair<any>[]) {
this.cachePairs = cachePairs;
};

updateAtIndex(index: number, entity: ICachePair<any>) {
const updatee = this.cachePairs[index];
Expand Down
10 changes: 10 additions & 0 deletions common/LocalStorageStrategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ export class LocalStorageStrategy extends IStorageStrategy {
this.storeRawData(allCachedData);
};


addMany(cachePairs: ICachePair<any>[], cacheKey: string) {
const allCachedData = this.getRawData();
if (!allCachedData[cacheKey]) {
allCachedData[cacheKey] = [];
}
allCachedData[cacheKey] = cachePairs;
this.storeRawData(allCachedData);
};

getAll(cacheKey: string) {
return this.getRawData()[cacheKey] || [];
};
Expand Down
3 changes: 3 additions & 0 deletions dist/cjs/cacheable.decorator.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/cjs/cacheable.decorator.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions dist/cjs/common/DOMStorageStrategy.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export declare class DOMStorageStrategy extends IStorageStrategy {
private masterCacheKey;
constructor();
add(cachePair: ICachePair<any>, cacheKey: string): void;
addMany(cachePairs: ICachePair<any>[], cacheKey: string): void;
getAll(cacheKey: string): ICachePair<any>[];
removeAtIndex(index: number, cacheKey: string): void;
updateAtIndex(index: number, entity: any, cacheKey: string): void;
Expand Down
9 changes: 9 additions & 0 deletions dist/cjs/common/DOMStorageStrategy.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 5561ed9

Please sign in to comment.