Current File : //usr/lib/node_modules/bower/packages/bower-registry-client/lib/util/Cache.js |
var fs = require('graceful-fs');
var path = require('path');
var async = require('async');
var mkdirp = require('mkdirp');
var LRU = require('lru-cache');
var md5 = require('./md5');
function Cache(dir, options) {
options = options || {};
this._dir = dir;
this._options = options;
this._cache = this.constructor._cache.get(this._dir);
if (!this._cache) {
this._cache = new LRU(options);
this.constructor._cache.set(this._dir, this._cache);
}
if (dir) {
mkdirp.sync(dir);
}
}
Cache.prototype.get = function(key, callback) {
var file;
var json = this._cache.get(key);
// Check in memory
if (json) {
if (this._hasExpired(json)) {
this.del(key, callback);
} else {
callback(null, json.value);
}
return;
}
// Check in disk
if (!this._dir) {
return callback(null);
}
file = this._getFile(key);
fs.readFile(
file,
function(err, contents) {
var json;
// Check if there was an error reading
// Note that if the file does not exist then
// we don't have its value
if (err) {
return callback(err.code === 'ENOENT' ? null : err);
}
// If there was an error reading the file as json
// simply assume it doesn't exist
try {
json = JSON.parse(contents.toString());
} catch (e) {
return this.del(key, callback); // If so, delete it
}
// Check if it has expired
if (this._hasExpired(json)) {
return this.del(key, callback);
}
this._cache.set(key, json);
callback(null, json.value);
}.bind(this)
);
};
Cache.prototype.set = function(key, value, maxAge, callback) {
var file;
var entry;
var str;
maxAge = maxAge != null ? maxAge : this._options.maxAge;
entry = {
expires: maxAge ? Date.now() + maxAge : null,
value: value
};
// Store in memory
this._cache.set(key, entry);
// Store in disk
if (!this._dir) {
return callback(null);
}
// If there was an error generating the json
// then there's some cyclic reference or some other issue
try {
str = JSON.stringify(entry);
} catch (e) {
return callback(e);
}
file = this._getFile(key);
fs.writeFile(file, str, callback);
};
Cache.prototype.del = function(key, callback) {
// Delete from memory
this._cache.del(key);
// Delete from disk
if (!this._dir) {
return callback(null);
}
fs.unlink(this._getFile(key), function(err) {
if (err && err.code !== 'ENOENT') {
return callback(err);
}
callback();
});
};
Cache.prototype.clear = function(callback) {
var dir = this._dir;
// Clear in memory cache
this._cache.reset();
// Clear everything from the disk
if (!dir) {
return callback(null);
}
fs.readdir(dir, function(err, files) {
if (err) {
return callback(err);
}
// Delete every file in parallel
async.forEach(
files,
function(file, next) {
fs.unlink(path.join(dir, file), function(err) {
if (err && err.code !== 'ENOENT') {
return next(err);
}
next();
});
},
callback
);
});
};
Cache.prototype.reset = function() {
this._cache.reset();
};
Cache.clearRuntimeCache = function() {
// Note that _cache refers to the static _cache variable
// that holds other caches per dir!
// Do not confuse it with the instance cache
// Clear cache of each directory
this._cache.forEach(function(cache) {
cache.reset();
});
// Clear root cache
this._cache.reset();
};
//-------------------------------
Cache.prototype._hasExpired = function(json) {
var expires = json.expires;
if (!expires || this._options.useStale) {
return false;
}
// Check if the key has expired
return Date.now() > expires;
};
Cache.prototype._getFile = function(key) {
// Append a truncated md5 to the end of the file to solve case issues
// on case insensitive file systems
// See: https://github.com/bower/bower/issues/859
return path.join(
this._dir,
encodeURIComponent(key) + '_' + md5(key).substr(0, 5)
);
};
Cache._cache = new LRU({
max: 5,
maxAge: 60 * 30 * 1000 // 30 minutes
});
module.exports = Cache;