Skip to content

Commit

Permalink
Implemented
Browse files Browse the repository at this point in the history
  • Loading branch information
kazupon committed Nov 24, 2013
1 parent a894874 commit 62a9636
Show file tree
Hide file tree
Showing 7 changed files with 675 additions and 4 deletions.
7 changes: 7 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.git
.gitignore
.travis.yml
.DS_Store
lib-cov
node_modules
npm-debug.log
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
language: node_js
node_js:
- "0.10"
services:
- mongodb
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ test-coveralls: lib-cov
@rm -rf ./lib-cov

lib-cov:
@./node_modules/.bin/jscoverage ./index.js ./lib-cov/index.js
@./node_modules/.bin/jscoverage ./lib ./lib-cov

clean:
@rm ./coverage.html
@rm -f ./coverage.html

.PHONY: test test-cov test-coveralls lib-cov clean
170 changes: 170 additions & 0 deletions lib/fetcher.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
/**
* Module(s)
*/

var debug = require('debug')('fendjs-model-mongo:fetcher');
var Collection = require('fendjs-collection');
var async = require('async');
var noop = function () {};

/**
* Export(s)
*/
exports.Fetcher = Fetcher;


/**
* MongoDB Fetcher
*/

function Fetcher (db) {
this._db = db;
}

Fetcher.prototype.save = function (model, fn) {
fn = fn || noop;

if (!model.isValid()) {
return fn(new Error('validation failed'));
}

model.Model.emit('saving', model);
model.emit('saving');

// TODO: should check db opened
var db = this._db;

// TODO: should set options
var options = {};

async.waterfall([
function (next) { db.collection(model.Model._base, next); },
function (collection, next) { collection.save(model.toJSON(), options, next); }
], function (err, result) {
debug('save', err, result);
if (err) return fn(err);
model.primary(result._id);
model.dirty = {};
model.Model.emit('save', model, result);
model.emit('save', result);
fn(null, result);
});
};

Fetcher.prototype.update = function (model, fn) {
fn = fn || noop;

if (!model.isValid()) {
return fn(new Error('validation failed'));
}

model.Model.emit('saving', model);
model.emit('saving');

// TODO: should check db opened
var db = this._db;

// TODO: should set options
var options = { new: true };

async.waterfall([
function (next) { db.collection(model.Model._base, next); },
function (collection, next) {
collection.findAndModify({
_id: model.primary()
}, [], { $set: model.dirty }, options, next);
}
], function (err, result) {
debug('update', err, result);
if (err) return fn(err);
model.dirty = {};
model.Model.emit('save', model, result);
model.emit('save');
fn(null, result);
});
};

Fetcher.prototype.destroy = function (model, fn) {
model.Model.emit('destroying', model);
model.emit('destroying');

// TODO: should check db opened
var db = this._db;

// TODO: should set options
var options = { new: true };

async.waterfall([
function (next) { db.collection(model.Model._base, next); },
function (collection, next) {
collection.findAndRemove({
_id: model.primary()
}, [], options, next);
}
], function (err, result) {
debug('destroy', err, result);
if (err) return fn(err);
model.destroyed = true;
model.Model.emit('destroy', model, result);
model.emit('destroy', result);
fn(null, result);
});
};

Fetcher.prototype.destroyAll = function (Model, fn) {
// TODO: should check db opened
var db = this._db;

// TODO: should set options
var options = {};

async.waterfall([
function (next) { db.collection(Model._base, next); },
function (collection, next) { collection.remove({}, options, next); }
], function (err, result) {
debug('destroyAll', err, result);
if (err) return fn(err, result);
fn(null, result);
});
};

Fetcher.prototype.all = function (Model, fn) {
// TODO: should check db opened
var db = this._db;

// TODO: should set options
var options = {};

async.waterfall([
function (next) { db.collection(Model._base, next); },
function (collection, next) { collection.find({}, options, next); }
], function (err, result) {
debug('all', err, result);
if (err) return fn(err, result);
result.toArray(function (err, recs) {
var col = new Collection;
recs.forEach(function (rec) {
col.push(new Model(rec));
});
fn(null, col, recs);
});
});
};

Fetcher.prototype.get = function (Model, id, fn) {
// TODO: should check db opened
var db = this._db;

// TODO: should set options
var options = {};

async.waterfall([
function (next) { db.collection(Model._base, next); },
function (collection, next) { collection.findOne({ _id: id }, options, next); }
], function (err, result) {
debug('get', err, result);
if (err) return fn(err, null, result);
if (!result) return fn(new Error('not found'), null, result);
fn(null, new Model(result), result);
});
};
93 changes: 93 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/**
* Module(s)
*/

var Fetcher = require('./fetcher').Fetcher;
var Db = require('mongodb').Db;
var Server = require('mongodb').Server;

/**
* Export(s)
*/

module.exports = Mongorable;


var db;

/**
* Extend mongorable model plugin constructor.
*
* @return {Function}
* @api public
*/
function Mongorable () {
return function (Model) {
// static
Model._base = Model.modelName.toLowerCase() + 's';
Model.fetcher = new Fetcher(db);
};
}

/**
* Bind connection
*
* @return {Object}
* @api private
*/
function bindConnection () { return db; }

/**
* Connect mongodb.
*
* @param {Object} options
* @param {Function} fn
* @return {Mongorable}
* @api public
*/
Mongorable.connect = function (options, fn) {
if (db) {
fn(new Error('already connected'));
return this;
}

options = options || {};
options.database = options.database || '';
options.host = options.host || 'localhost';
options.port = options.port || 27017;
options.w = options.w || 0;

db = new Db(
options.database,
new Server(options.host, options.port), {
native_parser: true, safe: { w: options.w }
});
db.open(function (err, db) {
return err ? fn(err) : fn();
});

return this;
};

/**
* Disconnect mongodb.
*
* @param {Function} fn
* @return {Mongorable}
* @api public
*/
Mongorable.disconnect = function (fn) {
// TODO: should think error message !!
if (!db) {
fn(new Error('not connected'));
return this;
}

db.close(function (err) {
if (err) return fn(err);
db = null;
fn();
});

return this;
};
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,14 @@
"database"
],
"dependencies": {
"fendjs-model": "~0.0.7",
"fendjs-collection": "~0.0.3",
"mongodb": "~1.3.19"
"mongodb": "~1.3.19",
"async": "~0.2.9"
},
"devDependencies": {
"debug": "~0.7.4",
"expect.js": "~0.2.0",
"mocha": "~1.14.0",
"mocha-lcov-reporter": "0.0.1",
"jscoverage": "~0.3.8",
Expand Down
Loading

0 comments on commit 62a9636

Please sign in to comment.