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

configable

v0.1.10

Published

Don't repeat yourself. Configure everything.

Downloads

4

Readme

Installation

npm install configable

Example

var cfg = require('configable'),
    Configable = cfg.Configable,
    setting = cfg.setting;

var Car = Configable.extend({
    fuel_efficiency: setting({
        required: true,
        parse: Number
    }),

    units: setting({
        default: 'metric',
        choices: [
            'metric',
            'english'
        ]
    }),

    mpg: function() {
        var eff = this.fuel_efficiency;
        return this.units === 'english' ? eff : 2.35214583 * eff;
    }
});

var car = new Car({
    'fuel_efficiency': 30,
    'units': 'metric'
});

console.log(car.mpg());

Tutorial

Configable

A new Configable subclass is created using the Configable.extend static method. Properties of the object passed into extend become simple properties of the new class' prototype unless they are declared as a setting. Settings are instead attached to your subclass prototype using javascript descriptors, so that Configable can intercept get/set operations for these properties for the purposes of type coercion, requirement checking, value parsing, etc.

Here's a very simple example of Configable.extend. For introductory purposes, the settings are specified without options. This means they are optional, and their values are taken as is from whatever configuration object instantiates the class.

var Animal = Configable.extend({
    species: setting(),
    sound: setting(),
    speak: function() {
        console.log(this.sound);
    }
});

WARNING: Because Configable uses Object.defineProperty() (and Object.create() for prototype chaining) an HTML5 shim is required if you want to support older browsers.

A Configable expects a single argument in its constructor: a configuration object. A config object is just a plain old javascript object, probably loaded from a JSON config file. It should contain properties that correspond to the settings defined on the Configable it is instantiating.

var cheetah = new Animal({
    'species': 'acinonyx jubatus',
    'sound': 'rawr'
});

Inheritance

Subclasses of Configable are equipped with their own extend method. The standard prototype chaining technique is used, so settings and other prototype properties are inherited as usual.

var Cheetah = Animal.extend({
    subtype: {species: 'acinonyx jubatus'},
    speak: function() {
        console.log('rawr'); // hard-coded sound
    }
});

The subtype property is special for Configable subclasses. It should be a plain object with keys/values that correspond to settings defined on the parent class. The purpose of the subtype property is to identify matching configuration objects (passed into the parent class constructor) as instances of a special subtype of the parent class. In other words, we're injecting an inheritance scheme into the inheritanceless hash table data structure.

It's way simpler by example; specifying the subtype property allows this craziness:

var cheetah = new Animal({
    'species': 'acinonyx jubatus'
});

console.log(cheetah instanceof Cheetah); // true!
cheetah.speak(); // 'rawr'

The Animal constructor was used to make a Cheetah instance.

So now you can have collections of animals in your config file:

{
    "cheetah": {
        "species": "acinonyx jubatus"
    },
    "grizzly": {
        "species": "ursus arctos",
        "sound": "roar!"
    }
}

and the correct subclass will be instantiated for each. Speaking of which...

ConfigableMap

A ConfigableMap is simply a mapping between strings and Configables. The class is dead simple; have a look at the source if you want to see what's goin down. Or, take it all in with this juicy example:

var cfg = require('configable'),
    Configable = cfg.Configable,
    ConfigableMap = cfg.ConfigableMap,
    setting = cfg.setting;

var Dog = Configable.extend({
    breed: setting()
});
var Dogs = ConfigableMap.extend({Type: Dog});

var dogs = new Dogs({
    gracie: {breed: 'golden'},
    spot: {breed: 'terrier'}
});

console.log(dogs.gracie instanceof Dog); // true!

Make sure you assign a Type property to a Configable class in the ConfigableMap prototype! You get all the benefits of subclass instantiation

ConfigableArray

Given ConfigableMap, you should be satisfied with an example,

var cfg = require('configable'),
    Configable = cfg.Configable,
    ConfigableArray = cfg.ConfigableArray,
    setting = cfg.setting;

var Dog = Configable.extend({
    breed: setting()
});
var Dogs = ConfigableArray.extend({Type: Dog});

var dogs = new Dogs([
    {breed: 'golden'},
    {breed: 'terrier'}
]);

console.log(dogs[0] instanceof Dog); // true!

setting

This is a function defined on the module.

var setting = require('configable').setting;

Call this function and assign the result to a property of the prototype passed in to Configable.extend(...) (see numerous examples above). Generically (where shown option values are the defaults),

var Type = Configable.extend({
    setting_name: setting({
        required: false,      // Boolean
        default: undefined,   // Raw value
        choices: undefined,   // Array of type expected in config obj
        parse: undefined,     // Function (e.g. Number)
        kind: undefined,      // Function (e.g. Date); called with new
        init: undefined       // Function (called with Configable instance as 'this')
    })
});

The following are short explanations of the setting options.

required {boolean}

If set to true, instantiation of the containing Configable subclass will fail horribly if the setting is undefined on the configuration object.

default {*}

Pretty self-explanatory. You probably want required to be false if you are supplying a default setting value. The default value should be a raw value, i.e. of a type expected in the configuration object (fundamental, like integer, string, object, array, etc). The default, if taken, will be run through all the following setting checks and ops.

choices {array<*>}

If your settings values are restricted to a small set, list them here. Configable instantiation will fail if the raw value is not in this set.

parse {function}

The raw value from the configuration object is run through this function; therefore, it should accept a single value and return the transformed value or throw an error. Parsing happens before kind.

kind {function}

After parsing, the value will be cast to this type using value = new kind(value). A common example would be Date.

init {function}

Finally, the init function is called. This is different from parse and kind in that this is set to the Configable instance inside this function. You should not try to access other settings from inside this function as they may not have been loaded yet.