carcass-couch
Version:
(Node.js) A simple wrap of cradle plus some stream APIs, in Carcass style.
593 lines (516 loc) • 14.1 kB
JavaScript
var CouchDB, Promise, carcass, config, debug, highland, httpError, through2, util, _,
__slice = [].slice;
debug = require('debug')('carcass:couch:db');
_ = require('lodash');
util = require('util');
Promise = require('bluebird');
through2 = require('through2');
carcass = require('carcass');
config = require('carcass-config');
httpError = carcass.httpError;
highland = carcass.highland;
/**
* Represents a CouchDB database.
*/
module.exports = CouchDB = (function() {
/**
* Constructor.
*/
function CouchDB(options) {
this.id(options);
this.id(this.id().toLowerCase().replace(/^[^a-z]+/, ''));
debug('initializing the %s db.', this.id());
}
/**
* Cache of the couch instance.
*
* @type {Function}
*/
CouchDB.prototype.couch = carcass.helpers.accessor('_couch');
/**
* Cache of the db instance.
*
* @type {Function}
*/
CouchDB.prototype.db = carcass.helpers.accessor('_db');
/**
* Declare.
*
* @public
*/
CouchDB.prototype.declare = function(done) {
var fulfilled, promise, _ref;
if (done == null) {
done = function() {};
}
fulfilled = function(db) {
return done(null, db);
};
promise = this.db();
if (promise != null) {
promise.then(fulfilled, done);
return this;
}
config = (_ref = this.config()) != null ? _ref : {};
debug('getting db %s from CouchDB', this.id(), config);
promise = new Promise((function(_this) {
return function(resolve, reject) {
return _this.couch().connect(function(err, conn) {
var db;
if (err) {
return reject(err);
}
db = conn.database(_this.id());
return db.exists(function(err, exists) {
if (err) {
return reject(err);
}
if (exists) {
return resolve(db);
}
return db.create(function(err) {
if (err) {
return reject(err);
}
return resolve(db);
});
});
});
};
})(this));
this.db(promise);
promise.then(fulfilled, done);
return this;
};
/**
* Destroy.
*
* @public
*/
CouchDB.prototype.destroy = function(done) {
var promise;
if (done == null) {
done = function() {};
}
promise = this.db();
if (promise == null) {
done();
return this;
}
promise.then(function(db) {
return db.destroy(done);
}, done);
this.db(null);
return this;
};
/**
* Save design documents.
*/
CouchDB.prototype.saveDesignDocs = function(done) {
var _ref;
if (done == null) {
done = function() {};
}
config = (_ref = this.config()) != null ? _ref : {};
this.declare(function(err, db) {
if (err) {
return done(err);
}
if (config.design == null) {
return done();
}
return highland(_.pairs(config.design)).flatMap(function(pair) {
return highland.wrapInvoke('save', '_design/' + pair[0], pair[1])(db);
}).on('error', function(err) {
return done(err);
}).toArray(function(res) {
return done(null, res);
});
});
return this;
};
/**
* Route read to get.
*
* @public
*/
CouchDB.prototype.read = function() {
var args, done, _i;
args = 2 <= arguments.length ? __slice.call(arguments, 0, _i = arguments.length - 1) : (_i = 0, []), done = arguments[_i++];
if (done == null) {
done = function() {};
}
return this.declare(function(err, db) {
if (err) {
return done(err);
} else {
return db.get.apply(db, __slice.call(args).concat([done]));
}
});
};
/**
* Route save.
*
* @public
*/
CouchDB.prototype.save = function() {
var args, done, _i;
args = 2 <= arguments.length ? __slice.call(arguments, 0, _i = arguments.length - 1) : (_i = 0, []), done = arguments[_i++];
if (done == null) {
done = function() {};
}
return this.declare(function(err, db) {
if (err) {
return done(err);
} else {
return db.save.apply(db, __slice.call(args).concat([done]));
}
});
};
/**
* Route remove.
*
* @public
*/
CouchDB.prototype.remove = function() {
var args, done, _i;
args = 2 <= arguments.length ? __slice.call(arguments, 0, _i = arguments.length - 1) : (_i = 0, []), done = arguments[_i++];
if (done == null) {
done = function() {};
}
return this.declare(function(err, db) {
if (err) {
return done(err);
} else {
return db.remove.apply(db, __slice.call(args).concat([done]));
}
});
};
/**
* Save and read; just a shortcut.
*
* @public
*/
CouchDB.prototype.saveAndRead = function() {
var args, done, _i;
args = 2 <= arguments.length ? __slice.call(arguments, 0, _i = arguments.length - 1) : (_i = 0, []), done = arguments[_i++];
if (done == null) {
done = function() {};
}
this.save.apply(this, __slice.call(args).concat([(function(_this) {
return function(err, res) {
if (err) {
return done(err);
} else {
return _this.read(res.id, res.rev, done);
}
};
})(this)]));
return this;
};
/**
* Route view.
*
* @public
*/
CouchDB.prototype.view = function() {
var args, done, _i;
args = 2 <= arguments.length ? __slice.call(arguments, 0, _i = arguments.length - 1) : (_i = 0, []), done = arguments[_i++];
if (done == null) {
done = function() {};
}
return this.declare(function(err, db) {
if (err) {
return done(err);
} else {
return db.view.apply(db, __slice.call(args).concat([done]));
}
});
};
/**
* Route all.
*
* @public
*/
CouchDB.prototype.all = function() {
var args, done, _i;
args = 2 <= arguments.length ? __slice.call(arguments, 0, _i = arguments.length - 1) : (_i = 0, []), done = arguments[_i++];
if (done == null) {
done = function() {};
}
return this.declare(function(err, db) {
if (err) {
return done(err);
} else {
return db.all.apply(db, __slice.call(args).concat([done]));
}
});
};
/**
* Stream APIs.
*/
/**
* Read data through a stream, where you pipe in an id or an object and pipe
* out a result or an error.
*
* @return {Transform} a transform stream
*
* @public
*/
CouchDB.prototype.streamRead = function() {
var self;
self = this;
return through2.obj(function(chunk, enc, done) {
var id, rev, _ref, _ref1, _ref2, _ref3;
if (_.isString(chunk)) {
self.read(chunk, (function(_this) {
return function(err, doc) {
if (err) {
return done(self.httpError(err));
}
_this.push(doc);
return done();
};
})(this));
return;
}
id = (_ref = (_ref1 = chunk._id) != null ? _ref1 : chunk.id) != null ? _ref : null;
rev = (_ref2 = (_ref3 = chunk._rev) != null ? _ref3 : chunk.rev) != null ? _ref2 : null;
if (id == null) {
return done(httpError('id is required'));
}
return self.read(id, rev, (function(_this) {
return function(err, doc) {
if (err) {
return done(self.httpError(err));
}
_this.push(doc);
return done();
};
})(this));
});
};
/**
* Save data through a stream, where you pipe in data and pipe out the
* results.
*
* @return {Transform} a transform stream
*
* @public
*/
CouchDB.prototype.streamSave = function() {
var self;
self = this;
return through2.obj(function(chunk, enc, done) {
var id, rev, _ref, _ref1, _ref2, _ref3;
id = (_ref = (_ref1 = chunk._id) != null ? _ref1 : chunk.id) != null ? _ref : null;
rev = (_ref2 = (_ref3 = chunk._rev) != null ? _ref3 : chunk.rev) != null ? _ref2 : null;
if ((id != null) && (rev != null)) {
return self.save(id, rev, chunk, (function(_this) {
return function(err, res) {
if (err) {
return done(self.httpError(err));
}
_this.push(res);
return done();
};
})(this));
} else if (id != null) {
return self.save(id, chunk, (function(_this) {
return function(err, res) {
if (err) {
return done(self.httpError(err));
}
_this.push(res);
return done();
};
})(this));
} else {
return self.save(chunk, (function(_this) {
return function(err, res) {
if (err) {
return done(self.httpError(err));
}
_this.push(res);
return done();
};
})(this));
}
});
};
/**
* Save data through a stream, ...
*
* @return {Transform} a transform stream
*
* @public
*/
CouchDB.prototype.streamSaveAndRead = function() {
var self;
self = this;
return through2.obj(function(chunk, enc, done) {
var id, rev, _ref, _ref1, _ref2, _ref3;
id = (_ref = (_ref1 = chunk._id) != null ? _ref1 : chunk.id) != null ? _ref : null;
rev = (_ref2 = (_ref3 = chunk._rev) != null ? _ref3 : chunk.rev) != null ? _ref2 : null;
if ((id != null) && (rev != null)) {
return self.saveAndRead(id, rev, chunk, (function(_this) {
return function(err, doc) {
if (err) {
return done(self.httpError(err));
}
_this.push(doc);
return done();
};
})(this));
} else if (id != null) {
return self.saveAndRead(id, chunk, (function(_this) {
return function(err, doc) {
if (err) {
return done(self.httpError(err));
}
_this.push(doc);
return done();
};
})(this));
} else {
return self.saveAndRead(chunk, (function(_this) {
return function(err, doc) {
if (err) {
return done(self.httpError(err));
}
_this.push(doc);
return done();
};
})(this));
}
});
};
/**
* Remove data through a stream, where you pipe in an id or an object and pipe
* out a result or an error.
*
* @return {Transform} a transform stream
*
* @public
*/
CouchDB.prototype.streamRemove = function() {
var self;
self = this;
return through2.obj(function(chunk, enc, done) {
var id, rev, _ref, _ref1, _ref2, _ref3;
if (_.isString(chunk)) {
self.remove(chunk, (function(_this) {
return function(err, res) {
if (err) {
return done(self.httpError(err));
}
_this.push(res);
return done();
};
})(this));
return;
}
id = (_ref = (_ref1 = chunk._id) != null ? _ref1 : chunk.id) != null ? _ref : null;
rev = (_ref2 = (_ref3 = chunk._rev) != null ? _ref3 : chunk.rev) != null ? _ref2 : null;
if (id == null) {
return done(httpError('id is required'));
}
return self.remove(id, rev, (function(_this) {
return function(err, res) {
if (err) {
return done(self.httpError(err));
}
_this.push(res);
return done();
};
})(this));
});
};
/**
* Read a view through a stream, where you pipe in a key or an object and
* pipe out the results or an error.
*
* @param {String} view the view name e.g. 'myDesign/myView'
*
* @return {Transform} a transform stream
*
* @public
*/
CouchDB.prototype.streamView = function(view) {
var self;
self = this;
return through2.obj(function(chunk, enc, done) {
if (!_.isObject(chunk)) {
chunk = chunk != null ? {
key: chunk
} : {};
}
return self.view(view, chunk, (function(_this) {
return function(err, docs) {
var doc, _i, _len;
if (err) {
return done(self.httpError(err));
}
for (_i = 0, _len = docs.length; _i < _len; _i++) {
doc = docs[_i];
_this.push(doc);
}
return done();
};
})(this));
});
};
/**
* Read all docs through a stream, where you pipe in the options (can be
* empty) and pipe out the results or an error.
*
* @return {Transform} a transform stream
*
* @public
*/
CouchDB.prototype.streamAll = function() {
var self;
self = this;
return through2.obj(function(chunk, enc, done) {
if (!_.isObject(chunk)) {
chunk = {};
}
return self.all(chunk, (function(_this) {
return function(err, docs) {
var doc, _i, _len;
if (err) {
return done(self.httpError(err));
}
for (_i = 0, _len = docs.length; _i < _len; _i++) {
doc = docs[_i];
_this.push(doc);
}
return done();
};
})(this));
});
};
/**
* Helper.
*
* Build an HTTP Error from a Cradle Error (not necessarily a real error).
*/
CouchDB.prototype.httpError = function(res) {
var code, err, message;
if (res == null) {
return httpError();
}
code = (res.headers != null) && (res.headers.status != null) ? res.headers.status : 500;
message = util.isError(res) ? res : (res.error != null) && (res.reason != null) ? res.error + ' (' + res.reason + ')' : res.error != null ? res.error : null;
err = httpError(code, message);
Error.captureStackTrace(err, this.httpError);
return err;
};
return CouchDB;
})();
/**
* Mixins.
*/
carcass.mixable(CouchDB);
CouchDB.prototype.mixin(carcass.proto.uid);
CouchDB.prototype.mixin(config.proto.consumer);