node package manager

easy

A general-purpose namespace container designed to help you better organize your components

Easy.js Framework

The Easy.js Framework is the anti-thesis of Express. Instead of attempting to be as general as possible and providing the widest coverage of API, Easy attempts to simplify the task of deploying web applications on NodeJS.

Namespace Builder

Easy builds the module namespace easily for you, so that you can access modules without using require(). Instead of writing require('./lib/foo/bar'), you can access it via easy.lib.foo.Bar. Note that the last token in the namespace string is automatically converted to Pascal case (similar to camel case but with first character in uppercase), while the rest of the namespace tokens will be converted from dash-delimited strings to camel cased strings.

Example:
./lib/flight-office/master -> easy.lib.flightOffice.Master
./com/metric/kg-to-lbs -> easy.lib.metric.KgToLbs

To use the namespace builder, simply tell Easy which folder to use as a root and it'll walk into the folder and sub-folders to build your namespace tree.

Example:
- lib
    |- mod
    |    |- metric
    |    |- str
    |    |- calc
    |- com
         |- controller
         |- model
         |- view

Will be converted to:
- easy.lib.mod.Metric
- easy.lib.mod.Str
- easy.lib.mod.Calc
- easy.lib.com.Controller
- easy.lib.com.Model
- easy.lib.com.View

To do this, simple put this in your main application code:

var easy = require('easy');
easy.root('./lib').ready(function onReady() {
    // Perform your app logic here
});

Looks familiar? Upon calling ready(), Easy will walk through the directory tree and build a namespace tree from it. Since this is done in an asynchronous manner, you will need to pass in callback in ready() in order for your code to be launched after Easy is done with building the namespace.

Usage Conventions

The reason why easy provides a namespace builder is because Javascript's indecision on whether it wants to be a pure prototypal language or provide support for class-based object model. More information here: Why do we need easy namespace.

When require() is called, it'll take all functions and parameters attached to your exports (or module.exports) object and return a new object with those properties and methods. However, if you wrote a module intending to have instances created off it, you need to be extra careful. See this as an example:

In your foo.js...

var Foo = module.exports = {
    bar : "bar",
    bar2 : {
        foo : "bar2 foo",
        bar : "bar2 bar"
    }
};

In another file...

var Foo = require('./foo.js');
var fooInstance = Object.create(Foo);

// Create a new instance of Foo
fooInstance.bar2.foo = "Oh lala";
console.log(fooInstance.bar2.foo);      // outputs "Oh lala"

// Create a new instance of Foo
var fooInstance2 = Object.create(Foo);

console.log(fooInstance2.bar2.foo);     // outputs "Oh lala", expected "bar2 foo"

This is because Object.create merely copies the properties from Foo to fooInstance, and since bar2 in Foo is actually a reference to the object bar2, all Foo, fooInstance and fooInstance2 will share the same reference to the same bar2 object. Any changes on one instance will overflow to other instances.

There are many ways one can sidestep this icky issue, but the truth is such behavior in a large system with many modules spread out in different locations will make debugging extremely difficult if you have bad programmers who code without understanding the delicates of Javascript. Therefore, Easy make these recommendations, and will enforce them if strict mode (easy.STRICT_MODE) is used:

Proposal #1 - always export object literals

Object literals are preferred by Easy rather than Constructor Functions, for one very simple reason - it fits better to Javascript's overall prototypal nature than Constructor Functions, whose existence is to solely to make the transition from class-based models to prototypal models easier.

// This is good...
var Foo = module.exports = {
    bar : "I am a bar",
    show : function show() {
        return this.bar;
    }
};

// This is not encouraged by Easy...
var Foo = module.exports = function Foo() {
    this.bar = "I am a bar";
};

Foo.prototype.show = function show() {
    return this.bar;
};

Proposal #2 - do NOT reference object in your object literal declaration

// This is good...
var Foo = module.exports = {
    bar : null,
    
    show : function show() {
        return this.bar;
    },
    
    construct : function construct() {
        this.bar = {
            data : "I am a bar"
        };
    }
};

// This is not encouraged by Easy...
var Foo = module.exports = {
    bar : {
        data : "I am a bar"
    },
    
    show : function show() {
        return this.bar;
    },
};

Notice the construct() function in Foo? If you specify a construct function in your module, Easy will automatically call that function once it's done building the namespace tree. This ensures that you only initialize the object AFTER the namespace tree has been built to avoid missing namespaces.

Proposal #3 - localize all namespaced modules in construct()

Because Easy needs to load all namespaced modules into the namespace tree, if you do this:

var Metric = easy.lib.Metric;

var Foo = module.exports = { ... }

It will fail, because when Easy loads Foo into the namespace tree, easy.lib.Metric might not have been created yet. This is where the construct() function comes in handy. Instead of the above, you should do:

var Foo = module.exports = {
    // Required modules
    Metric : null,
    
    construct : function construct() {
        this.Metric = easy.lib.Metric;
    }
};

Using the Namespace

Now that we've covered how the namespace is built, let's tackle the original problem again: instances of an object sharing the same property reference.

var Foo = easy.lib.Foo;
var fooInstance1 = Object.create(Foo);
var fooInstance2 = Object.create(Foo);

Will this work? Sadly, no. This is because Object.create() does not call the construct() function when instantiating a new object, which means that anything that you've constructed in Foo will be copied over to your instances anyway. To solve this, we provided our own function:

var Foo = easy.lib.Foo;
var fooInstance1 = easy.make(Foo);
var fooInstance2 = easy.make(Foo);

This works the same way as Object.create, but it also checks if construct() is defined. If it exists, it'll call it to construct the new object.

Why bother at about namespaces at all?

Unifying the object creation method allows module writers better control over their module's usage pattern. For instance, a module might have a property that is meant to be shared across all instances, like a static property. To achieve this, simply do:

var Foo = module.exports = {
    bar : { ... }
    
    construct : function construct() {
        // Don't change this.bar's value here
    }
};

which would allow all constructed objects from Foo to share the same reference to the bar object. When this behavior is not needed, all the module author needs to do is to set its default value to null, and construct it inside the construct() function. Easy provides you a choice on how to better control your module's usage pattern.

var Foo = module.exports = {
    bar : null,
    
    construct : function construct() {
        this.bar = { ... }
    }
};