bot18
Version:
A high-frequency cryptocurrency trading bot by Zenbot creator @carlos8f
246 lines (231 loc) • 7.05 kB
JavaScript
var path = require('path')
, fs = require('fs')
, EventEmitter = require('events').EventEmitter
, glob = require('glob')
, LRU = require('lru-cache')
, inherits = require('util').inherits
function Dollop (globs, options) {
EventEmitter.call(this);
this.setMaxListeners(0);
var self = this;
Object.keys(Dollop.prototype).forEach(function (method) {
self[method] = Dollop.prototype[method].bind(self);
});
this.closed = false;
this.isDollop = true;
this.setParams(globs, options);
this.reset();
setImmediate(this.scan);
}
inherits(Dollop, EventEmitter);
Dollop.prototype.setParams = function (globs, options) {
var self = this;
this.options || (this.options = {});
Object.keys(options || {}).forEach(function (k) {
self.options[k] = options[k];
});
var cwd = this.options.cwd || this.cwd;
if (!globs) globs = '.';
// if "globs" is a dir/file, detect options from fs
if (!cwd) {
try {
var stat = fs.statSync(globs);
if (stat.isFile()) cwd = path.dirname(globs)
else if (stat.isDirectory()) cwd = globs;
globs = [cwd, cwd + '/**/*'];
}
catch (e) {}
}
this.cwd = path.resolve(cwd || process.cwd());
if (!Array.isArray(globs)) globs = globs.replace(/^\{|\}$/g, '').split(',');
this.globs = globs;
var absGlobs = [], absParents = [this.cwd];
globs.forEach(function (g) {
var p = path.resolve(self.cwd, g);
absGlobs.push(p);
// find the part of the path preceding a *, if present
var baseMatch = p.match(/([^\*]*)\/\*/);
if (baseMatch) absParents.push(baseMatch[1]);
});
this.stripRegex = new RegExp('^(' + absParents.sort(function (a, b) {
var aMatch = a.match(/\//g);
var bMatch = b.match(/\//g);
if (aMatch && !bMatch) return -1;
if (!aMatch && bMatch) return 1;
if (aMatch && bMatch && aMatch.length !== bMatch.length) {
return aMatch.length > bMatch.length ? -1 : 1;
}
return 0;
}).join('|') + ')');
this.pattern = absGlobs.length < 2 ? absGlobs[0] : '{' + absGlobs.join(',') + '}';
this.watch = typeof this.options.watch !== 'undefined' ? this.options.watch : true;
this.dirWatchers = {};
if (this.watch) {
this.dirWatchers[this.cwd] = this.createWatcher(this.cwd);
}
};
Dollop.prototype.onErr = function (err) {
if (err.code !== 'ENOENT') this.emit('error', err);
};
Dollop.prototype.createWatcher = function (p) {
var self = this;
try {
return fs.watch(p, {persistent: this.options.persistent})
.on('change', function () {
self.scan();
})
.on('error', this.onErr);
}
catch (err) {
this.onErr(err);
}
};
Dollop.prototype.scan = function () {
if (this.closed) return false;
if (this.scanning) return this.once('scan', this.scan);
this.latch = 1;
this.scanning = true;
var self = this;
glob(this.pattern, {stat: true, cwd: this.cwd, dot: this.options.dot})
.on('error', this.onErr)
.on('stat', this.onStat)
.on('end', function () {
if (!--self.latch) self.onEnd();
});
};
Dollop.prototype.onStat = function (p, stat) {
var self = this;
var key = path.resolve(this.cwd, p).replace(this.stripRegex, '');
if (~this.keys.indexOf(key)) return;
this.keys.push(key);
var file = this.get(key);
var cached = !!file;
if (!cached) {
file = {
path: path.resolve(this.cwd, p).replace(this.cwd + '/', ''),
cwd: this.cwd,
fullPath: path.resolve(this.cwd, p),
stat: stat,
name: path.basename(p),
key: key,
read: fsMethod('readFile'),
readSync: fsMethod('readFileSync'),
readStream: fsMethod('createReadStream'),
write: fsMethod('writeFile'),
writeSync: fsMethod('writeFileSync'),
writeStream: fsMethod('createWriteStream'),
data: function (buf, options) {
if (buf && toString.call(buf) === '[object Object]') {
options = buf;
buf = null;
}
options || (options = {});
if (buf) return this.writeSync(buf, options);
if (!this._data || options.nocache) this._data = this.readSync();
if (options && options.encoding) return this._data.toString(options.encoding);
return this._data;
}
};
function fsMethod (method) {
return function () {
var args = [].slice.call(arguments);
args.unshift(file.fullPath);
return fs[method].apply(fs, args);
};
}
file.parentDir = path.dirname(file.path);
file.fullParentDir = path.dirname(file.fullPath);
}
var op = 'noop';
if (!cached || file.deleted) {
op = 'add';
}
else if (file.stat.isFile() && file.stat.mtime.getTime() !== stat.mtime.getTime()) {
op = 'update';
}
if (op !== 'noop') {
file.stat = stat;
delete file._data;
if (this.watch && !file.watcher || file.deleted) {
file.watcher = this.createWatcher(file.fullPath);
if (typeof this.dirWatchers[file.fullParentDir] === 'undefined') {
this.dirWatchers[file.fullParentDir] = this.createWatcher(file.fullParentDir);
}
}
this.set(key, file);
if (file.fullPath !== this.cwd) {
this.emit(op, file);
this.emit('all', op, file);
}
}
};
Dollop.prototype.onEnd = function () {
var self = this;
var files = this.files(true);
this.scanning = false;
this.keys = [];
this.emit('scan', files);
this.emit('all', 'scan', files);
if (!this.ready) {
this.ready = true;
this.emit('ready', files);
this.emit('all', 'ready', files);
}
};
Dollop.prototype.files = function (doCleanup) {
var self = this;
return this.cache.values().filter(function (file) {
if (file.deleted) return false;
if (doCleanup && !~self.keys.indexOf(file.key)) {
if (file.watcher) file.watcher.close();
file.deleted = true;
self.set(file.key, file);
if (file.fullPath !== self.cwd) {
self.emit('cleanup', file);
if (self.ready) {
self.emit('remove', file);
self.emit('all', 'remove', file);
}
else self.emit('all', 'cleanup', file);
}
}
return file.fullPath !== self.cwd;
});
};
Dollop.prototype.get = function (key) {
var file = this.cache.get(key);
if (file && file.deleted) return undefined;
return file;
};
Dollop.prototype.set = function (key, file) {
return this.cache.set(key, file);
};
Dollop.prototype.del = function (key) {
return this.cache.del(key);
};
Dollop.prototype.reset = function () {
this.keys = [];
var self = this;
if (this.cache) {
this.files(true);
Object.keys(this.dirWatchers).forEach(function (p) {
self.dirWatchers[p].close();
});
}
this.scanning = false;
this.ready = false;
this.cache = LRU(this.options.cache || {});
this.emit('reset');
this.emit('all', 'reset');
};
Dollop.prototype.close = function () {
this.closed = true;
this.reset();
};
module.exports = function (globs, options) {
return new Dollop(globs, options);
};
module.exports.Dollop = Dollop;
module.exports.isDollop = function (o) {
return !!o.isDollop;
};