Skip to content

Instantly share code, notes, and snippets.

@christophercliff
Created December 16, 2011 00:34
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save christophercliff/1483761 to your computer and use it in GitHub Desktop.
Save christophercliff/1483761 to your computer and use it in GitHub Desktop.
Base Classes for Binding Backbone.js to the CouchDB Documents API

Base Classes for Binding Backbone.js to the CouchDB Documents API

Backbone's default service layer depends on a fairly rigid API design. In order to integrate with CouchDB's slightly irregular Documents API, we can create a set of base Document classes that override key methods to work with CouchDB. By inheriting these Document classes, Models and Collections that correspond directly to CouchDB Documents fit seamlessly into normal Backbone workflow.

This approach is advantageous for several reasons:

  • The default Backbone library remains predictably intact
  • Documents can be bound to specific CouchDB view functions or databases
(function(window, undefined){
window.Document = Backbone.Model.extend({
url: function () {
var self = this,
base = '/' + self.database;
if (self.isNew())
{
return base;
}
return base + (base.charAt(base.length - 1) == '/' ? '' : '/') + encodeURIComponent(self.id);
},
parse: function (obj) {
obj['_rev'] = obj['rev'];
delete obj.rev;
delete obj.ok;
return obj;
},
destroy: function (options) {
var self = this;
options || (options = {});
options.url = self.url() + '/?rev=' + self.get('_rev');
if (this.isNew()) return this.trigger('destroy', this, this.collection, options);
var model = this;
var success = options.success;
options.success = function(resp) {
model.trigger('destroy', model, model.collection, options);
if (success) success(model, resp);
};
options.error = wrapError(options.error, model, options);
return (this.sync || Backbone.sync).call(this, 'delete', this, options);
}
});
window.Documents = Backbone.Collection.extend({
model: Document,
parse: function (response) {
return _.map(response.rows, function(obj){
obj.value['id'] = obj.value['_id'];
delete obj.value._id;
return obj.value;
});
},
fetch: function(options) {
options || (options = {});
options.data || (options.data = {});
_.extend(options.data, {
key: '\"' + this.key + '\"'
});
var collection = this;
var success = options.success;
options.success = function(resp, status, xhr) {
collection[options.add ? 'add' : 'reset'](collection.parse(resp, xhr), options);
if (success) success(collection, resp);
};
options.error = wrapError(options.error, collection, options);
return (this.sync || Backbone.sync).call(this, 'read', this, options);
}
});
var wrapError = function(onError, model, options) {
return function(resp) {
if (onError) {
onError(model, resp, options);
} else {
model.trigger('error', model, resp, options);
}
};
};
})(window);
(function(window, undefined){
window.Apple = Document.extend({
database: 'fruit',
defaults: {
type: 'apple'
}
});
window.Apples = Documents.extend({
model: Apple,
key: 'apple',
url: '/fruit/_design/fruit/_view/by_type'
});
var apples = new Apples();
apples.fetch({
success: function () {
var apple = apples.create({}, {
success: function () {
apple.save({}, {
success: function () {
apple.destroy();
}
});
}
});
}
});
})(window);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment