Deconstructing MEAN.IO

Im a big believer that you cannot truly understand a system or platform unless you understand the sum of it's parts. Weather you are talking about something like Laravel or something like MEANIO, there are many parts that work together to deliver a harmonious system. Simply knowing method calls, API behavior, and some terminology will only get you so far. That's why we are going to look at what makes up MEANIO. By learning the parts of it, we can understand how to work with it better.

1. The node module

To learn what makes a node package tick, we need to look at it's node module. So if we turn our attention to node_modules/meanio and look at the index.js we can see that all it really does is require the lib/mean.js file, so let's start there.

First off, looking at mean.js we can see that it makes use of the lazy-dependable package and the q package. Lazy-dependable is just a dependency management container with some lazy loading features mixed it. Q is a package for making it a little nicer to work with promises. We'll delve deeper into these in the near future.

So the first thing that happens in the mean.js is this: Some dependencies are required and a meanio superclass and consturctor are created.

'use strict';

var Container = require('lazy-dependable').Container;  
var Q         = require('q');

function Meanio() {  
  Container.call(this);

  if (this.active) return;
  Meanio.Singleton = this;
  this.version = require('../package').version;

 this.instanceWaitersQ = [];
  var defer;
  while(Meanio.instanceWaiters.length){
    defer = Q.defer();
    Meanio.instanceWaiters.shift()(this,defer);
    this.instanceWaitersQ.push(defer.promise);
  }
}

The meanio constructor is created. This guy does a few things.

  1. It uses the lazy-dependable container that is declared above it. This is the "super-class" that meanio inherits from.
  2. It sets some version information and assigns the Meanio instance, to the property Meanio.Singleton.
  3. Next, it sets up some instanceWaiters. These are the sub modules / packages like db, engine, config ect. They are assigned as promises so when they are loaded and done with setup then they are resolved.

The job of the instance waiters is to provide a promise interface to load the dependencies of the main mean module.

The next bit of code that we come to deals with how mean instances are constructed.

Meanio.prototype = Object.create(Container.prototype,{constructor:{  
  value: Meanio,
  enumerable: false,
  writable: false,
  configurable: false
}});

Basically, we are assigning the prototype of Meanio to be the Container prototype. This combined with the Container.call(this) from above ensures that Meanio effectively inherits all of the properties and methods of the Container package.

Setting the prototype like this lets us inherit all of the properties of the Container package ( which acts as our base class ).

Exploring the mean module creation a bit more..

For some of you who may not be familiar with JavaScript's prototypical inheritance system, let's break this example down a bit more ..... with another example:

Human = function(){  
    this.identify = function(){
        console.log('im a human');
    }
}

function Person(){  
    Human.call(this);
    this.whoAmI = function(){
        console.log('im a person');
    };
}

Person.prototype = Object.create(Human.prototype, {constructor:{  
  value: Human,
  enumerable: false,
  writable: false,
  configurable: false
}});

Paul = new Person();

Paul.identify();    //Im a human  
Paul.whoAmI();     //Im a person  

I omitted the var keyword so this code can be pasted in to chrome dev tools to test out without dev tools complaining.

The key here to realize is that without the Human.call(this) in the Person constructor, the prototype chain linking will not work correctly and the sub-class will not inherit the methods of the parent class. The way I like to think of this is, using the call method on the super class is like calling an initialize method of the super class and instantiating an instance of the superclass and extending it into the subclass.

Following these two gems of code, there is just a bit of support code where some methods are attached to the Meanio prototype at the end followed by where the rest of the magic happens.

Loading the rest of the framework

(require('./core_modules/config'))(Meanio);
(require('./menu'))(Meanio);
(require('./core_modules/module'))(Meanio);
(require('./core_modules/db'))(Meanio);
(require('./core_modules/server'))(Meanio);

module.exports = exports = new Meanio();  

At the end of this file the config is loaded, the menu is added, the module system is pulled in, the db config is put in place and lastly express is brought into the equation.

Note: that each package is pulled in as an anonymous function and the Meanio instance is given to them to be augmented.

Since JavaScript objects are passed by reference these methods do not have to explicitly return and overrite the meanio instance when they are augmenting it. Simply passing it in and acting on it will globally manipulate it.

I hope this has been a good exporatory article for you. As I progress deeper into the core myself I will be publishing more articles on meanio's composition.