Skip to content

Commit

Permalink
[Noder] Add ability to create a lazy loader on any object
Browse files Browse the repository at this point in the history
  * Fix the scope of Noder.$require()
  • Loading branch information
Nicolab committed Oct 30, 2014
1 parent 5401ccc commit 1085318
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 51 deletions.
67 changes: 54 additions & 13 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ Noder.prototype.$wrap = function $wrap(value) {
*
* The property configuration is :
* * enumerable: true,
* * configurable: false,
* * configurable: false,
* * writable: false
*
* @example
Expand All @@ -448,6 +448,15 @@ Noder.prototype.$wrap = function $wrap(value) {
* // Now load the `marked` module in the `markdown` property
* noder.markdown;
*
* @example
* // Register a sub-property.
* noder.models = {};
*
* noder.$require('User', './models/user', noder.models);
*
* // Load the `./models/user` module in `noder.models.User` property.
* noder.models.User.someMethod();
*
* @param {string|function} property The property name
* or `required` value if the `required`
* argument is not provided (shortcut).
Expand All @@ -463,29 +472,49 @@ Noder.prototype.$wrap = function $wrap(value) {
* * The `required` argument is passed to the function `require()`.
* * The `required` item is only loaded the first time (singleton).
*
* @param {object} [obj] The object where the property is created.
* If is not provided, it's the current instance of `Noder`.
*
* @return {Noder} The current `Noder` instance.
*/
Noder.prototype.$require = function $require(property, required) {
Noder.prototype.$require = function $require(property, required, obj) {

Object.defineProperty(this, property, {
var ref;
var _this = this;

obj = obj || this;

Object.defineProperty(obj, property, {

enumerable : true,
configurable : false,

get: function() {

if (this.$require.isLoaded(property)) {
return loaded[property];
// most cases, faster checking
if (typeof ref != 'undefined') {
return ref;
}

if(typeof required === 'undefined') {
// rare cases, ensure the modules with undefined return
if(_this.$require.isLoaded(property, obj)) {
return ref;
}

if(!required) {
required = property;
}

loaded[property] = (typeof required === 'function' ?
required.call(this._container) : require(required));
ref = (typeof required === 'function' ?
required.call(_this.$di._container) : require(required));

return loaded[property];
if(!loaded[property]) {
loaded[property] = [];
}

loaded[property].push(obj);

return ref;
},

set: function() {
Expand Down Expand Up @@ -513,13 +542,25 @@ Noder.prototype.$require = function $require(property, required) {
* // true
* console.log(noder.$require.isLoaded('express'));
*
* @param {string} property The property name.
* @param {string} property The property name.
* @param {object} [obj] Object to check the property. If is not provided,
* it's the current instance of `Noder`.
* @return {bool} `true` if the given module is loaded, `false` otherwise.
* @see Noder.$require()
*/
Noder.prototype.$require.isLoaded = function isLoaded(property) {
return (undefined !== loaded[property]);
};
Noder.prototype.$require.isLoaded = function isLoaded(property, obj) {

if(!obj) {
return loaded[property] ? true : false;
}

if(!loaded[property]) {
return false;
}

obj = obj || this;

return (loaded[property].indexOf(obj) !== -1);
};

module.exports = new Noder();
127 changes: 89 additions & 38 deletions test/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -279,50 +279,101 @@ describe('Noder', function(){
.object(noder['unit.js'])
.isIdenticalTo(require('unit.js'))
;
});

it('Custom loader', function() {
it('Lazy loading in another object', function(){

var spy = test.spy();
var obj = {
a: undefined
};

test
.case('define a custom loader')
.object(noder.$require('coll', function() {
spy();

test
.object(this)
.isIdenticalTo(noder.$di)
.isInstanceOf(noder.Collection)
;

return require('./collection');
}))
.isIdenticalTo(noder)
.isInstanceOf(noder.Noder)

.then('should be not loaded')
.bool(spy.called)
.isFalse()

.case('use the lazy loaded module')
.function(noder.coll)
.hasName('Collection')
.isIdenticalTo(require('./collection'))

.then('should be loaded with the custom loader')
.bool(spy.calledOnce)
.isTrue()
test
.object(noder.$require('unit', 'unit.js', obj))
.isIdenticalTo(noder)
.isInstanceOf(noder.Noder)

.bool(noder.$require.isLoaded('unit', obj))
.isFalse()

.if(obj.unit)
.bool(noder.$require.isLoaded('unit', obj))
.isTrue()

.case('reuse the lazy loaded module')
.function(noder.coll)
.hasName('Collection')
.isIdenticalTo(require('./collection'))
.object(obj.unit)
.isIdenticalTo(require('unit.js'))
;
});

it('Lazy loading in a property', function(){

var obj = {
a: {}
};

.then('should be a singleton')
.bool(spy.calledOnce)
test
.object(noder.$require('unit', 'unit.js', obj.a))
.isIdenticalTo(noder)
.isInstanceOf(noder.Noder)

.bool(noder.$require.isLoaded('unit', obj.a))
.isFalse()

.if(obj.a.unit)
.bool(noder.$require.isLoaded('unit', obj.a))
.isTrue()

.object(obj.a.unit)
.isIdenticalTo(require('unit.js'))
;
});

it('Custom loader', function() {

var spy = test.spy();

test
.given(noder.$di.set('customLoaderDep', true))
.case('define a custom loader')
.object(noder.$require('coll', function() {
spy();

test
.object(this)
.isIdenticalTo(noder.$di._container)

.bool(this.customLoaderDep)
.isTrue()
;
});
;

return require('../../src/collection');
}))
.isIdenticalTo(noder)
.isInstanceOf(noder.Noder)

.then('should be not loaded')
.bool(spy.called)
.isFalse()

.case('use the lazy loaded module')
.function(noder.coll)
.hasName('Collection')
.is(noder.Collection)
.isIdenticalTo(require('../../src/collection'))

.then('should be loaded with the custom loader')
.bool(spy.calledOnce)
.isTrue()

.case('reuse the lazy loaded module')
.function(noder.coll)
.hasName('Collection')
.is(noder.Collection)
.isIdenticalTo(require('../../src/collection'))

.then('should be a singleton')
.bool(spy.calledOnce)
.isTrue()
;
});
});
});
Expand Down

0 comments on commit 1085318

Please sign in to comment.