Skip to content

Instantly share code, notes, and snippets.

@plagi
Created January 10, 2012 11:51

Revisions

  1. plagi revised this gist Jan 10, 2012. 1 changed file with 38 additions and 0 deletions.
    38 changes: 38 additions & 0 deletions example.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,38 @@
    // holds ALL upload models
    this.collection = new ncs.collection.UploadCollection();


    // subset of archived models
    this.archiveCollection = new Backbone.Subset({
    superset: this.collection,
    filter: function(upload) {
    return (upload.hasFailed() || upload.isComplete());
    }
    });

    // subset of active models
    this.activeCollection = new Backbone.Subset({
    superset: this.collection,
    filter: function(upload) {
    return (upload.isQueued() || upload.isUploading());
    }
    });

    // subset of queued models
    this.queuedCollection = new Backbone.Subset({
    debug: 'queued',
    superset: this.collection,
    filter: function(upload) {
    return upload.isQueued();
    }
    });

    // subset of active models
    this.uploadingCollection = new Backbone.Subset({
    debug: 'uploading',
    superset: this.collection,
    filter: function(upload) {
    return upload.isUploading();
    }
    });

  2. plagi created this gist Jan 10, 2012.
    232 changes: 232 additions & 0 deletions Backbone.Subset.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,232 @@
    /*
    @date 05/31/2011
    @class Backbone.Subset
    @name Backbone Subset
    @desc
    Implements an imaginary subset of a Backbone Collection (as superset)
    */


    // Extend the default Backbone.Collection

    _.extend(Backbone.Collection.prototype, {

    build: function (attrs) {
    var model = new this.model(attrs);
    this.add(model);
    return model;
    },
    merge: function (collection) {
    this.add(collection.models);
    return this
    }
    });


    // Standard Constructor
    Backbone.Subset = function(options) {


    this.options = options || (options={});

    // use the comparator supplied by the options
    if(options.comparator) {
    this.comparator = options.comparator;
    delete options.comparator;
    }

    if(!options.superset) { throw 'Subset must belong to a superset!'; }
    if(!options.filter) { throw 'Subset must have a filter'; }
    if(!(options.superset instanceof Backbone.Collection) && !(options.superset instanceof Backbone.Subset)) {
    throw "Subset must have Backbone.Collection or Backbone.Subset as its superset!";
    }

    var self = this;

    // transform method, to be applied on models
    this.transform = options.transform || function(echo) { return echo; };
    this.filter = options.filter;
    this.superset = options.superset;

    // hook on superset's events
    this.superset.bind("all", function(ev) {

    // TODO: CLEAN UP RESET!!!

    switch(ev) {
    case "add":
    case "remove":
    if(self.filter(arguments[1])) {
    // we are affected, forward events on this subset
    self._reset();
    self.trigger.apply(self, arguments);
    }
    break;
    case "refresh":
    self._reset();
    break;
    default:
    // model has changed, maybe it doesn't belong in this subset anymore
    if(ev.indexOf("change:") === 0) {

    // sub collection already has object so it could be removed
    if(self.getByCid(arguments[1])) {
    // maybe trigger remove
    if(!self.filter(arguments[1])) {
    self._reset();
    self.trigger('remove', arguments[1], self);
    }
    else
    {
    // still in the set, forward event to this subset
    self._reset();
    self.trigger.apply(self, arguments);
    }
    }

    // we got a new element, yay!
    if(!self.getByCid(arguments[1]) && self.filter(arguments[1])) {
    self._reset();
    self.trigger('add', arguments[1], self);
    }
    }

    }

    });

    // remove crucial entries from options
    delete options.filter
    delete options.superset;

    // get an event if a model changes
    this._boundOnModelEvent = _.bind(this._onModelEvent, this);

    // refresh the models
    this._reset();

    // call custom constructor
    this.initialize(options);
    };




    _.extend(Backbone.Subset.prototype, Backbone.Collection.prototype, {

    // array holding the models as json objects
    toJSON: function() {
    return this.map(function(c) {
    return c.toJSON();
    })
    },

    // add models
    add: function(models, options) {

    var self = this;

    models = _.filter(models, this.filter);

    // return if no models resist
    if(models.length == 0) { return; }

    // actually add the models to the superset
    this.superset.add(models, options);
    return this;
    },

    // remove models
    remove: function(models, options) {
    // remove model from superset
    this.superset.remove(_.filter(_.filter(models, function(cm) {
    return m != null;
    }), this.filter), options);
    },

    // get a certain model by id!
    get: function(model_id) {
    return _.select(this.models, function(cm) {
    return cm.id == model_id;
    })[0]
    },

    // get a certain model by cid !
    getByCid: function(model_cid) {
    return _.select(this.models, function(cm) {
    return cm.cid == (model_cid.cid || model_cid);
    })[0]
    },

    // get a model at a certain position in the _subset_
    at: function(index) {
    return this.models[index]
    },

    // sorting
    sort: function(options) {
    this.superset.sort(options);
    return this;
    },

    // pluck an attribute from each model in the subset
    pluck: function(attr) {
    return _.map(this.models, function(model) {
    return model.get(m)
    })
    },

    // refresh the superset (triggers event to refresh this one too)
    refresh: function(models, options) {
    this.superset.refresh(models, options);
    return this;
    },

    fetch: function(options) {
    this.superset.fetch(options);
    return this;
    },

    create: function(model, options) {
    return this.superset.create(model, options);
    },

    parse: function(resp) {
    return resp;
    },

    length: function() {
    this._reset();
    return this.models.length;
    },

    chain: function() {
    return this.superset.chain();
    },

    // reset state and refresh the models
    _reset: function() {
    this.model = this.options.model || this.superset.model;
    this.models = this._models();
    },

    // get the models which belong to this collection
    _models: function() {
    // using internal filter method to filter the models that belong to this subset
    return _.filter(_.filter(this.transform(this.superset.models), function(cm) {
    return cm != null;
    }), this.filter);
    }

    });


    var subsetMethods = ["forEach", "each", "map", "reduce", "reduceRight", "find", "detect", "filter", "select", "reject", "every", "all", "some", "any", "include", "invoke", "max", "min", "sortBy", "sortedIndex", "toArray", "size", "first", "rest", "last", "without", "indexOf", "lastIndexOf", "isEmpty"];

    // add common function to this subset

    _.each(subsetMethods, function(cMethod) {
    Backbone.Subset.prototype[cMethod] = function() {
    return _[cMethod].apply(_, [this._models()].concat(_.toArray(arguments)))
    };
    });