Skip to content

Commit

Permalink
Implements toService support (#583) (#720)
Browse files Browse the repository at this point in the history
* Implemented #583

* Added docs
  • Loading branch information
remojansen authored Dec 18, 2017
1 parent 581c31f commit b813523
Show file tree
Hide file tree
Showing 8 changed files with 209 additions and 42 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "inversify",
"version": "4.8.0",
"version": "4.9.0",
"description": "A powerful and lightweight inversion of control container for JavaScript and Node.js apps powered by TypeScript.",
"main": "lib/inversify.js",
"jsnext:main": "es/inversify.js",
Expand Down
1 change: 1 addition & 0 deletions src/interfaces/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ namespace interfaces {
toFunction(func: T): BindingWhenOnSyntax<T>;
toAutoFactory<T2>(serviceIdentifier: ServiceIdentifier<T2>): BindingWhenOnSyntax<T>;
toProvider<T2>(provider: ProviderCreator<T2>): BindingWhenOnSyntax<T>;
toService(service: ServiceIdentifier<T>): void;
}

export interface ConstraintFunction extends Function {
Expand Down
1 change: 1 addition & 0 deletions src/inversify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ export { interfaces } from "./interfaces/interfaces";
export { decorate } from "./annotation/decorator_utils";
export { traverseAncerstors, taggedConstraint, namedConstraint, typeConstraint } from "./syntax/constraint_helpers";
export { getServiceIdentifierAsString } from "./utils/serialization";
export { multiBindToService } from "./utils/binding_utils";
6 changes: 6 additions & 0 deletions src/syntax/binding_to_syntax.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ class BindingToSyntax<T> implements interfaces.BindingToSyntax<T> {
return new BindingWhenOnSyntax<T>(this._binding);
}

public toService(service: string | symbol | interfaces.Newable<T> | interfaces.Abstract<T>): void {
this.toDynamicValue(
(context) => context.container.get<T>(service)
);
}

}

export { BindingToSyntax };
6 changes: 6 additions & 0 deletions src/utils/binding_utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { interfaces } from "../inversify";

export const multiBindToService = (container: interfaces.Container) =>
(service: interfaces.ServiceIdentifier<any>) =>
(...types: interfaces.ServiceIdentifier<any>[]) =>
types.forEach((t) => container.bind(t).toService(service));
88 changes: 88 additions & 0 deletions test/features/transitive_bindings.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { expect } from "chai";
import { Container, injectable, multiBindToService } from "../../src/inversify";

describe("Transitive bindings", () => {

it("Should be able to bind to a service", () => {

@injectable()
class MySqlDatabaseTransactionLog {
public time: number;
public name: string;
public constructor() {
this.time = new Date().getTime();
this.name = "MySqlDatabaseTransactionLog";
}
}

@injectable()
class DatabaseTransactionLog {
public time: number;
public name: string;
}

@injectable()
class TransactionLog {
public time: number;
public name: string;
}

const container = new Container();
container.bind(MySqlDatabaseTransactionLog).toSelf().inSingletonScope();
container.bind(DatabaseTransactionLog).toService(MySqlDatabaseTransactionLog);
container.bind(TransactionLog).toService(DatabaseTransactionLog);

const mySqlDatabaseTransactionLog = container.get(MySqlDatabaseTransactionLog);
const databaseTransactionLog = container.get(DatabaseTransactionLog);
const transactionLog = container.get(TransactionLog);

expect(mySqlDatabaseTransactionLog.name).to.eq("MySqlDatabaseTransactionLog");
expect(databaseTransactionLog.name).to.eq("MySqlDatabaseTransactionLog");
expect(transactionLog.name).to.eq("MySqlDatabaseTransactionLog");
expect(mySqlDatabaseTransactionLog.time).to.eq(databaseTransactionLog.time);
expect(databaseTransactionLog.time).to.eq(transactionLog.time);

});

it("Should be able to bulk bind to a service", () => {

@injectable()
class MySqlDatabaseTransactionLog {
public time: number;
public name: string;
public constructor() {
this.time = new Date().getTime();
this.name = "MySqlDatabaseTransactionLog";
}
}

@injectable()
class DatabaseTransactionLog {
public time: number;
public name: string;
}

@injectable()
class TransactionLog {
public time: number;
public name: string;
}

const container = new Container();
const mbts = multiBindToService(container);
container.bind(MySqlDatabaseTransactionLog).toSelf().inSingletonScope();
mbts(MySqlDatabaseTransactionLog)(DatabaseTransactionLog, TransactionLog);

const mySqlDatabaseTransactionLog = container.get(MySqlDatabaseTransactionLog);
const databaseTransactionLog = container.get(DatabaseTransactionLog);
const transactionLog = container.get(TransactionLog);

expect(mySqlDatabaseTransactionLog.name).to.eq("MySqlDatabaseTransactionLog");
expect(databaseTransactionLog.name).to.eq("MySqlDatabaseTransactionLog");
expect(transactionLog.name).to.eq("MySqlDatabaseTransactionLog");
expect(mySqlDatabaseTransactionLog.time).to.eq(databaseTransactionLog.time);
expect(databaseTransactionLog.time).to.eq(transactionLog.time);

});

});
87 changes: 46 additions & 41 deletions wiki/readme.md
Original file line number Diff line number Diff line change
@@ -1,47 +1,52 @@
# Wiki

Welcome to the InversifyJS wiki!

### Installation and environment support
- [Installation](https://github.com/inversify/InversifyJS/blob/master/wiki/installation.md)
- [Environment support and polyfills](https://github.com/inversify/InversifyJS/blob/master/wiki/environment.md)
- [Vanilla JavaScript example](https://github.com/inversify/InversifyJS/blob/master/wiki/basic_js_example.md)
- [ES5 and ES6 support](https://github.com/inversify/InversifyJS/blob/master/wiki/javascript.md)
- [Upgrade guide](https://github.com/inversify/InversifyJS/blob/master/wiki/upgrade.md)
## Installation and environment support

- [Installation](./installation.md)
- [Environment support and polyfills](./environment.md)
- [Vanilla JavaScript example](./basic_js_example.md)
- [ES5 and ES6 support](./javascript.md)
- [Upgrade guide](./upgrade.md)

## The InversifyJS Features and API

- [Support for classes](./classes_as_id.md)
- [Support for Symbols](./symbols_as_id.md)
- [Container API](./container_api.md)
- [Declaring container modules](./container_modules.md)
- [Container snapshots](./container_snapshots.md)
- [Controlling the scope of the dependencies](./scope.md)
- [Declaring optional dependencies](./optional_dependencies.md)
- [Injecting a constant or dynamic value](./value_injection.md)
- [Injecting a class constructor](./constructor_injection.md)
- [Injecting a Factory](./factory_injection.md)
- [Auto factory](./auto_factory.md)
- [Injecting a Provider (asynchronous Factory)](./provider_injection.md)
- [Activation handler](./activation_handler.md)
- [Post Construct decorator](./post_construct.md)
- [Middleware](./middleware.md)
- [Multi-injection](./multi_injection.md)
- [Tagged bindings](./tagged_bindings.md)
- [Create your own tag decorators](./custom_tag_decorators.md)
- [Named bindings](./named_bindings.md)
- [Default target](./default_targets.md)
- [Support for hierarchical DI systems](./hierarchical_di.md)
- [Contextual bindings & @targetName](./contextual_bindings.md)
- [Property injection](./property_injection.md)
- [Circular dependencies](./circular_dependencies.md)
- [Inheritance](./inheritance.md)
- [Transitive bindings](./transitive_bindings.md)

### The InversifyJS Features and API
- [Support for classes](https://github.com/inversify/InversifyJS/blob/master/wiki/classes_as_id.md)
- [Support for Symbols](https://github.com/inversify/InversifyJS/blob/master/wiki/symbols_as_id.md)
- [Container API](https://github.com/inversify/InversifyJS/blob/master/wiki/container_api.md)
- [Declaring container modules](https://github.com/inversify/InversifyJS/blob/master/wiki/container_modules.md)
- [Container snapshots](https://github.com/inversify/InversifyJS/blob/master/wiki/container_snapshots.md)
- [Controlling the scope of the dependencies](https://github.com/inversify/InversifyJS/blob/master/wiki/scope.md)
- [Declaring optional dependencies](https://github.com/inversify/InversifyJS/blob/master/wiki/optional_dependencies.md)
- [Injecting a constant or dynamic value](https://github.com/inversify/InversifyJS/blob/master/wiki/value_injection.md)
- [Injecting a class constructor](https://github.com/inversify/InversifyJS/blob/master/wiki/constructor_injection.md)
- [Injecting a Factory](https://github.com/inversify/InversifyJS/blob/master/wiki/factory_injection.md)
- [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)
- [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)
- [Tagged bindings](https://github.com/inversify/InversifyJS/blob/master/wiki/tagged_bindings.md)
- [Create your own tag decorators](https://github.com/inversify/InversifyJS/blob/master/wiki/custom_tag_decorators.md)
- [Named bindings](https://github.com/inversify/InversifyJS/blob/master/wiki/named_bindings.md)
- [Default target](https://github.com/inversify/InversifyJS/blob/master/wiki/default_targets.md)
- [Support for hierarchical DI systems](https://github.com/inversify/InversifyJS/blob/master/wiki/hierarchical_di.md)
- [Contextual bindings & @targetName](https://github.com/inversify/InversifyJS/blob/master/wiki/contextual_bindings.md)
- [Property injection](https://github.com/inversify/InversifyJS/blob/master/wiki/property_injection.md)
- [Circular dependencies](https://github.com/inversify/InversifyJS/blob/master/wiki/circular_dependencies.md)
- [Inheritance](https://github.com/inversify/InversifyJS/blob/master/wiki/inheritance.md)
## Other documents

### Other documents
- [Why InversifyJS?](https://github.com/inversify/InversifyJS/blob/master/wiki/purpose.md)
- [Object-oriented design](https://github.com/inversify/InversifyJS/blob/master/wiki/oo_design.md)
- [Good practices](https://github.com/inversify/InversifyJS/blob/master/wiki/good_practices.md)
- [Ecosystem](https://github.com/inversify/InversifyJS/blob/master/wiki/ecosystem.md)
- [Recipes](https://github.com/inversify/InversifyJS/blob/master/wiki/recipes.md)
- [Injecting npm modules](https://github.com/inversify/InversifyJS/blob/master/wiki/injecting_npm_modules.md)
- [Architecture overview](https://github.com/inversify/InversifyJS/blob/master/wiki/architecture.md)
- [Glossary](https://github.com/inversify/InversifyJS/blob/master/wiki/glossary.md)
- [Why InversifyJS?](./purpose.md)
- [Object-oriented design](./oo_design.md)
- [Good practices](./good_practices.md)
- [Ecosystem](./ecosystem.md)
- [Recipes](./recipes.md)
- [Injecting npm modules](./injecting_npm_modules.md)
- [Architecture overview](./architecture.md)
- [Glossary](./glossary.md)
- [Roadmap](https://github.com/inversify/InversifyJS/milestones)
60 changes: 60 additions & 0 deletions wiki/transitive_bindings.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Transitive bindings

A transitive type binding allows as to declare a type binding that is resolved by a prviously declared type binding.

A transitive binding can be declared using the `toService` method:

```ts
@injectable()
class MySqlDatabaseTransactionLog {
public time: number;
public name: string;
public constructor() {
this.time = new Date().getTime();
this.name = "MySqlDatabaseTransactionLog";
}
}

@injectable()
class DatabaseTransactionLog {
public time: number;
public name: string;
}

@injectable()
class TransactionLog {
public time: number;
public name: string;
}

const container = new Container();
container.bind(MySqlDatabaseTransactionLog).toSelf().inSingletonScope();
container.bind(DatabaseTransactionLog).toService(MySqlDatabaseTransactionLog);
container.bind(TransactionLog).toService(DatabaseTransactionLog);

const mySqlDatabaseTransactionLog = container.get(MySqlDatabaseTransactionLog);
const databaseTransactionLog = container.get(DatabaseTransactionLog);
const transactionLog = container.get(TransactionLog);

expect(mySqlDatabaseTransactionLog.name).to.eq("MySqlDatabaseTransactionLog");
expect(databaseTransactionLog.name).to.eq("MySqlDatabaseTransactionLog");
expect(transactionLog.name).to.eq("MySqlDatabaseTransactionLog");
expect(mySqlDatabaseTransactionLog.time).to.eq(databaseTransactionLog.time);
expect(databaseTransactionLog.time).to.eq(transactionLog.time);
```

There is also an utility function named `multiBindToService` which allows us to declare multiple transitive bindings in one go.

For example, instead of writing the following:

```ts
container.bind(DatabaseTransactionLog).toService(MySqlDatabaseTransactionLog);
container.bind(TransactionLog).toService(DatabaseTransactionLog);
```

We can use `multiBindToService` to write the following:

```ts
multiBindToService(container)(MySqlDatabaseTransactionLog)
(DatabaseTransactionLog, TransactionLog);
```

0 comments on commit b813523

Please sign in to comment.