adamrenklint/bap

View on GitHub
lib/Model.js

Summary

Maintainability
A
1 hr
Test Coverage
A
100%
var context = require('./utils/context');
var vent = require('./utils/vent');
var State = require('ampersand-state');
var uniqueId = require('./utils/uniqueId');
var merge = require('lodash.merge');
var memoize = require('meemo');
var changeRE = /^change:/;

function hashArgs () {
  var args = new Array(arguments.length);
  for (var i = 0, l = arguments.length; i < l; i++) {
    args[i] = arguments[i];
  }
  return args.join('//');
}

var Model = State.extend({

  type: 'model',

  props: {
    id: ['number']
  },

  extraProperties: 'reject',

  initialize: function (params, options) {
    this.context = context.get();
    this.vent = vent.get();
    this.cid = uniqueId(this.type);
    State.prototype.initialize.apply(this, arguments);
    if (params) {
      this.set(params, merge({
        silent: true,
        initial: true
      }, options));
    }
  },

  with: function (state) {
    state = merge({}, this.toJSON(), state);
    return new this.constructor(state);
  },

  cacheMethodUntilEvent: function (name, event) {
    var originalMethod = this[name].bind(this);
    var reset = function () {
      this[name] = memoize(originalMethod, hashArgs);
      return reset;
    }.bind(this);
    this.on(event, reset);
    return reset();
  },

  destroy: function () {
    this.off();
    this.stopListening();
  },

  _initCollections: function () {
    State.prototype._initCollections.call(this);
    for (var coll in this._collections) {
      this.listenTo(this[coll], 'all', this._getCollectionEventBubblingHandler(coll));
    }
  },

  _getCollectionEventBubblingHandler: function (propertyName) {
    return function (eventName, model, newValue, collection) {
      var validNames = ['change', 'add', 'remove'];
      if (~validNames.indexOf(eventName)) {
        this.trigger('change:' + propertyName, model, newValue);
        this.trigger(eventName, this);
      }
      else {
        var next;
        while (validNames.length) {
          next = validNames.shift();
          if (~eventName.indexOf(next + ':')) {
            var parts = eventName.split(':');
            var type = parts[0];
            var callers = [propertyName].concat(parts[1].split('.'));
            while (callers.length) {
              this.trigger(type + ':' + callers.join('.'), model, newValue);
              callers.pop();
            }
            this.trigger(type, model, newValue);
          }
        }
      }
    }.bind(this);
  }
});

module.exports = Model;