Skip to content

Commit

Permalink
fix(context): clear binding cache upon scope or value getter changes
Browse files Browse the repository at this point in the history
  • Loading branch information
raymondfeng committed Mar 29, 2019
1 parent e7e8b21 commit 815c23e
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 2 deletions.
64 changes: 63 additions & 1 deletion packages/context/src/__tests__/unit/binding.unit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {expect} from '@loopback/testlab';
import {expect, sinon, SinonSpy} from '@loopback/testlab';
import {
Binding,
BindingScope,
Expand Down Expand Up @@ -214,6 +214,68 @@ describe('Binding', () => {
});
});

describe('cache', () => {
let spy: SinonSpy;
beforeEach(() => {
spy = sinon.spy();
});

it('clears cache if scope changes', () => {
const indexBinding = ctx
.bind<number>('index')
.toDynamicValue(spy)
.inScope(BindingScope.SINGLETON);

ctx.getSync(indexBinding.key);
sinon.assert.calledOnce(spy);
spy.resetHistory();

// Singleton
ctx.getSync(indexBinding.key);
sinon.assert.notCalled(spy);
spy.resetHistory();

indexBinding.inScope(BindingScope.CONTEXT);
ctx.getSync(indexBinding.key);
sinon.assert.calledOnce(spy);
});

it('clears cache if _getValue changes', () => {
const providerSpy = sinon.spy();
class IndexProvider implements Provider<number> {
value() {
return providerSpy();
}
}
const indexBinding = ctx
.bind<number>('index')
.toDynamicValue(spy)
.inScope(BindingScope.SINGLETON);

ctx.getSync(indexBinding.key);
sinon.assert.calledOnce(spy);
spy.resetHistory();

// Singleton
ctx.getSync(indexBinding.key);
sinon.assert.notCalled(spy);
spy.resetHistory();

// Now change the value getter
indexBinding.toProvider(IndexProvider);
ctx.getSync(indexBinding.key);
sinon.assert.notCalled(spy);
sinon.assert.calledOnce(providerSpy);
spy.resetHistory();
providerSpy.resetHistory();

// Singleton
ctx.getSync(indexBinding.key);
sinon.assert.notCalled(spy);
sinon.assert.notCalled(providerSpy);
});
});

describe('toJSON()', () => {
it('converts a keyed binding to plain JSON object', () => {
const json = binding.toJSON();
Expand Down
14 changes: 13 additions & 1 deletion packages/context/src/binding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,15 @@ export class Binding<T = BoundValue> {
});
}

/**
* Clear the cache
*/
private _clearCache() {
if (!this._cache) return;
// WeakMap does not have a `clear` method
this._cache = new WeakMap();
}

/**
* This is an internal function optimized for performance.
* Users should use `@inject(key)` or `ctx.get(key)` instead.
Expand Down Expand Up @@ -346,6 +355,7 @@ export class Binding<T = BoundValue> {
* @param scope Binding scope
*/
inScope(scope: BindingScope): this {
if (this._scope !== scope) this._clearCache();
this._scope = scope;
return this;
}
Expand All @@ -357,7 +367,7 @@ export class Binding<T = BoundValue> {
*/
applyDefaultScope(scope: BindingScope): this {
if (!this._scope) {
this._scope = scope;
this.inScope(scope);
}
return this;
}
Expand All @@ -367,6 +377,8 @@ export class Binding<T = BoundValue> {
* @param getValue getValue function
*/
private _setValueGetter(getValue: ValueGetter<T>) {
// Clear the cache
this._clearCache();
this._getValue = getValue;
}

Expand Down

0 comments on commit 815c23e

Please sign in to comment.