castor-load
Version:
Traverse a directory to build a MongoDB collection with the found files. Then it's enable to keep directory and collection synchronised.
306 lines (277 loc) • 8.74 kB
JavaScript
/*jshint node:true,laxcomma:true*/
;
var path = require('path')
, basename = path.basename(__filename, '.js')
, debug = require('debug')('castor:load:' + basename)
, assert = require('assert')
, util = require('util')
, fs = require('fs')
, events = require('events')
, crypto = require('crypto')
, url = require('url')
, minimatch = require('minimatch')
, mkdirp = require('mkdirp')
, chokidar = require('chokidar')
, findit = require('findit')
, Mount = require('./mount.js')
, Sync = require('./sync.js')
, File = require('./file.js')
, readline = require('readline')
;
function Filerake(dir, options) {
assert.equal(typeof dir, 'string');
assert.ok(fs.existsSync(dir), 'Directory should exist to use Filerake');
options = options || {};
if (!(this instanceof Filerake)) {
return new Filerake(dir, options);
}
events.EventEmitter.call(this);
var self = this;
self.directory = path.normalize(dir);
self.options = {};
self.options.watch = options.hasOwnProperty('watch') ? options.watch : true;
self.options.strictCompare = options.strictCompare || false;
self.options.ignore = options.ignore || [];
self.options.include = options.include;
self.options.collectionName = options.collectionName || crypto.createHash('sha1').update(self.directory).digest('hex');
self.options.connexionURI = options.connexionURI || process.env.MONGO_URL;
self.options.concurrency = options.concurrency || 1;
self.options.delay = options.delay || undefined;
self.options.modifier = options.modifier || undefined;
self.options.writeConcern = options.writeConcern || undefined;
self.options.maxFileSize = options.maxFileSize || undefined;
self.options.directory = self.directory; // for tingodb
self.options.dateConfig = options.dateConfig || null;
if (!Array.isArray(self.options.ignore)) {
self.options.ignore = [];
}
if (!Array.isArray(self.options.include)) {
self.options.include = ['file'];
}
self.basedoc = {
dateConfig : self.options.dateConfig,
dateLoaded : new Date(),
basedir: self.directory
};
self.mount = new Mount(self.options);
self.syncr = new Sync(self.mount, self.options);
function onSaved(doc) {
self.emit('saved',doc);
}
self.syncr.on('saved', onSaved);
self.mount.on('loadError', function (err, location, number) {
self.emit('loadError', err, location, number);
});
}
util.inherits(Filerake, events.EventEmitter);
Filerake.prototype.use = function (pattern, fn)
{
var self = this;
self.mount.use(pattern, fn);
};
Filerake.prototype.ignore = function (file) {
var ignored = this.options.ignore.some(function (check) {
if (check instanceof RegExp) {
return check.test(file);
} else {
return minimatch(file, check, { matchBase: true });
}
});
if (ignored) { debug('ignore', file); }
return ignored;
};
Filerake.prototype.include = function (type) {
var self = this;
return self.options.include.indexOf(type) === -1 ? false : true;
};
Filerake.prototype.watch = function (callback) {
var self = this;
var watcher = chokidar.watch(self.directory, {
ignored: function(s) { return self.ignore(s); }
, ignoreInitial : true
, persistent: true
}
);
debug('Watcher is started');
watcher
.on('ready', function () {
if (typeof callback === 'function') { callback(); }
})
.on('add', function(path, stats) {
var f = new File(path, stats, self.basedoc, self.options.modifier);
if (self.ignore(path)) {
self.emit('preCancel', f.get());
return self.syncr.cancel(f, function(err) {
debug('cancelled - watch(add)', f.doc.filename);
self.emit('cancelled', err, f.get());
});
}
self.emit('preCheck', f.get());
self.syncr.check(f, function(err) {
debug('checked - watch(add)', f.doc.filename);
self.emit('added', err, f.get());
});
})
.on('change', function(path, stats) {
var f = new File(path, stats, self.basedoc, self.options.modifier);
if (self.ignore(path)) {
self.emit('preCancel', f.get());
return self.syncr.cancel(f, function(err) {
debug('cancelled - watch(change)', f.doc.filename);
self.emit('cancelled', err, f.get());
});
}
self.emit('preCheck', f.get());
self.syncr.check(f, function(err) {
debug('checked - watch(change)', f.doc.filename);
self.emit('changed', err, f.get());
});
})
.on('unlink', function(path) {
var f = new File(path, undefined, self.basedoc, self.options.modifier);
self.emit('preDrop', f.get());
self.syncr.drop(f, function(err) {
debug('droped - watch(unlink)', f.doc.filename);
self.emit('dropped', err, f.get());
});
})
.on('error', function(error) {
debug('Error happened', error);
});
};
Filerake.prototype.sync = function (done)
{
var self = this, i = 0, o = 0, browsing = true;
self.syncr.refresh();
function conclude(o) {
if (browsing) { return; }
self.syncr.clean(function (err) {
debug('clean', err);
if (typeof done === 'function') { done(o); }
process.nextTick(function() {
if (self.options.watch) {
self.watch(function () { self.emit('watching'); });
}
});
});
}
var findr = findit(self.directory);
findr.on('end', function () {
browsing = false;
self.emit('browseOver', i);
if (i === o) { conclude(o); }
});
findr.on('directory', function (dir, stats, stop) {
var f = new File(dir, stats, self.basedoc, self.options.modifier);
if (self.ignore(dir)) {
stop();
i++;
self.emit('preCancel', f.get());
self.syncr.cancel(f, function(err) {
o++;
debug('cancelled - sync(directory)', f.doc.filename);
self.emit('cancelled', err, f.get());
if (i === o) {
conclude(o);
}
});
}
else {
if (self.include('directory')) {
i++;
self.emit('preCheck', f.get());
self.syncr.check(f, function(err) {
o++;
debug('checked - sync(directory)', f.doc.filename);
self.emit('checked', err, f.get());
if (i === o) {
conclude(o);
}
});
}
}
}
);
findr.on('file', function (file, stats) {
var f = new File(file, stats, self.basedoc, self.options.modifier);
if (self.ignore(file)) {
i++;
self.emit('preCancel', f.get());
self.syncr.cancel(f, function(err) {
o++;
debug('cancelled - sync(file)', f.doc.filename);
self.emit('cancelled', err, f.get());
if (i === o) {
conclude(o);
}
});
}
else {
if (self.include('file')) {
i++;
self.emit('preCheck', f.get());
self.syncr.check(f, function(err) {
o++;
debug('checked - sync(file)', f.doc.filename);
self.emit('checked', err, f.get());
if (i === o) {
conclude(o);
}
});
}
}
}
);
};
Filerake.prototype.push = function (file, stats, basedoc, modifier)
{
var self = this;
var f = new File(file, stats, basedoc || self.basedoc, modifier || self.options.modifier);
if (self.ignore(file)) {
self.emit('preCancel', f.get());
self.syncr.cancel(f, function(err) {
debug('cancelled - push(file)', f.doc.filename);
self.emit('cancelled', err, f.get());
});
}
else {
if (self.include('file')) {
self.emit('preCheck', f.get());
self.syncr.check(f, function(err) {
debug('checked - push(file)', f.doc.filename);
self.emit('checked', err, f.get());
});
}
}
};
Filerake.prototype.submit = function (file, done)
{
var ret = false;
var self = this;
var f = new File(file, {}, self.basedoc, self.options.modifier);
if (self.ignore(file)) {
self.emit('preCancel', f.get());
self.syncr.cancel(f, function(err) {
debug('cancelled - submit(file)', f.doc.filename);
self.emit('cancelled', err, f.get());
if (typeof done === 'function') {
done(new Error('Cancelled'), f.get());
}
});
}
else {
if (self.include('file')) {
self.emit('preCheck', f.get());
self.syncr.check(f, function(err) {
debug('checked - submit(file)', f.doc.filename);
self.emit('checked', err, f.get());
if (typeof done === 'function') {
done(null, f.get());
}
});
ret = true;
}
}
return ret;
};
module.exports = Filerake;