IVERTED

Javascript IOC container

View the Project on GitHub

Inverted

Inverted is a Javascript Inversion of Control container that builds up on AMD or Common JS module loading systems to powerfully, yet unobtrusively, manage dependency injection and wire up modular Javascript applications.

Javascript module loading systems such as the Common JS module spec and AMD manage the loading of your application's dependencies, but they don't actually inject dependencies and wire your code together. Inverted uses a separate application configuration file to define how your Javascript "classes" interact without needing to add any library specific code within your modules.

Get it

For the browser:

Minfiied (~4.8kb/~1.7kb gzipped)
Uncompressed

Or for Node.js

npm install inverted

In the browser Inverted depends on a module loading system being present. Inverted will automatically detect and work with RequireJS or Curl, you can also specify a different module loading function.

How it works:

The following examples unimaginatively borrow and adapt the movie related examples from Martin Fowler's article on inversion of control.

#1: No dependency injection

In these examples two Javascript classes are defined within AMD modules. In the MovieLister module AMD's define mechanism loads the MovieFinder dependency, but does not inject it. Instead the construction of the MovieFinder object must be hard coded within the constructor function of the MovieLister.

#2: With dependency injection

The next example makes an improvement by injecting an instance of the MovieFinder into the MovieLister as a constructor argument. This makes it easier to test the MovieLister as a standalone unit by mocking its dependencies (using a framework such as JSMockito). But wiring up the dependencies in the require block, is ugly and would be much more difficult to maintain in an entire real world application.

#3: Inversion of control

The final example introduces Inverted and shows how inversion of control is used to wire up the dependencies using the application configuration.

Each dependency which can be managed by Inverted is called a proto. A proto must specify its module (which can be an AMD or CommonJS module) and can optionally specify its values and dependencies to be injected (as either arguments, properties or methods), its scope and another proto that it extends.

Dependencies and literal values can be managed in the application config. In the example, the JSONP url can be easily changed without touching the actual application code.

You can also see the mongoMovieFinder proto, another implementation of the movie finder, so different implementations can be easily switched out just by changing the movie lister's dependency.

Modules, AMD and Node

All the module examples on this page are written using AMD modules, but they could also be written using Common JS modules to run in Node.

If running in the browser, Inverted will attempt to use RequireJS or Curl.

If running in Node it will attempt to load modules as Common JS. You can also use the AMD format in Node using a library such as amdefine.

Here's a version of the example above written using the Common JS module spec:

Usage guide

Instantiation

A simple example of using Inverted to construct an instance of an object. In this example the result is the equivalent of using the new keyword.

Constructor injection: Literal values

Literal values are set in the application configuration and injected as arguments when the object is instantiated.

Constructor injection: Literal values map

Constructor injection: Dependencies

A tree of dependencies are instantiated and injected using the application configuration.

Beginning an injected string with a * will reference another prototype definition in the application configuration. A longer hand alternative is to create an object with a ref property.

Property and method injection

Scopes

Inverted supports three scopes prototype, singleton and static. The first is used by default and does not need to be explicitly specified in the application config. Protos that use the singleton scope will only be instantiated once.

Static scopes are just used to get a reference to an object. No injection is performed on static scoped protos.

Prototypal inheritance

Super objects in the prototype chain are treated as another form of dependency with Inverted and prototypal inheritance chains be managed entirely within the application configuration.

Factory methods

Factory methods can be used to generate injected values which are dynamic.

Mixins

This is an experimental feature, please create a Github issue with the 'feedback' label if you have any feedback.

Inverted Mixins can effectively create an inheritance chain via composition. The proto to mix is is instantiated and added as a property of the proto instance is to be mixed with. Inverted then decorated the proto with delegating methods.

You may choose whether methods which already exist on the proto to be mixed should be overriden, this setting defaults to true. If you set it to false, the method may still be accessed via instance.__protoId__.method().

Accessing the app context

This is an experimental feature, please create a Github issue with the 'feedback' label if you have any feedback.

Suppose, a user clicks a button to load a preferences screen. The modules and protos for the preferences screen are first loaded when this button is clicked. To handle this, the appContext instance can be configured to be injected into an instantiated proto, both globally and directly for each proto:

Interfaces

This is an experimental feature, please create a Github issue with the 'feedback' label if you have any feedback.

Inverted provides the ability to define interfaces, a contract which defines what methods a instance of a 'class' must have. The interface is not bound to the implementation though, like Java, but to to the relationship between a proto and its dependency.

Interfaces are defined as an array of method names at the root level of the application config and dependency references can reference one or more interface ID's by enclosing the interface(s) in square brackets.

If a dependency does not fully implement an interface, Inverted will throw an error and not build the dependency tree for that proto. The error can be handled in the failure callback of the promise returned by AppContext#getProto

API

AppContext Inverted#create(Object applicationConfiguration, [Object originalCommonJsModule])

Creates an instance of an Inverted AppContext using the given application configuration object.

Inverted uses the originalCommonJsModule parameter for loading modules on the correct path in Common JS environments such as Node.

Object InvertedPromise AppContext#getProto(String|Array protoIds, Function completeCallback(Object proto, Object proto...), Function errorCallback(Error error))

Gets a proto object, which maybe an instance of a class, if prototype or singleton scopes are used, or a direct referencing to a Javascript object, function or literal value if static scope is used.

If the first argument is a string, a single proto id is expected. If it is an array, a list of proto IDs are expected.

Pass a callback function as the second argument to evaluate the result or use the returned promise.

AppContext#loader(Function loaderFn)

Inverted will first try to determine the AMD implementations Require JS or Curl ar present, otherwise it will default to using a Common JS module loader.

If you wish to use another module loading mechanism use this method to pass a reference to a loader function.

InvertedPromise#then(successCallback(Object proto, Object proto...), [failureCallback(Error error)])

Calling AppContext#getProto will return a promise which can be used to evaluate success and failure results.

Application Config reference

FAQ

How should I inject 3rd party libraries such as jQuery?

You can use static scope to inject these dependencies, but for such common libs you may find it more practical to use your dependency loader's standard loading mechanism.