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

gearbox

v0.0.7

Published

Another Dependency Injection container for Node.js

Downloads

12

Readme

Gearbox

Build Status

Gearbox is a Node.js framework for assembling pretty-much anything from an extensible set of components. Within Gearbox, these components are known as Gears. There's a growing library of pre-canned Gears available on NPM and a Yeoman Generator to help roll your own.

Contents

Gearbox Module

Installation

$ npm install gearbox

Gear Prelude

Gearbox delivers functionality via configurable components referred to as 'Gears'. As explained later, Gears are little-more than Node.js modules that provide some bounded functionality. Gears can offer something as small as a textBox for use in a web form, through to something as big as a web server. Though a few internal Gears come bundled with the main Gearbox module (e.g. a logger), a one-NPM-package-per-Gear approach is employed for everything else.

  • Gears can inherit properties from other Gears
  • Gears can have other Gear instances injected into them
  • Gears can be arranged into tree structures

Basic usage

var Gearbox = require('gearbox');

var box = new Gearbox(); // Create a new Gearbox instance

// Get a 'car' gear
box.get('car', {maxSpeed: 128}, function(err, car) {
    car.parkingBrake(false);
    car.accelerateToSpeed(30);
});

Configuration

When creating a new Gearbox instance, it's possible to provide an optional configuration object:

var box = new Gearbox(config);

###gearConfig

  • Required: No
  • Type: Object

#####Description This is one of several ways to configure Gears - it's especially useful for configuring Gearbox-wide singleton-Gears such as a logger or a database connection.

  • The keys of the configuration-object should be a Gear name, and the value should be an object that will be passed as config when the Gear is instantiated.
  • Providing configuration for a Gear does not imply it will ever get instantiated... only when a new Gear instance is required will this config be consulted.
Example
var Gearbox = require('gearbox');
var box = new Gearbox(
  {
    gearConfig: {

      logger: {
        level: 'debug'
      },

      database: {
        dataSource: {
          connector: 'postgresql',
          host: 'localhost',
          port: 5432,
          database: 'gearbox',
          username: 'gearbox',
          password: 'gearbox'
        }
      }
    }
  }
);
  • Here, when a logger Gear is first required, it will be configured with a level of debug, and if a database Gear is required, it will be instantiated with a dataSource object.
  • Gears which are designed to behave like singletons are usually configured in this way.

###modulePrefixes

  • Required: No
  • Default: ['gear']
  • Type: Array of Strings

#####Description

If all fails, Gearbox will use Node's internal machinery to require a Gear module (as described here). By default the requested Gear name will be turned into a module name with a prepended gear-, but if you're developing your own Gears you may prefer to use your own prefixes. This can be achieved by providing alternative modulePrefixes, Gearbox will iterate over the provided prefixes in order, looking for a resolvable module.

#####Example

{
  modulePrefixes: ['funky-startup', 'gear']
}

###maxDepth

  • Required: No
  • Default: 100
  • Type: Integer

#####Description

This places a limit on the recursive depth things will be allowed to descend when constructing dependencies. Pretty internal - probably doesn't need changing.


###maxConstructors

  • Required: No
  • Default: 1500
  • Type: Integer

#####Description

The maximum number of cached Gear-constructors that will be held in memory at any one time.


###maxSingletons

  • Required: No
  • Default: 1500
  • Type: Integer

#####Description

The maximum number of cached singleton instances that will be held in memory at any one time.


###maxModules

  • Required: No
  • Default: 1500
  • Type: Integer

#####Description

The maximum number of 'loaded' Gear modules that will be held in memory at any one time.


###path

  • Required: No
  • Default: []
  • Type: Array of Strings

#####Description


###registeredGearDirs

  • Required: No
  • Default: {}
  • Type: Object

#####Description

Gearbox API

###registerGearPath(gearName, absDir, relDir)

Explicitly signposts the directory where the named Gear can be found. Gears that are registered in this way will resolve to the specified directory ahead of employing less specific discovery techniques.

| Parameter | Required? | Description | --------- | ----------| ----------- | gearName | Yes | The name of the Gear to register a directory for. | absDir | Yes | An absolute directory path where the Gear module can be found. Can be used in conjunction with relDir. | relDir | No | An optional relative path to be taken from absDir.

#####Example

var Gearbox = require('gearbox');
var box = new Gearbox();
box.registerGearPath('fancyGear', __dirname, '../gears');

###appendToPath(relDir, absPath)

If a Gear can't be found (because it's not been registered via registerGearPath) then Gearbox will use an internal path looking for Gears. The appendToPath method simply adds another directory to this internal path.

  • The directory added to the path can either signpost a Gear module directory (consider using the more explicit registerGearPath) or perhaps more usefully: a directory which contains many Gear module directories. In this instance, all the Gears in the containing directory are 'discoverable'.

| Parameter | Required? | Description | --------- | ----------| ----------- | dir | Yes | Either a relative or absolute directory path. | absDir | No | If provided it's assumed the contents of dir is a relative directory path, and will be resolved from absDir.

#####Example

var Gearbox = require('gearbox');
var box = new Gearbox();
box.appendToPath('./dev-gears', __dirname);

###discover(callback)

Once the internal path has all the required directories appended to it (via appendToPath) then calling discover will trawl the various directories and add any Gears it finds.

| Parameter | Required? | Description | --------- | ----------| ----------- | callback | Yes | Simple function(err) callback.

#####Example

var Gearbox = require('gearbox');
var box = new Gearbox();
box.appendToPath('./test-gears', __dirname);
box.discover (
  function (err) {
    // Some filesystem fail
  }
);

###get(gearName, instanceConfig, callback)

Gets a fully instantiated Gear instance, with all dependencies injected and any Gear-inheritance taken care of.

  • If the Gear behaves like a singletons (e.g. a logger) the single Gearbox-wide instance will be returned - otherwise a fresh instance will be created.

| Parameter | Required? | Description | --------- | ----------| ----------- | gearName | Yes | Name of the Gear to get | instanceConfig | No | If the Gear is not a singleton, it's possible to specify config for the fresh instance here. This is a simple config object. | callback | Yes | Of the form function (err, gear) where if successful gear is the instantiated Gear instance.

#####Example

var Gearbox = require('gearbox');
var box = new Gearbox(); // Create a new Gearbox instance

// Get a 'car' gear
box.get('car', {maxSpeed: 128}, function(err, car) {
  if (err) {
    // Handle it.
  }
  else {
    car.parkingBrake(false);
    car.accelerateToSpeed(30);
  }
});

Gears

Overview

Loading Gears

Gears are nothing more than Node.js modules which export an object with certain properties.

If you encounter a GearError: [unresolvedGear] Can't find module for name 'someMadeUpGearName' then Gearbox has failed to require a suitable Gear module.

Troubleshooting

For Gearbox to successfully load a Gear, at least one of the following must be true:

  • The Gear is one of the few internal Gears.
  • The directory containing the Gear's module has been registered with gearbox.registerGearPath().
  • The directory containing the Gear's module is either directly in Gearbox's internal path, or a direct sub-directory from a directory specified in Gearbox's internal path.
    • Either use gearbox.appendToPath() to add new directories to Gearbox's internal path
    • Or specify absolute directories as part of the Gearbox config object.#
    • Regardless of which path building method is used, always be sure to finish by calling gearbox.discover().
  • If Gearbox still has no joy in finding a module, then it will rely on Node's own module loading machinery. The specified Gear name will be converted into a module name in the following way:
    • First the Gear name is dasherized.
    • Then at least one prefix is prepended - Gearbox will always try the gear- prefix.
      • It's possible to provide alternative prefixes via the modulePrefixes property in Gearbox's configuration object.
    • So looking for a Gear named webServer will ultimately result in Gearbox trying require('gear-web-server').

Export object

There's not much to a Gear... just a simple Node.js module that exports a single object. For example:


module.exports = {

    name: 'cow', // Name of the gear (camelcase)

    create: 'each', // Gears can behave as singletons or instances

    extending: 'farmAnimal', // Gear-inheritance is possible
    dependencies: ['logger'], // Gear Dependency-Injection is possible
    allowedParents: ['barn'], // Gears can be structured into trees

    // Each Gear can have config supplied to it...
    // values of which can be defaulted:
    defaults: {
        milkable: true
        friendly: true
    },

    // Config values can be validated via JSON Schema:
    schema: require('./cow-config-schema.json'),

    // Gears can have the equivalent of a constructor:
    initFunction: function (config, gears, callback) {
        this.logger.info('Created ' + config.cowName + '!' );
    },

    // Gears can have their own methods:
    methods: function (prototype) {

        prototype.moo = function moo (loudness) {
            if (loudness > 10} {
                this.logger.info('MOO!');
            }
            else {
                this.logger.info('Moo.');
            }
        };

    }
};

This is the full list of properties you can export in a Gear module:

###name

  • Required: Yes
  • Type: String

#####Description The unique name of the Gear within a Gearbox instance. There are a few naming conventions to adhere to:

  • Letters and numbers only
  • Start with a lowercase letter
  • Use camelcase for multi-word gear names
  • Try to keep a Gear's name short and punchy

#####Example

name: 'farmAnimal'

###create

  • Required: No
  • Default: each
  • Type: String

#####Description When Gearbox creates a new Gear, there are a couple of options:

| Option | Description | ------ | ----------- | one | The Gear should be considered a singleton... only one Gear instance will ever be created. Any subsequent requests for a Gear of this type will return that same instance. For example the logger Gear has a create value of one. | each | A new Gear instance will be created on each request. If a set of Gears were being arranged to form a web form, then textBox would be an example of a Gear requiring a create value of each. This is the default behaviour.

#####Example

{
    name: 'farmAnimal',
    create: 'each'
}

###extending

  • Required: No
  • Type: String

#####Description

Gearbox supports Gear inheritance. For example a car Gear can inherit properties from a more general vehicle Gear.

To achieve all this, the value of extending should simply be the name of another gear, perhaps better termed as a 'Supergear' (e.g. a car Gear could have an extending value of vehicle). In this continuing example, vehicle is just a normal Gear from which several properties will be harvested when creating car Gears:

| Property | How it's inherited | -------- | ------------------- | defaults | The car Gear's defaults will be used as usual, but any other defaults defined in vehicle will be applied as well. In the case of both Gears defining the same default value, the car Gear value will override that of vehicle. | allowedParents | In the case of both Gears providing allowedParents arrays, they are unioned together. If there's just one Gear providing an array, that's what is used. | allowedChildren | Just like allowedParents, but for children. | methods | Exactly the same 'override' behaviour as used in defaults, but applied to methods instead. | implicitGears | If vehicle provides implicitGears, then that value is used, unless car provides it - in which case there's no merging of the two arrays - the car array just wins.

  • It's quite feasible to have a superCar Gear, extending a car Gear, which in-turn is extending a vehicle Gear. These rules are applied at each step along the chain.

#####Example

{
    name: 'car',
    extending: 'vehicle'
}

###dependencies

  • Required: No
  • Type: Array of strings

#####Description

Gearbox supports a Gear-based Dependency Injection mechanism - provide an array of Gear names via dependencies, and fully-instantiated Gear instances will be available as simple properties when the Gear is created.

  • Dependent Gears should be singletons (e.g. all the Gears referred to in the dependencies array should have their respective create value set to one).
  • Dependency loops (gearA has a dependency on gearB, yet gearB has a dependency on gearA) will cause an error.
  • The usual config machinery will be employed when creating dependencies.

#####Example

{
    name: 'databaseTable',
    dependencies: ['logger', 'relationalDatabase']
}

###allowedParents

  • Required: No
  • Type: Array of strings

#####Description

Blueprints provide a way to arrange Gears into a tree structure. To help apply some integrity to these trees, placing a Gear 'under' a Gear whose type isn't named in the allowedParents array will cause an error (though allowedChildren can potentially save the day).

  • Include the special name $root in the allowedParents array if the Gear is permissible on the root of a tree structure.
  • Withstanding the potential use of allowedChildren, if no allowedParents is defined then trying to use the Gear as part of a tree will cause an error.

#####Example

{
    name: 'databaseColumn',
    allowedParents: ['databaseTable']
}

###allowedChildren

###defaults

###schema

###initFunction

###secondPassFunction

Tests

$ npm test
$ npm test --coverage

License

MIT