Example #1
0
  constructor: function(attributes, options={}){
    var self = this;
    if(attributes === null || attributes === undefined){ attributes = {}; }
    attributes.isModel && (attributes = attributes.attributes);
    this.helpers = {};
    this.defaults = this.defaults || {};
    this.setParent( options.parent || this );
    this.setRoot( options.root || this );
    this.__path = options.path || this.__path;

    // Convert getters and setters to computed properties
    $.extractComputedProps(attributes);

    Backbone.Model.call( this, attributes, options );

  },
Example #2
0
Model.extend = function(protoProps, staticProps) {
  $.extractComputedProps(protoProps.defaults);
  return Backbone.Model.extend.call(this, protoProps, staticProps);
};
Example #3
0
  set: function(key, value, options){

    var attrs, newKey, destination, props = [];

    if (typeof key === 'object') {
      attrs = (key.isModel) ? key.attributes : key;
      options = value;
    }
    else (attrs = {})[key] = value;
    options || (options = {});

    // Convert getters and setters to computed properties
    $.extractComputedProps(attrs);

    // If reset option passed, do a reset. If nothing passed, return.
    if(options.reset === true) return this.reset(attrs, options);
    if(options.defaults === true) this.defaults = attrs;
    if(_.isEmpty(attrs)){ return void 0; }

    // For each attribute passed:
    for(key in attrs){
      let val = attrs[key],
          paths = $.splitPath(key),
          attr  = (paths.pop() || ''),       // The key          ex: foo[0].bar --> bar
          target = this.get(paths.join('.')),  // The element    ex: foo.bar.baz --> foo.bar
          lineage;

      // If target currently doesnt exist, construct its tree
      if(_.isUndefined(target)){
        target = this;
        _.each(paths, function(part){
          var tmp = target.get(part);
          if(_.isUndefined(tmp)) tmp = target.set(part, {}).get(part);
          target = tmp;
        }, this);
      }

      // The old value of `attr` in `target`
      destination = target.get(attr, {raw: true}) || {};

      // Create this new object's lineage.
      lineage = {
        name: key,
        parent: target,
        root: this.__root__,
        path: pathGenerator(target, attr),
        silent: true,
        defaults: options.defaults
      };
      // - If val is `null` or `undefined`, set to default value.
      // - If val is a `Computed Property`, get its current cache object.
      // - If val (default value or evaluated computed property) is `null`, set to default value or (fallback `undefined`).
      // - Else If val is a primitive object instance, convert to primitive value.
      // - Else If `{raw: true}` option is passed, set the exact object that was passed. No promotion to a Rebound Data object.
      // - Else If this function is the same as the current computed property, continue.
      // - Else If this value is a `Function`, turn it into a `Computed Property`.
      // - Else If this is going to be a cyclical dependancy, use the original object, don't make a copy.
      // - Else If updating an existing object with its respective data type, let Backbone handle the merge.
      // - Else If this value is a `Model` or `Collection`, create a new copy of it using its constructor, preserving its defaults while ensuring no shared memory between objects.
      // - Else If this value is an `Array`, turn it into a `Collection`.
      // - Else If this value is a `Object`, turn it into a `Model`.
      // - Else val is a primitive value, set it accordingly.


      if(_.isNull(val) || _.isUndefined(val)) val = this.defaults[key];
      if(val && val.isComputedProperty) val = val.value();
      if(_.isNull(val) || _.isUndefined(val)) val = undefined;
      else if(val instanceof String) val = String(val);
      else if(val instanceof Number) val = Number(val);
      else if(val instanceof Boolean) val = Boolean(val.valueOf());
      else if(options.raw === true) val = val;
      else if(destination.isComputedProperty && destination.func === val) continue;
      else if(val.isComputedProto) val = new ComputedProperty(val.get, val.set, lineage);
      else if(val.isData && target.hasParent(val)) val = val;
      else if( destination.isComputedProperty ||
              ( destination.isCollection && ( _.isArray(val) || val.isCollection )) ||
              ( destination.isModel && ( _.isObject(val) || val.isModel ))){
        destination.set(val, options);
        continue;
      }
      else if(val.isData && options.clone !== false) val = new val.constructor(val.attributes || val.models, lineage);
      else if(_.isArray(val)) val = new Rebound.Collection(val, lineage); // TODO: Remove global referance
      else if(_.isObject(val)) val = new Model(val, lineage);

      // If val is a data object, let this object know it is now a parent
      this._hasAncestry = (val && val.isData || false);

      // Set the value
      Backbone.Model.prototype.set.call(target, attr, val, options); // TODO: Event cleanup when replacing a model or collection with another value

    }

    return this;

  },