Skip to content
Inversion Of Control (IoC) container for JavaScript
JavaScript
Find file
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.
demos
js
tests/bdd Removed dependency on Modular (still required for tests)
vendor
.braids
.jshintrc
NCSA-LICENSE.txt
README.md
bower.json Set up as Bower package
service.js Set up as Bower package

README.md

service.js

Service.JS is an Inversion Of Control (IoC) container for JavaScript. Similar to Symfony's Dependency Injection Component (DIC) it allows you to separate the construction of objects in your application from the rest of the code.

Parameters

Parameters allow you to the separate the configuration of the options for your services from the service definitions themselves. It is possible to use a container just to store parameters, as in the following:

require([
    "service"  // Path to /vendor/service.js/service.js, with ".js" auto-appended by Modular
], function (
    service
) {
    "use strict";

    var components = service.create(),
        container = components.container,
        objectLoader = components.objectLoader,
        object = {
            "parameters": {
                "majorVersion": 4,
                "minorVersion": 2,
                "revision": 1024,
                "version": "%majorVersion%.%minorVersion%.%revision%"
            }
        };

    objectLoader.load(object);

    console.log(container.getParameter("majorVersion")); // Will print "4" to the console
    console.log(container.getParameter("minorVersion")); // Will print "2" to the console
    console.log(container.getParameter("revision"));     // Will print "1024" to the console
    console.log(container.getParameter("version"));      // Will print "4.2.1024" to the console
});

As can be seen in the example above, it is possible to refer to other parameters from within a string value for another parameter. This is achieved by enclosing the name of the referenced parameters in percentage signs, as shown above.

Services

Services allow you to separate the construction of objects for your application from the logic that uses them.

Real-world example

You wish to create a Virtual Machine for a programming language called "Flow". "Flow" will be a superset of ECMAScript, with one addition: a new language construct called "print" has been added:

// ...
vm.run("print 4;"); // Should print "4" to the console
// ...

To do so, you require a parser, interpreter and console for output. Note that, as demonstrated below, it can still make sense to create "hard dependencies" outside of the IoC container, eg. for FlowProgram below. A typical setup may look like the following:

/*
 * NB: Named defines will be used to encapsulate the module folder structure within this one file.
 *     Ordinarily, the definitions below would be anonymous and stored in separate module files:
 *     "Parser/Flow" would point to "/Parser/Flow.js", etc.
 */

// Module /util.js
define("util", [
    "modular" // The "modular" special dependency - used here to give a few useful utilities
], function (
    modular
) {
    "use strict";

    return modular.util;
});

// Module /Console.js
define("Console", [
    "util"
], function (
    util
) {
    "use strict";

    function Console() {
        this.writer = function () {};
    }

    util.extend(Console.prototype, {
        bind: function (args) {
            if (args.write) {
                this.writer = args.write;
            }
        },

        write: function (message) {
            this.writer(message);
        }
    });

    return Console;
});

// Module /Interpreter/Flow.js
define("Interpreter/Flow", [
    "util"
], function (
    util
) {
    "use strict";

    function FlowInterpreter(console) {
        this.console = console;
    }

    util.extend(FlowInterpreter.prototype, {
        run: function (program) {
            var console = this.console,
                runner = program.compile();

            runner(function (message) {
                console.write(message);
            });
        }
    });

    return FlowInterpreter;
});

// Module /Parser/Flow.js
define("Parser/Flow", [
    "util",
    "Program/Flow"
], function (
    util,
    FlowProgram
) {
    "use strict";

    function FlowParser() {

    }

    util.extend(FlowParser.prototype, {
        parse: function (code) {
            code = code.replace(/print\s+([\d.]+|"[^"]*");/g, "print($1);");
            return new FlowProgram(code);
        }
    });

    return FlowParser;
});

// Module /Program/Flow.js
define("Program/Flow", [
    "util"
], function (
    util
) {
    "use strict";

    function FlowProgram(code) {
        this.code = code;
    }

    util.extend(FlowProgram.prototype, {
        compile: function () {
            return new Function("print", this.code);
        }
    });

    return FlowProgram;
});

// Module /VM/Flow.js
define("VM/Flow", [
    "util"
], function (
    util
) {
    "use strict";

    function FlowVM(parser, interpreter) {
        this.parser = parser;
        this.interpreter = interpreter;
    }

    util.extend(FlowVM.prototype, {
        run: function (code) {
            var program = this.parser.parse(code);
            this.interpreter.run(program);
        }
    });

    return FlowVM;
});

require([
    "../service" // Path to service.js, with ".js" auto-appended by Modular
], function (
    service
) {
    "use strict";

    var components = service.create(),
        container = components.container,
        objectLoader = components.objectLoader,
        object = {
            "services": {
                "parser": {
                    "class": "Parser/Flow"
                },
                "interpreter": {
                    "class": "Interpreter/Flow",
                    "arguments": ["@console"]
                },
                "console": {
                    "class": "Console"
                },
                "vm": {
                    "class": "VM/Flow",
                    "arguments": ["@parser", "@interpreter"]
                }
            }
        },
        console,
        vm;

    objectLoader.load(object);

    container.get("console", "vm")
        .done(function (console, vm) {
            console.bind({
                "write": function (message) {
                    var item = document.createElement("li");
                    item.appendChild(document.createTextNode(message));
                    document.getElementById("console").appendChild(item);
                }
            });

            vm.run("print \"Hello from the Flow!\";");  // Will print "Hello from the Flow!" to the console
            vm.run("print 47;");  // Will print "47" to the console
        });
});

Note that the actual construction of the service objects ("parser", "interpreter", "console" and "vm") is handled by Service.JS.

Something went wrong with that request. Please try again.