Skip to content

Commit

Permalink
Merge pull request #1132 from parisholley/async-v2
Browse files Browse the repository at this point in the history
Promise (Lazy) Support + Global onActivation/onDeactivation
  • Loading branch information
notaphplover authored Apr 30, 2021
2 parents 9098a14 + d06d0b0 commit 094bcd6
Show file tree
Hide file tree
Showing 39 changed files with 3,599 additions and 472 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ test/**/*.js.map
src/**/*.js.map
src/*.js.map
type_definitions/**/*.js
.DS_store
.idea

.nyc_output
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@ All notable changes to this project from 5.0.0 forward will be documented in thi
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Added
- Async bindings #1132
- Async binding resolution (getAllAsync, getAllNamedAsync, getAllTaggedAsync, getAsync, getNamedAsync, getTaggedAsync, rebindAsync, unbindAsync, unbindAllAsync, unloadAsync) #1132
- Global onActivation / onDeactivation #1132
- Parent/Child onActivation / onDeactivation #1132
- Module onActivation / onDeactivation #1132
- Added @preDestroy decorator #1132

### Changed
- @postConstruct can target an asyncronous function #1132

## [5.1.1] - 2021-04-25
-Fix pre-publish for build artifacts

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ Let's take a look to the InversifyJS features!
- [Auto factory](https://github.com/inversify/InversifyJS/blob/master/wiki/auto_factory.md)
- [Injecting a Provider (asynchronous Factory)](https://github.com/inversify/InversifyJS/blob/master/wiki/provider_injection.md)
- [Activation handler](https://github.com/inversify/InversifyJS/blob/master/wiki/activation_handler.md)
- [Deactivation handler](https://github.com/inversify/InversifyJS/blob/master/wiki/deactivation_handler.md)
- [Post Construct decorator](https://github.com/inversify/InversifyJS/blob/master/wiki/post_construct.md)
- [Middleware](https://github.com/inversify/InversifyJS/blob/master/wiki/middleware.md)
- [Multi-injection](https://github.com/inversify/InversifyJS/blob/master/wiki/multi_injection.md)
Expand Down
16 changes: 5 additions & 11 deletions src/annotation/post_construct.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
import * as ERRORS_MSGS from "../constants/error_msgs";
import * as METADATA_KEY from "../constants/metadata_keys";
import { Metadata } from "../planning/metadata";
import { propertyEventDecorator } from "./property_event_decorator";

function postConstruct() {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const metadata = new Metadata(METADATA_KEY.POST_CONSTRUCT, propertyKey);

if (Reflect.hasOwnMetadata(METADATA_KEY.POST_CONSTRUCT, target.constructor)) {
throw new Error(ERRORS_MSGS.MULTIPLE_POST_CONSTRUCT_METHODS);
}
Reflect.defineMetadata(METADATA_KEY.POST_CONSTRUCT, metadata, target.constructor);
};
}
const postConstruct = propertyEventDecorator(
METADATA_KEY.POST_CONSTRUCT,
ERRORS_MSGS.MULTIPLE_POST_CONSTRUCT_METHODS,
);

export { postConstruct };
10 changes: 10 additions & 0 deletions src/annotation/pre_destroy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import * as ERRORS_MSGS from "../constants/error_msgs";
import * as METADATA_KEY from "../constants/metadata_keys";
import { propertyEventDecorator } from "./property_event_decorator";

const preDestroy = propertyEventDecorator(
METADATA_KEY.PRE_DESTROY,
ERRORS_MSGS.MULTIPLE_PRE_DESTROY_METHODS,
);

export { preDestroy };
16 changes: 16 additions & 0 deletions src/annotation/property_event_decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Metadata } from "../planning/metadata";

function propertyEventDecorator(eventKey: string, errorMessage: string) {
return () => {
return (target: any, propertyKey: string) => {
const metadata = new Metadata(eventKey, propertyKey);

if (Reflect.hasOwnMetadata(eventKey, target.constructor)) {
throw new Error(errorMessage);
}
Reflect.defineMetadata(eventKey, metadata, target.constructor);
}
}
}

export { propertyEventDecorator }
11 changes: 8 additions & 3 deletions src/bindings/binding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { id } from "../utils/id";
class Binding<T> implements interfaces.Binding<T> {

public id: number;
public moduleId: string;
public moduleId: interfaces.ContainerModuleBase["id"];

// Determines weather the bindings has been already activated
// The activation action takes place when an instance is resolved
Expand All @@ -22,7 +22,7 @@ class Binding<T> implements interfaces.Binding<T> {
public cache: T | null;

// Cache used to allow BindingType.DynamicValue bindings
public dynamicValue: ((context: interfaces.Context) => T) | null;
public dynamicValue: interfaces.DynamicValue<T> | null;

// The scope mode to be used
public scope: interfaces.BindingScope;
Expand All @@ -40,7 +40,10 @@ class Binding<T> implements interfaces.Binding<T> {
public constraint: (request: interfaces.Request) => boolean;

// On activation handler (invoked just before an instance is added to cache and injected)
public onActivation: ((context: interfaces.Context, injectable: T) => T) | null;
public onActivation: interfaces.BindingActivation<T> | null;

// On deactivation handler (invoked just before an instance is unbinded and removed from container)
public onDeactivation: interfaces.BindingDeactivation<T> | null;

public constructor(serviceIdentifier: interfaces.ServiceIdentifier<T>, scope: interfaces.BindingScope) {
this.id = id();
Expand All @@ -54,6 +57,7 @@ class Binding<T> implements interfaces.Binding<T> {
this.factory = null;
this.provider = null;
this.onActivation = null;
this.onDeactivation = null;
this.dynamicValue = null;
}

Expand All @@ -68,6 +72,7 @@ class Binding<T> implements interfaces.Binding<T> {
clone.provider = this.provider;
clone.constraint = this.constraint;
clone.onActivation = this.onActivation;
clone.onDeactivation = this.onDeactivation;
clone.cache = this.cache;
return clone;
}
Expand Down
16 changes: 11 additions & 5 deletions src/constants/error_msgs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export const INVALID_BINDING_TYPE = "Invalid binding type:";
export const NO_MORE_SNAPSHOTS_AVAILABLE = "No snapshot available to restore.";
export const INVALID_MIDDLEWARE_RETURN = "Invalid return type in middleware. Middleware must return!";
export const INVALID_FUNCTION_BINDING = "Value provided to function binding must be a function!";
export const LAZY_IN_SYNC = (key: unknown) => `You are attempting to construct '${key}' in a synchronous way
but it has asynchronous dependencies.`;

export const INVALID_TO_SELF_VALUE = "The toSelf function can only be applied when a constructor is " +
"used as service identifier";
Expand All @@ -39,11 +41,15 @@ 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 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 POST_CONSTRUCT_ERROR = (...values: any[]) => `@postConstruct error in class ${values[0]}: ${values[1]}`;

export const CIRCULAR_DEPENDENCY_IN_FACTORY = (...values: any[]) => "It looks like there is a circular dependency " +
`in one of the '${values[0]}' bindings. Please investigate bindings with` +
`service identifier '${values[1]}'.`;
export const ASYNC_UNBIND_REQUIRED = "Attempting to unbind dependency with asynchronous destruction (@preDestroy or onDeactivation)";
export const POST_CONSTRUCT_ERROR = (clazz: string, errorMessage: string) => `@postConstruct error in class ${clazz}: ${errorMessage}`;
export const PRE_DESTROY_ERROR = (clazz: string, errorMessage: string) => `@preDestroy error in class ${clazz}: ${errorMessage}`;
export const ON_DEACTIVATION_ERROR = (clazz: string, errorMessage: string) => `onDeactivation() error in class ${clazz}: ${errorMessage}`;

export const CIRCULAR_DEPENDENCY_IN_FACTORY = (factoryType: string, serviceIdentifier: string) =>
`It looks like there is a circular dependency in one of the '${factoryType}' bindings. Please investigate bindings with` +
`service identifier '${serviceIdentifier}'.`;

export const STACK_OVERFLOW = "Maximum call stack size exceeded";
3 changes: 3 additions & 0 deletions src/constants/metadata_keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ export const DESIGN_PARAM_TYPES = "design:paramtypes";
// used to identify postConstruct functions
export const POST_CONSTRUCT = "post_construct";

// used to identify preDestroy functions
export const PRE_DESTROY = "pre_destroy";

function getNonCustomTagKeys(): string[] {
return [
INJECT_TAG,
Expand Down
Loading

0 comments on commit 094bcd6

Please sign in to comment.