npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

somersault

v1.3.0

Published

Simple framework for Inversion of Control in NodeJS

Downloads

12

Readme

somersault

Acrobatic yet-simple IoC Dependency Injection for Node.js Applications

Build Status Prod Dependencies Dev Dependencies Code Coverage npm version

Stats Downloads

Summary

somersault is a simple and flexible IoC solution for NodeJS (4.x+) projects that allows you to simplify your application and create beautiful code. As Node.js is untyped we have to rely on the declared names of your variables in order to determine what to inject.

The somersault library supports injection/building of:

  • Functions
  • ES6+ Arrow functions
  • ES6+ Classes

Depdencies are registered by a declared name (known as a 'Tag' in somersault), or by multiple such-tags. You can then either resolve a tag (i.e. build a tag and it's dependencies from scratch) or build a generator function - essentially fill in the blanks for parameters and constructors.

Installation

To install somersault into your node application:

npm install somersault --save

Usage

Container Creation

To create an IoC Container:

const somersault = require('somersault');
const myContainer = somersault.createContainer();

Registering Dependencies (.register(tags, value))

You can register a class, arrow function or function with:

myContainer.register('someAlias', myFuncOrClass);

You can also register the same object with multiple tags via:

myContainer.register(['someAlias', 'anotherAlias'], myFuncOrClass);

Resolving by Tag

To generate a complete object or graph of object from a tag, use .resolve(), such as:

const myInstance = myContainer.resolve('someAlias')

This will look up the most recently registered object with the tag 'someAlias'.

Building from a Template

To generate a complete value from a function, arrow function or class, pass it as the parameter to the .build() operation, such as:

const myResult = myContainer.build(MyClassName);

This will create a new instance of MyClassName, populating any constructor parameters that are known from other dependencies.

Worked Example

In this example we create a new somersault container, register a 'database' class and a constant-value of 'someConnectionString'. This allows us to inject the connection string into our database object at runtime.

const container = require('somersault').createContainer();

container.register('database', class Database {
  constructor(connectionString) {
    console.log('Built database class with %s', connectionString);
    this.connectionStringValue = connectionString;
  }
});
container.register('connectionString', 'someConnectionString');

const result = container.resolve('database');

The .resolve() operation looks up a defined 'tag' within the container, and builds an instance. In the example we are also registering the connectionString tag, which allows the parameter connectionString on our database class to be satisifeid.

somersault allows the inputs for registration to be:

  • Constant values
  • Generator values, such as:
    • Functions (parameters are resolved by tag/name)
    • Arrow Functions (parameters as per functions)
    • ES6+ classes (Constructor parameters are resolved by tag/name).

Container Methods and Properties

.createContainer()

Create a child container/context that uses the specified container as a fallback/parent.

const childContainer = myContainer.createChild();

.build(funcArrowOrClass)

Creates an instance of a class, or if a function/arrow-function, will execute the function and return it's value. The required parameters of the class constructor or function declaration are filled in using the container registrations.

const myObject = myContainer.build(SomeClass);

Overload .build(funcArrowOrClass, constructorTags)

Creates an instance of a class or executes a function/arrow, but remaps the detected names of the parameters to use alternate tags when resolving. This allows for consumption of types that do not have suitable IoC focused names that match pre-existing registrations:

  function myGenerator(someInstance, someFunc) {
    return someInstance.value + someFunc.value;
  }    
  const instance = container.build(myGenerator,
    ['alternateClass', 'alternateFunc']);

In this example the arguments someInstance and someFunc are resolved as if they'd been written with the overridden names.

.filterAll(tag|tags)

Creates a child container that will only include any registrations that match the nominated tag (or ALL of a list of nominated tags). This allows filtering down to a subset where registrations have multiple tags that combine to provide grouping:

// Register connection settings for environments
container.register(['connectionString', 'QA', 'serverA'], 'foo=bar1');
container.register(['connectionString', 'QA', 'serverB'], 'foo=bar2');
container.register(['connectionString', 'UAT', 'serverA'], 'foo=waa1');
container.register(['connectionString', 'UAT', 'serverB'], 'foo=waa2');

// Filter
const filteredChild = container.filterAll(['QA', 'serverB']);
// filteredChild will now only resolve connectionString = foo=bar2

This filter applies to the current container and all upstream registrations in the hierarchy, but does not modify results returned from references to the original container. Filter operations can be chained to create composed behaviours.

.filterAny(tag|tags)

Creates a child container that will only include any registrations that match the nominated tag (or any of a list of nominated tags). This allows filtering down to a subset where registrations have multiple tags that group them:

// Register connection settings for environments
container.register(['connectionString', 'QA'], 'foo=bar');
container.register(['connectionString', 'UAT'], 'foo=waa');

// Filter
const filteredChild = container.filterAny('QA');
// filteredChild will now only resolve registrations that include a QA tag.

This filter applies to the current container and all upstream registrations in the hierarchy, but does not modify results returned from references to the original container. Filter operations can be chained to create composed behaviours.

.filterOut(tag|tags)

Creates a child container that will exclude any dependencies that have the nominated tag or tags array assigned to them. This allows hiding of certain groups of dependencies in child containers.

// Register a tag, then overload it
container.register(['someTag'], 1);
container.register(['someTag', 'excludeMe'], -1);

// Initial result is -1
const firstResult = container.resolve('someTag');

// Resolve from filtered child
const filteredChild = container.filterOut('excludeMe');
// Will now be 1 (because excludeMe is hidden)

This filter hides all dependencies with the nominated tag/tags, no matter where they are in the parent container hierarchy. Filter operations can be chained to create composed behaviours.

.parent

Returns the parent container of a container context.

const myParent = myContainer.parent;

.register(tag|tags, value)

Register a value with the container. Value can be any of:

  • Arrow Function
  • Class
  • Function
  • Any other value (assumed to be a singleton/constant-value).

Classes are generated as new instances of the class on-demand, however arrows and functions are simply evaluated during resolution/build and returned. If you need to use prototypes or functions, register a 'generator' function to avoid problems around usage of this.

myContainer.register('someTag', MyClassHere);
myContainer.register(['can','have','many','tags'], function (foo) { ... });
myContainer.register('generator', (x, y, z) => new PrototypeNameHere(x, y, z));
myContainer.register('config', { someKey: 'value' });

Overload: .register(tag|tags, value, constructorTags)

It is possible to override the detection of parameter names on a function, arrow and class by passing a third parameter to register() - an array of alternate parameter/tag names.

myContainer.register(['can','have','many','tags'], function (foo, bar) { ... }, ['hello', 'world']);

In this example, any attempt to .resolve('tag') will now act as if function was function (hello, world) and resolve those parameter names instead.

.resolve(tag)

Produces the most recently registered tag value (and completes dependencies, if required). The tag value must match a tag used during registration.

const myInstance = myContainer.resolve('someTag');
myInstance.doSomething();

.resolveAll(tag)

Produces an instance of all instances of the specified tag registration in the container hierarchy all the way to the root.

const myWidgets = myContainer.resolveAll('widget');
for (const widget of myWidgets) {
  widget.frob();
}

Registrations are returned in most-recent registration order ascending through the hierarchy, so more recent registrations from parents appear after oldest child registrations.

Advanced Usage

Nested Dependencies

The somersault package will resolve dependencies, and then dependencies of those dependencies as required until all elements are populated and built. This allows complex object graphs to be satisfied easily.

Repeated Dependencies

Each specific dependency is only built once per resolve/build operation. It assumed that the same instance/value can be passed to each object in a graph requiring a specific dependency to be resolved.

Licencing

This repository is MIT licensed. That means you can use it freely without restriction or attribution. If this helped you get started, give the repo a star on GitHub and help spread the word.

Contribution & Development Guide

The library is built using the following tools and techniques:

These are all validated and working on NodeJS 4.x and above, we test for multiple variations of node which can be seen in our .travis.yml

Getting Started on Development

To get started for your contributions, please do the following:

  • Create a fork of our repository into your own workspace or organisation (this way you can track improvements and updates as we make them).
  • If you aren't using VSCode, then you can also drop the .vscode directory, which defines some IDE options to make sure the standards for tabs and spacing are used.

Then it's as simple as:

    npm install
    gulp

Please note all contributions require test coverage to be accepted.

Other Commands

The following gulp tasks are defined:

  • gulp docs - Regenerate esdoc documentation.
  • gulp lint - Run ESLint validation of code standards.
    • You can also lint-lib or lint-tests to look at specific areas.
  • gulp test - Run unit tests.