Skip to content

Instantly share code, notes, and snippets.

@plagi
Created January 10, 2012 11:51
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save plagi/1588629 to your computer and use it in GitHub Desktop.
Save plagi/1588629 to your computer and use it in GitHub Desktop.
Implements an imaginary subset of a Backbone Collection (as superset)
/*
@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)))
};
});
// 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();
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment