SkillAgentSearch skills...

Syringe

An ultra-lightweight dependency injection framework for JavaScript

Install / Use

/learn @holt/Syringe
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

syringe.js Build Status

<img src="https://github.com/holt/syringe/blob/master/img/syringe.png?raw=true" align="right" title="# Just a little pin prick ... there'll be no more AAAAAAAAH!"/>

Syringe is a minimalist JavaScript framework that allows you to quickly and easily implement inversion of control when building applications.

The Syringe library provides a comprehensive suite of robust yet straightforward API methods that facilitates the development of loosely-coupled code.

Now, let's roll up our sleeves and begin!

Table of Contents

Installation

Platform | Description ------------------|---------------------------------------------------- Browser | Just download syringe.min.js and add it to your to your environment.<br/><br/>Syringe uses JSON.parse and also the following ECMAScript 5 / JavaScript 1.6 methods:<br/><br/>- Array.filter<br/>- Array.map<br/>- Array.reduce<br/>- Function.bind<br/>- Object.keys<br/>- Object.create<br/>- String.trim<br/><br/><img align="left" src="https://github.com/holt/syringe/blob/master/img/note.png?raw=true"/>Note: All of the above methods are available natively on modern browsers. If you need to support older browsers, the polyfills for these methods are provided in lib/polyfill.min.js<br/><br/> Node | Ensure that you have installed the latest version of node.js and run the following from the command prompt:<br/><br/>npm install syringejs<br/><br/> Bower | Ensure that you have installed the latest version of Bower and run the following from the command prompt:<br/><br/>bower install syringe --save<br/><br/> NuGet | Run the following command in the Package Manager Console:<br/><br/>Install-Package syringe.js<br/><br/>

Overview

Syringe works by taking a function and binding it with shallow or deep references to data items located within its data registry. When this pre-bound function executes, the references are reconciled against the registry and the corresponding data items are passed into the function automatically. This technique is known as dependency injection.

Tutorial

In the following sections we'll demonstrate how dependency injection works by using Syringe to build a small set of pre-bound functions that allow us to interact in various ways with a simple datastore held inside a Syringe object.

Defining the Data

Let's start by creating a Syringe object that holds some departmental information for a fictional organization. Our object will also hold the utility methods that allow other callers to retrieve and interact with this data.

The API method Syringe.create() is used to generate a new instance of a Syringe object, which we'll call syr. By default, new object instances are empty, however an instance can also be initialized with a payload of new data if we pass create an object map, like this:

var syr = Syringe.create({
    depts: {
        'sales'     : {id: 'A1', name: 'Sales'},
        'finance'   : {id: 'B2', name: 'Finance'},
        'marketing' : {id: 'C3', name: 'Marketing'}
    }
});

Let's examine syr to ensure that it does indeed contain departmental information:

syr.get('depts.sales.id');          // Returns: "A1"
syr.get('depts.finance.name');      // Returns: "Finance"

The first thing to note here is that the syr object's get method uses a dot-delimited string to retrieve data.

<img align="left" src="https://github.com/holt/syringe/blob/master/img/note.png?raw=true"/>Note: Unlike using dot-notation to fetch items directly from a JavaScript object, this method of retrieval will not cause the system to throw an exception if you attempt to access the data of a property that doesn't exist. So if we execute syr.get('depts.hr.id') we'll get a return value of false.

Now we've confirmed that we have now got a brand new syr object that holds some basic data we can use Syringe's binding capabilities to create a retrieval function called report that receives the depts object data automatically when executed.

To do this we use the on method to create a new bound function:

var report = syr.on(['depts'], function (data, key) {
    return data[key] || false;
});

Now we can execute our report function like so:

report('sales');    // Returns: {id: "A1", name: "Sales"}

Notice how we only passed in one argument: the name of the department we're interested in. Arguments passed by the function's caller are known as free arguments. The depts argument is pre-bound to the function and passed in automatically.

When defining a pre-bound function, data registry item names and argument names (the depts array item and the data argument respectively in the above example) do not have to match. However, there must be at least as many pre-bound arguments as registry items.

To make this a little clearer, consider the following example:


var func = syr.on(['foo', 'bar.fly', 'buzz.lightyear'], function (a, b, c, key) {
    // ...
});

In the above example the a, b, and c arguments are bound to their corresponding data registry items foo, bar.fly, and buzz.lightyear, with key being the only free (unbound) argument.

Bound arguments always match the array order of their corresponding data registry items and precede the free arguments (if any) in the function signature.

Storing Bound Functions

Syringe's on method allows us the create ad hoc pre-bound functions and assign them to a variable or object. However the report function is useful, so let's add it to the Syringe data registry as a utility method so it can be used throughout our system as an injectable item in its own right.

The code for adding pre-bound functions to the registry looks like this:

syr.add('utils.report', function (data, key) {
    return data[key] || false;
}, ['depts']);

The first argument specifies where we want our method to reside inside the Syringe data registry. It doesn't matter that the registry does not yet have a utils property as it is created automatically when we add the utils.report item.

<img align="left" src="https://github.com/holt/syringe/blob/master/img/note.png?raw=true"/> **Note:** If a `utils` property containing a `report` property *was* already present in the registry, Syringe would throw an error and suggest that we first use the `remove` method to unregister the `report` property.

The second argument is the function definition, and the third argument is the array of items we want to pull from the Syringe data registry and inject directly into our utils.report function when it executes.

<img align="left" src="https://github.com/holt/syringe/blob/master/img/note.png?raw=true"/> **Note:** The third argument is optional. You don't *have* to store a pre-bound function; you could just store a regular function (however, in this example we want to).

Unlike on, the add operation returns the entire Syringe object, so it isn't useful to assign it to a variable. If we want to test our newly added method (or any a method stored within the Syringe registry) we can execute it like this:

syr.exec('report', ['sales']);  // Returns: {id: "A1", name: "Sales"}

The great value of adding pre-bound methods to the Syringe data registry is that they too can be injected into other functions. And that's what we're going to look at next.

Injecting Bound Functions

So far we've created a Syringe object that contains some basic departmental information, and defined a getter-like report function that grants any executor access to a specific named item within the depts data object.

Let's enhance report by improving its data-retrieval capabilities. We'll expand it to accept either a name or an ID as a lookup key in order to locate departmental information.

Because utils.report already exists, we must use the set method instead of the add method to change the function definition:

syr.set('utils.report', function(data, key) {
    if (data[key]) {
        return data[key];
    } else {
        var str = Object.keys(data).filter(function(item) {
            return data[item].id === key;
        });
        return str ? data[str] : false;
    }
}, ['depts']);

As before, we can use the exec method test our enhanced utils.report function:

syr.exec('utils.report', ['sales']);    // Returns: {id: "A1", name: "Sales"}
syr.exec('utils.report', ['B2']);       // Returns: {id: "B2", name: "Finance"}

We are now going to extend the Syringe registry to include some members of staff:


syr.add({
    personnel: {
        'smith_r': {id: '001', name: 'Robert Smith', dept: 'A1'},
        'jones_t': {id: '002', name: 'Edward Jones', dept: 'B2'},
        'coope_a': {id: '003', name: 'Andrea Coope', dept: 'C3'}
    }
});

It would be useful to create a function that uses what we've built so far in order to provide us with profile data about a member of staff that also includes the information about their department.

Like report, this function should be able to accept either a name or an ID as a lookup key in order to locate the corresponding profile.

We'll call this new method utils.profile. When it's executed, the method gets passed the utils.report function as its first argument:

Related Skills

View on GitHub
GitHub Stars14
CategoryDevelopment
Updated3y ago
Forks1

Languages

JavaScript

Security Score

75/100

Audited on Jan 7, 2023

No findings