Skip to content

Commit

Permalink
Add allowLazyInSync container option
Browse files Browse the repository at this point in the history
This is useful when binding promise objects while still using the
synchronous APIs.
  • Loading branch information
paul-marechal committed May 19, 2022
1 parent 1df31d6 commit 12af285
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Added
- Add `allowLazyInSync` container option

### Changed

Expand Down
3 changes: 3 additions & 0 deletions src/constants/error_msgs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ export const CONTAINER_OPTIONS_INVALID_AUTO_BIND_INJECTABLE = "Invalid Container
export const CONTAINER_OPTIONS_INVALID_SKIP_BASE_CHECK = "Invalid Container option. Skip base check must " +
"be a boolean";

export const CONTAINER_OPTIONS_INVALID_ALLOW_LAZY_IN_SYNC = "Invalid Container option. Allow lazy in sync must" +
"be a boolean";

export const MULTIPLE_PRE_DESTROY_METHODS = "Cannot apply @preDestroy decorator multiple times in the same class";
export const MULTIPLE_POST_CONSTRUCT_METHODS = "Cannot apply @postConstruct decorator multiple times in the same class";
export const ASYNC_UNBIND_REQUIRED = "Attempting to unbind dependency with asynchronous destruction (@preDestroy or onDeactivation)";
Expand Down
13 changes: 11 additions & 2 deletions src/container/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@ class Container implements interfaces.Container {
throw new Error(`${ERROR_MSGS.CONTAINER_OPTIONS_MUST_BE_AN_OBJECT}`);
}

if (options.allowLazyInSync === undefined) {
options.allowLazyInSync = false;
} else if (
typeof options.allowLazyInSync !== "boolean"
) {
throw new Error(ERROR_MSGS.CONTAINER_OPTIONS_INVALID_ALLOW_LAZY_IN_SYNC);
}

if (options.defaultScope === undefined) {
options.defaultScope = BindingScopeEnum.Transient;
} else if (
Expand Down Expand Up @@ -92,9 +100,10 @@ class Container implements interfaces.Container {
}

this.options = {
allowLazyInSync: options.allowLazyInSync,
autoBindInjectable: options.autoBindInjectable,
defaultScope: options.defaultScope,
skipBaseClassChecks: options.skipBaseClassChecks
skipBaseClassChecks: options.skipBaseClassChecks,
};

this.id = id();
Expand Down Expand Up @@ -579,7 +588,7 @@ class Container implements interfaces.Container {
): (T | T[]) {
const result = this._get<T>(getArgs);

if (isPromiseOrContainsPromise<T>(result)) {
if (!this.options.allowLazyInSync && isPromiseOrContainsPromise<T>(result)) {
throw new Error(ERROR_MSGS.LAZY_IN_SYNC(getArgs.serviceIdentifier));
}

Expand Down
1 change: 1 addition & 0 deletions src/interfaces/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ namespace interfaces {
autoBindInjectable?: boolean;
defaultScope?: BindingScope;
skipBaseClassChecks?: boolean;
allowLazyInSync?: boolean;
}

export interface Container {
Expand Down
19 changes: 18 additions & 1 deletion test/container/container.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,10 @@ describe("Container", () => {
const wrong3 = () => new Container(invalidOptions3);
expect(wrong3).to.throw(`${ERROR_MSGS.CONTAINER_OPTIONS_INVALID_DEFAULT_SCOPE}`);

const invalidOptions4: any = { allowLazyInSync: "wrongValue" };
const wrong4 = () => new Container(invalidOptions4);
expect(wrong4).to.throw(ERROR_MSGS.CONTAINER_OPTIONS_INVALID_ALLOW_LAZY_IN_SYNC);

});

it("Should be able to merge two containers", () => {
Expand Down Expand Up @@ -1170,6 +1174,19 @@ describe("Container", () => {
myContainer.resolve(CompositionRoot);
// tslint:disable-next-line: no-unused-expression
expect(() => myContainer.resolve(CompositionRoot)).not.to.throw;
})
});

it("Should throw when synchronously resolving to a promise object", () => {
const identifier = Symbol('PromiseObject') as symbol & interfaces.Abstract<Promise<object>>;
const defaultContainer = new Container();
defaultContainer.bind(identifier).toConstantValue(Promise.resolve({}));
expect(() => defaultContainer.get(identifier)).to.throw(ERROR_MSGS.LAZY_IN_SYNC(identifier));
});

it("Should not throw when allowLazyInSync is set", () => {
const identifier = Symbol('PromiseObject') as symbol & interfaces.Abstract<Promise<object>>;
const testContainer = new Container({ allowLazyInSync: true });
testContainer.bind(identifier).toConstantValue(Promise.resolve({}));
expect(testContainer.get(identifier)).to.be.instanceof(Promise);
});
});
12 changes: 11 additions & 1 deletion wiki/container_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@ You can even provide your own annotation solution.
Container options can be passed to the Container constructor and defaults will be provided if you do not or if you do but omit an option.
Options can be changed after construction and will be shared by child containers created from the Container if you do not provide options for them.

### allowLazyInSync

You can use this to skip the promise check when using the synchronous APIs:

```ts
let container = new Container({ allowLazyInSync: true });
container.bind(TYPES.AsyncData).toConstantValue(Promise.resolve({ ... }));
container.get(TYPES.AsyncData); // returns the promise without throwing
```

### defaultScope

The default scope is `transient` when binding to/toSelf/toDynamicValue/toService.
Expand Down Expand Up @@ -458,7 +468,7 @@ Calls the registration method of each module. See [container modules](https://gi

## container.loadAsync(...modules: interfaces.AsyncContainerModule[]): Promise\<void>

As per load but for asynchronous registration.
As per load but for asynchronous registration.

## container.rebind\<T>(serviceIdentifier: interfaces.ServiceIdentifier\<T>): : interfaces.BindingToSyntax\<T>

Expand Down

0 comments on commit 12af285

Please sign in to comment.