Source: mixin/dynamic_mixin.js

/*! dynamic_mixin.js */

/**
 * This mixin provides a dynamic mixin loader which awakes at the first time when the methods called.
 *
 * @class dynamic_mixin
 * @mixin
 * @example
 * var MyKago = KagoDB.inherit();
 *
 * var hello_mixin = {
 *   hello: function() {
 *     console.log('world!');
 *   }
 * };
 *
 * var dynamic_mixin = KagoDB.bundle.dynamic_mixin;
 * MyKago.mixin(dynamic_mixin('keyname'));
 * MyKago.bundle.hello_mixin = hello_mixin;
 *
 * var opts = {
 *   storage: 'memory',
 *   keyname: 'hello_mixin'
 * };
 *
 * var collection = new MyKago(opts);
 * collection.noop(); // abrakadabra
 * collection.hello(); // => 'world!'
 */

var intercept_mixin = require('../mixin/intercept_mixin');

module.exports = function(routing) {
  var _routing = '_' + routing + '_dynamic_mixin';
  return mixin;

  function mixin() {
    return intercept_mixin.call(this, wrapper);
  }

  function wrapper(method, defaultFunction) {
    return function() {
      var mixin = this[_routing];
      if (!mixin) {
        mixin = dynamic_loader.call(this, routing);
        if (mixin) this[_routing] = mixin;
      }
      var func = mixin && mixin[method] || defaultFunction;
      return func.apply(this, arguments);
    };
  }
};

function dynamic_loader(routing) {
  var mixin = this.get(routing);
  if (!mixin) {
    // throw new Error(routing + ' not specified');
    return null;
  }

  this.emit('dynamic_mixin', routing, mixin);

  var str = mixin + '';
  if ('function' != typeof mixin) {
    var preloaded = this.bundle || {};
    mixin = preloaded[str];
    if (!mixin) {
      throw new Error('invalid ' + routing + ' class: ' + str);
    }
  }

  // emulate mixin's recursive call
  while ('function' == typeof mixin) {
    mixin = mixin.call(this);
  }

  // install new methods
  for (var key in mixin) {
    if (!this[key]) {
      this[key] = mixin[key];
    }
  }

  return mixin;
}