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

jsimple

v2.0.0-alpha.4

Published

jsimple - a JS dependency injection/service locator highly inspired from Pimple for PHP

Downloads

10

Readme

jsimple - a JS dependency injection container/service locator Build Status

Installation

Before using jsimple in your project, add it to your package.json:

npm install --save --no-optional jsimple

Using --no-optional will prevent NPM from installing the harmony-reflect package and you won't be able to use the Proxy features of jsimple. If you want them simply remove the flag:

npm install --save jsimple

Usage

Creating a jsimple container is as simple as instanciating it:

"use strict";

let Jsimple = require("jsimple"),
    container = new Jsimple();

Defining services

There are four ways to defines services with jsimple:

  • Defining them as shared services
  • Defining them through factory functions
  • Defining function as services
  • Defining parameters as services

Shared services

Shared services are defined through a function which will build them once and jsimple will then share the same instance across every call:

container.share("app", () => {
    let express = require("express");

    return express();
});

This will define a service called app which is an Express application. Now everytime you request this service you will get the exact same instance:

console.log(container.get("app") === container.get("app")); //true

Service factories

Sometime you will want to have a new instance of a service each time you fetch it from jsimple. This is where factories are useful:

container.factory("session", (c) => {
    return {
        id: c.get("session.id_generator")()
    };
});

This will define a factory called session which will return a new fresh object each time you fetch it from jsimple:

console.log(container.get("session") === container.get("session")); //false

As you can see in the previous example, the factory function received one argument (c): this is the current jsimple instance. jsimple will automatically pass itself as an argument of both factories and shared service factories.

Function services

Sometimes you will need to store a function inside jsimple to use it later. For example, in our previous example (the session factory) the session.id_generator is just a plain function. But how did we do that ?

container.share("session.id_generator", container.protect(require('uuid').v4));

Doing so we store the uuid#v4 function in the container and we can use it later.

Parameters as services

Defining parameters as services is the process of storing simple values inside jsimple. This can be usefull to store configuration values. They can be of any kind but not function (scalars, objects, arrays):

container.define("port", 4242);

container.get("app").listen(container.get("port"));

Extending/Overriding services

Any service of any kind defined insde jsimple can be extended or overridden. The only rule here is you can't modify a service of any kind once it has been fetched from jsimple.

Extending shared services

Let's see how we would extend our app service:

container.extend("app", (app, c) => {
    app.locals.title = "My jsimple Powered App";

    return app;
});

Now everytime we fetch the app service it will automatically have its title defined:

console.log(container.get("app").locals.title); //My jsimple Powered App

Extending service factories

Extending service factories is as simple as extending shared services:

container.extend("session", (session, c) => {
    session.start = new Date();

    return session;
});

Now everytime we fetch a session instance it will automatically have its start date defined:

console.log(container.get("session").start); //Mon Sep 21 2015 16:52:51 GMT+0200 (CEST)

Overriding services

Overriding services is just the process of replacing any service's definition in the container:

container.factory("session", (c) => {
    return {};
});

Now if we fetch the session from jsimple we'll just get an empty object:

console.log(container.get("session")); //{}

No more id nor start attributes.

Using tags

In jsimple each service can have one or more associated tags. This is useful to create groups of services. Let's see an example:

container.share("app.static", container.protect(express.static("public")), ["middleware"]);
container.share("app.static", container.protect(express.static("files")), ["middleware"]);

container.extend("app", (app, c) => {
    c.tagged("middleware").forEach(middleware => app.use(c.get(middleware)));

    return app;
});

Our Express app will now have the static middleware configured to lookup the public and files folders when we request resources.

Using the proxy

jsimple provides a proxy mode which will ease fetching and defining service in some cases. No more calls to Jsimple#get or Jsimple#share:

"use strict";

let Jsimple = require("jsimple"),
    container = (new Jsimple()).proxify(); //Here the magic happens!

container.app = () => {
    let express = require("express");

    return express();
};

container["app.static"] = container.protect(express.static("public")), ["middleware"]);
container["app.static"] = container.protect(express.static("files")), ["middleware"]);

container.extend("app", (app, c) => {
    c.tagged("middleware").forEach(middleware => app.use(c[middleware]));

    return app;
});

See how we removed every call to share and get! We now call services as if they were direct property on the container object. Do not forget that to use this feature you have to remove the --no-optional flag from the npm install command.

Using decorators

jsimple also take advantage of ES7 decorators and provides some usefull annotations to help you define services and factories.

All decorators apply to the last instanciated Jsimple instance. This can be customized using the jsimple argument on decorators.

Keep in mind that decorators are experimental and support is provided through Babel which only supports class decorators.

Shared services

Given a file where you create your Jsimple instance:

"use strict";

let Jsimple = require("jsimple"),
    container = new Jsimple();

//...

And a file where you define a class to be used as a shared service:

"use strict";

let Shared = require("jsimple/decorator").Shared;

@Shared({ id: "myService" })
class MyService {
    //...
}

This will declare a shared service identified by myService in the Jsimple instance.

Service factories

Declaring factory services is not really different from the previous example. Once you have created your Jsimple instance, use the Factory decorator:

"use strict";

let Factory = require("jsimple/decorator").Factory;

@Factory({ id: "myFactory" })
class MyService {
    //...
}

Extending/Overriding services

You can also extend shared services or factory using a dedicated decorator:

"use strict";

let Extend = require("jsimple/decorator").Extend;

@Extend({ id: "myService" })
class MyService {
    constructor(service) {
        //...
    }

    //...
}

Here, we are extending the myService service. Note that the extending service will receive an instance of the extended service as its first constructor argument.

Defining dependencies

In addition to defining services and factories, decorators let you define your classes' dependencies. You can do that using one of those two syntaxes:

"use strict";

let Shared = require("jsimple/decorator").Shared;

@Shared({
    id: "myService",
    use: ["myOtherService"]
})
class MyService {
    constructor(otherService) {
        //...
    }

    //...
}

As you can see, the Shared decorator (but also Factory and Extend) takes an extra use argument by which you can define an array of dependencies. A more elegant way of doing this is by using the Inject decorator:

"use strict";

let Shared = require("jsimple/decorator").Shared,
    Inject = require("jsimple/decorator").Inject;

@Shared({ id: "myService" })
@Inject(["myOtherService"])
class MyService {
    constructor(otherService) {
        //...
    }

    //...
}

The Inject decorator will create a Proxy around the annotated class so that when it's instanciated it will automatically fetch its dependencies through jsimple.

Note that using Inject applies a Proxy on the class itself so even when you instanciate it by hand, it will try to fetch its dependencies through jsimple:

"use strict";

let Shared = require("jsimple/decorator").Shared,
    Inject = require("jsimple/decorator").Inject;

@Shared({ id: "myService" })
@Inject(["myOtherService"])
class MyService {
    constructor(otherService) {
        //...
    }

    //...
}

let myService = new MyService();

// Is equivalent to

let myService = new MyService(container.get("myService"));

The Inject helper will only inject service for arguments you don't manually provide:

"use strict";

let Shared = require("jsimple/decorator").Shared,
    Inject = require("jsimple/decorator").Inject;

@Shared({ id: "myService" })
@Inject(["myOtherService"])
class MyService {
    constructor(otherService) {
        //...
    }

    //...
}

let myService = new MyService(new OtherService());

This will never call jsimple as all the constructor arguments are manually provided.

License

The MIT License (MIT)

Copyright (c) 2015 jubianchi