diff --git a/.gitignore b/.gitignore index 8248d8e2..c8736757 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,16 @@ dist src/*.js src/**/*.js + +src/*.d.ts +src/activation/*.d.ts +src/bindings/*.d.ts +src/constants/*.d.ts +src/kernel/*.d.ts +src/planning/*.d.ts +src/resolution/*.d.ts +src/syntax/*.d.ts + test/*.js test/**/*.js type_definitions/*.js diff --git a/.npmignore b/.npmignore index ffb7388e..160fc1d3 100644 --- a/.npmignore +++ b/.npmignore @@ -1,16 +1,20 @@ -source +src test typings bundled build coverage -documentation +docs gulpfile.js bower.json karma.conf.js tsconfig.json CONTRIBUTING.md -tsd.json +ISSUE_TEMPLATE.md +PULL_REQUEST_TEMPLATE.md +typings.json +tslint.json +wallaby.js .travis.yml .gitignore .vscode \ No newline at end of file diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index fab50043..9f6adb36 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -31,6 +31,8 @@ - [ ] My code follows the code style of this project. - [ ] My change requires a change to the documentation. - [ ] I have updated the documentation accordingly. +- [ ] My change requires a change to the type definitions. +- [ ] I have updated the type definitions accordingly. - [ ] I have read the **CONTRIBUTING** document. - [ ] I have added tests to cover my changes. - [ ] All new and existing tests passed. diff --git a/README.md b/README.md index c645b68b..20a75603 100644 --- a/README.md +++ b/README.md @@ -13,12 +13,13 @@ A lightweight IoC container written in TypeScript. Visit http://inversify.io/ for more information. ### About -InversifyJS is a lightweight (4KB) pico inversion of control (IoC) container for TypeScript and JavaScript apps. A pico IoC container uses a class constructor to identify and inject its dependencies. - -InversifyJS is easy to integrate with the majority of existing JavaScript frameworks and encourage the usage of the best OOP and IoC practices. +InversifyJS is a lightweight (4KB) pico inversion of control (IoC) container for TypeScript and JavaScript apps. +A pico IoC container uses a class constructor to identify and inject its dependencies. +InversifyJS has a friendly API and encourage the usage of the best OOP and IoC practices. ### Motivation -JavaScript applications are becoming larger and larger day after day. As a result we are using a lot of our architecture experience from languages like Java or C# in JavaScript. We are embracing OOP with JavaScript but we are not writing SOLID JavaScript. InversifyJS has been designed to allow JavaScript developers to write code that adheres to the SOLID principles. +JavaScript applications are becoming larger and larger day after day. +InversifyJS has been designed to allow JavaScript developers to write code that adheres to the SOLID principles. ### Philosophy InversifyJS has been developed with 3 main goals: @@ -31,30 +32,22 @@ InversifyJS has been developed with 3 main goals: # Installation -You can get the latest release and the type definitions using npm. +You can get the latest release and the type definitions using npm: ``` npm install inversify --save ``` -**Note**: We have decided to [drop support for bower](https://twitter.com/nachocoloma/status/663622545162280960) and tsd. The InversifyJS type definitions are included in the npm package as it is [recommended by the TypeScript development team](https://github.com/Microsoft/TypeScript/wiki/Typings-for-npm-packages). +**Note**: We have decided to [drop support for bower](https://twitter.com/nachocoloma/status/663622545162280960) and tsd. -If you are planing to use inversify as a global you will need to add a reference to the file named `inversify-global.d.ts` this file is included in the npm package: +The InversifyJS type definitions are included in the npm package: ``` -/// +/// ``` -# The Basics (with TypeScript) -The main goal of InversifyJS is top allow JavaScript developers to write code that adheres to the SOLID principles. -Many of these principles refer to the usage of interfaces. -The main reason why it is not possible to write native SOLID JavaScript is because the language lacks interfaces. -In the other hand, TypeScript features interfaces, so, if you are going to use InversifyJS it is recommended -to work with TypeScript to get the most out of it. - -#### 1. Declare interfaces & implementations - -Our goal is to write SOLID code. This means that we should "depend upon Abstractions. Do not depend upon concretions." -so we will start by declaring some interfaces (abstractions). +# The Basics +Let’s take a look to the basic usage and APIs of InversifyJS: +#### Step 1: Declare your interfaces ``` interface INinja { fight(): string; @@ -70,10 +63,10 @@ interface IShuriken { } ``` -We can continue declaring some classes which implement them (concretions). -We will start by declaring two classes (`Katana` & `Shuriken`) which don't have any dependencies. - +#### Step 2: Implement the interfaces and declare dependencies using the `@inject` decorator ``` +import { inject } from "inversify"; + class Katana implements IKatana { public hit() { return "cut!"; @@ -85,19 +78,97 @@ class Shuriken implements IShuriken { return "hit!"; } } + +@inject("IKatana", "IShuriken") +class Ninja implements INinja { + + private _katana: IKatana; + private _shuriken: IShuriken; + + public constructor(katana: IKatana, shuriken: IShuriken) { + this._katana = katana; + this._shuriken = shuriken; + } + + public fight() { return this._katana.hit(); }; + public sneak() { return this._shuriken.throw(); }; + +} +``` + +#### Step 3: Create and configure a Kernel +We recommend to do this in a file named `inversify.config.ts`. This is the only place in which there is some coupling. +In the rest of your application your classes should be free of references to other classes. ``` +import { Kernel } from "inversify"; + +import { Ninja } from "./entities/ninja"; +import { Katana } from "./entities/katana"; +import { Shuriken} from "./entities/shuriken"; + +var kernel = new Kernel(); +kernel.bind("INinja").to(Ninja); +kernel.bind("IKatana").to(Katana); +kernel.bind("IShuriken").to(Shuriken); + +export default kernel; +``` + +#### Step 4: Resolve dependencies +You can use the method `get` from the `Kernel` classs to resolve a dependency. +Remember that you should do this only in your [composition root](http://blog.ploeh.dk/2011/07/28/CompositionRoot/) +to avoid the [service locator anti-pattern](http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/). + +``` +import kernel = from "./inversify.config"; + +var ninja = kernel.get("INinja"); -Now we are going to declare a class named `Ninja`, which has two dependencies (`IKatana` & `IShuriken`): +expect(ninja.fight()).eql("cut!"); // true +expect(ninja.sneak()).eql("hit!"); // true +``` +As we can see the `IKatana` and `IShuriken` were successfully resolved and injected into `Ninja`. + +# Features (v2.0.0 alpha.3) +## Declaring kernel modules + +Kernel modules can help you to manage the complexity of your bindings in very large applications. +``` +let someModule: IKernelModule = (kernel: IKernel) => { + kernel.bind("INinja").to(Ninja); + kernel.bind("IKatana").to(Katana); + kernel.bind("IShuriken").to(Shuriken); +}; + +let kernel = new Kernel({ modules: [ someModule ] }); +``` + +## Controlling the scope of the dependencies + +InversifyJS uses transient scope by default but you can also use singleton scope: +``` +kernel.bind("IShuriken").to(Shuriken).inTransientScope(); // Default +kernel.bind("IShuriken").to(Shuriken).inSingletonScope(); ``` -@Inject("IKatana", "IShuriken") + +## Injecting a value +Binds an abstraction to a constant value. +``` +kernel.bind("IKatana").toValue(new Katana()); +``` + +## Injecting a class constructor +Binds an abstraction to a class constructor. +``` +@inject("IKatana", "IShuriken") class Ninja implements INinja { private _katana: IKatana; private _shuriken: IShuriken; - public constructor(katana: IKatana, shuriken: IShuriken) { - this._katana = katana; + public constructor(Katana: INewable, shuriken: IShuriken) { + this._katana = new Katana(); this._shuriken = shuriken; } @@ -107,100 +178,362 @@ class Ninja implements INinja { } ``` -#### 2. Bind interfaces to implementations +``` +kernel.bind>("INewable").toConstructor(Katana); +``` + +## Injecting a Factory +Binds an abstraction to a user defined Factory. +``` +@inject("IKatana", "IShuriken") +class Ninja implements INinja { + + private _katana: IKatana; + private _shuriken: IShuriken; -Before we can start resolving and injecting dependencies we need to create an instance of the InversifyJS Kernel class. -The Kernel will automatically detect is a class has some dependencies by examining the `@Inject` annotation. -The Kernel will automatically detect if a class has some dependencies by examining the metadata provided by the Inject decorator. + public constructor(katanaFactory: IFactory, shuriken: IShuriken) { + this._katana = katanaFactory(); + this._shuriken = shuriken; + } + public fight() { return this._katana.hit(); }; + public sneak() { return this._shuriken.throw(); }; + +} ``` -import { Kernel } from "inversify"; -var kernel = new Kernel(); + +``` +kernel.bind>("IFactory").toFactory((context) => { + return () => { + return context.kernel.get("IKatana"); + }; +}); ``` -In order to resolve a dependency, the kernel needs to be told which implementation type (classes) to associate with each service type (interfaces). -We will use type bindings for this purpose. A type binding (or just a binding) is a mapping between a service type (an interface), and an implementation type (class). +## Auto factory +Binds an abstraction to a auto-generated Factory. +``` +@inject("IKatana", "IShuriken") +class Ninja implements INinja { + + private _katana: IKatana; + private _shuriken: IShuriken; + + public constructor(katanaFactory: IFactory, shuriken: IShuriken) { + this._katana = katanaFactory(); + this._shuriken = shuriken; + } + public fight() { return this._katana.hit(); }; + public sneak() { return this._shuriken.throw(); }; + +} ``` -kernel.bind("INinja").to(Ninja); - kernel.bind("IKatana").to(Katana); - kernel.bind("IShuriken").to(Shuriken).inSingletonScope(); + +``` +kernel.bind>("IFactory").toAutoFactory(); ``` -When we declare a type binding, the TypeScript compiler will check that the implementation type (class) is actually and implementation of the service type (interface) and throw a compilation error if that is not the case. +## Injecting a Provider (asynchronous Factory) +Binds an abstraction to a Provider. A provider is an asynchronous factory, this is useful when dealing with asynchronous I/O operations. +``` +@inject("IKatana", "IShuriken") +class Ninja implements INinja { + + public katana: IKatana; + public shuriken: IShuriken; + public katanaProvider: IProvider; + public constructor(katanaProvider: IProvider, shuriken: IShuriken) { + this.katanaProvider = katanaProvider; + this.katana= null; + this.shuriken = shuriken; + } + + public fight() { return this._katana.hit(); }; + public sneak() { return this._shuriken.throw(); }; + +} + +var ninja = kernel.get("INinja"); + +ninja.katanaProvider() + .then((katana) => { ninja.katana = katana; }) + .catch((e) => { console.log(e); }); ``` -// Compilation error: Shuriken is not assignable to type IKatana -kernel.bind("IKatana").to(Shuriken); + +``` +kernel.bind>("IProvider").toProvider((context) => { + return () => { + return new Promise((resolve) => { + let katana = context.kernel.get("IKatana"); + resolve(katana); + }); + }; +}); ``` -We should keep the InversifyJS Kernel instantiation and type bindings centralized in one unique IoC configuration file. -This will help us to abstract our application from the IoC configuration. +## Injecting a proxy +It is possible to create a [proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) of +a dependency just before it is injected. This is useful to keep our dependencies agnostic of the implementation of crosscutting +concerns like caching or logging. +``` +interface IKatana { + use: () => void; +} -#### 3. Resolve & inject dependencies +class Katana implements IKatana { + public use() { + console.log("Used Katana!"); + } +} -After declaring the type bindings, we can invoke the kernel resolve method to resolve a dependency. -We will use a string as the interface identifier (instead of the interface itself) because the TypeScript interfaces are not available at runtime. +interface INinja { + katana: IKatana; +} +@inject("IKatana") +class Ninja implements INinja { + public katana: IKatana; + public constructor(katana: IKatana) { + this.katana = katana; + } +} ``` -let ninja = kernel.get("INinja"); + ``` +kernel.bind("INinja").to(Ninja); -If the interface that we are trying to resolve is bind to a class that has some dependencies, InversifyJS will -resolve and inject them into a new instance via the class constructor. +kernel.bind("IKatana").to(Katana).proxy((ninja) => { + let handler = { + apply: function(target, thisArgument, argumentsList) { + console.log(`Starting: ${performance.now()}`); + let result = target.apply(thisArgument, argumentsList); + console.log(`Finished: ${performance.now()}`); + return result; + } + }; + return new Proxy(ninja, handler); +}); +``` ``` -// Katana and Shuriken instances has been injected into a new Ninja instance via its constructor -expect(ninja.fight()).eql("cut!"); // true -expect(ninja.sneak()).eql("hit!"); // true +let ninja = kernelget(); +ninja.katana.use(); +> Starting: 460495.88000000006 +> Used Katana! +> Finished: 460496.585 +``` + +## Multi-injection +We can use multi-injection When two or more concretions have been bound to the an abstraction. +Notice how an array of `IWeapon` is injected into the `Ninja` class via its constructor: +``` +interface IWeapon { + name: string; +} + +class Katana implements IWeapon { + public name = "Katana"; +} +class Shuriken implements IWeapon { + public name = "Shuriken"; +} + +interface INinja { + katana: IWeapon; + shuriken: IWeapon; +} + +@inject("IWeapon[]") +class Ninja implements INinja { + public katana: IWeapon; + public shuriken: IWeapon; + public constructor(weapons: IWeapon[]) { + this.katana = weapons[0]; + this.shuriken = weapons[1]; + } +} +``` + +We are binding `Katana` and `Shuriken` to `IWeapon`: + +``` +kernel.bind("INinja").to(Ninja); +kernel.bind("IWeapon").to(Katana); +kernel.bind("IWeapon").to(Shuriken); ``` -Our application dependency tree should have one unique root element, known as the application composition root, which is the -only place where we should invoke the resolve method. +## Tagged bindings +We can use tagged bindings to fix `AMBIGUOUS_MATCH` errors when two or more +concretions have been bound to the an abstraction. Notice how the constructor +arguments of the `Ninja` class have been annotated using the `@tagged` decorator: +``` +interface IWeapon {} +class Katana implements IWeapon { } +class Shuriken implements IWeapon {} -Invoking resolve every time we need to inject something, as if it was a Service Locator is an anti-pattern. -If we are working with an MVC framework the composition root should be located in the application class, somewhere -along the routing logic or in a controller factory class. Please refer to the integration examples if you need additional help. +interface INinja { + katana: IWeapon; + shuriken: IWeapon; +} -# Integration with popular frameworks +@inject("IWeapon", "IWeapon") +class Ninja implements INinja { + public katana: IWeapon; + public shuriken: IWeapon; + public constructor( + @tagged("canThrow", false) katana: IWeapon, + @tagged("canThrow", true) shuriken: IWeapon + ) { + this.katana = katana; + this.shuriken = shuriken; + } +} +``` -InversifyJS was designed with many popular JavaScript frameworks in mind. As a result, it is really easy to integrate with existing JavaScript frameworks and examples of integration with many popular frameworks are available in the [official examples repository](https://github.com/inversify/Inversify-code-samples). +We are binding `Katana` and `Shuriken` to `IWeapon` but a `whenTargetTagged` +constraint is added to avoid `AMBIGUOUS_MATCH` errors: -# Good Practices -Dependency Inversion (DI) isn't rocket science. We just need to try to avoid new and singleton except when there's a compelling reason to use them, such as a utility method that has no external dependencies, or a utility class that could not possibly have any purpose outside the framework (interop wrappers and dictionary keys are common examples of this). +``` +kernel.bind(ninjaId).to(Ninja); +kernel.bind(weaponId).to(Katana).whenTargetTagged("canThrow", false); +kernel.bind(weaponId).to(Shuriken).whenTargetTagged("canThrow", true); +``` -Many of the problems with IoC frameworks come up when developers are first learning how to use them, and instead of actually changing the way they handle dependencies and abstractions to fit the IoC model, instead try to manipulate the IoC container to meet the expectations of their old coding style, which would often involve high coupling and low cohesion. +## Create your own tag decorators -#### Use a Composition Root to avoid the Service Locator anti-pattern +Creating your own decorators is really simple: -Our application dependency tree should have one unique root element (known as the application composition root) which is the only component where we should invoke the resolve method. +``` +let throwable = tagged("canThrow", true); +let notThrowable = tagged("canThrow", false); -Invoking resolve every time we need to inject something, as if it was a Service Locator is an anti-pattern. If we are working with an MVC framework the composition root should be located in the application class, somewhere along the routing logic or in a controller factory class. +@inject("IWeapon", "IWeapon") +class Ninja implements INinja { + public katana: IWeapon; + public shuriken: IWeapon; + public constructor( + @notThrowable katana: IWeapon, + @throwable shuriken: IWeapon + ) { + this.katana = katana; + this.shuriken = shuriken; + } +} +``` -#### Avoid Constructor over-injection +## Named bindings +We can use named bindings to fix `AMBIGUOUS_MATCH` errors when two or more concretions have +been bound to the an abstraction. Notice how the constructor arguments of the `Ninja` class +have been annotated using the `@named` decorator: +``` +interface IWeapon {} +class Katana implements IWeapon { } +class Shuriken implements IWeapon {} -Constructor over-injection is a violation of the Single Responsibility Principle. Too many constructor arguments indicates too many dependencies; too many dependencies indicates that the class is trying to do too much. Usually this error correlates with other code smells, such as unusually long or ambiguous ("manager") class names. +interface INinja { + katana: IWeapon; + shuriken: IWeapon; +} -#### Avoid the injection of data, as opposed to behaviour +@inject("IWeapon", "IWeapon") +class Ninja implements INinja { + public katana: IWeapon; + public shuriken: IWeapon; + public constructor( + @named("strong")katana: IWeapon, + @named("weak") shuriken: IWeapon + ) { + this.katana = katana; + this.shuriken = shuriken; + } +} +``` +We are binding `Katana` and `Shuriken` to `IWeapon` but a `whenTargetNamed` constraint is +added to avoid `AMBIGUOUS_MATCH` errors: +``` +kernel.bind("INinja").to(Ninja); +kernel.bind("IWeapon").to(Katana).whenTargetNamed("strong"); +kernel.bind("IWeapon").to(Shuriken).whenTargetNamed("weak"); +``` -Injection of data, as opposed to behaviour, is a subtype of the poltergeist anti-pattern, with the 'geist in this case being the container. If a class needs to be aware of the current date and time, you don't inject a DateTime, which is data; instead, you inject an abstraction over the system clock. This is not only correct for DI; it is absolutely essential for testability, so that you can test time-varying functions without needing to actually wait on them. +## Contextual bindings & @paramNames +The `@paramNames` decorator is used to access the names of the constructor arguments from a +contextual constraint even when the code is compressed. The `constructor(katana, shuriken) { ...` +becomes `constructor(a, b) { ...` after compression but thanks to `@paramNames` we can still +refer to the design-time names `katana` and `shuriken`. +``` +interface IWeapon {} +class Katana implements IWeapon { } +class Shuriken implements IWeapon {} -#### Avoid declaring every life cycle as Singleton +interface INinja { + katana: IWeapon; + shuriken: IWeapon; +} -Declaring every life cycle as Singleton is, to me, a perfect example of cargo cult programming and to a lesser degree the colloquially-named "object cesspool". I've seen more singleton abuse than I care to remember, and very little of it involves DI. +@inject("IWeapon", "IWeapon") +@paramNames("katana","shuriken") +class Ninja implements INinja { + public katana: IWeapon; + public shuriken: IWeapon; + public constructor( + katana: IWeapon, + shuriken: IWeapon + ) { + this.katana = katana; + this.shuriken = shuriken; + } +} +``` +We are binding `Katana` and `Shuriken` to `IWeapon` but a custom `when` constraint is added to avoid `AMBIGUOUS_MATCH` errors: +``` +kernel.bind(ninjaId).to(Ninja); + +kernel.bind("IWeapon").to(Katana).when((request: IRequest) => { + return request.target.name.equals("katana"); +}); + +kernel.bind("IWeapon").to(Shuriken).when((request: IRequest) => { + return request.target.name.equals("shuriken"); +}); +``` +The target fields implement the `IQueryableString` interface to help you to create your custom constraints: +``` +interface IQueryableString { + startsWith(searchString: string): boolean; + endsWith(searchString: string): boolean; + contains(searchString: string): boolean; + equals(compareString: string): boolean; + value(): string; +} +``` + +## Circular dependencies +InversifyJS is able to identify circular dependencies and will throw an exception to help you to +identify the location of the problem if a circular dependency is detected: -#### Avoid implementation-specific interface types +``` +Error: Circular dependency found between services: IKatana and INinja +``` -Another common error is implementation-specific interface types done just to be able to register it in the container. This is in and of itself a violation of the Dependency Inversion Principle (just because it's an interface, does not mean it's truly abstract) and often also includes interface bloat which violates the Interface Segregation Principle. +Plese refer to the [wiki](https://github.com/inversify/InversifyJS/wiki) for additional details. -#### Avoid optional dependencies +# Integration with other frameworks +Some integration examples are available in the [official examples repository](https://github.com/inversify/Inversify-code-samples). -In other words, there is a constructor that accepts dependency injection, but also another constructor that uses a "default" implementation. This also violates the DIP and tends to lead to LSP violations as well, as developers, over time, start making assumptions around the default implementation, and/or start new-ing up instances using the default constructor. # Support -If you are experience any kind of issues we will be happy to help. You can report an issue using the [issues page](https://github.com/inversify/InversifyJS/issues) or the [chat](https://gitter.im/inversify/InversifyJS). You can also ask questions at [Stack overflow](http://stackoverflow.com/tags/inversifyjs) using the `inversifyjs` tag. +If you are experience any kind of issues we will be happy to help. You can report an issue using the +[issues page](https://github.com/inversify/InversifyJS/issues) or the +[chat](https://gitter.im/inversify/InversifyJS). You can also ask questions at +[Stack overflow](http://stackoverflow.com/tags/inversifyjs) using the `inversifyjs` tag. -If you want to share your thoughts with the development team or join us you will be able to do so using the [official the mailing list](https://groups.google.com/forum/#!forum/inversifyjs). You can check out the [development wiki](https://github.com/inversify/InversifyJS/wiki) and browse the [documented source code](http://inversify.io/documentation/index.html) to learn more about InversifyJS internals. +If you want to share your thoughts with the development team or join us you will be able to do so using the +[official the mailing list](https://groups.google.com/forum/#!forum/inversifyjs). You can check out the +[development wiki](https://github.com/inversify/InversifyJS/wiki) and browse the +[documented source code](http://inversify.io/documentation/index.html) to learn more about InversifyJS internals. # License @@ -208,8 +541,17 @@ License under the MIT License (MIT) Copyright © 2015 [Remo H. Jansen](http://www.remojansen.com) -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the Software without restriction, including without +limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do so, subject to the +following conditions: -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 00000000..c0f0a2f7 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,4428 @@ + + + + + + InversifyJS | InversifyJS + + + + + +
+
+
+
+
+ InversifyJS +
+
+
+ Options +
+
+ All +
    +
  • Public
  • +
  • Public/Protected
  • +
  • All
  • +
+
+ + + + + + +
+
+ Menu +
+
+
+
+
+ +
+
+
+

InversifyJS

+

Join the chat at https://gitter.im/inversify/InversifyJS + Build Status + Coverage Status + npm version + Dependencies + img + img

+

+

A lightweight IoC container written in TypeScript. + Visit http://inversify.io/ for more information.

+

About

+

InversifyJS is a lightweight (4KB) pico inversion of control (IoC) container for TypeScript and JavaScript apps. A pico IoC container uses a class constructor to identify and inject its dependencies.

+

InversifyJS is easy to integrate with the majority of existing JavaScript frameworks and encourage the usage of the best OOP and IoC practices.

+

Motivation

+

JavaScript applications are becoming larger and larger day after day. As a result we are using a lot of our architecture experience from languages like Java or C# in JavaScript. We are embracing OOP with JavaScript but we are not writing SOLID JavaScript. InversifyJS has been designed to allow JavaScript developers to write code that adheres to the SOLID principles.

+

Philosophy

+

InversifyJS has been developed with 3 main goals:

+
    +
  1. Allow JavaScript developers to write code that adheres to the SOLID principles.

    +
  2. +
  3. Facilitate and encourage the adherence to the best OOP and IoC practices.

    +
  4. +
  5. Add as little runtime overhead as possible.

    +
  6. +
+

Installation

+

You can get the latest release and the type definitions using npm.

+
npm install inversify --save
+

Note: We have decided to drop support for bower and tsd. The InversifyJS type definitions are included in the npm package as it is recommended by the TypeScript development team.

+

If you are planing to use inversify as a global you will need to add a reference to the file named inversify-global.d.ts this file is included in the npm package:

+
/// <reference path="./node_modules/inversify/type_definitions/inversify-global.d.ts" />
+

The Basics (with TypeScript)

+

The main goal of InversifyJS is top allow JavaScript developers to write code that adheres to the SOLID principles. + Many of these principles refer to the usage of interfaces. + The main reason why it is not possible to write native SOLID JavaScript is because the language lacks interfaces. + In the other hand, TypeScript features interfaces, so, if you are going to use InversifyJS it is recommended + to work with TypeScript to get the most out of it.

+

1. Declare interfaces & implementations

+

Our goal is to write SOLID code. This means that we should "depend upon Abstractions. Do not depend upon concretions." + so we will start by declaring some interfaces (abstractions).

+
interface INinja {
+    fight(): string;
+    sneak(): string;
+}
+
+interface IKatana {
+    hit(): string;
+}
+
+interface IShuriken {
+    throw();
+}
+

We can continue declaring some classes which implement them (concretions). + We will start by declaring two classes (Katana & Shuriken) which don't have any dependencies.

+
class Katana implements IKatana {
+    public hit() {
+        return "cut!";
+    }
+}
+
+class Shuriken implements IShuriken {
+    public throw() {
+        return "hit!";
+    }
+}
+

Now we are going to declare a class named Ninja, which has two dependencies (IKatana & IShuriken):

+
@Inject("IKatana", "IShuriken")
+class Ninja implements INinja {
+
+    private _katana: IKatana;
+    private _shuriken: IShuriken;
+
+    public constructor(katana: IKatana, shuriken: IShuriken) {
+        this._katana = katana;
+        this._shuriken = shuriken;
+    }
+
+    public fight() { return this._katana.hit(); };
+    public sneak() { return this._shuriken.throw(); };
+
+}
+

2. Bind interfaces to implementations

+

Before we can start resolving and injecting dependencies we need to create an instance of the InversifyJS Kernel class. + The Kernel will automatically detect is a class has some dependencies by examining the @Inject annotation. + The Kernel will automatically detect if a class has some dependencies by examining the metadata provided by the Inject decorator.

+
import { Kernel } from "inversify";
+var kernel = new Kernel();
+

In order to resolve a dependency, the kernel needs to be told which implementation type (classes) to associate with each service type (interfaces). + We will use type bindings for this purpose. A type binding (or just a binding) is a mapping between a service type (an interface), and an implementation type (class).

+
kernel.bind<INinja>("INinja").to(Ninja);
+      kernel.bind<IKatana>("IKatana").to(Katana);
+      kernel.bind<IShuriken>("IShuriken").to(Shuriken).inSingletonScope();
+

When we declare a type binding, the TypeScript compiler will check that the implementation type (class) is actually and implementation of the service type (interface) and throw a compilation error if that is not the case.

+
// Compilation error: Shuriken is not assignable to type IKatana
+kernel.bind<IKatana>("IKatana").to(Shuriken);
+

We should keep the InversifyJS Kernel instantiation and type bindings centralized in one unique IoC configuration file. + This will help us to abstract our application from the IoC configuration.

+

3. Resolve & inject dependencies

+

After declaring the type bindings, we can invoke the kernel resolve method to resolve a dependency. + We will use a string as the interface identifier (instead of the interface itself) because the TypeScript interfaces are not available at runtime.

+
let ninja = kernel.get<INinja>("INinja");
+

If the interface that we are trying to resolve is bind to a class that has some dependencies, InversifyJS will + resolve and inject them into a new instance via the class constructor.

+
// Katana and Shuriken instances has been injected into a new Ninja instance via its constructor
+expect(ninja.fight()).eql("cut!"); // true
+expect(ninja.sneak()).eql("hit!"); // true
+

Our application dependency tree should have one unique root element, known as the application composition root, which is the + only place where we should invoke the resolve method.

+

Invoking resolve every time we need to inject something, as if it was a Service Locator is an anti-pattern. + If we are working with an MVC framework the composition root should be located in the application class, somewhere + along the routing logic or in a controller factory class. Please refer to the integration examples if you need additional help.

+

Integration with popular frameworks

+

InversifyJS was designed with many popular JavaScript frameworks in mind. As a result, it is really easy to integrate with existing JavaScript frameworks and examples of integration with many popular frameworks are available in the official examples repository.

+

Good Practices

+

Dependency Inversion (DI) isn't rocket science. We just need to try to avoid new and singleton except when there's a compelling reason to use them, such as a utility method that has no external dependencies, or a utility class that could not possibly have any purpose outside the framework (interop wrappers and dictionary keys are common examples of this).

+

Many of the problems with IoC frameworks come up when developers are first learning how to use them, and instead of actually changing the way they handle dependencies and abstractions to fit the IoC model, instead try to manipulate the IoC container to meet the expectations of their old coding style, which would often involve high coupling and low cohesion.

+

Use a Composition Root to avoid the Service Locator anti-pattern

+

Our application dependency tree should have one unique root element (known as the application composition root) which is the only component where we should invoke the resolve method.

+

Invoking resolve every time we need to inject something, as if it was a Service Locator is an anti-pattern. If we are working with an MVC framework the composition root should be located in the application class, somewhere along the routing logic or in a controller factory class.

+

Avoid Constructor over-injection

+

Constructor over-injection is a violation of the Single Responsibility Principle. Too many constructor arguments indicates too many dependencies; too many dependencies indicates that the class is trying to do too much. Usually this error correlates with other code smells, such as unusually long or ambiguous ("manager") class names.

+

Avoid the injection of data, as opposed to behaviour

+

Injection of data, as opposed to behaviour, is a subtype of the poltergeist anti-pattern, with the 'geist in this case being the container. If a class needs to be aware of the current date and time, you don't inject a DateTime, which is data; instead, you inject an abstraction over the system clock. This is not only correct for DI; it is absolutely essential for testability, so that you can test time-varying functions without needing to actually wait on them.

+

Avoid declaring every life cycle as Singleton

+

Declaring every life cycle as Singleton is, to me, a perfect example of cargo cult programming and to a lesser degree the colloquially-named "object cesspool". I've seen more singleton abuse than I care to remember, and very little of it involves DI.

+

Avoid implementation-specific interface types

+

Another common error is implementation-specific interface types done just to be able to register it in the container. This is in and of itself a violation of the Dependency Inversion Principle (just because it's an interface, does not mean it's truly abstract) and often also includes interface bloat which violates the Interface Segregation Principle.

+

Avoid optional dependencies

+

In other words, there is a constructor that accepts dependency injection, but also another constructor that uses a "default" implementation. This also violates the DIP and tends to lead to LSP violations as well, as developers, over time, start making assumptions around the default implementation, and/or start new-ing up instances using the default constructor.

+

Support

+

If you are experience any kind of issues we will be happy to help. You can report an issue using the issues page or the chat. You can also ask questions at Stack overflow using the inversifyjs tag.

+

If you want to share your thoughts with the development team or join us you will be able to do so using the official the mailing list. You can check out the development wiki and browse the documented source code to learn more about InversifyJS internals.

+

License

+

License under the MIT License (MIT)

+

Copyright © 2015 Remo H. Jansen

+

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

+

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

+

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+
+
+
+

Index

+
+ +
+
+
+

External modules

+
+ +

"activation/decorator_utils"

+
"activation/decorator_utils":
+ +
+ +

_decorate

+
    +
  • _decorate(decorators: any, target: any): void
  • +
+ +
+
+ +

_param

+
    +
  • _param(paramIndex: any, decorator: any): (Anonymous function)
  • +
+ +
+
+ +

decorate

+
    +
  • decorate(decorator: function | function, target: any, parameterIndex?: number): void
  • +
+
    +
  • + +

    Parameters

    +
      +
    • +
      decorator: function | function
      +
    • +
    • +
      target: any
      +
    • +
    • +
      Optional parameterIndex: number
      +
    • +
    +

    Returns void

    +
  • +
+
+
+ +

tagParameter

+
    +
  • tagParameter(target: any, targetKey: string, index: number, metadata: IMetadata): any
  • +
+
    +
  • + +

    Parameters

    +
      +
    • +
      target: any
      +
    • +
    • +
      targetKey: string
      +
    • +
    • +
      index: number
      +
    • +
    • +
      metadata: IMetadata
      +
    • +
    +

    Returns any

    +
  • +
+
+
+
+ +

"activation/inject"

+
"activation/inject":
+ +
+ +

inject

+
    +
  • inject(...paramTypes: string[]): (Anonymous function)
  • +
+
    +
  • + +

    Parameters

    +
      +
    • +
      Rest ...paramTypes: string[]
      +
    • +
    +

    Returns (Anonymous function)

    +
  • +
+
+
+
+ +

"activation/metadata"

+
"activation/metadata":
+ +
+ +

Metadata

+
Metadata:
+ +
+ +

constructor

+
    +
  • new Metadata(key: string, value: any): Metadata
  • +
+ +
+
+ +

key

+
key: string
+ +
+
+ +

value

+
value: any
+ +
+
+
+
+ +

"activation/named"

+
"activation/named":
+ +
+ +

named

+
    +
  • named(name: string): (Anonymous function)
  • +
+
    +
  • + +

    Parameters

    +
      +
    • +
      name: string
      +
    • +
    +

    Returns (Anonymous function)

    +
  • +
+
+
+
+ +

"activation/paramnames"

+
"activation/paramnames":
+ +
+ +

paramNames

+
    +
  • paramNames(...names: string[]): (Anonymous function)
  • +
+
    +
  • + +

    Parameters

    +
      +
    • +
      Rest ...names: string[]
      +
    • +
    +

    Returns (Anonymous function)

    +
  • +
+
+
+
+ +

"activation/tagged"

+
"activation/tagged":
+ +
+ +

tagged

+
    +
  • tagged(metadataKey: string, metadataValue: any): (Anonymous function)
  • +
+
    +
  • + +

    Parameters

    +
      +
    • +
      metadataKey: string
      +
    • +
    • +
      metadataValue: any
      +
    • +
    +

    Returns (Anonymous function)

    +
  • +
+
+
+
+ +

"bindings/binding"

+
"bindings/binding":
+ +
+ +

Binding

+
Binding:
+ +
+ +

constructor

+
    +
  • new Binding(runtimeIdentifier: string): Binding
  • +
+ +
+
+ +

cache

+
cache: T
+ +
+
+ +

constraint

+
constraint: function
+ +
+

Type declaration

+
    +
  • +
      +
    • (request: IRequest): boolean
    • +
    +
      +
    • +

      Parameters

      +
        +
      • +
        request: IRequest
        +
      • +
      +

      Returns boolean

      +
    • +
    +
  • +
+
+
+
+ +

factory

+
factory: IFactoryCreator<T>
+ +
+
+ +

implementationType

+
implementationType: INewable<T>
+ +
+
+ +

provider

+
provider: IProviderCreator<T>
+ +
+
+ +

proxyMaker

+
proxyMaker: function
+ +
+

Type declaration

+
    +
  • +
      +
    • (injectable: T): T
    • +
    +
      +
    • +

      Parameters

      +
        +
      • +
        injectable: T
        +
      • +
      +

      Returns T

      +
    • +
    +
  • +
+
+
+
+ +

runtimeIdentifier

+
runtimeIdentifier: string
+ +
+
+ +

scope

+ + +
+
+ +

type

+ + +
+
+
+
+ +

"bindings/binding_count"

+
"bindings/binding_count":
+ +
+ +

BindingCount

+
BindingCount:
+ +
+ +

MultipleBindingsAvailable

+
MultipleBindingsAvailable:
+ +
+
+ +

NoBindingsAvailable

+
NoBindingsAvailable:
+ +
+
+ +

OnlyOneBindingAvailable

+
OnlyOneBindingAvailable:
+ +
+
+
+
+ +

"bindings/binding_scope"

+
"bindings/binding_scope":
+ +
+ +

BindingScope

+
BindingScope:
+ +
+ +

Singleton

+
Singleton:
+ +
+
+ +

Transient

+
Transient:
+ +
+
+
+
+ +

"bindings/binding_type"

+
"bindings/binding_type":
+ +
+ +

BindingType

+
BindingType:
+ +
+ +

Constructor

+
Constructor:
+ +
+
+ +

Factory

+
Factory:
+ +
+
+ +

Instance

+
Instance:
+ +
+
+ +

Invalid

+
Invalid:
+ +
+
+ +

Provider

+
Provider:
+ +
+
+ +

Value

+
Value:
+ +
+
+
+
+ +

"constants/error_msgs"

+
"constants/error_msgs":
+ +
+ +

AMBIGUOUS_MATCH

+
AMBIGUOUS_MATCH: string
+ +
+
+ +

CANNOT_UNBIND

+
CANNOT_UNBIND: string
+ +
+
+ +

CIRCULAR_DEPENDENCY

+
CIRCULAR_DEPENDENCY: string
+ +
+
+ +

DUPLICATED_INJECT_DECORATOR

+
DUPLICATED_INJECT_DECORATOR: string
+ +
+
+ +

DUPLICATED_PARAM_NAMES_DECORATOR

+
DUPLICATED_PARAM_NAMES_DECORATOR: string
+ +
+
+ +

INVALID_BINDING_TYPE

+
INVALID_BINDING_TYPE: string
+ +
+
+ +

KEY_NOT_FOUND

+
KEY_NOT_FOUND: string
+ +
+
+ +

NOT_IMPLEMENTED

+
NOT_IMPLEMENTED: string
+ +
+
+ +

NOT_REGISTERED

+
NOT_REGISTERED: string
+ +
+
+ +

NULL_ARGUMENT

+
NULL_ARGUMENT: string
+ +
+
+
+ +

"constants/metadata_keys"

+
"constants/metadata_keys":
+ +
+ +

INJECT

+
INJECT: string
+ +
+
+ +

PARAM_NAMES

+
PARAM_NAMES: string
+ +
+
+ +

TAGGED

+
TAGGED: string
+ +
+
+
+ +

"inversify"

+
"inversify":
+ +
+
+ +

"kernel/kernel"

+
"kernel/kernel":
+ +
+ +

Kernel

+
Kernel:
+ +
+ +

constructor

+
    +
  • new Kernel(options?: IKernelOptions): Kernel
  • +
+
    +
  • + +

    Parameters

    +
      +
    • +
      Default value options: IKernelOptions = { middleware: [], modules: [] }
      +
    • +
    +

    Returns Kernel

    +
  • +
+
+
+ +

_bindingDictionary

+
_bindingDictionary: ILookup<IBinding<any>>
+ +
+
+ +

_planner

+
_planner: IPlanner
+ +
+
+ +

_resolver

+
_resolver: IResolver
+ +
+
+ +

_planAndResolve

+
    +
  • _planAndResolve<Service>(binding: IBinding<Service>): Service
  • +
+
    +
  • + +

    Type parameters

    +
      +
    • +

      Service

      +
    • +
    +

    Parameters

    +
      +
    • +
      binding: IBinding<Service>
      +
    • +
    +

    Returns Service

    +
  • +
+
+
+ +

bind

+
    +
  • bind<T>(runtimeIdentifier: string): IBindingToSyntax<T>
  • +
+
    +
  • + +

    Type parameters

    +
      +
    • +

      T

      +
    • +
    +

    Parameters

    +
      +
    • +
      runtimeIdentifier: string
      +
    • +
    +

    Returns IBindingToSyntax<T>

    +
  • +
+
+
+ +

get

+
    +
  • get<Service>(runtimeIdentifier: string): Service
  • +
+
    +
  • + +

    Type parameters

    +
      +
    • +

      Service

      +
    • +
    +

    Parameters

    +
      +
    • +
      runtimeIdentifier: string
      +
    • +
    +

    Returns Service

    +
  • +
+
+
+ +

getAll

+
    +
  • getAll<Service>(runtimeIdentifier: string): Service[]
  • +
+
    +
  • + +

    Type parameters

    +
      +
    • +

      Service

      +
    • +
    +

    Parameters

    +
      +
    • +
      runtimeIdentifier: string
      +
    • +
    +

    Returns Service[]

    +
  • +
+
+
+ +

unbind

+
    +
  • unbind(runtimeIdentifier: string): void
  • +
+
    +
  • + +

    Parameters

    +
      +
    • +
      runtimeIdentifier: string
      +
    • +
    +

    Returns void

    +
  • +
+
+
+ +

unbindAll

+
    +
  • unbindAll(): void
  • +
+ +
+
+
+
+ +

"kernel/key_value_pair"

+
"kernel/key_value_pair":
+ +
+ +

KeyValuePair

+
KeyValuePair:
+ +
+ +

constructor

+ + +
+
+ +

key

+
key: string
+ +
+
+ +

value

+
value: Array<T>
+ +
+
+
+
+ +

"kernel/lookup"

+
"kernel/lookup":
+ +
+ +

Lookup

+
Lookup:
+ +
+ +

constructor

+ + +
+
+ +

_dictionary

+
_dictionary: Array<IKeyValuePair<T>>
+ +
+
+ +

add

+
    +
  • add(key: string, value: T): void
  • +
+
    +
  • + +

    Parameters

    +
      +
    • +
      key: string
      +
    • +
    • +
      value: T
      +
    • +
    +

    Returns void

    +
  • +
+
+
+ +

get

+
    +
  • get(key: string): Array<T>
  • +
+
    +
  • + +

    Parameters

    +
      +
    • +
      key: string
      +
    • +
    +

    Returns Array<T>

    +
  • +
+
+
+ +

getIndexByKey

+
    +
  • getIndexByKey(key: string): number
  • +
+
    +
  • + +

    Parameters

    +
      +
    • +
      key: string
      +
    • +
    +

    Returns number

    +
  • +
+
+
+ +

hasKey

+
    +
  • hasKey(key: string): boolean
  • +
+
    +
  • + +

    Parameters

    +
      +
    • +
      key: string
      +
    • +
    +

    Returns boolean

    +
  • +
+
+
+ +

remove

+
    +
  • remove(key: string): void
  • +
+
    +
  • + +

    Parameters

    +
      +
    • +
      key: string
      +
    • +
    +

    Returns void

    +
  • +
+
+
+
+
+ +

"planning/context"

+
"planning/context":
+ +
+ +

Context

+
Context:
+ +
+ +

constructor

+
    +
  • new Context(kernel: IKernel): Context
  • +
+ +
+
+ +

kernel

+
kernel: IKernel
+ +
+
+ +

plan

+
plan: IPlan
+ +
+
+ +

addPlan

+
    +
  • addPlan(plan: IPlan): void
  • +
+ +
+
+
+
+ +

"planning/plan"

+
"planning/plan":
+ +
+ +

Plan

+
Plan:
+ +
+ +

constructor

+
    +
  • new Plan(parentContext: IContext, rootRequest: IRequest): Plan
  • +
+
    +
  • + +

    Parameters

    +
      +
    • +
      parentContext: IContext
      +
    • +
    • +
      rootRequest: IRequest
      +
    • +
    +

    Returns Plan

    +
  • +
+
+
+ +

parentContext

+
parentContext: IContext
+ +
+
+ +

rootRequest

+
rootRequest: IRequest
+ +
+
+
+
+ +

"planning/planner"

+
"planning/planner":
+ +
+ +

Planner

+
Planner:
+ +
+ +

_createSubRequest

+
    +
  • _createSubRequest(parentRequest: IRequest, target: ITarget): void
  • +
+
    +
  • + +

    Parameters

    +
      +
    • +
      parentRequest: IRequest
      +
    • +
    • +
      target: ITarget
      +
    • +
    +

    Returns void

    +
  • +
+
+
+ +

_getDependencies

+
    +
  • _getDependencies(func: Function): Target[]
  • +
+ +
+
+ +

_throwWhenCircularDependenciesFound

+
    +
  • _throwWhenCircularDependenciesFound(request: IRequest, previousServices?: string[]): void
  • +
+
    +
  • + +

    Parameters

    +
      +
    • +
      request: IRequest
      +
    • +
    • +
      Default value previousServices: string[] = []
      +
    • +
    +

    Returns void

    +
  • +
+
+
+ +

createContext

+
    +
  • createContext(kernel: IKernel): IContext
  • +
+ +
+
+ +

createPlan

+
    +
  • createPlan(context: IContext, binding: IBinding<any>): IPlan
  • +
+
    +
  • + +

    Parameters

    +
      +
    • +
      context: IContext
      +
    • +
    • +
      binding: IBinding<any>
      +
    • +
    +

    Returns IPlan

    +
  • +
+
+
+ +

getBindings

+
    +
  • getBindings<T>(kernel: IKernel, service: string): IBinding<T>[]
  • +
+
    +
  • + +

    Type parameters

    +
      +
    • +

      T

      +
    • +
    +

    Parameters

    +
      +
    • +
      kernel: IKernel
      +
    • +
    • +
      service: string
      +
    • +
    +

    Returns IBinding<T>[]

    +
  • +
+
+
+
+
+ +

"planning/queryable_string"

+
"planning/queryable_string":
+ +
+ +

QueryableString

+
QueryableString:
+ +
+ +

constructor

+ + +
+
+ +

str

+
str: string
+ +
+
+ +

contains

+
    +
  • contains(searchString: string): boolean
  • +
+ +
+
+ +

endsWith

+
    +
  • endsWith(searchString: string): boolean
  • +
+ +
+
+ +

equals

+
    +
  • equals(compareString: string): boolean
  • +
+ +
+
+ +

startsWith

+
    +
  • startsWith(searchString: string): boolean
  • +
+ +
+
+ +

value

+
    +
  • value(): string
  • +
+ +
+
+
+
+ +

"planning/request"

+
"planning/request":
+ +
+ +

Request

+
Request:
+ +
+ +

constructor

+
    +
  • new Request(service: string, parentContext: IContext, parentRequest: IRequest, bindings: IBinding<any> | Array<IBinding<any>>, target?: ITarget): Request
  • +
+
    +
  • + +

    Parameters

    +
      +
    • +
      service: string
      +
    • +
    • +
      parentContext: IContext
      +
    • +
    • +
      parentRequest: IRequest
      +
    • +
    • +
      bindings: IBinding<any> | Array<IBinding<any>>
      +
    • +
    • +
      Default value target: ITarget = null
      +
    • +
    +

    Returns Request

    +
  • +
+
+
+ +

bindings

+
bindings: IBinding<any>[]
+ +
+
+ +

childRequests

+
childRequests: IRequest[]
+ +
+
+ +

guid

+
guid: string
+ +
+
+ +

parentContext

+
parentContext: IContext
+ +
+
+ +

parentRequest

+
parentRequest: IRequest
+ +
+
+ +

service

+
service: string
+ +
+
+ +

target

+
target: ITarget
+ +
+
+ +

_guid

+
    +
  • _guid(): string
  • +
+ +
+
+ +

_s4

+
    +
  • _s4(): string
  • +
+ +
+
+ +

addChildRequest

+
    +
  • addChildRequest(service: string, bindings: IBinding<any> | Array<IBinding<any>>, target: ITarget): IRequest
  • +
+
    +
  • + +

    Parameters

    +
      +
    • +
      service: string
      +
    • +
    • +
      bindings: IBinding<any> | Array<IBinding<any>>
      +
    • +
    • +
      target: ITarget
      +
    • +
    +

    Returns IRequest

    +
  • +
+
+
+
+
+ +

"planning/target"

+
"planning/target":
+ +
+ +

Target

+
Target:
+ +
+ +

constructor

+
    +
  • new Target(name: string, service: string, namedOrTagged?: string | IMetadata): Target
  • +
+
    +
  • + +

    Parameters

    +
      +
    • +
      name: string
      +
    • +
    • +
      service: string
      +
    • +
    • +
      Optional namedOrTagged: string | IMetadata
      +
    • +
    +

    Returns Target

    +
  • +
+
+
+ +

metadata

+
metadata: Array<IMetadata>
+ +
+
+ +

name

+ + +
+
+ +

service

+ + +
+
+ +

isArray

+
    +
  • isArray(): boolean
  • +
+ +
+
+ +

isNamed

+
    +
  • isNamed(): boolean
  • +
+ +
+
+ +

isTagged

+
    +
  • isTagged(): boolean
  • +
+ +
+
+ +

matchesName

+
    +
  • matchesName(name: string): boolean
  • +
+ +
+
+ +

matchesTag

+
    +
  • matchesTag(metadata: IMetadata): boolean
  • +
+
    +
  • + +

    Parameters

    +
      +
    • +
      metadata: IMetadata
      +
    • +
    +

    Returns boolean

    +
  • +
+
+
+
+
+ +

"resolution/resolver"

+
"resolution/resolver":
+ +
+ +

Resolver

+
Resolver:
+ +
+ +

constructor

+
    +
  • new Resolver(middleWare?: IMiddleware[]): Resolver
  • +
+ +
+
+ +

_middleWare

+
_middleWare: IMiddleware[]
+ +
+
+ +

_createInstance

+
    +
  • _createInstance(Func: object, injections: Object[]): any
  • +
+
    +
  • + +

    Parameters

    +
      +
    • +
      Func: object
      +
        +
      • +
        constructor: function
        +
          +
        • new __type(...args: any[]): __type
        • +
        +
      • +
      +
    • +
    • +
      injections: Object[]
      +
    • +
    +

    Returns any

    +
  • +
+
+
+ +

_inject

+
    +
  • _inject(request: IRequest): any
  • +
+ +
+
+ +

resolve

+
    +
  • resolve<Service>(context: IContext): Service
  • +
+
    +
  • + +

    Type parameters

    +
      +
    • +

      Service

      +
    • +
    +

    Parameters

    +
      +
    • +
      context: IContext
      +
    • +
    +

    Returns Service

    +
  • +
+
+
+
+
+ +

"syntax/binding_in_when_proxy_syntax"

+
"syntax/binding_in_when_proxy_syntax":
+ +
+ +

BindingInWhenProxySyntax

+
BindingInWhenProxySyntax:
+ +
+ +

constructor

+ +
    +
  • + +

    Parameters

    +
      +
    • +
      binding: IBinding<T>
      +
    • +
    +

    Returns BindingInWhenProxySyntax

    +
  • +
+
+
+ +

_binding

+
_binding: IBinding<T>
+ +
+
+ +

inSingletonScope

+
    +
  • inSingletonScope(): IBindingInWhenProxySyntax<T>
  • +
+
    +
  • + +

    Returns IBindingInWhenProxySyntax<T>

    +
  • +
+
+
+ +

inTransientScope

+
    +
  • inTransientScope(): IBindingInWhenProxySyntax<T>
  • +
+
    +
  • + +

    Returns IBindingInWhenProxySyntax<T>

    +
  • +
+
+
+ +

proxy

+
    +
  • proxy(proxymaker: function): IBindingInWhenProxySyntax<T>
  • +
+
    +
  • + +

    Parameters

    +
      +
    • +
      proxymaker: function
      +
        +
      • +
          +
        • (injectable: T): T
        • +
        +
          +
        • +

          Parameters

          +
            +
          • +
            injectable: T
            +
          • +
          +

          Returns T

          +
        • +
        +
      • +
      +
    • +
    +

    Returns IBindingInWhenProxySyntax<T>

    +
  • +
+
+
+ +

when

+
    +
  • when(constraint: function): IBindingInWhenProxySyntax<T>
  • +
+
    +
  • + +

    Parameters

    +
      +
    • +
      constraint: function
      +
        +
      • +
          +
        • (request: IRequest): boolean
        • +
        +
          +
        • +

          Parameters

          +
            +
          • +
            request: IRequest
            +
          • +
          +

          Returns boolean

          +
        • +
        +
      • +
      +
    • +
    +

    Returns IBindingInWhenProxySyntax<T>

    +
  • +
+
+
+
+
+ +

"syntax/binding_to_syntax"

+
"syntax/binding_to_syntax":
+ +
+ +

BindingToSyntax

+
BindingToSyntax:
+ +
+ +

constructor

+ + +
+
+ +

_binding

+
_binding: IBinding<T>
+ +
+
+ +

to

+
    +
  • to(constructor: object): IBindingInWhenProxySyntax<T>
  • +
+ +
+
+ +

toConstructor

+
    +
  • toConstructor<T2>(constructor: INewable<T2>): IBindingInWhenProxySyntax<T>
  • +
+
    +
  • + +

    Type parameters

    +
      +
    • +

      T2

      +
    • +
    +

    Parameters

    +
      +
    • +
      constructor: INewable<T2>
      +
    • +
    +

    Returns IBindingInWhenProxySyntax<T>

    +
  • +
+
+
+ +

toFactory

+
    +
  • toFactory<T2>(factory: IFactoryCreator<T2>): IBindingInWhenProxySyntax<T>
  • +
+
    +
  • + +

    Type parameters

    +
      +
    • +

      T2

      +
    • +
    +

    Parameters

    +
      +
    • +
      factory: IFactoryCreator<T2>
      +
    • +
    +

    Returns IBindingInWhenProxySyntax<T>

    +
  • +
+
+
+ +

toProvider

+
    +
  • toProvider<T2>(provider: IProviderCreator<T2>): IBindingInWhenProxySyntax<T>
  • +
+
    +
  • + +

    Type parameters

    +
      +
    • +

      T2

      +
    • +
    +

    Parameters

    +
      +
    • +
      provider: IProviderCreator<T2>
      +
    • +
    +

    Returns IBindingInWhenProxySyntax<T>

    +
  • +
+
+
+ +

toValue

+
    +
  • toValue(value: T): IBindingInWhenProxySyntax<T>
  • +
+ +
+
+
+
+
+
+

Legend

+
+
    +
  • Module
  • +
  • Object literal
  • +
  • Variable
  • +
  • Function
  • +
  • Function with type parameter
  • +
  • Index signature
  • +
  • Type alias
  • +
+
    +
  • Enumeration
  • +
  • Enumeration member
  • +
  • Property
  • +
  • Method
  • +
+
    +
  • Interface
  • +
  • Interface with type parameter
  • +
  • Constructor
  • +
  • Property
  • +
  • Method
  • +
  • Index signature
  • +
+
    +
  • Class
  • +
  • Class with type parameter
  • +
  • Constructor
  • +
  • Property
  • +
  • Method
  • +
  • Accessor
  • +
  • Index signature
  • +
+
    +
  • Inherited constructor
  • +
  • Inherited property
  • +
  • Inherited method
  • +
  • Inherited accessor
  • +
+
    +
  • Protected property
  • +
  • Protected method
  • +
  • Protected accessor
  • +
+
    +
  • Private property
  • +
  • Private method
  • +
  • Private accessor
  • +
+
    +
  • Static property
  • +
  • Static method
  • +
+
+
+
+
+

Generated using TypeDoc

+
+
+
+ + + \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js index 10c029c7..f10b3f06 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -28,7 +28,7 @@ gulp.task("lint", function() { return gulp.src([ "src/**/**.ts", "test/**/**.test.ts", - "type_definitions/**/**.ts" + "type_definitions/inversify/*.ts" ]) .pipe(tslint()) .pipe(tslint.report("verbose", config)); @@ -87,7 +87,11 @@ gulp.task("build", function(cb) { //****************************************************************************** gulp.task("document", function () { return gulp - .src(["src/*.ts"]) + .src([ + "src/**/**.ts", + "typings/browser.d.ts", + "node_modules/reflect-metadata/reflect-metadata.d.ts" + ]) .pipe(typedoc({ // TypeScript options (see typescript docs) target: "es5", @@ -102,9 +106,10 @@ gulp.task("document", function () { preserveConstEnums: true, suppressImplicitAnyIndexErrors: true, // Output options (see typedoc docs) - out: "./documentation", + out: "./docs", name: "InversifyJS", - version: true + version: true, + theme: "minimal" })); }); diff --git a/package.json b/package.json index 6faea28a..f1f0118f 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,8 @@ { "name": "inversify", - "version": "2.0.0-alpha.2", + "version": "2.0.0-alpha.3", "description": "A lightweight IoC container written in TypeScript.", "main": "dist/inversify.js", - "typings": "type_definitions/inversify-npm.d.ts", "directories": { "test": "test" }, diff --git a/src/activation/decorator_utils.ts b/src/activation/decorator_utils.ts index 38c57f9a..8e84564f 100644 --- a/src/activation/decorator_utils.ts +++ b/src/activation/decorator_utils.ts @@ -46,10 +46,10 @@ function _param(paramIndex, decorator) { } // Allows VanillaJS developers to use decorators: -// decorate(Inject("IFoo", "IBar"), FooBar); -// decorate(ParamNames("foo", "bar"), FooBar); -// decorate(Named("foo"), FooBar, 0); -// decorate(Tagged("bar"), FooBar, 1); +// decorate(inject("IFoo", "IBar"), FooBar); +// decorate(paramNames("foo", "bar"), FooBar); +// decorate(named("foo"), FooBar, 0); +// decorate(tagged("bar"), FooBar, 1); function decorate( decorator: (ClassDecorator|ParameterDecorator), target: any, diff --git a/src/activation/inject.ts b/src/activation/inject.ts index 47568d7f..52f6e561 100644 --- a/src/activation/inject.ts +++ b/src/activation/inject.ts @@ -3,7 +3,7 @@ import * as METADATA_KEY from "../constants/metadata_keys"; import * as ERRORS_MSGS from "../constants/error_msgs"; -function Inject(...paramTypes: string[]) { +function inject(...paramTypes: string[]) { return function(target: any) { if (Reflect.hasOwnMetadata(METADATA_KEY.INJECT, target) === true) { @@ -16,4 +16,4 @@ function Inject(...paramTypes: string[]) { }; } -export default Inject; +export default inject; diff --git a/src/activation/named.ts b/src/activation/named.ts index ae4a9b4c..fe45b21f 100644 --- a/src/activation/named.ts +++ b/src/activation/named.ts @@ -4,11 +4,11 @@ import Metadata from "../activation/metadata"; import { tagParameter } from "./decorator_utils"; // Used to add named metadata which is used to resolve name-based contextual bindings. -function Named(name: string) { +function named(name: string) { return function(target: any, targetKey: string, index: number) { let metadata = new Metadata("named", name); return tagParameter(target, targetKey, index, metadata); }; } -export default Named; +export default named; diff --git a/src/activation/paramnames.ts b/src/activation/paramnames.ts index 44f201db..7402d3e8 100644 --- a/src/activation/paramnames.ts +++ b/src/activation/paramnames.ts @@ -3,17 +3,17 @@ import * as METADATA_KEY from "../constants/metadata_keys"; import * as ERRORS_MSGS from "../constants/error_msgs"; -function ParamNames(...paramNames: string[]) { +function paramNames(...names: string[]) { return function(target: any) { if (Reflect.hasOwnMetadata(METADATA_KEY.PARAM_NAMES, target) === true) { throw new Error(ERRORS_MSGS.DUPLICATED_PARAM_NAMES_DECORATOR); } - Reflect.defineMetadata(METADATA_KEY.PARAM_NAMES, paramNames, target); + Reflect.defineMetadata(METADATA_KEY.PARAM_NAMES, names, target); return target; }; } -export default ParamNames; +export default paramNames; diff --git a/src/activation/tagged.ts b/src/activation/tagged.ts index 10e3cd8a..ff9ab759 100644 --- a/src/activation/tagged.ts +++ b/src/activation/tagged.ts @@ -4,11 +4,11 @@ import Metadata from "../activation/metadata"; import { tagParameter } from "./decorator_utils"; // Used to add custom metadata which is used to resolve metadata-based contextual bindings. -function Tagged(metadataKey: string, metadataValue: any) { +function tagged(metadataKey: string, metadataValue: any) { return function(target: any, targetKey: string, index: number) { let metadata = new Metadata(metadataKey, metadataValue); return tagParameter(target, targetKey, index, metadata); }; } -export default Tagged; +export default tagged; diff --git a/src/bindings/binding.ts b/src/bindings/binding.ts index 0dc4a8a7..b6ea507a 100644 --- a/src/bindings/binding.ts +++ b/src/bindings/binding.ts @@ -33,13 +33,22 @@ class Binding implements IBinding { // An async factory method used in BindingType.Provider bindings public provider: IProviderCreator; + // A constraint used to limit the contexts in which this binding is applicable + public constraint: (request: IRequest) => boolean; + + // A method used to create a proxy for a dependency + public proxyMaker: (injectable: T) => T; + constructor(runtimeIdentifier: string) { this.runtimeIdentifier = runtimeIdentifier; this.scope = BindingScope.Transient; this.type = BindingType.Invalid; + this.constraint = (request: IRequest) => { return true; }; this.implementationType = null; this.cache = null; this.factory = null; + this.provider = null; + this.proxyMaker = null; } } diff --git a/src/constants/error_msgs.ts b/src/constants/error_msgs.ts index 64c53263..5734a7b7 100644 --- a/src/constants/error_msgs.ts +++ b/src/constants/error_msgs.ts @@ -1,7 +1,7 @@ /// export const DUPLICATED_INJECT_DECORATOR = "Cannot apply @Inject decorator multiple times."; -export const DUPLICATED_PARAM_NAMES_DECORATOR = "Cannot apply @ParamNames decorator multiple times."; +export const DUPLICATED_PARAM_NAMES_DECORATOR = "Cannot apply @paramNames decorator multiple times."; export const NULL_ARGUMENT = "NULL argument"; export const KEY_NOT_FOUND = "Key Not Found"; export const AMBIGUOUS_MATCH = "Ambiguous match found for service:"; diff --git a/src/interfaces/bindings/binding.d.ts b/src/interfaces/bindings/binding.d.ts index 92a7fc09..5171c17c 100644 --- a/src/interfaces/bindings/binding.d.ts +++ b/src/interfaces/bindings/binding.d.ts @@ -5,6 +5,8 @@ interface IBinding { implementationType: INewable; factory: IFactoryCreator; provider: IProviderCreator; + constraint: (request: IRequest) => boolean; + proxyMaker: (injectable: T) => T; cache: T; scope: number; // BindingScope type: number; // BindingType diff --git a/src/interfaces/bindings/constraint.d.ts b/src/interfaces/bindings/constraint.d.ts deleted file mode 100644 index 40864c9f..00000000 --- a/src/interfaces/bindings/constraint.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -interface IConstraint { - -} diff --git a/src/interfaces/interfaces.d.ts b/src/interfaces/interfaces.d.ts index 100dfd92..bbb4f200 100644 --- a/src/interfaces/interfaces.d.ts +++ b/src/interfaces/interfaces.d.ts @@ -19,7 +19,6 @@ // BINDINGS /// -/// /// /// /// @@ -33,6 +32,5 @@ /// // SYNTAX -/// /// -/// +/// diff --git a/src/interfaces/kernel/kernel.d.ts b/src/interfaces/kernel/kernel.d.ts index 29ceafaf..7763d2d4 100644 --- a/src/interfaces/kernel/kernel.d.ts +++ b/src/interfaces/kernel/kernel.d.ts @@ -4,6 +4,6 @@ interface IKernel { bind(runtimeIdentifier: string): IBindingToSyntax; unbind(runtimeIdentifier: string): void; unbindAll(): void; - get(runtimeIdentifier: string): Service; - getAll(runtimeIdentifier: string): Service[]; + get(runtimeIdentifier: string): T; + getAll(runtimeIdentifier: string): T[]; } diff --git a/src/interfaces/syntax/binding_in_syntax.d.ts b/src/interfaces/syntax/binding_in_syntax.d.ts deleted file mode 100644 index f7cc7546..00000000 --- a/src/interfaces/syntax/binding_in_syntax.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -/// - -interface IBindingInSyntax { - inTransientScope(): IBindingWhenSyntax; - inSingletonScope(): IBindingWhenSyntax; -} diff --git a/src/interfaces/syntax/binding_in_when_proxy_syntax.d.ts b/src/interfaces/syntax/binding_in_when_proxy_syntax.d.ts new file mode 100644 index 00000000..374ac20f --- /dev/null +++ b/src/interfaces/syntax/binding_in_when_proxy_syntax.d.ts @@ -0,0 +1,10 @@ +/// + +interface IBindingInWhenProxySyntax { + inTransientScope(): IBindingInWhenProxySyntax; + inSingletonScope(): IBindingInWhenProxySyntax; + when(constraint: (request: IRequest) => boolean): IBindingInWhenProxySyntax; + whenTargetNamed(name: string): IBindingInWhenProxySyntax; + whenTargetTagged(tag: string, value: any): IBindingInWhenProxySyntax; + proxy(fn: (injectable: T) => T): IBindingInWhenProxySyntax; +} diff --git a/src/interfaces/syntax/binding_to_syntax.d.ts b/src/interfaces/syntax/binding_to_syntax.d.ts index 091f0450..342760e6 100644 --- a/src/interfaces/syntax/binding_to_syntax.d.ts +++ b/src/interfaces/syntax/binding_to_syntax.d.ts @@ -1,10 +1,10 @@ -/// -/// +/// interface IBindingToSyntax { - to(constructor: { new(...args: any[]): T; }): IBindingInSyntax; - toValue(value: T): IBindingWhenSyntax; - toConstructor(constructor: INewable): IBindingWhenSyntax; - toFactory(factory: IFactoryCreator): IBindingWhenSyntax; - toProvider(provider: IProviderCreator): IBindingWhenSyntax; + to(constructor: { new(...args: any[]): T; }): IBindingInWhenProxySyntax; + toValue(value: T): IBindingInWhenProxySyntax; + toConstructor(constructor: INewable): IBindingInWhenProxySyntax; + toFactory(factory: IFactoryCreator): IBindingInWhenProxySyntax; + toAutoFactory(): IBindingInWhenProxySyntax; + toProvider(provider: IProviderCreator): IBindingInWhenProxySyntax; } diff --git a/src/interfaces/syntax/binding_when_syntax.d.ts b/src/interfaces/syntax/binding_when_syntax.d.ts deleted file mode 100644 index 24042936..00000000 --- a/src/interfaces/syntax/binding_when_syntax.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -declare type Constraint = (request: IRequest) => boolean; - -interface IBindingWhenSyntax { - when(constraint: Constraint): void; -} diff --git a/src/inversify.ts b/src/inversify.ts index d0292818..c0deb155 100644 --- a/src/inversify.ts +++ b/src/inversify.ts @@ -6,15 +6,15 @@ // The Inversify main file, the library entry point. import Kernel from "./kernel/kernel"; -import Inject from "./activation/inject"; -import Tagged from "./activation/tagged"; -import Named from "./activation/named"; -import ParamNames from "./activation/paramnames"; +import inject from "./activation/inject"; +import tagged from "./activation/tagged"; +import named from "./activation/named"; +import paramNames from "./activation/paramnames"; import { decorate } from "./activation/decorator_utils"; export { Kernel }; export { decorate }; -export { Inject }; -export { Tagged }; -export { Named }; -export { ParamNames }; +export { inject }; +export { tagged }; +export { named }; +export { paramNames }; diff --git a/src/kernel/kernel.ts b/src/kernel/kernel.ts index 4b1d194b..5246110e 100644 --- a/src/kernel/kernel.ts +++ b/src/kernel/kernel.ts @@ -63,9 +63,9 @@ class Kernel implements IKernel { // Resolves a dependency by its runtime identifier // The runtime identifier must be associated with only one binding // use getAll when the runtime identifier is associated with multiple bindings - public get(runtimeIdentifier: string): Service { + public get(runtimeIdentifier: string): T { - let bindings = this._planner.getBindings(this, runtimeIdentifier); + let bindings = this._planner.getBindings(this, runtimeIdentifier); switch (bindings.length) { @@ -75,7 +75,7 @@ class Kernel implements IKernel { // CASE 2: There is 1 binding case BindingCount.OnlyOneBindingAvailable: - return this._planAndResolve(bindings[0]); + return this._planAndResolve(bindings[0]); // CASE 3: There are multiple bindings throw as don't have enough information (metadata) case BindingCount.MultipleBindingsAvailable: @@ -86,9 +86,9 @@ class Kernel implements IKernel { // Resolves a dependency by its runtime identifier // The runtime identifier can be associated with one or multiple bindings - public getAll(runtimeIdentifier: string): Service[] { + public getAll(runtimeIdentifier: string): T[] { - let bindings = this._planner.getBindings(this, runtimeIdentifier); + let bindings = this._planner.getBindings(this, runtimeIdentifier); switch (bindings.length) { @@ -101,13 +101,13 @@ class Kernel implements IKernel { case BindingCount.MultipleBindingsAvailable: default: return bindings.map((binding) => { - return this._planAndResolve(binding); + return this._planAndResolve(binding); }); } } // Generates an executes a resolution plan - private _planAndResolve(binding: IBinding): Service { + private _planAndResolve(binding: IBinding): T { // STEP 1: generate resolution context let context = this._planner.createContext(this); @@ -116,7 +116,7 @@ class Kernel implements IKernel { this._planner.createPlan(context, binding); // STEP 3: execute resolution plan - return this._resolver.resolve(context); + return this._resolver.resolve(context); } } diff --git a/src/planning/planner.ts b/src/planning/planner.ts index f6a28675..ff5bfad6 100644 --- a/src/planning/planner.ts +++ b/src/planning/planner.ts @@ -30,7 +30,7 @@ class Planner implements IPlanner { let dependencies = this._getDependencies(binding.implementationType); - dependencies.forEach((d) => { this._createSubRequest(rootRequest, d); }); + dependencies.forEach((target) => { this._createSubRequest(rootRequest, target); }); return plan; } @@ -38,8 +38,9 @@ class Planner implements IPlanner { let bindings: IBinding[] = []; let _kernel: any = kernel; let _bindingDictionary = _kernel._bindingDictionary; - if (_bindingDictionary.hasKey(service)) { - bindings = _bindingDictionary.get(service); + let _service = service.split("[]").join(""); + if (_bindingDictionary.hasKey(_service)) { + bindings = _bindingDictionary.get(_service); } return bindings; } @@ -47,31 +48,49 @@ class Planner implements IPlanner { private _createSubRequest(parentRequest: IRequest, target: ITarget) { try { + let bindings = this.getBindings(parentRequest.parentContext.kernel, target.service.value()); + let activeBindings = []; - // mutiple bindings available - if (bindings.length > 1) { + if (bindings.length > 1 && target.isArray() === false) { - // TODO 2.0.0-alpha.3 - // TODO handle multi-injection, named, tagged and contextual binsingd here - throw new Error(`${ERROR_MSGS.AMBIGUOUS_MATCH} ${target.service.value()}`); + // apply constraints if available to reduce the number of active bindings + activeBindings = bindings.filter((binding) => { + + let request = new Request( + binding.runtimeIdentifier, + parentRequest.parentContext, + parentRequest, + binding, + target + ); + + return binding.constraint(request); + + }); } else { + activeBindings = bindings; + } - // Use the only active binding to create a child request - let binding = bindings[0]; - let childRequest = parentRequest.addChildRequest(target.service.value(), binding, target); + if (activeBindings.length === 0) { - // Only try to plan sub-dependencies when binding type is BindingType.Instance - if (binding.type === BindingType.Instance) { + // no matching bindings found + throw new Error(`${ERROR_MSGS.NOT_REGISTERED} ${target.service.value()}`); + + } else if (activeBindings.length > 1 && target.isArray() === false) { + + // more than one matching binding found but target is not an array + throw new Error(`${ERROR_MSGS.AMBIGUOUS_MATCH} ${target.service.value()}`); + + } else { + + // one ore more than one matching bindings found + // when more than 1 matching bindings found target is an array + this._createChildRequest(parentRequest, target, activeBindings); - // Create child requests for sub-dependencies if any - let subDependencies = this._getDependencies(binding.implementationType); - subDependencies.forEach((d, index) => { - this._createSubRequest(childRequest, d); - }); - } } + } catch (error) { if (error instanceof RangeError) { this._throwWhenCircularDependenciesFound(parentRequest.parentContext.plan.rootRequest); @@ -81,6 +100,31 @@ class Planner implements IPlanner { } } + private _createChildRequest(parentRequest: IRequest, target: ITarget, bindings: IBinding[]) { + + // Use the only active binding to create a child request + let childRequest = parentRequest.addChildRequest(target.service.value(), bindings, target); + let subChildRequest = childRequest; + + bindings.forEach((binding) => { + + if (target.isArray()) { + subChildRequest = childRequest.addChildRequest(binding.runtimeIdentifier, binding, target); + } + + // Only try to plan sub-dependencies when binding type is BindingType.Instance + if (binding.type === BindingType.Instance) { + + // Create child requests for sub-dependencies if any + let subDependencies = this._getDependencies(binding.implementationType); + subDependencies.forEach((d, index) => { + this._createSubRequest(subChildRequest, d); + }); + } + + }); + } + private _throwWhenCircularDependenciesFound(request: IRequest, previousServices: string[] = []) { previousServices.push(request.service); @@ -107,12 +151,14 @@ class Planner implements IPlanner { let paramNames = Reflect.getMetadata(METADATA_KEY.PARAM_NAMES, func) || []; let tags = Reflect.getMetadata(METADATA_KEY.TAGGED, func) || []; - return injections.map((inject, index) => { + let targets = injections.map((inject, index) => { let targetName = paramNames[index]; let target = new Target(targetName, inject); target.metadata = tags[index.toString()] || []; return target; }); + + return targets; } } diff --git a/src/resolution/resolver.ts b/src/resolution/resolver.ts index 50ed3b63..7de80764 100644 --- a/src/resolution/resolver.ts +++ b/src/resolution/resolver.ts @@ -19,49 +19,77 @@ class Resolver implements IResolver { private _inject(request: IRequest) { + let bindings = request.bindings; let childRequests = request.childRequests; - let binding = request.bindings[0]; // TODO handle multi-injection - switch (binding.type) { - case BindingType.Value: + if (request.target && request.target.isArray() && bindings.length > 1) { + + // Create an array instead of creating an instance + return childRequests.map((childRequest) => { return this._inject(childRequest); }); + + } else { + + let result = null; + let binding = bindings[0]; + let isSingleton = binding.scope === BindingScope.Singleton; + + if (isSingleton && binding.cache !== null) { return binding.cache; + } + + switch (binding.type) { + + case BindingType.Value: + result = binding.cache; + break; + + case BindingType.Constructor: + result = binding.implementationType; + break; + + case BindingType.Factory: + result = binding.factory(request.parentContext); + break; + + case BindingType.Provider: + result = binding.provider(request.parentContext); + break; - case BindingType.Constructor: - return binding.implementationType; - - case BindingType.Factory: - return binding.factory(request.parentContext); - - case BindingType.Provider: - return binding.provider(request.parentContext); - - case BindingType.Instance: - let constr = binding.implementationType; - let isSingleton = binding.scope === BindingScope.Singleton; - - if (isSingleton && binding.cache !== null) { - return binding.cache; - } - - if (childRequests.length > 0) { - let injections = childRequests.map((childRequest) => { - return this._inject(childRequest); - }); - let instance = this._createInstance(constr, injections); - if (isSingleton) { binding.cache = instance; } - return instance; - } else { - let instance = new constr(); - if (isSingleton) { binding.cache = instance; } - return instance; - } - - case BindingType.Invalid: - default: - // The user probably created a binding but didn't finish it - // e.g. kernel.bind("ISomething"); missing BindingToSyntax - throw new Error(`${ERROR_MSGS.INVALID_BINDING_TYPE} ${request.service}`); + case BindingType.Instance: + + let constr = binding.implementationType; + + if (childRequests.length > 0) { + let injections = childRequests.map((childRequest) => { + return this._inject(childRequest); + }); + result = this._createInstance(constr, injections); + } else { + result = new constr(); + } + + break; + + case BindingType.Invalid: + default: + // The user probably created a binding but didn't finish it + // e.g. kernel.bind("ISomething"); missing BindingToSyntax + throw new Error(`${ERROR_MSGS.INVALID_BINDING_TYPE} ${request.service}`); + } + + // create proxy if requested + if (typeof binding.proxyMaker === "function") { + result = binding.proxyMaker(result); + } + + // store in cache if scope is singleton + if (isSingleton) { + binding.cache = result; + } + + return result; } + } private _createInstance(Func: { new(...args: any[]) : any }, injections: Object[]) { diff --git a/src/syntax/binding_in_syntax.ts b/src/syntax/binding_in_syntax.ts deleted file mode 100644 index 54ad8904..00000000 --- a/src/syntax/binding_in_syntax.ts +++ /dev/null @@ -1,26 +0,0 @@ -/// - -import BindingWhenSyntax from "./binding_when_syntax"; -import BindingScope from "../bindings/binding_scope"; - -class BindingInSyntax implements IBindingInSyntax { - - private _binding: IBinding; - - public constructor(binding: IBinding) { - this._binding = binding; - } - - public inTransientScope(): IBindingWhenSyntax { - this._binding.scope = BindingScope.Transient; - return new BindingWhenSyntax(this._binding); - } - - public inSingletonScope(): IBindingWhenSyntax { - this._binding.scope = BindingScope.Singleton; - return new BindingWhenSyntax(this._binding); - } - -} - -export default BindingInSyntax; diff --git a/src/syntax/binding_in_when_proxy_syntax.ts b/src/syntax/binding_in_when_proxy_syntax.ts new file mode 100644 index 00000000..e7e760ee --- /dev/null +++ b/src/syntax/binding_in_when_proxy_syntax.ts @@ -0,0 +1,51 @@ +/// + +import BindingScope from "../bindings/binding_scope"; +import Metadata from "../activation/metadata"; + +class BindingInWhenProxySyntax implements IBindingInWhenProxySyntax { + + private _binding: IBinding; + + public constructor(binding: IBinding) { + this._binding = binding; + } + + public inTransientScope(): IBindingInWhenProxySyntax { + this._binding.scope = BindingScope.Transient; + return new BindingInWhenProxySyntax(this._binding); + } + + public inSingletonScope(): IBindingInWhenProxySyntax { + this._binding.scope = BindingScope.Singleton; + return new BindingInWhenProxySyntax(this._binding); + } + + public when(constraint: (request: IRequest) => boolean): IBindingInWhenProxySyntax { + this._binding.constraint = constraint; + return new BindingInWhenProxySyntax(this._binding); + } + + public whenTargetNamed(name: string): IBindingInWhenProxySyntax { + this._binding.constraint = (request: IRequest) => { + return request.target.matchesName(name); + }; + return new BindingInWhenProxySyntax(this._binding); + } + + public whenTargetTagged(tag: string, value: any): IBindingInWhenProxySyntax { + this._binding.constraint = (request: IRequest) => { + let metadata = new Metadata(tag, value); + return request.target.matchesTag(metadata); + }; + return new BindingInWhenProxySyntax(this._binding); + } + + public proxy(proxymaker: (injectable: T) => T): IBindingInWhenProxySyntax { + this._binding.proxyMaker = proxymaker; + return new BindingInWhenProxySyntax(this._binding); + } + +} + +export default BindingInWhenProxySyntax; diff --git a/src/syntax/binding_to_syntax.ts b/src/syntax/binding_to_syntax.ts index 41c824f8..e64178f7 100644 --- a/src/syntax/binding_to_syntax.ts +++ b/src/syntax/binding_to_syntax.ts @@ -1,7 +1,6 @@ /// -import BindingInSyntax from "./binding_in_syntax"; -import BindingWhenSyntax from "./binding_when_syntax"; +import BindingInWhenProxySyntax from "./binding_in_when_proxy_syntax"; import BindingType from "../bindings/binding_type"; class BindingToSyntax implements IBindingToSyntax { @@ -12,34 +11,45 @@ class BindingToSyntax implements IBindingToSyntax { this._binding = binding; } - public to(constructor: { new(...args: any[]): T; }): IBindingInSyntax { + public to(constructor: { new(...args: any[]): T; }): IBindingInWhenProxySyntax { this._binding.type = BindingType.Instance; this._binding.implementationType = constructor; - return new BindingInSyntax(this._binding); + return new BindingInWhenProxySyntax(this._binding); } - public toValue(value: T): IBindingWhenSyntax { + public toValue(value: T): IBindingInWhenProxySyntax { this._binding.type = BindingType.Value; this._binding.cache = value; - return new BindingWhenSyntax(this._binding); + return new BindingInWhenProxySyntax(this._binding); } - public toConstructor(constructor: INewable): IBindingWhenSyntax { + public toConstructor(constructor: INewable): IBindingInWhenProxySyntax { this._binding.type = BindingType.Constructor; this._binding.implementationType = constructor; - return new BindingWhenSyntax(this._binding); + return new BindingInWhenProxySyntax(this._binding); } - public toFactory(factory: IFactoryCreator): IBindingWhenSyntax { + public toFactory(factory: IFactoryCreator): IBindingInWhenProxySyntax { this._binding.type = BindingType.Factory; this._binding.factory = factory; - return new BindingWhenSyntax(this._binding); + return new BindingInWhenProxySyntax(this._binding); } - public toProvider(provider: IProviderCreator) { + public toAutoFactory(): IBindingInWhenProxySyntax { + this._binding.type = BindingType.Factory; + let id = this._binding.runtimeIdentifier.split("IFactory<").join("").split(">").join(""); + this._binding.factory = (context) => { + return () => { + return context.kernel.get(id); + }; + }; + return new BindingInWhenProxySyntax(this._binding); + } + + public toProvider(provider: IProviderCreator): IBindingInWhenProxySyntax { this._binding.type = BindingType.Provider; this._binding.provider = provider; - return new BindingWhenSyntax(this._binding); + return new BindingInWhenProxySyntax(this._binding); } } diff --git a/src/syntax/binding_when_syntax.ts b/src/syntax/binding_when_syntax.ts deleted file mode 100644 index f78588cb..00000000 --- a/src/syntax/binding_when_syntax.ts +++ /dev/null @@ -1,19 +0,0 @@ -/// - -import * as ERROR_MSGS from "../constants/error_msgs"; - -class BindingWhenSyntax implements IBindingWhenSyntax { - - private _binding: IBinding; - - public constructor(binding: IBinding) { - this._binding = binding; - } - - public when(constraint: Constraint): void { - throw new Error(`${ERROR_MSGS.NOT_IMPLEMENTED}`); - } - -} - -export default BindingWhenSyntax; diff --git a/test/activation/inject.test.ts b/test/activation/inject.test.ts index c125da4b..25ffcfe4 100644 --- a/test/activation/inject.test.ts +++ b/test/activation/inject.test.ts @@ -1,12 +1,12 @@ /// import { expect } from "chai"; -import { Inject, decorate } from "../../src/inversify"; +import { inject, decorate } from "../../src/inversify"; import * as METADATA_KEY from "../../src/constants/metadata_keys"; import * as ERRORS_MSGS from "../../src/constants/error_msgs"; import * as Stubs from "../utils/stubs"; -describe("@Inject", () => { +describe("@inject", () => { let WarriotWithoutInjections = Stubs.WarriotWithoutInjections; let Warrior = Stubs.Warrior; @@ -31,8 +31,8 @@ describe("@Inject", () => { class Test {} let useDecoratorMoreThanOnce = function() { - decorate(Inject("IKatana", "IShuriken"), Test); - decorate(Inject("IKatana", "IShuriken"), Test); + decorate(inject("IKatana", "IShuriken"), Test); + decorate(inject("IKatana", "IShuriken"), Test); }; expect(useDecoratorMoreThanOnce).to.throw(ERRORS_MSGS.DUPLICATED_INJECT_DECORATOR); @@ -47,7 +47,7 @@ describe("@Inject", () => { return VanillaJSWarrior; })(); - decorate(Inject("IKatana", "IShuriken"), VanillaJSWarrior); + decorate(inject("IKatana", "IShuriken"), VanillaJSWarrior); let metadata = Reflect.getMetadata(METADATA_KEY.INJECT, VanillaJSWarrior); expect(metadata).to.be.instanceof(Array); diff --git a/test/activation/named.test.ts b/test/activation/named.test.ts index c6d418bd..e1af0e64 100644 --- a/test/activation/named.test.ts +++ b/test/activation/named.test.ts @@ -5,7 +5,7 @@ declare function __param(paramIndex, decorator); import { expect } from "chai"; import { decorate } from "../../src/activation/decorator_utils"; -import Named from "../../src/activation/named"; +import named from "../../src/activation/named"; import * as METADATA_KEY from "../../src/constants/metadata_keys"; interface IWeapon {} @@ -32,8 +32,8 @@ class NamedWarrior { private _secondaryWeapon: IWeapon; constructor( - @Named("more_powerful") primary: IWeapon, - @Named("less_powerful") secondary: IWeapon) { + @named("more_powerful") primary: IWeapon, + @named("less_powerful") secondary: IWeapon) { this._primaryWeapon = primary; this._secondaryWeapon = secondary; @@ -55,7 +55,7 @@ class InvalidDecoratorUsageWarrior { public test(a: string) { /*...*/ } } -describe("@Named", () => { +describe("@named", () => { it("Should not generate metadata for unnamed parameters", () => { let metadataKey = METADATA_KEY.TAGGED; @@ -89,7 +89,7 @@ describe("@Named", () => { it("Should throw when applayed mutiple times", () => { let useDecoratorMoreThanOnce = function() { - __decorate([ __param(0, Named("a")), __param(0, Named("b")) ], InvalidDecoratorUsageWarrior); + __decorate([ __param(0, named("a")), __param(0, named("b")) ], InvalidDecoratorUsageWarrior); }; let msg = "Metadadata key named was used more than once in a parameter."; @@ -99,7 +99,7 @@ describe("@Named", () => { it("Should throw when not applayed to a constructor", () => { let useDecoratorOnMethodThatIsNotAContructor = function() { - __decorate([ __param(0, Named("a")) ], + __decorate([ __param(0, named("a")) ], InvalidDecoratorUsageWarrior.prototype, "test", Object.getOwnPropertyDescriptor(InvalidDecoratorUsageWarrior.prototype, "test")); }; @@ -117,8 +117,8 @@ describe("@Named", () => { return NamedVanillaJSWarrior; })(); - decorate(Named("more_powerful"), VanillaJSWarrior, 0); - decorate(Named("less_powerful"), VanillaJSWarrior, 1); + decorate(named("more_powerful"), VanillaJSWarrior, 0); + decorate(named("less_powerful"), VanillaJSWarrior, 1); let metadataKey = METADATA_KEY.TAGGED; let paramsMetadata = Reflect.getMetadata(metadataKey, VanillaJSWarrior); diff --git a/test/activation/paramnames.test.ts b/test/activation/paramnames.test.ts index d41fd592..d2191ef8 100644 --- a/test/activation/paramnames.test.ts +++ b/test/activation/paramnames.test.ts @@ -2,12 +2,12 @@ import { expect } from "chai"; import { decorate } from "../../src/activation/decorator_utils"; -import ParamNames from "../../src/activation/paramnames"; +import paramNames from "../../src/activation/paramnames"; import * as METADATA_KEY from "../../src/constants/metadata_keys"; import * as ERRORS_MSGS from "../../src/constants/error_msgs"; import * as Stubs from "../utils/stubs"; -describe("@ParamNames", () => { +describe("@paramNames", () => { it("Should not generate metadata when not applied", () => { @@ -29,7 +29,7 @@ describe("@ParamNames", () => { it("Should generate metadata if declared parameter names", () => { - @ParamNames("katana", "shuriken") + @paramNames("katana", "shuriken") class Warrior { public katana: Stubs.IKatana; @@ -65,8 +65,8 @@ describe("@ParamNames", () => { } let useDecoratorMoreThanOnce = function() { - decorate(ParamNames("katana", "shuriken"), Warrior); - decorate(ParamNames("katana", "shuriken"), Warrior); + decorate(paramNames("katana", "shuriken"), Warrior); + decorate(paramNames("katana", "shuriken"), Warrior); }; expect(useDecoratorMoreThanOnce).to.throw(ERRORS_MSGS.DUPLICATED_PARAM_NAMES_DECORATOR); @@ -81,7 +81,7 @@ describe("@ParamNames", () => { return VanillaJSWarrior; })(); - decorate(ParamNames("katana", "shuriken"), VanillaJSWarrior); + decorate(paramNames("katana", "shuriken"), VanillaJSWarrior); let metadata = Reflect.getMetadata(METADATA_KEY.PARAM_NAMES, VanillaJSWarrior); expect(metadata).to.be.instanceof(Array); diff --git a/test/inversify.test.ts b/test/inversify.test.ts index 47c88bcc..ac197e78 100644 --- a/test/inversify.test.ts +++ b/test/inversify.test.ts @@ -1,7 +1,7 @@ /// import { expect } from "chai"; -import { Kernel, Inject } from "../src/inversify"; +import { Kernel, inject } from "../src/inversify"; describe("InversifyJS", () => { @@ -32,7 +32,7 @@ describe("InversifyJS", () => { } } - @Inject("IKatana", "IShuriken") + @inject("IKatana", "IShuriken") class Ninja implements INinja { private _katana: IKatana; diff --git a/test/planning/planner.test.ts b/test/planning/planner.test.ts index d7fda31c..1213fde2 100644 --- a/test/planning/planner.test.ts +++ b/test/planning/planner.test.ts @@ -8,9 +8,10 @@ import Kernel from "../../src/kernel/kernel"; import Request from "../../src/planning/request"; import Plan from "../../src/planning/plan"; import Target from "../../src/planning/target"; -import Inject from "../../src/activation/inject"; -import ParamNames from "../../src/activation/paramnames"; +import inject from "../../src/activation/inject"; +import paramNames from "../../src/activation/paramnames"; import * as ERROR_MSGS from "../../src/constants/error_msgs"; +import tagged from "../../src/activation/tagged"; describe("Planner", () => { @@ -45,8 +46,8 @@ describe("Planner", () => { interface IKatana {} - @Inject("IKatanaHandler", "IKatanaBlade") - @ParamNames("handler", "blade") + @inject("IKatanaHandler", "IKatanaBlade") + @paramNames("handler", "blade") class Katana implements IKatana { public handler: IKatanaHandler; public blade: IKatanaBlade; @@ -61,8 +62,8 @@ describe("Planner", () => { interface INinja {} - @Inject("IKatana", "IShuriken") - @ParamNames("katana", "shuriken") + @inject("IKatana", "IShuriken") + @paramNames("katana", "shuriken") class Ninja implements INinja { public katana: IKatana; public shuriken: IShuriken; @@ -180,44 +181,6 @@ describe("Planner", () => { }); - it("Should generate plans with multi-injections", () => { - - // TODO 2.0.0-alpha.3 throw for now - - interface IWeapon {} - - class Katana implements IWeapon {} - class Shuriken implements IWeapon {} - - interface INinja {} - - @Inject("IWeapon", "IWeapon") - @ParamNames("katana", "shuriken") - class Ninja implements INinja { - public katana: IWeapon; - public shuriken: IWeapon; - public constructor(katana: IWeapon, shuriken: IWeapon) { - this.katana = katana; - this.shuriken = shuriken; - } - } - - let ninjaId = "INinja"; - let weaponId = "IWeapon"; - - let kernel = new Kernel(); - kernel.bind(ninjaId).to(Ninja); - kernel.bind(weaponId).to(Shuriken); - kernel.bind(weaponId).to(Katana); - - let throwErroFunction = () => { - kernel.get(ninjaId); - }; - - expect(throwErroFunction).to.throw(`${ERROR_MSGS.AMBIGUOUS_MATCH} ${weaponId}`); - - }); - it("Should throw when circular dependencies found", () => { interface IA {} @@ -225,7 +188,7 @@ describe("Planner", () => { interface IC {} interface ID {} - @Inject("IA") + @inject("IA") class D implements IC { public a: IA; public constructor(a: IA) { // circular dependency @@ -233,7 +196,7 @@ describe("Planner", () => { } } - @Inject("ID") + @inject("ID") class C implements IC { public d: ID; public constructor(d: ID) { @@ -243,7 +206,7 @@ describe("Planner", () => { class B implements IB {} - @Inject("IB", "IC") + @inject("IB", "IC") class A implements IA { public b: IB; public c: IC; @@ -282,8 +245,8 @@ describe("Planner", () => { interface IKatana {} - @Inject("IKatanaHandler", "IKatanaBlade") - @ParamNames("handler", "blade") + @inject("IKatanaHandler", "IKatanaBlade") + @paramNames("handler", "blade") class Katana implements IKatana { public handler: IKatanaHandler; public blade: IKatanaBlade; @@ -298,8 +261,8 @@ describe("Planner", () => { interface INinja {} - @Inject("IFactory", "IShuriken") - @ParamNames("katanaFactory", "shuriken") + @inject("IFactory", "IShuriken") + @paramNames("katanaFactory", "shuriken") class Ninja implements INinja { public katanaFactory: IFactory; public shuriken: IShuriken; @@ -343,4 +306,203 @@ describe("Planner", () => { }); + it("Should generate plans with multi-injections", () => { + + interface IWeapon {} + + class Katana implements IWeapon {} + class Shuriken implements IWeapon {} + + interface INinja {} + + @inject("IWeapon[]") + @paramNames("weapons") + class Ninja implements INinja { + public katana: IWeapon; + public shuriken: IWeapon; + public constructor(weapons: IWeapon[]) { + this.katana = weapons[0]; + this.shuriken = weapons[1]; + } + } + + let ninjaId = "INinja"; + let weaponId = "IWeapon"; + + let kernel = new Kernel(); + kernel.bind(ninjaId).to(Ninja); + kernel.bind(weaponId).to(Shuriken); + kernel.bind(weaponId).to(Katana); + + let _kernel: any = kernel; + let ninjaBinding = _kernel._bindingDictionary.get(ninjaId)[0]; + let planner = new Planner(); + let context = planner.createContext(kernel); + let actualPlan = planner.createPlan(context, ninjaBinding); + + // root request has no target + expect(actualPlan.rootRequest.service).eql(ninjaId); + expect(actualPlan.rootRequest.target).eql(null); + + // root request should only have one child request with target weapons/IWeapon[] + expect(actualPlan.rootRequest.childRequests[0].service).eql("IWeapon[]"); + expect(actualPlan.rootRequest.childRequests[1]).eql(undefined); + expect(actualPlan.rootRequest.childRequests[0].target.name.value()).eql("weapons"); + expect(actualPlan.rootRequest.childRequests[0].target.service.value()).eql("IWeapon[]"); + + // child request should have to child requests with targets weapons/IWeapon[] but bindings Katana and Shuriken + expect(actualPlan.rootRequest.childRequests[0].childRequests.length).eql(2); + + expect(actualPlan.rootRequest.childRequests[0].childRequests[0].service).eql(weaponId); + expect(actualPlan.rootRequest.childRequests[0].childRequests[0].target.name.value()).eql("weapons"); + expect(actualPlan.rootRequest.childRequests[0].childRequests[0].target.service.value()).eql("IWeapon[]"); + expect(actualPlan.rootRequest.childRequests[0].childRequests[0].service).eql("IWeapon"); + expect(actualPlan.rootRequest.childRequests[0].childRequests[0].bindings[0].runtimeIdentifier).eql("IWeapon"); + let shurikenImplementationType: any = actualPlan.rootRequest.childRequests[0].childRequests[0].bindings[0].implementationType; + expect(shurikenImplementationType.name).eql("Shuriken"); + + expect(actualPlan.rootRequest.childRequests[0].childRequests[1].service).eql(weaponId); + expect(actualPlan.rootRequest.childRequests[0].childRequests[1].target.name.value()).eql("weapons"); + expect(actualPlan.rootRequest.childRequests[0].childRequests[1].target.service.value()).eql("IWeapon[]"); + expect(actualPlan.rootRequest.childRequests[0].childRequests[1].service).eql("IWeapon"); + expect(actualPlan.rootRequest.childRequests[0].childRequests[1].bindings[0].runtimeIdentifier).eql("IWeapon"); + let katanaImplementationType: any = actualPlan.rootRequest.childRequests[0].childRequests[1].bindings[0].implementationType; + expect(katanaImplementationType.name).eql("Katana"); + + }); + + it("Should throw when an not matching bindings are found", () => { + + interface IKatana {} + class Katana implements IKatana { } + + interface IShuriken {} + class Shuriken implements IShuriken {} + + interface INinja {} + + @inject("IKatana", "IShuriken") + @paramNames("katana", "shuriken") + class Ninja implements INinja { + public katana: IKatana; + public shuriken: IShuriken; + public constructor(katana: IKatana, shuriken: IShuriken) { + this.katana = katana; + this.shuriken = shuriken; + } + } + + let ninjaId = "INinja"; + let shurikenId = "IShuriken"; + + let kernel = new Kernel(); + kernel.bind(ninjaId).to(Ninja); + kernel.bind(shurikenId).to(Shuriken); + + let _kernel: any = kernel; + let ninjaBinding = _kernel._bindingDictionary.get(ninjaId)[0]; + let planner = new Planner(); + let context = planner.createContext(kernel); + + let throwFunction = () => { planner.createPlan(context, ninjaBinding); }; + expect(throwFunction).to.throw(`${ERROR_MSGS.NOT_REGISTERED} IKatana`); + + }); + + it("Should throw when an ambiguous match is found", () => { + + interface IKatana {} + class Katana implements IKatana { } + class SharpKatana implements IKatana { } + + interface IShuriken {} + class Shuriken implements IShuriken {} + + interface INinja {} + + @inject("IKatana", "IShuriken") + @paramNames("katana", "shuriken") + class Ninja implements INinja { + public katana: IKatana; + public shuriken: IShuriken; + public constructor(katana: IKatana, shuriken: IShuriken) { + this.katana = katana; + this.shuriken = shuriken; + } + } + + let ninjaId = "INinja"; + let katanaId = "IKatana"; + let shurikenId = "IShuriken"; + + let kernel = new Kernel(); + kernel.bind(ninjaId).to(Ninja); + kernel.bind(katanaId).to(Katana); + kernel.bind(katanaId).to(SharpKatana); + kernel.bind(shurikenId).to(Shuriken); + + let _kernel: any = kernel; + let ninjaBinding = _kernel._bindingDictionary.get(ninjaId)[0]; + let planner = new Planner(); + let context = planner.createContext(kernel); + + let throwFunction = () => { planner.createPlan(context, ninjaBinding); }; + expect(throwFunction).to.throw(`${ERROR_MSGS.AMBIGUOUS_MATCH} IKatana`); + + }); + + it("Should apply constrains when an ambiguous match is found", () => { + + interface IWeapon {} + class Katana implements IWeapon { } + class Shuriken implements IWeapon {} + + interface INinja {} + + @inject("IWeapon", "IWeapon") + @paramNames("katana", "shuriken") + class Ninja implements INinja { + public katana: IWeapon; + public shuriken: IWeapon; + public constructor( + @tagged("canThrow", false) katana: IWeapon, + @tagged("canThrow", true) shuriken: IWeapon + ) { + this.katana = katana; + this.shuriken = shuriken; + } + } + + let ninjaId = "INinja"; + let weaponId = "IWeapon"; + + let kernel = new Kernel(); + kernel.bind(ninjaId).to(Ninja); + kernel.bind(weaponId).to(Katana).whenTargetTagged("canThrow", false); + kernel.bind(weaponId).to(Shuriken).whenTargetTagged("canThrow", true); + + let _kernel: any = kernel; + let ninjaBinding = _kernel._bindingDictionary.get(ninjaId)[0]; + let planner = new Planner(); + let context = planner.createContext(kernel); + + let actualPlan = planner.createPlan(context, ninjaBinding); + + // root request has no target + expect(actualPlan.rootRequest.service).eql(ninjaId); + expect(actualPlan.rootRequest.target).eql(null); + + // root request should have 2 child requests + expect(actualPlan.rootRequest.childRequests[0].service).eql(weaponId); + expect(actualPlan.rootRequest.childRequests[0].target.name.value()).eql("katana"); + expect(actualPlan.rootRequest.childRequests[0].target.service.value()).eql(weaponId); + + expect(actualPlan.rootRequest.childRequests[1].service).eql(weaponId); + expect(actualPlan.rootRequest.childRequests[1].target.name.value()).eql("shuriken"); + expect(actualPlan.rootRequest.childRequests[1].target.service.value()).eql(weaponId); + + expect(actualPlan.rootRequest.childRequests[2]).eql(undefined); + + }); + }); diff --git a/test/resolution/resolver.test.ts b/test/resolution/resolver.test.ts index a5fb592e..39e6c118 100644 --- a/test/resolution/resolver.test.ts +++ b/test/resolution/resolver.test.ts @@ -8,8 +8,10 @@ import Kernel from "../../src/kernel/kernel"; import Request from "../../src/planning/request"; import Plan from "../../src/planning/plan"; import Target from "../../src/planning/target"; -import Inject from "../../src/activation/inject"; -import ParamNames from "../../src/activation/paramnames"; +import inject from "../../src/activation/inject"; +import tagged from "../../src/activation/tagged"; +import named from "../../src/activation/named"; +import paramNames from "../../src/activation/paramnames"; import * as ERROR_MSGS from "../../src/constants/error_msgs"; import BindingType from "../../src/bindings/binding_type"; @@ -38,8 +40,8 @@ describe("Resolver", () => { blade: IKatanaBlade; } - @Inject("IKatanaHandler", "IKatanaBlade") - @ParamNames("handler", "blade") + @inject("IKatanaHandler", "IKatanaBlade") + @paramNames("handler", "blade") class Katana implements IKatana { public handler: IKatanaHandler; public blade: IKatanaBlade; @@ -57,8 +59,8 @@ describe("Resolver", () => { shuriken: IShuriken; } - @Inject("IKatana", "IShuriken") - @ParamNames("katana", "shuriken") + @inject("IKatana", "IShuriken") + @paramNames("katana", "shuriken") class Ninja implements INinja { public katana: IKatana; public shuriken: IShuriken; @@ -132,8 +134,8 @@ describe("Resolver", () => { blade: IKatanaBlade; } - @Inject("IKatanaHandler", "IKatanaBlade") - @ParamNames("handler", "blade") + @inject("IKatanaHandler", "IKatanaBlade") + @paramNames("handler", "blade") class Katana implements IKatana { public handler: IKatanaHandler; public blade: IKatanaBlade; @@ -151,8 +153,8 @@ describe("Resolver", () => { shuriken: IShuriken; } - @Inject("IKatana", "IShuriken") - @ParamNames("katana", "shuriken") + @inject("IKatana", "IShuriken") + @paramNames("katana", "shuriken") class Ninja implements INinja { public katana: IKatana; public shuriken: IShuriken; @@ -236,8 +238,8 @@ describe("Resolver", () => { shuriken: IShuriken; } - @Inject("IKatana", "IShuriken") - @ParamNames("katana", "shuriken") + @inject("IKatana", "IShuriken") + @paramNames("katana", "shuriken") class Ninja implements INinja { public katana: IKatana; public shuriken: IShuriken; @@ -305,8 +307,8 @@ describe("Resolver", () => { shuriken: IShuriken; } - @Inject("IKatana", "IShuriken") - @ParamNames("katana", "shuriken") + @inject("IKatana", "IShuriken") + @paramNames("katana", "shuriken") class Ninja implements INinja { public katana: IKatana; public shuriken: IShuriken; @@ -370,8 +372,8 @@ describe("Resolver", () => { blade: IKatanaBlade; } - @Inject("IKatanaHandler", "IKatanaBlade") - @ParamNames("handler", "blade") + @inject("IKatanaHandler", "IKatanaBlade") + @paramNames("handler", "blade") class Katana implements IKatana { public handler: IKatanaHandler; public blade: IKatanaBlade; @@ -389,8 +391,8 @@ describe("Resolver", () => { shuriken: IShuriken; } - @Inject("IKatana", "IShuriken") - @ParamNames("katana", "shuriken") + @inject("IKatana", "IShuriken") + @paramNames("katana", "shuriken") class Ninja implements INinja { public katana: IKatana; public shuriken: IShuriken; @@ -451,8 +453,8 @@ describe("Resolver", () => { (): IKatana; } - @Inject("IKatanaHandler", "IKatanaBlade") - @ParamNames("handler", "blade") + @inject("IKatanaHandler", "IKatanaBlade") + @paramNames("handler", "blade") class Katana implements IKatana { public handler: IKatanaHandler; public blade: IKatanaBlade; @@ -470,8 +472,8 @@ describe("Resolver", () => { shuriken: IShuriken; } - @Inject("IKatana", "IShuriken") - @ParamNames("katana", "shuriken") + @inject("IKatana", "IShuriken") + @paramNames("katana", "shuriken") class Ninja implements INinja { public katana: IKatana; public shuriken: IShuriken; @@ -526,6 +528,94 @@ describe("Resolver", () => { }); + it("Should be able to resolve bindings with auto factory", () => { + + interface IKatanaBlade {} + class KatanaBlade implements IKatanaBlade {} + + interface IKatanaHandler {} + class KatanaHandler implements IKatanaHandler {} + + interface IKatana { + handler: IKatanaHandler; + blade: IKatanaBlade; + } + + interface IKatanaFactory extends Function { + (): IKatana; + } + + @inject("IKatanaHandler", "IKatanaBlade") + @paramNames("handler", "blade") + class Katana implements IKatana { + public handler: IKatanaHandler; + public blade: IKatanaBlade; + public constructor(handler: IKatanaHandler, blade: IKatanaBlade) { + this.handler = handler; + this.blade = blade; + } + } + + interface IShuriken {} + class Shuriken implements IShuriken {} + + interface INinja { + katana: IKatana; + shuriken: IShuriken; + } + + @inject("IKatana", "IShuriken") + @paramNames("katana", "shuriken") + class Ninja implements INinja { + public katana: IKatana; + public shuriken: IShuriken; + public constructor(makeKatana: IKatanaFactory, shuriken: IShuriken) { + this.katana = makeKatana(); // IMPORTANT! + this.shuriken = shuriken; + } + } + + let ninjaId = "INinja"; + let shurikenId = "IShuriken"; + let katanaFactoryId = "IFactory"; + let katanaId = "IKatana"; + let katanaHandlerId = "IKatanaHandler"; + let katanaBladeId = "IKatanaBlade"; + + let kernel = new Kernel(); + kernel.bind(ninjaId).to(Ninja); + kernel.bind(shurikenId).to(Shuriken); + kernel.bind(katanaId).to(Katana); + kernel.bind(katanaBladeId).to(KatanaBlade); + kernel.bind(katanaHandlerId).to(KatanaHandler); + + kernel.bind>(katanaFactoryId).toAutoFactory(); + + let _kernel: any = kernel; + let ninjaBinding = _kernel._bindingDictionary.get(ninjaId)[0]; + let katanaFactoryBinding = _kernel._bindingDictionary.get(katanaFactoryId)[0]; + let shurikenBinding = _kernel._bindingDictionary.get(shurikenId)[0]; + + let planner = new Planner(); + let context = planner.createContext(kernel); + + let ninjaRequest = new Request(ninjaId, context, null, ninjaBinding, null); + let plan = new Plan(context, ninjaRequest); + plan.rootRequest.addChildRequest(katanaFactoryId, katanaFactoryBinding, new Target("makeKatana", katanaFactoryId)); + plan.rootRequest.addChildRequest(shurikenId, shurikenBinding, new Target("shuriken", shurikenId)); + context.addPlan(plan); + + let resolver = new Resolver(); + let ninja = resolver.resolve(context); + + expect(ninja instanceof Ninja).eql(true); + expect(ninja.katana instanceof Katana).eql(true); + expect(ninja.katana.handler instanceof KatanaHandler).eql(true); + expect(ninja.katana.blade instanceof KatanaBlade).eql(true); + expect(ninja.shuriken instanceof Shuriken).eql(true); + + }); + it("Should be able to resolve BindingType.Provider bindings", (done) => { interface IKatanaBlade {} @@ -543,8 +633,8 @@ describe("Resolver", () => { (): IKatana; } - @Inject("IKatanaHandler", "IKatanaBlade") - @ParamNames("handler", "blade") + @inject("IKatanaHandler", "IKatanaBlade") + @paramNames("handler", "blade") class Katana implements IKatana { public handler: IKatanaHandler; public blade: IKatanaBlade; @@ -563,8 +653,8 @@ describe("Resolver", () => { shuriken: IShuriken; } - @Inject("IKatana", "IShuriken") - @ParamNames("katana", "shuriken") + @inject("IKatana", "IShuriken") + @paramNames("katana", "shuriken") class Ninja implements INinja { public katana: IKatana; public katanaProvider: IProvider; @@ -578,7 +668,7 @@ describe("Resolver", () => { let ninjaId = "INinja"; let shurikenId = "IShuriken"; - let katanaFactoryId = "IFactory"; + let katanaProviderId = "IProvider"; let katanaId = "IKatana"; let katanaHandlerId = "IKatanaHandler"; let katanaBladeId = "IKatanaBlade"; @@ -590,7 +680,7 @@ describe("Resolver", () => { kernel.bind(katanaBladeId).to(KatanaBlade); kernel.bind(katanaHandlerId).to(KatanaHandler); - kernel.bind>(katanaFactoryId).toProvider((context: IContext) => { + kernel.bind>(katanaProviderId).toProvider((context: IContext) => { return () => { return new Promise((resolve) => { // Using setTimeout to simulate complex initialization @@ -601,7 +691,7 @@ describe("Resolver", () => { let _kernel: any = kernel; let ninjaBinding = _kernel._bindingDictionary.get(ninjaId)[0]; - let katanaFactoryBinding = _kernel._bindingDictionary.get(katanaFactoryId)[0]; + let katanaFactoryBinding = _kernel._bindingDictionary.get(katanaProviderId)[0]; let shurikenBinding = _kernel._bindingDictionary.get(shurikenId)[0]; let planner = new Planner(); @@ -609,7 +699,7 @@ describe("Resolver", () => { let ninjaRequest = new Request(ninjaId, context, null, ninjaBinding, null); let plan = new Plan(context, ninjaRequest); - plan.rootRequest.addChildRequest(katanaFactoryId, katanaFactoryBinding, new Target("makeKatana", katanaFactoryId)); + plan.rootRequest.addChildRequest(katanaProviderId, katanaFactoryBinding, new Target("katanaProvider", katanaProviderId)); plan.rootRequest.addChildRequest(shurikenId, shurikenBinding, new Target("shuriken", shurikenId)); context.addPlan(plan); @@ -628,4 +718,282 @@ describe("Resolver", () => { }); + it("Should be able to resolve plans with constraints on tagged targets", () => { + + interface IWeapon {} + class Katana implements IWeapon { } + class Shuriken implements IWeapon {} + + interface INinja { + katana: IWeapon; + shuriken: IWeapon; + } + + @inject("IWeapon", "IWeapon") + @paramNames("katana", "shuriken") + class Ninja implements INinja { + public katana: IWeapon; + public shuriken: IWeapon; + public constructor( + @tagged("canThrow", false) katana: IWeapon, + @tagged("canThrow", true) shuriken: IWeapon + ) { + this.katana = katana; + this.shuriken = shuriken; + } + } + + let ninjaId = "INinja"; + let weaponId = "IWeapon"; + + let kernel = new Kernel(); + kernel.bind(ninjaId).to(Ninja); + kernel.bind(weaponId).to(Katana).whenTargetTagged("canThrow", false); + kernel.bind(weaponId).to(Shuriken).whenTargetTagged("canThrow", true); + + let _kernel: any = kernel; + let ninjaBinding = _kernel._bindingDictionary.get(ninjaId)[0]; + let planner = new Planner(); + let context = planner.createContext(kernel); + let plan = planner.createPlan(context, ninjaBinding); + context.addPlan(plan); + + let resolver = new Resolver(); + let ninja = resolver.resolve(context); + + expect(ninja instanceof Ninja).eql(true); + expect(ninja.katana instanceof Katana).eql(true); + expect(ninja.shuriken instanceof Shuriken).eql(true); + + }); + + it("Should be able to resolve plans with constraints on named targets", () => { + + interface IWeapon {} + class Katana implements IWeapon { } + class Shuriken implements IWeapon {} + + interface INinja { + katana: IWeapon; + shuriken: IWeapon; + } + + @inject("IWeapon", "IWeapon") + @paramNames("katana", "shuriken") + class Ninja implements INinja { + public katana: IWeapon; + public shuriken: IWeapon; + public constructor( + @named("strong")katana: IWeapon, + @named("weak") shuriken: IWeapon + ) { + this.katana = katana; + this.shuriken = shuriken; + } + } + + let ninjaId = "INinja"; + let weaponId = "IWeapon"; + + let kernel = new Kernel(); + kernel.bind(ninjaId).to(Ninja); + kernel.bind(weaponId).to(Katana).whenTargetNamed("strong"); + kernel.bind(weaponId).to(Shuriken).whenTargetNamed("weak"); + + let _kernel: any = kernel; + let ninjaBinding = _kernel._bindingDictionary.get(ninjaId)[0]; + let planner = new Planner(); + let context = planner.createContext(kernel); + let plan = planner.createPlan(context, ninjaBinding); + context.addPlan(plan); + + let resolver = new Resolver(); + let ninja = resolver.resolve(context); + + expect(ninja instanceof Ninja).eql(true); + expect(ninja.katana instanceof Katana).eql(true); + expect(ninja.shuriken instanceof Shuriken).eql(true); + + }); + + it("Should be able to resolve plans with custom contextual constraints", () => { + + interface IWeapon {} + class Katana implements IWeapon { } + class Shuriken implements IWeapon {} + + interface INinja { + katana: IWeapon; + shuriken: IWeapon; + } + + @inject("IWeapon", "IWeapon") + @paramNames("katana", "shuriken") + class Ninja implements INinja { + public katana: IWeapon; + public shuriken: IWeapon; + public constructor( + katana: IWeapon, + shuriken: IWeapon + ) { + this.katana = katana; + this.shuriken = shuriken; + } + } + + let ninjaId = "INinja"; + let weaponId = "IWeapon"; + + let kernel = new Kernel(); + kernel.bind(ninjaId).to(Ninja); + + kernel.bind(weaponId).to(Katana).when((request: IRequest) => { + return request.target.name.equals("katana"); + }); + + kernel.bind(weaponId).to(Shuriken).when((request: IRequest) => { + return request.target.name.equals("shuriken"); + }); + + let _kernel: any = kernel; + let ninjaBinding = _kernel._bindingDictionary.get(ninjaId)[0]; + let planner = new Planner(); + let context = planner.createContext(kernel); + let plan = planner.createPlan(context, ninjaBinding); + context.addPlan(plan); + + let resolver = new Resolver(); + let ninja = resolver.resolve(context); + + expect(ninja instanceof Ninja).eql(true); + expect(ninja.katana instanceof Katana).eql(true); + expect(ninja.shuriken instanceof Shuriken).eql(true); + }); + + it("Should be able to resolve plans with multi-injections", () => { + + interface IWeapon { + name: string; + } + + class Katana implements IWeapon { + public name = "Katana"; + } + class Shuriken implements IWeapon { + public name = "Shuriken"; + } + + interface INinja { + katana: IWeapon; + shuriken: IWeapon; + } + + @inject("IWeapon[]") + @paramNames("weapons") + class Ninja implements INinja { + public katana: IWeapon; + public shuriken: IWeapon; + public constructor(weapons: IWeapon[]) { + this.katana = weapons[0]; + this.shuriken = weapons[1]; + } + } + + let ninjaId = "INinja"; + let weaponId = "IWeapon"; + + let kernel = new Kernel(); + kernel.bind(ninjaId).to(Ninja); + kernel.bind(weaponId).to(Katana); + kernel.bind(weaponId).to(Shuriken); + + let _kernel: any = kernel; + let ninjaBinding = _kernel._bindingDictionary.get(ninjaId)[0]; + let planner = new Planner(); + let context = planner.createContext(kernel); + let plan = planner.createPlan(context, ninjaBinding); + context.addPlan(plan); + + let resolver = new Resolver(); + let ninja = resolver.resolve(context); + + expect(ninja instanceof Ninja).eql(true); + expect(ninja.katana instanceof Katana).eql(true); + expect(ninja.shuriken instanceof Shuriken).eql(true); + + }); + + it("Should be able to resolve plans with proxy injections", () => { + + console.log(` + WARNING: Proxy canot be tested due to blocking issue: + http://stackoverflow.com/questions/35906938/how-to-enable-harmony-proxies-in-gulp-mocha + `); + + interface IKatana { + use: () => void; + } + + class Katana implements IKatana { + public use() { + console.log("Used Katana!"); + } + } + + interface INinja { + katana: IKatana; + } + + @inject("IKatana") + class Ninja implements INinja { + public katana: IKatana; + public constructor(katana: IKatana) { + this.katana = katana; + } + } + + let ninjaId = "INinja"; + let katanaId = "IKatana"; + + let kernel = new Kernel(); + kernel.bind(ninjaId).to(Ninja); + + // This is a global for unit testing but remember + // that it is not a good idea to use globals + let timeTracker = []; + + kernel.bind(katanaId).to(Katana).proxy((ninja) => { + // BLOCK http://stackoverflow.com/questions/35906938/how-to-enable-harmony-proxies-in-gulp-mocha + /* + let handler = { + apply: function(target, thisArgument, argumentsList) { + timeTracker.push(`Starting ${target.name} ${performance.now()}`); + let result = target.apply(thisArgument, argumentsList); + timeTracker.push(`Finished ${target.name} ${performance.now()}`); + return result; + } + }; + return new Proxy(ninja, handler); + */ + return ninja; + }); + + let _kernel: any = kernel; + let ninjaBinding = _kernel._bindingDictionary.get(ninjaId)[0]; + let planner = new Planner(); + let context = planner.createContext(kernel); + let plan = planner.createPlan(context, ninjaBinding); + context.addPlan(plan); + + let resolver = new Resolver(); + let ninja = resolver.resolve(context); + + ninja.katana.use(); + expect(Array.isArray(timeTracker)).eql(true); + + // BLOCK http://stackoverflow.com/questions/35906938/how-to-enable-harmony-proxies-in-gulp-mocha + // expect(timeTracker.length).eql(2); + + }); + }); diff --git a/test/syntax/binding_in_syntax.test.ts b/test/syntax/binding_in_syntax.test.ts deleted file mode 100644 index 9c4807f8..00000000 --- a/test/syntax/binding_in_syntax.test.ts +++ /dev/null @@ -1,41 +0,0 @@ -/// - -import { expect } from "chai"; -import Binding from "../../src/bindings/binding"; -import BindingScope from "../../src/bindings/binding_scope"; -import BindingInSyntax from "../../src/syntax/binding_in_syntax"; - -describe("BindingInSyntax", () => { - - it("Should set its own properties correctly", () => { - - interface INinja {} - let ninjaIdentifier = "INinja"; - - let binding = new Binding(ninjaIdentifier); - let bindingInSyntax = new BindingInSyntax(binding); - - // cast to any to be able to access private props - let _bindingInSyntax: any = bindingInSyntax; - - expect(_bindingInSyntax._binding.runtimeIdentifier).eql(ninjaIdentifier); - - }); - - it("Should be able to configure the scope of a binding", () => { - - interface INinja {} - let ninjaIdentifier = "INinja"; - - let binding = new Binding(ninjaIdentifier); - let bindingInSyntax = new BindingInSyntax(binding); - - bindingInSyntax.inSingletonScope(); - expect(binding.scope).eql(BindingScope.Singleton); - - bindingInSyntax.inTransientScope(); - expect(binding.scope).eql(BindingScope.Transient); - - }); - -}); diff --git a/test/syntax/binding_in_when_proxy_syntax.test.ts b/test/syntax/binding_in_when_proxy_syntax.test.ts new file mode 100644 index 00000000..f8e202f8 --- /dev/null +++ b/test/syntax/binding_in_when_proxy_syntax.test.ts @@ -0,0 +1,125 @@ +/// + +import { expect } from "chai"; +import Binding from "../../src/bindings/binding"; +import Request from "../../src/planning/request"; +import Target from "../../src/planning/target"; +import Metadata from "../../src/activation/metadata"; +import BindingScope from "../../src/bindings/binding_scope"; +import BindingInWhenProxySyntax from "../../src/syntax/binding_in_when_proxy_syntax"; + +describe("BindingInWhenProxySyntax", () => { + + it("Should set its own properties correctly", () => { + + interface INinja {} + let ninjaIdentifier = "INinja"; + + let binding = new Binding(ninjaIdentifier); + let bindingInSyntax = new BindingInWhenProxySyntax(binding); + + // cast to any to be able to access private props + let _bindingInSyntax: any = bindingInSyntax; + + expect(_bindingInSyntax._binding.runtimeIdentifier).eql(ninjaIdentifier); + + }); + + it("Should be able to configure the scope of a binding", () => { + + interface INinja {} + let ninjaIdentifier = "INinja"; + + let binding = new Binding(ninjaIdentifier); + let bindingInWhenProxySyntax = new BindingInWhenProxySyntax(binding); + + bindingInWhenProxySyntax.inSingletonScope(); + expect(binding.scope).eql(BindingScope.Singleton); + + bindingInWhenProxySyntax.inTransientScope(); + expect(binding.scope).eql(BindingScope.Transient); + + }); + + it("Should be able to configure custom constraints of a binding", () => { + + interface INinja {} + let ninjaIdentifier = "INinja"; + + let binding = new Binding(ninjaIdentifier); + let bindingInWhenProxySyntax = new BindingInWhenProxySyntax(binding); + + bindingInWhenProxySyntax.when((request: IRequest) => { + return request.target.name.equals("ninja"); + }); + + let target = new Target("ninja", ninjaIdentifier); + let request = new Request(ninjaIdentifier, null, null, binding, target); + expect(binding.constraint(request)).eql(true); + + }); + + it("Should be able to constraints a binding to a named target", () => { + + interface INinja {} + let ninjaIdentifier = "INinja"; + + let binding = new Binding(ninjaIdentifier); + let bindingInWhenProxySyntax = new BindingInWhenProxySyntax(binding); + + let named = "primary"; + + bindingInWhenProxySyntax.whenTargetNamed(named); + expect(binding.constraint).not.to.eql(null); + + let target = new Target("ninja", ninjaIdentifier, named); + let request = new Request(ninjaIdentifier, null, null, binding, target); + expect(binding.constraint(request)).eql(true); + + let target2 = new Target("ninja", ninjaIdentifier); + let request2 = new Request(ninjaIdentifier, null, null, binding, target2); + expect(binding.constraint(request2)).eql(false); + + }); + + it("Should be able to constraints a binding to a tagged target", () => { + + interface INinja {} + let ninjaIdentifier = "INinja"; + + let binding = new Binding(ninjaIdentifier); + let bindingInWhenProxySyntax = new BindingInWhenProxySyntax(binding); + + bindingInWhenProxySyntax.whenTargetTagged("canSwim", true); + expect(binding.constraint).not.to.eql(null); + + let target = new Target("ninja", ninjaIdentifier, new Metadata("canSwim", true)); + let request = new Request(ninjaIdentifier, null, null, binding, target); + expect(binding.constraint(request)).eql(true); + + let target2 = new Target("ninja", ninjaIdentifier, new Metadata("canSwim", false)); + let request2 = new Request(ninjaIdentifier, null, null, binding, target2); + expect(binding.constraint(request2)).eql(false); + + }); + + it("Should be able to configure the proxyMaker of a binding", () => { + + interface INinja {} + let ninjaIdentifier = "INinja"; + + let binding = new Binding(ninjaIdentifier); + let bindingInWhenProxySyntax = new BindingInWhenProxySyntax(binding); + + bindingInWhenProxySyntax.proxy((ninja: INinja) => { + // let handler = {}; + // return new Proxy(ninja, handler); + // BLOCK http://stackoverflow.com/questions/35906938/how-to-enable-harmony-proxies-in-gulp-mocha + return ninja; + }); + + expect(binding.proxyMaker).not.to.eql(null); + + }); + +}); diff --git a/test/syntax/binding_to_syntax.test.ts b/test/syntax/binding_to_syntax.test.ts index 719011c2..c4cde921 100644 --- a/test/syntax/binding_to_syntax.test.ts +++ b/test/syntax/binding_to_syntax.test.ts @@ -54,6 +54,11 @@ describe("BindingToSyntax", () => { expect(binding.type).eql(BindingType.Factory); expect(binding.factory).not.to.eql(null); + bindingToSyntax.toAutoFactory(); + + expect(binding.type).eql(BindingType.Factory); + expect(binding.factory).not.to.eql(null); + bindingToSyntax.toProvider((context) => { return () => { return new Promise((resolve) => { diff --git a/test/syntax/binding_when_syntax.test.ts b/test/syntax/binding_when_syntax.test.ts deleted file mode 100644 index 35b4e2a6..00000000 --- a/test/syntax/binding_when_syntax.test.ts +++ /dev/null @@ -1,40 +0,0 @@ -/// - -import { expect } from "chai"; -import Binding from "../../src/bindings/binding"; -import BindingWhenSyntax from "../../src/syntax/binding_when_syntax"; -import * as ERROR_MSGS from "../../src/constants/error_msgs"; - -describe("BindingWhenSyntax", () => { - - it("Should set its own properties correctly", () => { - - interface INinja {} - let ninjaIdentifier = "INinja"; - - let binding = new Binding(ninjaIdentifier); - let bindingWhenSyntax = new BindingWhenSyntax(binding); - - // cast to any to be able to access private props - let _bindingWhenSyntax: any = bindingWhenSyntax; - - expect(_bindingWhenSyntax._binding.runtimeIdentifier).eql(ninjaIdentifier); - - }); - - it("Should be able to configure the constraints of a binding", () => { - - interface INinja {} - let ninjaIdentifier = "INinja"; - - let binding = new Binding(ninjaIdentifier); - let bindingWhenSyntax = new BindingWhenSyntax(binding); - - let throwErroFunction = () => { - bindingWhenSyntax.when((context) => { return true; }); - }; - - expect(throwErroFunction).to.throw(`${ERROR_MSGS.NOT_IMPLEMENTED}`); - }); - -}); diff --git a/test/utils/stubs.ts b/test/utils/stubs.ts index 91f8f997..56c57295 100644 --- a/test/utils/stubs.ts +++ b/test/utils/stubs.ts @@ -1,4 +1,4 @@ -import { Inject, Named, Tagged, ParamNames } from "../../src/inversify"; +import { inject, named, tagged, paramNames } from "../../src/inversify"; export interface FooInterface { name: string; @@ -36,7 +36,7 @@ export class Bar implements BarInterface { } } -@Inject("FooInterface", "BarInterface") +@inject("FooInterface", "BarInterface") export class FooBar implements FooBarInterface { public foo: FooInterface; public bar: BarInterface; @@ -60,10 +60,10 @@ export class Shuriken implements IShuriken {} export class WarriotWithoutInjections {} -@Inject() +@inject() export class DecoratedWarriotWithoutInjections {} -@Inject("IKatana","IShuriken") +@inject("IKatana", "IShuriken") export class Warrior { private _primaryWeapon: IKatana; private _secondaryWeapon: IShuriken; @@ -99,28 +99,28 @@ export class MissingInjectionWarrior { } } -@Inject("IKatana","IShuriken") +@inject("IKatana", "IShuriken") export class NamedWarrior { private _primaryWeapon: IWeapon; private _secondaryWeapon: IWeapon; constructor( - @Named("strong") primary: IWeapon, - @Named("weak") secondary: IWeapon) { + @named("strong") primary: IWeapon, + @named("weak") secondary: IWeapon) { // ... } } -@Inject("IKatana","IShuriken") +@inject("IKatana", "IShuriken") export class TaggedWarrior { private _primaryWeapon: IWeapon; private _secondaryWeapon: IWeapon; constructor( - @Tagged("power", 5) primary: IWeapon, - @Tagged("power", 1) secondary: IWeapon) { + @tagged("power", 5) primary: IWeapon, + @tagged("power", 1) secondary: IWeapon) { // ... } } diff --git a/type_definitions/bluebird/bluebird.d.ts b/type_definitions/bluebird/bluebird.d.ts new file mode 100644 index 00000000..6528ce47 --- /dev/null +++ b/type_definitions/bluebird/bluebird.d.ts @@ -0,0 +1,755 @@ +// Compiled using typings@0.6.8 +// Source: https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/dd328830dddffbe19e9addd7cf8532cbd3600816/bluebird/bluebird.d.ts +// Type definitions for bluebird 2.0.0 +// Project: https://github.com/petkaantonov/bluebird +// Definitions by: Bart van der Schoor , falsandtru +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +// ES6 model with generics overload was sourced and trans-multiplied from es6-promises.d.ts +// By: Campredon + +// Warning: recommended to use `tsc > v0.9.7` (critical bugs in earlier generic code): +// - https://github.com/borisyankov/DefinitelyTyped/issues/1563 + +// Note: replicate changes to all overloads in both definition and test file +// Note: keep both static and instance members inline (so similar) + +// TODO fix remaining TODO annotations in both definition and test + +// TODO verify support to have no return statement in handlers to get a Promise (more overloads?) + +declare var Promise: PromiseConstructor; + +interface PromiseConstructor { + /** + * Create a new promise. The passed in function will receive functions `resolve` and `reject` as its arguments which can be called to seal the fate of the created promise. + */ + new (callback: (resolve: (thenableOrResult?: T | PromiseLike) => void, reject: (error: any) => void) => void): Promise; + + // Ideally, we'd define e.g. "export class RangeError extends Error {}", + // but as Error is defined as an interface (not a class), TypeScript doesn't + // allow extending Error, only implementing it. + // However, if we want to catch() only a specific error type, we need to pass + // a constructor function to it. So, as a workaround, we define them here as such. + RangeError(): RangeError; + CancellationError(): Promise.CancellationError; + TimeoutError(): Promise.TimeoutError; + TypeError(): Promise.TypeError; + RejectionError(): Promise.RejectionError; + OperationalError(): Promise.OperationalError; + + /** + * Changes how bluebird schedules calls a-synchronously. + * + * @param scheduler Should be a function that asynchronously schedules + * the calling of the passed in function + */ + setScheduler(scheduler: (callback: (...args: any[]) => void) => void): void; + + /** + * Start the chain of promises with `Promise.try`. Any synchronous exceptions will be turned into rejections on the returned promise. + * + * Note about second argument: if it's specifically a true array, its values become respective arguments for the function call. Otherwise it is passed as is as the first argument for the function call. + * + * Alias for `attempt();` for compatibility with earlier ECMAScript version. + */ + try(fn: () => T | PromiseLike, args?: any[], ctx?: any): Promise; + + attempt(fn: () => T | PromiseLike, args?: any[], ctx?: any): Promise; + + /** + * Returns a new function that wraps the given function `fn`. The new function will always return a promise that is fulfilled with the original functions return values or rejected with thrown exceptions from the original function. + * This method is convenient when a function can sometimes return synchronously or throw synchronously. + */ + method(fn: Function): Function; + + /** + * Create a promise that is resolved with the given `value`. If `value` is a thenable or promise, the returned promise will assume its state. + */ + resolve(value: T | PromiseLike): Promise; + resolve(): Promise; + + /** + * Create a promise that is rejected with the given `reason`. + */ + reject(reason: any): Promise; + reject(reason: any): Promise; + + /** + * Create a promise with undecided fate and return a `PromiseResolver` to control it. See resolution?: Promise(#promise-resolution). + */ + defer(): Promise.Resolver; + + /** + * Cast the given `value` to a trusted promise. If `value` is already a trusted `Promise`, it is returned as is. If `value` is not a thenable, a fulfilled is: Promise returned with `value` as its fulfillment value. If `value` is a thenable (Promise-like object, like those returned by jQuery's `$.ajax`), returns a trusted that: Promise assimilates the state of the thenable. + */ + cast(value: T | PromiseLike): Promise; + + /** + * Sugar for `Promise.resolve(undefined).bind(thisArg);`. See `.bind()`. + */ + bind(thisArg: any): Promise; + + /** + * See if `value` is a trusted Promise. + */ + is(value: any): boolean; + + /** + * Call this right after the library is loaded to enabled long stack traces. Long stack traces cannot be disabled after being enabled, and cannot be enabled after promises have alread been created. Long stack traces imply a substantial performance penalty, around 4-5x for throughput and 0.5x for latency. + */ + longStackTraces(): void; + + /** + * Returns a promise that will be fulfilled with `value` (or `undefined`) after given `ms` milliseconds. If `value` is a promise, the delay will start counting down when it is fulfilled and the returned promise will be fulfilled with the fulfillment value of the `value` promise. + */ + // TODO enable more overloads + delay(ms: number, value: T | PromiseLike): Promise; + delay(ms: number): Promise; + + /** + * Returns a function that will wrap the given `nodeFunction`. Instead of taking a callback, the returned function will return a promise whose fate is decided by the callback behavior of the given node function. The node function should conform to node.js convention of accepting a callback as last argument and calling that callback with error as the first argument and success value on the second argument. + * + * If the `nodeFunction` calls its callback with multiple success values, the fulfillment value will be an array of them. + * + * If you pass a `receiver`, the `nodeFunction` will be called as a method on the `receiver`. + */ + promisify(func: (callback: (err: any, result: T) => void) => void, receiver?: any): () => Promise; + promisify(func: (arg1: A1, callback: (err: any, result: T) => void) => void, receiver?: any): (arg1: A1) => Promise; + promisify(func: (arg1: A1, arg2: A2, callback: (err: any, result: T) => void) => void, receiver?: any): (arg1: A1, arg2: A2) => Promise; + promisify(func: (arg1: A1, arg2: A2, arg3: A3, callback: (err: any, result: T) => void) => void, receiver?: any): (arg1: A1, arg2: A2, arg3: A3) => Promise; + promisify(func: (arg1: A1, arg2: A2, arg3: A3, arg4: A4, callback: (err: any, result: T) => void) => void, receiver?: any): (arg1: A1, arg2: A2, arg3: A3, arg4: A4) => Promise; + promisify(func: (arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5, callback: (err: any, result: T) => void) => void, receiver?: any): (arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5) => Promise; + promisify(nodeFunction: Function, receiver?: any): Function; + + /** + * Promisifies the entire object by going through the object's properties and creating an async equivalent of each function on the object and its prototype chain. The promisified method name will be the original method name postfixed with `Async`. Returns the input object. + * + * Note that the original methods on the object are not overwritten but new methods are created with the `Async`-postfix. For example, if you `promisifyAll()` the node.js `fs` object use `fs.statAsync()` to call the promisified `stat` method. + */ + // TODO how to model promisifyAll? + promisifyAll(target: Object, options?: Promise.PromisifyAllOptions): any; + + + /** + * Returns a promise that is resolved by a node style callback function. + */ + fromNode(resolver: (callback: (err: any, result?: any) => void) => void, options? : {multiArgs? : boolean}): Promise; + fromCallback(resolver: (callback: (err: any, result?: any) => void) => void, options? : {multiArgs? : boolean}): Promise; + + /** + * Returns a function that can use `yield` to run asynchronous code synchronously. This feature requires the support of generators which are drafted in the next version of the language. Node version greater than `0.11.2` is required and needs to be executed with the `--harmony-generators` (or `--harmony`) command-line switch. + */ + // TODO fix coroutine GeneratorFunction + coroutine(generatorFunction: Function): Function; + + /** + * Spawn a coroutine which may yield promises to run asynchronous code synchronously. This feature requires the support of generators which are drafted in the next version of the language. Node version greater than `0.11.2` is required and needs to be executed with the `--harmony-generators` (or `--harmony`) command-line switch. + */ + // TODO fix spawn GeneratorFunction + spawn(generatorFunction: Function): Promise; + + /** + * This is relevant to browser environments with no module loader. + * + * Release control of the `Promise` namespace to whatever it was before this library was loaded. Returns a reference to the library namespace so you can attach it to something else. + */ + noConflict(): typeof Promise; + + /** + * Add `handler` as the handler to call when there is a possibly unhandled rejection. The default handler logs the error stack to stderr or `console.error` in browsers. + * + * Passing no value or a non-function will have the effect of removing any kind of handling for possibly unhandled rejections. + */ + onPossiblyUnhandledRejection(handler: (reason: any) => any): void; + + /** + * Given an array, or a promise of an array, which contains promises (or a mix of promises and values) return a promise that is fulfilled when all the items in the array are fulfilled. The promise's fulfillment value is an array with fulfillment values at respective positions to the original array. If any promise in the array rejects, the returned promise is rejected with the rejection reason. + */ + // TODO enable more overloads + // promise of array with promises of value + all(values: PromiseLike[]>): Promise; + // promise of array with values + all(values: PromiseLike): Promise; + // array with promises of value + all(values: PromiseLike[]): Promise; + // array with promises of different types + all(values: [PromiseLike, PromiseLike, PromiseLike, PromiseLike, PromiseLike]): Promise<[T1, T2, T3, T4, T5]>; + all(values: [PromiseLike, PromiseLike, PromiseLike, PromiseLike]): Promise<[T1, T2, T3, T4]>; + all(values: [PromiseLike, PromiseLike, PromiseLike]): Promise<[T1, T2, T3]>; + all(values: [PromiseLike, PromiseLike]): Promise<[T1, T2]>; + // array with values + all(values: T[]): Promise; + + /** + * Like ``Promise.all`` but for object properties instead of array items. Returns a promise that is fulfilled when all the properties of the object are fulfilled. The promise's fulfillment value is an object with fulfillment values at respective keys to the original object. If any promise in the object rejects, the returned promise is rejected with the rejection reason. + * + * If `object` is a trusted `Promise`, then it will be treated as a promise for object rather than for its properties. All other objects are treated for their properties as is returned by `Object.keys` - the object's own enumerable properties. + * + * *The original object is not modified.* + */ + // TODO verify this is correct + // trusted promise for object + props(object: Promise): Promise; + // object + props(object: Object): Promise; + + /** + * Given an array, or a promise of an array, which contains promises (or a mix of promises and values) return a promise that is fulfilled when all the items in the array are either fulfilled or rejected. The fulfillment value is an array of ``PromiseInspection`` instances at respective positions in relation to the input array. + * + * *original: The array is not modified. The input array sparsity is retained in the resulting array.* + */ + // promise of array with promises of value + settle(values: PromiseLike[]>): Promise[]>; + // promise of array with values + settle(values: PromiseLike): Promise[]>; + // array with promises of value + settle(values: PromiseLike[]): Promise[]>; + // array with values + settle(values: T[]): Promise[]>; + + /** + * Like `Promise.some()`, with 1 as `count`. However, if the promise fulfills, the fulfillment value is not an array of 1 but the value directly. + */ + // promise of array with promises of value + any(values: PromiseLike[]>): Promise; + // promise of array with values + any(values: PromiseLike): Promise; + // array with promises of value + any(values: PromiseLike[]): Promise; + // array with values + any(values: T[]): Promise; + + /** + * Given an array, or a promise of an array, which contains promises (or a mix of promises and values) return a promise that is fulfilled or rejected as soon as a promise in the array is fulfilled or rejected with the respective rejection reason or fulfillment value. + * + * **Note** If you pass empty array or a sparse array with no values, or a promise/thenable for such, it will be forever pending. + */ + // promise of array with promises of value + race(values: PromiseLike[]>): Promise; + // promise of array with values + race(values: PromiseLike): Promise; + // array with promises of value + race(values: PromiseLike[]): Promise; + // array with values + race(values: T[]): Promise; + + /** + * Initiate a competetive race between multiple promises or values (values will become immediately fulfilled promises). When `count` amount of promises have been fulfilled, the returned promise is fulfilled with an array that contains the fulfillment values of the winners in order of resolution. + * + * If too many promises are rejected so that the promise can never become fulfilled, it will be immediately rejected with an array of rejection reasons in the order they were thrown in. + * + * *The original array is not modified.* + */ + // promise of array with promises of value + some(values: PromiseLike[]>, count: number): Promise; + // promise of array with values + some(values: PromiseLike, count: number): Promise; + // array with promises of value + some(values: PromiseLike[], count: number): Promise; + // array with values + some(values: T[], count: number): Promise; + + /** + * Like `Promise.all()` but instead of having to pass an array, the array is generated from the passed variadic arguments. + */ + // variadic array with promises of value + join(...values: PromiseLike[]): Promise; + // variadic array with values + join(...values: T[]): Promise; + + /** + * Map an array, or a promise of an array, which contains a promises (or a mix of promises and values) with the given `mapper` function with the signature `(item, index, arrayLength)` where `item` is the resolved value of a respective promise in the input array. If any promise in the input array is rejected the returned promise is rejected as well. + * + * If the `mapper` function returns promises or thenables, the returned promise will wait for all the mapped results to be resolved as well. + * + * *The original array is not modified.* + */ + // promise of array with promises of value + map(values: PromiseLike[]>, mapper: (item: T, index: number, arrayLength: number) => U | PromiseLike, options?: Promise.ConcurrencyOption): Promise; + + // promise of array with values + map(values: PromiseLike, mapper: (item: T, index: number, arrayLength: number) => U | PromiseLike, options?: Promise.ConcurrencyOption): Promise; + + // array with promises of value + map(values: PromiseLike[], mapper: (item: T, index: number, arrayLength: number) => U | PromiseLike, options?: Promise.ConcurrencyOption): Promise; + + // array with values + map(values: T[], mapper: (item: T, index: number, arrayLength: number) => U | PromiseLike, options?: Promise.ConcurrencyOption): Promise; + + /** + * Similar to `map` with concurrency set to 1 but guaranteed to execute in sequential order + * + * If the `mapper` function returns promises or thenables, the returned promise will wait for all the mapped results to be resolved as well. + * + * *The original array is not modified.* + */ + // promise of array with promises of value + mapSeries(values: PromiseLike[]>, mapper: (item: R, index: number, arrayLength: number) => U | PromiseLike): Promise; + + // promise of array with values + mapSeries(values: PromiseLike, mapper: (item: R, index: number, arrayLength: number) => U | PromiseLike): Promise; + + // array with promises of value + mapSeries(values: PromiseLike[], mapper: (item: R, index: number, arrayLength: number) => U | PromiseLike): Promise; + + // array with values + mapSeries(values: R[], mapper: (item: R, index: number, arrayLength: number) => U | PromiseLike): Promise; + + + /** + * Reduce an array, or a promise of an array, which contains a promises (or a mix of promises and values) with the given `reducer` function with the signature `(total, current, index, arrayLength)` where `item` is the resolved value of a respective promise in the input array. If any promise in the input array is rejected the returned promise is rejected as well. + * + * If the reducer function returns a promise or a thenable, the result for the promise is awaited for before continuing with next iteration. + * + * *The original array is not modified. If no `intialValue` is given and the array doesn't contain at least 2 items, the callback will not be called and `undefined` is returned. If `initialValue` is given and the array doesn't have at least 1 item, `initialValue` is returned.* + */ + // promise of array with promises of value + reduce(values: PromiseLike[]>, reducer: (total: U, current: T, index: number, arrayLength: number) => U | PromiseLike, initialValue?: U): Promise; + + // promise of array with values + reduce(values: PromiseLike, reducer: (total: U, current: T, index: number, arrayLength: number) => U | PromiseLike, initialValue?: U): Promise; + + // array with promises of value + reduce(values: PromiseLike[], reducer: (total: U, current: T, index: number, arrayLength: number) => U | PromiseLike, initialValue?: U): Promise; + + // array with values + reduce(values: T[], reducer: (total: U, current: T, index: number, arrayLength: number) => U | PromiseLike, initialValue?: U): Promise; + + /** + * Filter an array, or a promise of an array, which contains a promises (or a mix of promises and values) with the given `filterer` function with the signature `(item, index, arrayLength)` where `item` is the resolved value of a respective promise in the input array. If any promise in the input array is rejected the returned promise is rejected as well. + * + * The return values from the filtered functions are coerced to booleans, with the exception of promises and thenables which are awaited for their eventual result. + * + * *The original array is not modified. + */ + // promise of array with promises of value + filter(values: PromiseLike[]>, filterer: (item: T, index: number, arrayLength: number) => boolean | PromiseLike, option?: Promise.ConcurrencyOption): Promise; + + // promise of array with values + filter(values: PromiseLike, filterer: (item: T, index: number, arrayLength: number) => boolean | PromiseLike, option?: Promise.ConcurrencyOption): Promise; + + // array with promises of value + filter(values: PromiseLike[], filterer: (item: T, index: number, arrayLength: number) => boolean | PromiseLike, option?: Promise.ConcurrencyOption): Promise; + + // array with values + filter(values: T[], filterer: (item: T, index: number, arrayLength: number) => boolean | PromiseLike, option?: Promise.ConcurrencyOption): Promise; + + /** + * Iterate over an array, or a promise of an array, which contains promises (or a mix of promises and values) with the given iterator function with the signature (item, index, value) where item is the resolved value of a respective promise in the input array. Iteration happens serially. If any promise in the input array is rejected the returned promise is rejected as well. + * + * Resolves to the original array unmodified, this method is meant to be used for side effects. If the iterator function returns a promise or a thenable, the result for the promise is awaited for before continuing with next iteration. + */ + // promise of array with promises of value + each(values: PromiseLike[]>, iterator: (item: T, index: number, arrayLength: number) => U | PromiseLike): Promise; + // array with promises of value + each(values: PromiseLike[], iterator: (item: T, index: number, arrayLength: number) => U | PromiseLike): Promise; + // array with values OR promise of array with values + each(values: T[] | PromiseLike, iterator: (item: T, index: number, arrayLength: number) => U | PromiseLike): Promise; +} + +interface Promise extends PromiseLike, Promise.Inspection { + /** + * Promises/A+ `.then()` with progress handler. Returns a new promise chained from this promise. The new promise will be rejected or resolved dedefer on the passed `fulfilledHandler`, `rejectedHandler` and the state of this promise. + */ + then(onFulfill: (value: T) => U | PromiseLike, onReject?: (error: any) => U | PromiseLike, onProgress?: (note: any) => any): Promise; + then(onFulfill: (value: T) => U | PromiseLike, onReject?: (error: any) => void | PromiseLike, onProgress?: (note: any) => any): Promise; + + /** + * This is a catch-all exception handler, shortcut for calling `.then(null, handler)` on this promise. Any exception happening in a `.then`-chain will propagate to nearest `.catch` handler. + * + * Alias `.caught();` for compatibility with earlier ECMAScript version. + */ + catch(onReject?: (error: any) => T | PromiseLike | void | PromiseLike): Promise; + caught(onReject?: (error: any) => T | PromiseLike | void | PromiseLike): Promise; + + catch(onReject?: (error: any) => U | PromiseLike): Promise; + caught(onReject?: (error: any) => U | PromiseLike): Promise; + + /** + * This extends `.catch` to work more like catch-clauses in languages like Java or C#. Instead of manually checking `instanceof` or `.name === "SomeError"`, you may specify a number of error constructors which are eligible for this catch handler. The catch handler that is first met that has eligible constructors specified, is the one that will be called. + * + * This method also supports predicate-based filters. If you pass a predicate function instead of an error constructor, the predicate will receive the error as an argument. The return result of the predicate will be used determine whether the error handler should be called. + * + * Alias `.caught();` for compatibility with earlier ECMAScript version. + */ + catch(predicate: (error: any) => boolean, onReject: (error: any) => T | PromiseLike | void | PromiseLike): Promise; + caught(predicate: (error: any) => boolean, onReject: (error: any) => T | PromiseLike | void | PromiseLike): Promise; + + catch(predicate: (error: any) => boolean, onReject: (error: any) => U | PromiseLike): Promise; + caught(predicate: (error: any) => boolean, onReject: (error: any) => U | PromiseLike): Promise; + + catch(ErrorClass: Function, onReject: (error: any) => T | PromiseLike | void | PromiseLike): Promise; + caught(ErrorClass: Function, onReject: (error: any) => T | PromiseLike | void | PromiseLike): Promise; + + catch(ErrorClass: Function, onReject: (error: any) => U | PromiseLike): Promise; + caught(ErrorClass: Function, onReject: (error: any) => U | PromiseLike): Promise; + + + /** + * Like `.catch` but instead of catching all types of exceptions, it only catches those that don't originate from thrown errors but rather from explicit rejections. + */ + error(onReject: (reason: any) => PromiseLike): Promise; + error(onReject: (reason: any) => U): Promise; + + /** + * Pass a handler that will be called regardless of this promise's fate. Returns a new promise chained from this promise. There are special semantics for `.finally()` in that the final value cannot be modified from the handler. + * + * Alias `.lastly();` for compatibility with earlier ECMAScript version. + */ + finally(handler: () => U | PromiseLike): Promise; + + lastly(handler: () => U | PromiseLike): Promise; + + /** + * Create a promise that follows this promise, but is bound to the given `thisArg` value. A bound promise will call its handlers with the bound value set to `this`. Additionally promises derived from a bound promise will also be bound promises with the same `thisArg` binding as the original promise. + */ + bind(thisArg: any): Promise; + + /** + * Like `.then()`, but any unhandled rejection that ends up here will be thrown as an error. + */ + done(onFulfilled?: (value: T) => PromiseLike, onRejected?: (error: any) => U | PromiseLike, onProgress?: (note: any) => any): void; + done(onFulfilled?: (value: T) => U, onRejected?: (error: any) => U | PromiseLike, onProgress?: (note: any) => any): void; + + /** + * Like `.finally()`, but not called for rejections. + */ + tap(onFulFill: (value: T) => U | PromiseLike): Promise; + + /** + * Shorthand for `.then(null, null, handler);`. Attach a progress handler that will be called if this promise is progressed. Returns a new promise chained from this promise. + */ + progressed(handler: (note: any) => any): Promise; + + /** + * Same as calling `Promise.delay(this, ms)`. With the exception that if this promise is bound to a value, the returned promise is bound to that value too. + */ + delay(ms: number): Promise; + + /** + * Returns a promise that will be fulfilled with this promise's fulfillment value or rejection reason. However, if this promise is not fulfilled or rejected within `ms` milliseconds, the returned promise is rejected with a `Promise.TimeoutError` instance. + * + * You may specify a custom error message with the `message` parameter. + */ + timeout(ms: number, message?: string): Promise; + + /** + * Register a node-style callback on this promise. When this promise is is either fulfilled or rejected, the node callback will be called back with the node.js convention where error reason is the first argument and success value is the second argument. The error argument will be `null` in case of success. + * Returns back this promise instead of creating a new one. If the `callback` argument is not a function, this method does not do anything. + */ + nodeify(callback: (err: any, value?: T) => void, options?: Promise.SpreadOption): Promise; + nodeify(...sink: any[]): Promise; + + /** + * Marks this promise as cancellable. Promises by default are not cancellable after v0.11 and must be marked as such for `.cancel()` to have any effect. Marking a promise as cancellable is infectious and you don't need to remark any descendant promise. + */ + cancellable(): Promise; + + /** + * Cancel this promise. The cancellation will propagate to farthest cancellable ancestor promise which is still pending. + * + * That ancestor will then be rejected with a `CancellationError` (get a reference from `Promise.CancellationError`) object as the rejection reason. + * + * In a promise rejection handler you may check for a cancellation by seeing if the reason object has `.name === "Cancel"`. + * + * Promises are by default not cancellable. Use `.cancellable()` to mark a promise as cancellable. + */ + // TODO what to do with this? + cancel(reason?: any): Promise; + + /** + * Like `.then()`, but cancellation of the the returned promise or any of its descendant will not propagate cancellation to this promise or this promise's ancestors. + */ + fork(onFulfilled?: (value: T) => U | PromiseLike, onRejected?: (error: any) => U | PromiseLike, onProgress?: (note: any) => any): Promise; + + /** + * Create an uncancellable promise based on this promise. + */ + uncancellable(): Promise; + + /** + * See if this promise can be cancelled. + */ + isCancellable(): boolean; + + /** + * See if this `promise` has been fulfilled. + */ + isFulfilled(): boolean; + + /** + * See if this `promise` has been rejected. + */ + isRejected(): boolean; + + /** + * See if this `promise` is still defer. + */ + isPending(): boolean; + + /** + * See if this `promise` is resolved -> either fulfilled or rejected. + */ + isResolved(): boolean; + + /** + * Get the fulfillment value of the underlying promise. Throws if the promise isn't fulfilled yet. + * + * throws `TypeError` + */ + value(): T; + + /** + * Get the rejection reason for the underlying promise. Throws if the promise isn't rejected yet. + * + * throws `TypeError` + */ + reason(): any; + + /** + * Synchronously inspect the state of this `promise`. The `PromiseInspection` will represent the state of the promise as snapshotted at the time of calling `.inspect()`. + */ + inspect(): Promise.Inspection; + + /** + * This is a convenience method for doing: + * + * + * promise.then(function(obj){ + * return obj[propertyName].call(obj, arg...); + * }); + * + */ + call(propertyName: string, ...args: any[]): Promise; + + /** + * This is a convenience method for doing: + * + * + * promise.then(function(obj){ + * return obj[propertyName]; + * }); + * + */ + // TODO find way to fix get() + // get(propertyName: string): Promise; + + /** + * Convenience method for: + * + * + * .then(function() { + * return value; + * }); + * + * + * in the case where `value` doesn't change its value. That means `value` is bound at the time of calling `.return()` + * + * Alias `.thenReturn();` for compatibility with earlier ECMAScript version. + */ + return(): Promise; + thenReturn(): Promise; + return(value: U): Promise; + thenReturn(value: U): Promise; + + /** + * Convenience method for: + * + * + * .then(function() { + * throw reason; + * }); + * + * Same limitations apply as with `.return()`. + * + * Alias `.thenThrow();` for compatibility with earlier ECMAScript version. + */ + throw(reason: Error): Promise; + thenThrow(reason: Error): Promise; + + /** + * Convert to String. + */ + toString(): string; + + /** + * This is implicitly called by `JSON.stringify` when serializing the object. Returns a serialized representation of the `Promise`. + */ + toJSON(): Object; + + /** + * Like calling `.then`, but the fulfillment value or rejection reason is assumed to be an array, which is flattened to the formal parameters of the handlers. + */ + // TODO how to model instance.spread()? like Q? + spread(onFulfill: Function, onReject?: (reason: any) => U | PromiseLike): Promise; + /* + // TODO or something like this? + spread(onFulfill: (...values: W[]) => PromiseLike, onReject?: (reason: any) => PromiseLike): Promise; + spread(onFulfill: (...values: W[]) => PromiseLike, onReject?: (reason: any) => U): Promise; + spread(onFulfill: (...values: W[]) => U, onReject?: (reason: any) => PromiseLike): Promise; + spread(onFulfill: (...values: W[]) => U, onReject?: (reason: any) => U): Promise; + */ + /** + * Same as calling `Promise.all(thisPromise)`. With the exception that if this promise is bound to a value, the returned promise is bound to that value too. + */ + // TODO type inference from array-resolving promise? + all(): Promise; + + /** + * Same as calling `Promise.props(thisPromise)`. With the exception that if this promise is bound to a value, the returned promise is bound to that value too. + */ + // TODO how to model instance.props()? + props(): Promise; + + /** + * Same as calling `Promise.settle(thisPromise)`. With the exception that if this promise is bound to a value, the returned promise is bound to that value too. + */ + // TODO type inference from array-resolving promise? + settle(): Promise[]>; + + /** + * Same as calling `Promise.any(thisPromise)`. With the exception that if this promise is bound to a value, the returned promise is bound to that value too. + */ + // TODO type inference from array-resolving promise? + any(): Promise; + + /** + * Same as calling `Promise.some(thisPromise)`. With the exception that if this promise is bound to a value, the returned promise is bound to that value too. + */ + // TODO type inference from array-resolving promise? + some(count: number): Promise; + + /** + * Same as calling `Promise.race(thisPromise, count)`. With the exception that if this promise is bound to a value, the returned promise is bound to that value too. + */ + // TODO type inference from array-resolving promise? + race(): Promise; + + /** + * Same as calling `Promise.map(thisPromise, mapper)`. With the exception that if this promise is bound to a value, the returned promise is bound to that value too. + */ + // TODO type inference from array-resolving promise? + map(mapper: (item: Q, index: number, arrayLength: number) => U | PromiseLike, options?: Promise.ConcurrencyOption): Promise; + + /** + * Same as `Promise.mapSeries(thisPromise, mapper)`. + */ + // TODO type inference from array-resolving promise? + mapSeries(mapper: (item: Q, index: number, arrayLength: number) => U | PromiseLike): Promise; + + /** + * Same as calling `Promise.reduce(thisPromise, Function reducer, initialValue)`. With the exception that if this promise is bound to a value, the returned promise is bound to that value too. + */ + // TODO type inference from array-resolving promise? + reduce(reducer: (memo: U, item: Q, index: number, arrayLength: number) => U | PromiseLike, initialValue?: U): Promise; + + /** + * Same as calling ``Promise.filter(thisPromise, filterer)``. With the exception that if this promise is bound to a value, the returned promise is bound to that value too. + */ + // TODO type inference from array-resolving promise? + filter(filterer: (item: U, index: number, arrayLength: number) => boolean | PromiseLike, options?: Promise.ConcurrencyOption): Promise; + + /** + * Same as calling ``Promise.each(thisPromise, iterator)``. With the exception that if this promise is bound to a value, the returned promise is bound to that value too. + */ + each(iterator: (item: T, index: number, arrayLength: number) => U | PromiseLike): Promise; +} + +/** + * Don't use variable namespace such as variables, functions, and classes. + * If you use this namespace, it will conflict in es6. + */ +declare namespace Promise { + export interface RangeError extends Error { + } + export interface CancellationError extends Error { + } + export interface TimeoutError extends Error { + } + export interface TypeError extends Error { + } + export interface RejectionError extends Error { + } + export interface OperationalError extends Error { + } + + export interface ConcurrencyOption { + concurrency: number; + } + export interface SpreadOption { + spread: boolean; + } + export interface PromisifyAllOptions { + suffix?: string; + filter?: (name: string, func: Function, target?: any, passesDefaultFilter?: boolean) => boolean; + // The promisifier gets a reference to the original method and should return a function which returns a promise + promisifier?: (originalMethod: Function) => () => PromiseLike; + } + + export interface Resolver { + /** + * Returns a reference to the controlled promise that can be passed to clients. + */ + promise: Promise; + + /** + * Resolve the underlying promise with `value` as the resolution value. If `value` is a thenable or a promise, the underlying promise will assume its state. + */ + resolve(value: T): void; + resolve(): void; + + /** + * Reject the underlying promise with `reason` as the rejection reason. + */ + reject(reason: any): void; + + /** + * Progress the underlying promise with `value` as the progression value. + */ + progress(value: any): void; + + /** + * Gives you a callback representation of the `PromiseResolver`. Note that this is not a method but a property. The callback accepts error object in first argument and success values on the 2nd parameter and the rest, I.E. node js conventions. + * + * If the the callback is called with multiple success values, the resolver fullfills its promise with an array of the values. + */ + // TODO specify resolver callback + callback: (err: any, value: T, ...values: T[]) => void; + } + + export interface Inspection { + /** + * See if the underlying promise was fulfilled at the creation time of this inspection object. + */ + isFulfilled(): boolean; + + /** + * See if the underlying promise was rejected at the creation time of this inspection object. + */ + isRejected(): boolean; + + /** + * See if the underlying promise was defer at the creation time of this inspection object. + */ + isPending(): boolean; + + /** + * Get the fulfillment value of the underlying promise. Throws if the promise wasn't fulfilled at the creation time of this inspection object. + * + * throws `TypeError` + */ + value(): T; + + /** + * Get the rejection reason for the underlying promise. Throws if the promise wasn't rejected at the creation time of this inspection object. + * + * throws `TypeError` + */ + reason(): any; + } +} + +declare module 'bluebird' { + export = Promise; +} \ No newline at end of file diff --git a/type_definitions/inversify-global-test.ts b/type_definitions/inversify-global-test.ts deleted file mode 100644 index b782b001..00000000 --- a/type_definitions/inversify-global-test.ts +++ /dev/null @@ -1,99 +0,0 @@ -/// - -module inversify_global_test { - - interface INinja { - fight(): string; - sneak(): string; - } - - interface IKatana { - hit(): string; - } - - interface IShuriken { - throw(); - } - - class Katana implements IKatana { - public hit() { - return "cut!"; - } - } - - class Shuriken implements IShuriken { - public throw() { - return "hit!"; - } - } - - let Inject = inversify.Inject; - - @Inject("IKatana", "IShuriken") - class Ninja implements INinja { - - private _katana: IKatana; - private _shuriken: IShuriken; - - public constructor(katana: IKatana, shuriken: IShuriken) { - this._katana = katana; - this._shuriken = shuriken; - } - - public fight() { return this._katana.hit(); }; - public sneak() { return this._shuriken.throw(); }; - - } - - let Kernel = inversify.Kernel; - - let kernel1 = new Kernel(); - kernel1.bind("INinja").to(Ninja); - kernel1.bind("IKatana").to(Katana); - kernel1.bind("IShuriken").to(Shuriken).inSingletonScope(); - - let ninja = kernel1.get("INinja"); - console.log(ninja); - - // Unbind - kernel1.unbind("INinja"); - kernel1.unbindAll(); - - // Kernel modules - let module: inversify.IKernelModule = (kernel: inversify.IKernel) => { - kernel.bind("INinja").to(Ninja); - kernel.bind("IKatana").to(Katana); - kernel.bind("IShuriken").to(Shuriken).inSingletonScope(); - }; - - let options: inversify.IKernelOptions = { - middleware: [], - modules: [module] - }; - - let kernel2 = new Kernel(options); - let ninja2 = kernel2.get("INinja"); - console.log(ninja2); - - // binding types - kernel2.bind("IKatana").to(Katana); - kernel2.bind("IKatana").toValue(new Katana()); - - kernel2.bind<__inversify.INewable>("IKatana").toConstructor(Katana); - - kernel2.bind<__inversify.IFactory>("IKatana").toFactory((context) => { - return () => { - return kernel2.get("IKatana"); - }; - }); - - kernel2.bind<__inversify.IProvider>("IKatana").toProvider((context) => { - return () => { - return new Promise((resolve) => { - let katana = kernel2.get("IKatana"); - resolve(katana); - }); - }; - }); - -} diff --git a/type_definitions/inversify-npm.d.ts b/type_definitions/inversify-npm.d.ts deleted file mode 100644 index ffa43911..00000000 --- a/type_definitions/inversify-npm.d.ts +++ /dev/null @@ -1,150 +0,0 @@ -// Type definitions for inversify 2.0.0-alpha.2 -// Project: https://github.com/inversify/InversifyJS -// Definitions by: inversify -// Definitions: https://github.com/borisyankov/DefinitelyTyped - -/// - -interface IMiddleware extends Function { - (...args: any[]): any; -} - -interface IKernelModule extends Function { - (kernel: IKernel): void; -} - -interface IKernelOptions { - middleware?: IMiddleware[]; - modules?: IKernelModule[]; -} - -interface IKernelConstructor { - new(options?: IKernelOptions): IKernel; -} - -interface IKernel { - bind(runtimeIdentifier: string): IBindingToSyntax; - unbind(runtimeIdentifier: string): void; - unbindAll(): void; - get(runtimeIdentifier: string): Service; - getAll(runtimeIdentifier: string): Service[]; -} - -interface IBindingWhenSyntax { - when(constraint: Constraint): void; -} - -interface IFactoryCreator extends Function { - (context: IContext): IFactory; -} - -interface IProviderCreator extends Function { - (context: IContext): IProvider; -} - -interface IFactory extends Function { - (): T; -} - -interface IProvider extends Function { - (): Promise; -} - -interface INewable { - new(...args: any[]): T; -} - -interface IBindingToSyntax { - to(constructor: { new(...args: any[]): T; }): IBindingInSyntax; - toValue(value: T): IBindingWhenSyntax; - toConstructor(constructor: INewable): IBindingWhenSyntax; - toFactory(factory: IFactoryCreator): IBindingWhenSyntax; - toProvider(provider: IProviderCreator): IBindingWhenSyntax; -} - -interface IBindingInSyntax { - inTransientScope(): IBindingWhenSyntax; - inSingletonScope(): IBindingWhenSyntax; -} - -interface IBinding { - runtimeIdentifier: string; - implementationType: { new(): T; }; - factory: IFactoryCreator; - provider: IProviderCreator; - cache: T; - scope: number; // BindingScope - type: number; // BindingType -} - -interface IMetadata { - key: string; - value: any; -} - -interface IContext { - - /// Gets the kernel that is driving the activation. - kernel: IKernel; - - /// Gets or sets the activation plan. - plan: IPlan; - - addPlan(plan: IPlan); -} - -interface IPlan { - parentContext: IContext; - rootRequest: IRequest; -} - -interface ITarget { - service: IQueryableString; - name: IQueryableString; - metadata: Array; - isArray(): boolean; - isNamed(): boolean; - isTagged(): boolean; - matchesName(name: string): boolean; - matchesTag(name: IMetadata): boolean; -} - -interface IQueryableString { - startsWith(searchString: string): boolean; - endsWith(searchString: string): boolean; - contains(searchString: string): boolean; - equals(compareString: string): boolean; - value(): string; -} - -interface IRequest { - - /// The service that was requested. - service: string; - - /// The parent context. - parentContext: IContext; - - /// The parent request. - parentRequest: IRequest; - - // The child requests - childRequests: IRequest[]; - - /// Gets the target that will receive the injection, if any. - target: ITarget; - - /// Gets the stack of bindings which have been activated by this request. - bindings: IBinding[]; - - // Adds a child request to the request - addChildRequest( - service: string, - bindings: (IBinding|IBinding[]), - target: ITarget): IRequest; -} - -declare type Constraint = (request: IRequest) => boolean; - -export var Kernel: IKernelConstructor; -export function Inject(...typeIdentifiers: string[]): (typeConstructor: any) => void; diff --git a/type_definitions/inversify-test.ts b/type_definitions/inversify-test.ts deleted file mode 100644 index 17ace77b..00000000 --- a/type_definitions/inversify-test.ts +++ /dev/null @@ -1,97 +0,0 @@ -/// - -import { Kernel, Inject, IKernel, IKernelOptions, INewable, IKernelModule, IFactory, IProvider } from "inversify"; - -module inversify_external_module_test { - - interface INinja { - fight(): string; - sneak(): string; - } - - interface IKatana { - hit(): string; - } - - interface IShuriken { - throw(); - } - - class Katana implements IKatana { - public hit() { - return "cut!"; - } - } - - class Shuriken implements IShuriken { - public throw() { - return "hit!"; - } - } - - @Inject("IKatana", "IShuriken") - class Ninja implements INinja { - - private _katana: IKatana; - private _shuriken: IShuriken; - - public constructor(katana: IKatana, shuriken: IShuriken) { - this._katana = katana; - this._shuriken = shuriken; - } - - public fight() { return this._katana.hit(); }; - public sneak() { return this._shuriken.throw(); }; - - } - - let kernel1 = new Kernel(); - kernel1.bind("INinja").to(Ninja); - kernel1.bind("IKatana").to(Katana); - kernel1.bind("IShuriken").to(Shuriken).inSingletonScope(); - - let ninja = kernel1.get("INinja"); - console.log(ninja); - - // Unbind - kernel1.unbind("INinja"); - kernel1.unbindAll(); - - // Kernel modules - let module: IKernelModule = (kernel: IKernel) => { - kernel.bind("INinja").to(Ninja); - kernel.bind("IKatana").to(Katana); - kernel.bind("IShuriken").to(Shuriken).inSingletonScope(); - }; - - let options: IKernelOptions = { - middleware: [], - modules: [module] - }; - - let kernel2 = new Kernel(options); - let ninja2 = kernel2.get("INinja"); - console.log(ninja2); - - // binding types - kernel2.bind("IKatana").to(Katana); - kernel2.bind("IKatana").toValue(new Katana()); - - kernel2.bind>("IKatana").toConstructor(Katana); - - kernel2.bind>("IKatana").toFactory((context) => { - return () => { - return kernel2.get("IKatana"); - }; - }); - - kernel2.bind>("IKatana").toProvider((context) => { - return () => { - return new Promise((resolve) => { - let katana = kernel2.get("IKatana"); - resolve(katana); - }); - }; - }); - -} diff --git a/type_definitions/inversify.d.ts b/type_definitions/inversify.d.ts deleted file mode 100644 index a361fccf..00000000 --- a/type_definitions/inversify.d.ts +++ /dev/null @@ -1,157 +0,0 @@ -// Type definitions for inversify 2.0.0-alpha.2 -// Project: https://github.com/inversify/InversifyJS -// Definitions by: inversify -// Definitions: https://github.com/borisyankov/DefinitelyTyped - -/// - -declare namespace __inversify { - - export interface IMiddleware extends Function { - (...args: any[]): any; - } - - export interface IKernelModule extends Function { - (kernel: IKernel): void; - } - - interface IKernelOptions { - middleware?: IMiddleware[]; - modules?: IKernelModule[]; - } - - export interface IKernelConstructor { - new(options?: IKernelOptions): IKernel; - } - - export interface IKernel { - bind(runtimeIdentifier: string): IBindingToSyntax; - unbind(runtimeIdentifier: string): void; - unbindAll(): void; - get(runtimeIdentifier: string): Service; - getAll(runtimeIdentifier: string): Service[]; - } - - interface IBindingWhenSyntax { - when(constraint: Constraint): void; - } - - interface IFactoryCreator extends Function { - (context: IContext): IFactory; - } - - interface IProviderCreator extends Function { - (context: IContext): IProvider; - } - - export interface IFactory extends Function { - (): T; - } - - export interface IProvider extends Function { - (): Promise; - } - - export interface INewable { - new(...args: any[]): T; - } - - interface IBindingToSyntax { - to(constructor: { new(...args: any[]): T; }): IBindingInSyntax; - toValue(value: T): IBindingWhenSyntax; - toConstructor(constructor: INewable): IBindingWhenSyntax; - toFactory(factory: IFactoryCreator): IBindingWhenSyntax; - toProvider(provider: IProviderCreator): IBindingWhenSyntax; - } - - interface IBindingInSyntax { - inTransientScope(): IBindingWhenSyntax; - inSingletonScope(): IBindingWhenSyntax; - } - - export interface IBinding { - runtimeIdentifier: string; - implementationType: { new(): T; }; - factory: IFactoryCreator; - provider: IProviderCreator; - cache: T; - scope: number; // BindingScope - type: number; // BindingType - } - - export interface IContext { - - /// Gets the kernel that is driving the activation. - kernel: IKernel; - - /// Gets or sets the activation plan. - plan: IPlan; - - addPlan(plan: IPlan); - } - - export interface IMetadata { - key: string; - value: any; - } - - export interface IPlan { - parentContext: IContext; - rootRequest: IRequest; - } - - export interface ITarget { - service: IQueryableString; - name: IQueryableString; - metadata: Array; - isArray(): boolean; - isNamed(): boolean; - isTagged(): boolean; - matchesName(name: string): boolean; - matchesTag(name: IMetadata): boolean; - } - - export interface IQueryableString { - startsWith(searchString: string): boolean; - endsWith(searchString: string): boolean; - contains(searchString: string): boolean; - equals(compareString: string): boolean; - value(): string; - } - - export interface IRequest { - - /// The service that was requested. - service: string; - - /// The parent context. - parentContext: IContext; - - /// The parent request. - parentRequest: IRequest; - - // The child requests - childRequests: IRequest[]; - - /// Gets the target that will receive the injection, if any. - target: ITarget; - - /// Gets the stack of bindings which have been activated by this request. - bindings: IBinding[]; - - // Adds a child request to the request - addChildRequest( - service: string, - bindings: (IBinding|IBinding[]), - target: ITarget): IRequest; - } - - export type Constraint = (request: IRequest) => boolean; - - export var Kernel: IKernelConstructor; - export function Inject(...typeIdentifiers: string[]): (typeConstructor: any) => void; -} - -declare module "inversify" { - export = __inversify; -} diff --git a/type_definitions/inversify/inversify-global-tests.js b/type_definitions/inversify/inversify-global-tests.js new file mode 100644 index 00000000..8ac58de5 --- /dev/null +++ b/type_definitions/inversify/inversify-global-tests.js @@ -0,0 +1,152 @@ +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +}; +var global_module_test; +(function (global_module_test) { + var Katana = (function () { + function Katana() { + } + Katana.prototype.hit = function () { + return "cut!"; + }; + return Katana; + }()); + var Shuriken = (function () { + function Shuriken() { + } + Shuriken.prototype.throw = function () { + return "hit!"; + }; + return Shuriken; + }()); + var Ninja = (function () { + function Ninja(katana, shuriken) { + this._katana = katana; + this._shuriken = shuriken; + } + Ninja.prototype.fight = function () { return this._katana.hit(); }; + ; + Ninja.prototype.sneak = function () { return this._shuriken.throw(); }; + ; + Ninja = __decorate([ + inversify.inject("IKatana", "IShuriken"), + __metadata('design:paramtypes', [Object, Object]) + ], Ninja); + return Ninja; + }()); + var kernel = new inversify.Kernel(); + kernel.bind("INinja").to(Ninja); + kernel.bind("IKatana").to(Katana); + kernel.bind("IShuriken").to(Shuriken).inSingletonScope(); + var ninja = kernel.get("INinja"); + console.log(ninja); + kernel.unbind("INinja"); + kernel.unbindAll(); + var module = function (k) { + k.bind("INinja").to(Ninja); + k.bind("IKatana").to(Katana).inTransientScope(); + k.bind("IShuriken").to(Shuriken).inSingletonScope(); + }; + var options = { + middleware: [], + modules: [module] + }; + kernel = new inversify.Kernel(options); + var ninja2 = kernel.get("INinja"); + console.log(ninja2); + kernel.bind("IKatana").to(Katana); + kernel.bind("IKatana").toValue(new Katana()); + kernel.bind("IKatana").toConstructor(Katana); + kernel.bind("IKatana").toFactory(function (context) { + return function () { + return kernel.get("IKatana"); + }; + }); + kernel.bind("IKatana").toAutoFactory(); + kernel.bind("IKatana").toProvider(function (context) { + return function () { + return new Promise(function (resolve) { + var katana = kernel.get("IKatana"); + resolve(katana); + }); + }; + }); + kernel.bind("IKatana").to(Katana).proxy(function (katanaToBeInjected) { + return katanaToBeInjected; + }); + var Samurai = (function () { + function Samurai(katana, shuriken) { + this.katana = katana; + this.shuriken = shuriken; + } + Samurai = __decorate([ + inversify.inject("IWeapon", "IWeapon"), + __param(0, inversify.tagged("canThrow", false)), + __param(1, inversify.tagged("canThrow", true)), + __metadata('design:paramtypes', [Object, Object]) + ], Samurai); + return Samurai; + }()); + kernel.bind("Samurai").to(Samurai); + kernel.bind("IWeapon").to(Katana).whenTargetTagged("canThrow", false); + kernel.bind("IWeapon").to(Shuriken).whenTargetTagged("canThrow", true); + var throwable = inversify.tagged("canThrow", true); + var notThrowable = inversify.tagged("canThrow", false); + var Samurai2 = (function () { + function Samurai2(katana, shuriken) { + this.katana = katana; + this.shuriken = shuriken; + } + Samurai2 = __decorate([ + inversify.inject("IWeapon", "IWeapon"), + __param(0, throwable("canThrow", false)), + __param(1, notThrowable("canThrow", true)), + __metadata('design:paramtypes', [Object, Object]) + ], Samurai2); + return Samurai2; + }()); + var Samurai3 = (function () { + function Samurai3(katana, shuriken) { + this.katana = katana; + this.shuriken = shuriken; + } + Samurai3 = __decorate([ + inversify.inject("IWeapon", "IWeapon"), + __param(0, inversify.named("strong")), + __param(1, inversify.named("weak")), + __metadata('design:paramtypes', [Object, Object]) + ], Samurai3); + return Samurai3; + }()); + kernel.bind("ISamurai").to(Samurai3); + kernel.bind("IWeapon").to(Katana).whenTargetNamed("strong"); + kernel.bind("IWeapon").to(Shuriken).whenTargetNamed("weak"); + var Samurai4 = (function () { + function Samurai4(katana, shuriken) { + this.katana = katana; + this.shuriken = shuriken; + } + Samurai4 = __decorate([ + inversify.inject("IWeapon", "IWeapon"), + inversify.paramNames("katana", "shuriken"), + __metadata('design:paramtypes', [Object, Object]) + ], Samurai4); + return Samurai4; + }()); + kernel.bind("ISamurai").to(Samurai4); + kernel.bind("IWeapon").to(Katana).when(function (request) { + return request.target.name.equals("katana"); + }); + kernel.bind("IWeapon").to(Shuriken).when(function (request) { + return request.target.name.equals("shuriken"); + }); +})(global_module_test || (global_module_test = {})); diff --git a/type_definitions/inversify/inversify-global-tests.ts b/type_definitions/inversify/inversify-global-tests.ts new file mode 100644 index 00000000..902f23ae --- /dev/null +++ b/type_definitions/inversify/inversify-global-tests.ts @@ -0,0 +1,193 @@ +/// + +module global_module_test { + + interface INinja { + fight(): string; + sneak(): string; + } + + interface IKatana { + hit(): string; + } + + interface IShuriken { + throw(); + } + + class Katana implements IKatana { + public hit() { + return "cut!"; + } + } + + class Shuriken implements IShuriken { + public throw() { + return "hit!"; + } + } + + @inversify.inject("IKatana", "IShuriken") + class Ninja implements INinja { + + private _katana: IKatana; + private _shuriken: IShuriken; + + public constructor(katana: IKatana, shuriken: IShuriken) { + this._katana = katana; + this._shuriken = shuriken; + } + + public fight() { return this._katana.hit(); }; + public sneak() { return this._shuriken.throw(); }; + + } + + let kernel = new inversify.Kernel(); + kernel.bind("INinja").to(Ninja); + kernel.bind("IKatana").to(Katana); + kernel.bind("IShuriken").to(Shuriken).inSingletonScope(); + + let ninja = kernel.get("INinja"); + console.log(ninja); + + // Unbind + kernel.unbind("INinja"); + kernel.unbindAll(); + + // Kernel modules + let module: inversify.IKernelModule = (k: inversify.IKernel) => { + k.bind("INinja").to(Ninja); + k.bind("IKatana").to(Katana).inTransientScope(); + k.bind("IShuriken").to(Shuriken).inSingletonScope(); + }; + + let options: inversify.IKernelOptions = { + middleware: [], + modules: [module] + }; + + kernel = new inversify.Kernel(options); + let ninja2 = kernel.get("INinja"); + console.log(ninja2); + + // binding types + kernel.bind("IKatana").to(Katana); + kernel.bind("IKatana").toValue(new Katana()); + + kernel.bind>("IKatana").toConstructor(Katana); + + kernel.bind>("IKatana").toFactory((context) => { + return () => { + return kernel.get("IKatana"); + }; + }); + + kernel.bind>("IKatana").toAutoFactory(); + + kernel.bind>("IKatana").toProvider((context) => { + return () => { + return new Promise((resolve) => { + let katana = kernel.get("IKatana"); + resolve(katana); + }); + }; + }); + + kernel.bind("IKatana").to(Katana).proxy((katanaToBeInjected: IKatana) => { + // BLOCK http://stackoverflow.com/questions/35906938/how-to-enable-harmony-proxies-in-gulp-mocha + /* + let handler = { + apply: function(target, thisArgument, argumentsList) { + console.log(`Starting: ${performance.now()}`); + let result = target.apply(thisArgument, argumentsList); + console.log(`Finished: ${performance.now()}`); + return result; + } + }; + return new Proxy(katanaToBeInjected, handler); + */ + return katanaToBeInjected; + }); + + interface IWeapon {} + interface ISamurai { + katana: IWeapon; + shuriken: IWeapon; + } + + @inversify.inject("IWeapon", "IWeapon") + class Samurai implements ISamurai { + public katana: IWeapon; + public shuriken: IWeapon; + public constructor( + @inversify.tagged("canThrow", false) katana: IWeapon, + @inversify.tagged("canThrow", true) shuriken: IWeapon + ) { + this.katana = katana; + this.shuriken = shuriken; + } + } + + kernel.bind("Samurai").to(Samurai); + kernel.bind("IWeapon").to(Katana).whenTargetTagged("canThrow", false); + kernel.bind("IWeapon").to(Shuriken).whenTargetTagged("canThrow", true); + + let throwable = inversify.tagged("canThrow", true); + let notThrowable = inversify.tagged("canThrow", false); + + @inversify.inject("IWeapon", "IWeapon") + class Samurai2 implements ISamurai { + public katana: IWeapon; + public shuriken: IWeapon; + public constructor( + @throwable("canThrow", false) katana: IWeapon, + @notThrowable("canThrow", true) shuriken: IWeapon + ) { + this.katana = katana; + this.shuriken = shuriken; + } + } + + @inversify.inject("IWeapon", "IWeapon") + class Samurai3 implements ISamurai { + public katana: IWeapon; + public shuriken: IWeapon; + public constructor( + @inversify.named("strong") katana: IWeapon, + @inversify.named("weak") shuriken: IWeapon + ) { + this.katana = katana; + this.shuriken = shuriken; + } + } + + kernel.bind("ISamurai").to(Samurai3); + kernel.bind("IWeapon").to(Katana).whenTargetNamed("strong"); + kernel.bind("IWeapon").to(Shuriken).whenTargetNamed("weak"); + + @inversify.inject("IWeapon", "IWeapon") + @inversify.paramNames("katana", "shuriken") + class Samurai4 implements ISamurai { + public katana: IWeapon; + public shuriken: IWeapon; + public constructor( + katana: IWeapon, + shuriken: IWeapon + ) { + this.katana = katana; + this.shuriken = shuriken; + } + } + + kernel.bind("ISamurai").to(Samurai4); + + kernel.bind("IWeapon").to(Katana).when((request: inversify.IRequest) => { + return request.target.name.equals("katana"); + }); + + kernel.bind("IWeapon").to(Shuriken).when((request: inversify.IRequest) => { + return request.target.name.equals("shuriken"); + }); + +} diff --git a/type_definitions/inversify/inversify-test.js b/type_definitions/inversify/inversify-test.js new file mode 100644 index 00000000..8e9b7073 --- /dev/null +++ b/type_definitions/inversify/inversify-test.js @@ -0,0 +1,154 @@ +"use strict"; +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __metadata = (this && this.__metadata) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; +var __param = (this && this.__param) || function (paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } +}; +var inversify_1 = require("inversify"); +var external_module_test; +(function (external_module_test) { + var Katana = (function () { + function Katana() { + } + Katana.prototype.hit = function () { + return "cut!"; + }; + return Katana; + }()); + var Shuriken = (function () { + function Shuriken() { + } + Shuriken.prototype.throw = function () { + return "hit!"; + }; + return Shuriken; + }()); + var Ninja = (function () { + function Ninja(katana, shuriken) { + this._katana = katana; + this._shuriken = shuriken; + } + Ninja.prototype.fight = function () { return this._katana.hit(); }; + ; + Ninja.prototype.sneak = function () { return this._shuriken.throw(); }; + ; + Ninja = __decorate([ + inversify_1.inject("IKatana", "IShuriken"), + __metadata('design:paramtypes', [Object, Object]) + ], Ninja); + return Ninja; + }()); + var kernel = new inversify_1.Kernel(); + kernel.bind("INinja").to(Ninja); + kernel.bind("IKatana").to(Katana); + kernel.bind("IShuriken").to(Shuriken).inSingletonScope(); + var ninja = kernel.get("INinja"); + console.log(ninja); + kernel.unbind("INinja"); + kernel.unbindAll(); + var module = function (k) { + k.bind("INinja").to(Ninja); + k.bind("IKatana").to(Katana).inTransientScope(); + k.bind("IShuriken").to(Shuriken).inSingletonScope(); + }; + var options = { + middleware: [], + modules: [module] + }; + kernel = new inversify_1.Kernel(options); + var ninja2 = kernel.get("INinja"); + console.log(ninja2); + kernel.bind("IKatana").to(Katana); + kernel.bind("IKatana").toValue(new Katana()); + kernel.bind("IKatana").toConstructor(Katana); + kernel.bind("IKatana").toFactory(function (context) { + return function () { + return kernel.get("IKatana"); + }; + }); + kernel.bind("IKatana").toAutoFactory(); + kernel.bind("IKatana").toProvider(function (context) { + return function () { + return new Promise(function (resolve) { + var katana = kernel.get("IKatana"); + resolve(katana); + }); + }; + }); + kernel.bind("IKatana").to(Katana).proxy(function (katanaToBeInjected) { + return katanaToBeInjected; + }); + var Samurai = (function () { + function Samurai(katana, shuriken) { + this.katana = katana; + this.shuriken = shuriken; + } + Samurai = __decorate([ + inversify_1.inject("IWeapon", "IWeapon"), + __param(0, inversify_1.tagged("canThrow", false)), + __param(1, inversify_1.tagged("canThrow", true)), + __metadata('design:paramtypes', [Object, Object]) + ], Samurai); + return Samurai; + }()); + kernel.bind("Samurai").to(Samurai); + kernel.bind("IWeapon").to(Katana).whenTargetTagged("canThrow", false); + kernel.bind("IWeapon").to(Shuriken).whenTargetTagged("canThrow", true); + var throwable = inversify_1.tagged("canThrow", true); + var notThrowable = inversify_1.tagged("canThrow", false); + var Samurai2 = (function () { + function Samurai2(katana, shuriken) { + this.katana = katana; + this.shuriken = shuriken; + } + Samurai2 = __decorate([ + inversify_1.inject("IWeapon", "IWeapon"), + __param(0, throwable("canThrow", false)), + __param(1, notThrowable("canThrow", true)), + __metadata('design:paramtypes', [Object, Object]) + ], Samurai2); + return Samurai2; + }()); + var Samurai3 = (function () { + function Samurai3(katana, shuriken) { + this.katana = katana; + this.shuriken = shuriken; + } + Samurai3 = __decorate([ + inversify_1.inject("IWeapon", "IWeapon"), + __param(0, inversify_1.named("strong")), + __param(1, inversify_1.named("weak")), + __metadata('design:paramtypes', [Object, Object]) + ], Samurai3); + return Samurai3; + }()); + kernel.bind("ISamurai").to(Samurai3); + kernel.bind("IWeapon").to(Katana).whenTargetNamed("strong"); + kernel.bind("IWeapon").to(Shuriken).whenTargetNamed("weak"); + var Samurai4 = (function () { + function Samurai4(katana, shuriken) { + this.katana = katana; + this.shuriken = shuriken; + } + Samurai4 = __decorate([ + inversify_1.inject("IWeapon", "IWeapon"), + inversify_1.paramNames("katana", "shuriken"), + __metadata('design:paramtypes', [Object, Object]) + ], Samurai4); + return Samurai4; + }()); + kernel.bind("ISamurai").to(Samurai4); + kernel.bind("IWeapon").to(Katana).when(function (request) { + return request.target.name.equals("katana"); + }); + kernel.bind("IWeapon").to(Shuriken).when(function (request) { + return request.target.name.equals("shuriken"); + }); +})(external_module_test || (external_module_test = {})); diff --git a/type_definitions/inversify/inversify-test.ts b/type_definitions/inversify/inversify-test.ts new file mode 100644 index 00000000..c69bd064 --- /dev/null +++ b/type_definitions/inversify/inversify-test.ts @@ -0,0 +1,200 @@ +/// + +import { + Kernel, + inject, tagged, named, paramNames, + IKernel, IKernelOptions, INewable, + IKernelModule, IFactory, IProvider, IRequest +} from "inversify"; + +module external_module_test { + + interface INinja { + fight(): string; + sneak(): string; + } + + interface IKatana { + hit(): string; + } + + interface IShuriken { + throw(); + } + + class Katana implements IKatana { + public hit() { + return "cut!"; + } + } + + class Shuriken implements IShuriken { + public throw() { + return "hit!"; + } + } + + @inject("IKatana", "IShuriken") + class Ninja implements INinja { + + private _katana: IKatana; + private _shuriken: IShuriken; + + public constructor(katana: IKatana, shuriken: IShuriken) { + this._katana = katana; + this._shuriken = shuriken; + } + + public fight() { return this._katana.hit(); }; + public sneak() { return this._shuriken.throw(); }; + + } + + let kernel = new Kernel(); + kernel.bind("INinja").to(Ninja); + kernel.bind("IKatana").to(Katana); + kernel.bind("IShuriken").to(Shuriken).inSingletonScope(); + + let ninja = kernel.get("INinja"); + console.log(ninja); + + // Unbind + kernel.unbind("INinja"); + kernel.unbindAll(); + + // Kernel modules + let module: IKernelModule = (k: IKernel) => { + k.bind("INinja").to(Ninja); + k.bind("IKatana").to(Katana).inTransientScope(); + k.bind("IShuriken").to(Shuriken).inSingletonScope(); + }; + + let options: IKernelOptions = { + middleware: [], + modules: [module] + }; + + kernel = new Kernel(options); + let ninja2 = kernel.get("INinja"); + console.log(ninja2); + + // binding types + kernel.bind("IKatana").to(Katana); + kernel.bind("IKatana").toValue(new Katana()); + + kernel.bind>("IKatana").toConstructor(Katana); + + kernel.bind>("IKatana").toFactory((context) => { + return () => { + return kernel.get("IKatana"); + }; + }); + + kernel.bind>("IKatana").toAutoFactory(); + + kernel.bind>("IKatana").toProvider((context) => { + return () => { + return new Promise((resolve) => { + let katana = kernel.get("IKatana"); + resolve(katana); + }); + }; + }); + + kernel.bind("IKatana").to(Katana).proxy((katanaToBeInjected: IKatana) => { + // BLOCK http://stackoverflow.com/questions/35906938/how-to-enable-harmony-proxies-in-gulp-mocha + /* + let handler = { + apply: function(target, thisArgument, argumentsList) { + console.log(`Starting: ${performance.now()}`); + let result = target.apply(thisArgument, argumentsList); + console.log(`Finished: ${performance.now()}`); + return result; + } + }; + return new Proxy(katanaToBeInjected, handler); + */ + return katanaToBeInjected; + }); + + interface IWeapon {} + interface ISamurai { + katana: IWeapon; + shuriken: IWeapon; + } + + @inject("IWeapon", "IWeapon") + class Samurai implements ISamurai { + public katana: IWeapon; + public shuriken: IWeapon; + public constructor( + @tagged("canThrow", false) katana: IWeapon, + @tagged("canThrow", true) shuriken: IWeapon + ) { + this.katana = katana; + this.shuriken = shuriken; + } + } + + kernel.bind("Samurai").to(Samurai); + kernel.bind("IWeapon").to(Katana).whenTargetTagged("canThrow", false); + kernel.bind("IWeapon").to(Shuriken).whenTargetTagged("canThrow", true); + + let throwable = tagged("canThrow", true); + let notThrowable = tagged("canThrow", false); + + @inject("IWeapon", "IWeapon") + class Samurai2 implements ISamurai { + public katana: IWeapon; + public shuriken: IWeapon; + public constructor( + @throwable("canThrow", false) katana: IWeapon, + @notThrowable("canThrow", true) shuriken: IWeapon + ) { + this.katana = katana; + this.shuriken = shuriken; + } + } + + @inject("IWeapon", "IWeapon") + class Samurai3 implements ISamurai { + public katana: IWeapon; + public shuriken: IWeapon; + public constructor( + @named("strong") katana: IWeapon, + @named("weak") shuriken: IWeapon + ) { + this.katana = katana; + this.shuriken = shuriken; + } + } + + kernel.bind("ISamurai").to(Samurai3); + kernel.bind("IWeapon").to(Katana).whenTargetNamed("strong"); + kernel.bind("IWeapon").to(Shuriken).whenTargetNamed("weak"); + + @inject("IWeapon", "IWeapon") + @paramNames("katana", "shuriken") + class Samurai4 implements ISamurai { + public katana: IWeapon; + public shuriken: IWeapon; + public constructor( + katana: IWeapon, + shuriken: IWeapon + ) { + this.katana = katana; + this.shuriken = shuriken; + } + } + + kernel.bind("ISamurai").to(Samurai4); + + kernel.bind("IWeapon").to(Katana).when((request: IRequest) => { + return request.target.name.equals("katana"); + }); + + kernel.bind("IWeapon").to(Shuriken).when((request: IRequest) => { + return request.target.name.equals("shuriken"); + }); + +} diff --git a/type_definitions/inversify-global.d.ts b/type_definitions/inversify/inversify.d.ts similarity index 55% rename from type_definitions/inversify-global.d.ts rename to type_definitions/inversify/inversify.d.ts index 9817daec..12a8c011 100644 --- a/type_definitions/inversify-global.d.ts +++ b/type_definitions/inversify/inversify.d.ts @@ -1,18 +1,22 @@ -// Type definitions for inversify 2.0.0-alpha.2 +// Type definitions for inversify 2.0.0-alpha.3 // Project: https://github.com/inversify/InversifyJS // Definitions by: inversify // Definitions: https://github.com/borisyankov/DefinitelyTyped -/// +/// declare namespace inversify { - export interface IMiddleware extends Function { - (...args: any[]): any; + interface IKernelConstructor { + new(options?: IKernelOptions): IKernel; } - export interface IKernelModule extends Function { - (kernel: IKernel): void; + export interface IKernel { + bind(runtimeIdentifier: string): IBindingToSyntax; + unbind(runtimeIdentifier: string): void; + unbindAll(): void; + get(runtimeIdentifier: string): T; + getAll(runtimeIdentifier: string): T[]; } export interface IKernelOptions { @@ -20,86 +24,88 @@ declare namespace inversify { modules?: IKernelModule[]; } - export interface IKernelConstructor { - new(options?: IKernelOptions): IKernel; - } - - export interface IKernel { - bind(runtimeIdentifier: string): IBindingToSyntax; - unbind(runtimeIdentifier: string): void; - unbindAll(): void; - get(runtimeIdentifier: string): Service; - getAll(runtimeIdentifier: string): Service[]; + interface IMiddleware extends Function { + (...args: any[]): any; } - interface IBindingWhenSyntax { - when(constraint: Constraint): void; + export interface IKernelModule extends Function { + (kernel: IKernel): void; } - interface IFactoryCreator extends Function { - (context: IContext): IFactory; + interface IBindingToSyntax { + to(constructor: { new(...args: any[]): T; }): IBindingInWhenProxySyntax; + toValue(value: T): IBindingInWhenProxySyntax; + toConstructor(constructor: INewable): IBindingInWhenProxySyntax; + toFactory(factory: IFactoryCreator): IBindingInWhenProxySyntax; + toAutoFactory(): IBindingInWhenProxySyntax; + toProvider(provider: IProviderCreator): IBindingInWhenProxySyntax; } - interface IProviderCreator extends Function { - (context: IContext): IProvider; + interface IBindingInWhenProxySyntax { + inTransientScope(): IBindingInWhenProxySyntax; + inSingletonScope(): IBindingInWhenProxySyntax; + when(constraint: (request: IRequest) => boolean): IBindingInWhenProxySyntax; + whenTargetNamed(name: string): IBindingInWhenProxySyntax; + whenTargetTagged(tag: string, value: any): IBindingInWhenProxySyntax; + proxy(fn: (injectable: T) => T): IBindingInWhenProxySyntax; } export interface IFactory extends Function { (): T; } - export interface IProvider extends Function { - (): Promise; + interface IFactoryCreator extends Function { + (context: IContext): IFactory; } export interface INewable { new(...args: any[]): T; } - interface IBindingToSyntax { - to(constructor: { new(...args: any[]): T; }): IBindingInSyntax; - toValue(value: T): IBindingWhenSyntax; - toConstructor(constructor: INewable): IBindingWhenSyntax; - toFactory(factory: IFactoryCreator): IBindingWhenSyntax; - toProvider(provider: IProviderCreator): IBindingWhenSyntax; + export interface IProvider extends Function { + (): Promise; + } + + interface IProviderCreator extends Function { + (context: IContext): IProvider; } - interface IBindingInSyntax { - inTransientScope(): IBindingWhenSyntax; - inSingletonScope(): IBindingWhenSyntax; + export interface IContext { + kernel: IKernel; + plan: IPlan; + addPlan(plan: IPlan); + } + + export interface IPlan { + parentContext: IContext; + rootRequest: IRequest; + } + + export interface IRequest { + service: string; + parentContext: IContext; + parentRequest: IRequest; + childRequests: IRequest[]; + target: ITarget; + bindings: IBinding[]; + addChildRequest( + service: string, + bindings: (IBinding|IBinding[]), + target: ITarget): IRequest; } export interface IBinding { runtimeIdentifier: string; - implementationType: { new(): T; }; + implementationType: INewable; factory: IFactoryCreator; provider: IProviderCreator; + constraint: (request: IRequest) => boolean; + proxyMaker: (injectable: T) => T; cache: T; scope: number; // BindingScope type: number; // BindingType } - export interface IContext { - - /// Gets the kernel that is driving the activation. - kernel: IKernel; - - /// Gets or sets the activation plan. - plan: IPlan; - - addPlan(plan: IPlan); - } - - export interface IMetadata { - key: string; - value: any; - } - - export interface IPlan { - parentContext: IContext; - rootRequest: IRequest; - } - export interface ITarget { service: IQueryableString; name: IQueryableString; @@ -119,35 +125,19 @@ declare namespace inversify { value(): string; } - export interface IRequest { - - /// The service that was requested. - service: string; - - /// The parent context. - parentContext: IContext; - - /// The parent request. - parentRequest: IRequest; - - // The child requests - childRequests: IRequest[]; - - /// Gets the target that will receive the injection, if any. - target: ITarget; - - /// Gets the stack of bindings which have been activated by this request. - bindings: IBinding[]; - - // Adds a child request to the request - addChildRequest( - service: string, - bindings: (IBinding|IBinding[]), - target: ITarget): IRequest; + export interface IMetadata { + key: string; + value: any; } - export type Constraint = (request: IRequest) => boolean; - export var Kernel: IKernelConstructor; - export function Inject(...typeIdentifiers: string[]): (typeConstructor: any) => void; + export var decorate: any; + export function inject(...typeIdentifiers: string[]): (typeConstructor: any) => void; + export var tagged: any; + export var named: any; + export var paramNames: any; +} + +declare module "inversify" { + export = inversify; }