UNPKG

13.1 kBJavaScriptView Raw
1/*
2 * node-cache 4.1.0 ( 2016-09-23 )
3 * https://github.com/mpneuried/nodecache
4 *
5 * Released under the MIT license
6 * https://github.com/mpneuried/nodecache/blob/master/LICENSE
7 *
8 * Maintained by M. Peter ( https://github.com/mpneuried )
9*/
10(function() {
11 var EventEmitter, NodeCache, _assignIn, _isArray, _isFunction, _isNumber, _isObject, _isString, _size, _template, clone,
12 bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
13 extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
14 hasProp = {}.hasOwnProperty,
15 slice = [].slice,
16 indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
17
18 _assignIn = require("lodash/assignIn");
19
20 _isArray = require("lodash/isArray");
21
22 _isString = require("lodash/isString");
23
24 _isFunction = require("lodash/isFunction");
25
26 _isNumber = require("lodash/isNumber");
27
28 _isObject = require("lodash/isObject");
29
30 _size = require("lodash/size");
31
32 _template = require("lodash/template");
33
34 clone = require("clone");
35
36 EventEmitter = require('events').EventEmitter;
37
38 module.exports = NodeCache = (function(superClass) {
39 extend(NodeCache, superClass);
40
41 function NodeCache(options) {
42 this.options = options != null ? options : {};
43 this._initErrors = bind(this._initErrors, this);
44 this._error = bind(this._error, this);
45 this._getValLength = bind(this._getValLength, this);
46 this._wrap = bind(this._wrap, this);
47 this._isInvalidKey = bind(this._isInvalidKey, this);
48 this._check = bind(this._check, this);
49 this._checkData = bind(this._checkData, this);
50 this.close = bind(this.close, this);
51 this.flushAll = bind(this.flushAll, this);
52 this.getStats = bind(this.getStats, this);
53 this.keys = bind(this.keys, this);
54 this.getTtl = bind(this.getTtl, this);
55 this.ttl = bind(this.ttl, this);
56 this.del = bind(this.del, this);
57 this.set = bind(this.set, this);
58 this.mget = bind(this.mget, this);
59 this.get = bind(this.get, this);
60 this._initErrors();
61 this.data = {};
62 this.options = _assignIn({
63 forceString: false,
64 objectValueSize: 80,
65 arrayValueSize: 40,
66 stdTTL: 0,
67 checkperiod: 600,
68 useClones: true,
69 errorOnMissing: false
70 }, this.options);
71 this.stats = {
72 hits: 0,
73 misses: 0,
74 keys: 0,
75 ksize: 0,
76 vsize: 0
77 };
78 this.validKeyTypes = ["string", "number"];
79 this._checkData();
80 return;
81 }
82
83 NodeCache.prototype.get = function(key, cb, errorOnMissing) {
84 var _err, _ret, err;
85 if (typeof cb === "boolean" && arguments.length === 2) {
86 errorOnMissing = cb;
87 cb = void 0;
88 }
89 if ((err = this._isInvalidKey(key)) != null) {
90 if (cb != null) {
91 cb(err);
92 return;
93 } else {
94 throw err;
95 }
96 }
97 if ((this.data[key] != null) && this._check(key, this.data[key])) {
98 this.stats.hits++;
99 _ret = this._unwrap(this.data[key]);
100 if (cb != null) {
101 cb(null, _ret);
102 }
103 return _ret;
104 } else {
105 this.stats.misses++;
106 if (this.options.errorOnMissing || errorOnMissing) {
107 _err = this._error("ENOTFOUND", {
108 key: key
109 }, cb);
110 if (_err != null) {
111 throw _err;
112 }
113 return;
114 } else {
115 if (cb != null) {
116 cb(null, void 0);
117 }
118 }
119 return void 0;
120 }
121 };
122
123 NodeCache.prototype.mget = function(keys, cb) {
124 var _err, err, i, key, len, oRet;
125 if (!_isArray(keys)) {
126 _err = this._error("EKEYSTYPE");
127 if (cb != null) {
128 cb(_err);
129 }
130 return _err;
131 }
132 oRet = {};
133 for (i = 0, len = keys.length; i < len; i++) {
134 key = keys[i];
135 if ((err = this._isInvalidKey(key)) != null) {
136 if (cb != null) {
137 cb(err);
138 return;
139 } else {
140 throw err;
141 }
142 }
143 if ((this.data[key] != null) && this._check(key, this.data[key])) {
144 this.stats.hits++;
145 oRet[key] = this._unwrap(this.data[key]);
146 } else {
147 this.stats.misses++;
148 }
149 }
150 if (cb != null) {
151 cb(null, oRet);
152 }
153 return oRet;
154 };
155
156 NodeCache.prototype.set = function(key, value, ttl, cb) {
157 var err, existent;
158 if (this.options.forceString && !_isString(value)) {
159 value = JSON.stringify(value);
160 }
161 if (arguments.length === 3 && _isFunction(ttl)) {
162 cb = ttl;
163 ttl = this.options.stdTTL;
164 }
165 if ((err = this._isInvalidKey(key)) != null) {
166 if (cb != null) {
167 cb(err);
168 return;
169 } else {
170 throw err;
171 }
172 }
173 existent = false;
174 if (this.data[key]) {
175 existent = true;
176 this.stats.vsize -= this._getValLength(this._unwrap(this.data[key], false));
177 }
178 this.data[key] = this._wrap(value, ttl);
179 this.stats.vsize += this._getValLength(value);
180 if (!existent) {
181 this.stats.ksize += this._getKeyLength(key);
182 this.stats.keys++;
183 }
184 this.emit("set", key, value);
185 if (cb != null) {
186 cb(null, true);
187 }
188 return true;
189 };
190
191 NodeCache.prototype.del = function(keys, cb) {
192 var delCount, err, i, key, len, oldVal;
193 if (!_isArray(keys)) {
194 keys = [keys];
195 }
196 delCount = 0;
197 for (i = 0, len = keys.length; i < len; i++) {
198 key = keys[i];
199 if ((err = this._isInvalidKey(key)) != null) {
200 if (cb != null) {
201 cb(err);
202 return;
203 } else {
204 throw err;
205 }
206 }
207 if (this.data[key] != null) {
208 this.stats.vsize -= this._getValLength(this._unwrap(this.data[key], false));
209 this.stats.ksize -= this._getKeyLength(key);
210 this.stats.keys--;
211 delCount++;
212 oldVal = this.data[key];
213 delete this.data[key];
214 this.emit("del", key, oldVal.v);
215 } else {
216 this.stats.misses++;
217 }
218 }
219 if (cb != null) {
220 cb(null, delCount);
221 }
222 return delCount;
223 };
224
225 NodeCache.prototype.ttl = function() {
226 var arg, args, cb, err, i, key, len, ttl;
227 key = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
228 for (i = 0, len = args.length; i < len; i++) {
229 arg = args[i];
230 switch (typeof arg) {
231 case "number":
232 ttl = arg;
233 break;
234 case "function":
235 cb = arg;
236 }
237 }
238 ttl || (ttl = this.options.stdTTL);
239 if (!key) {
240 if (cb != null) {
241 cb(null, false);
242 }
243 return false;
244 }
245 if ((err = this._isInvalidKey(key)) != null) {
246 if (cb != null) {
247 cb(err);
248 return;
249 } else {
250 throw err;
251 }
252 }
253 if ((this.data[key] != null) && this._check(key, this.data[key])) {
254 if (ttl >= 0) {
255 this.data[key] = this._wrap(this.data[key].v, ttl, false);
256 } else {
257 this.del(key);
258 }
259 if (cb != null) {
260 cb(null, true);
261 }
262 return true;
263 } else {
264 if (cb != null) {
265 cb(null, false);
266 }
267 return false;
268 }
269 };
270
271 NodeCache.prototype.getTtl = function(key, cb) {
272 var _ttl, err;
273 if (!key) {
274 if (cb != null) {
275 cb(null, void 0);
276 }
277 return void 0;
278 }
279 if ((err = this._isInvalidKey(key)) != null) {
280 if (cb != null) {
281 cb(err);
282 return;
283 } else {
284 throw err;
285 }
286 }
287 if ((this.data[key] != null) && this._check(key, this.data[key])) {
288 _ttl = this.data[key].t;
289 if (cb != null) {
290 cb(null, _ttl);
291 }
292 return _ttl;
293 } else {
294 if (cb != null) {
295 cb(null, void 0);
296 }
297 return void 0;
298 }
299 };
300
301 NodeCache.prototype.keys = function(cb) {
302 var _keys;
303 _keys = Object.keys(this.data);
304 if (cb != null) {
305 cb(null, _keys);
306 }
307 return _keys;
308 };
309
310 NodeCache.prototype.getStats = function() {
311 return this.stats;
312 };
313
314 NodeCache.prototype.flushAll = function(_startPeriod) {
315 if (_startPeriod == null) {
316 _startPeriod = true;
317 }
318 this.data = {};
319 this.stats = {
320 hits: 0,
321 misses: 0,
322 keys: 0,
323 ksize: 0,
324 vsize: 0
325 };
326 this._killCheckPeriod();
327 this._checkData(_startPeriod);
328 this.emit("flush");
329 };
330
331 NodeCache.prototype.close = function() {
332 this._killCheckPeriod();
333 };
334
335 NodeCache.prototype._checkData = function(startPeriod) {
336 var key, ref, value;
337 if (startPeriod == null) {
338 startPeriod = true;
339 }
340 ref = this.data;
341 for (key in ref) {
342 value = ref[key];
343 this._check(key, value);
344 }
345 if (startPeriod && this.options.checkperiod > 0) {
346 this.checkTimeout = setTimeout(this._checkData, this.options.checkperiod * 1000);
347 if (this.checkTimeout.unref != null) {
348 this.checkTimeout.unref();
349 }
350 }
351 };
352
353 NodeCache.prototype._killCheckPeriod = function() {
354 if (this.checkTimeout != null) {
355 return clearTimeout(this.checkTimeout);
356 }
357 };
358
359 NodeCache.prototype._check = function(key, data) {
360 if (data.t !== 0 && data.t < Date.now()) {
361 this.del(key);
362 this.emit("expired", key, this._unwrap(data));
363 return false;
364 } else {
365 return true;
366 }
367 };
368
369 NodeCache.prototype._isInvalidKey = function(key) {
370 var ref;
371 if (ref = typeof key, indexOf.call(this.validKeyTypes, ref) < 0) {
372 return this._error("EKEYTYPE", {
373 type: typeof key
374 });
375 }
376 };
377
378 NodeCache.prototype._wrap = function(value, ttl, asClone) {
379 var livetime, now, oReturn, ttlMultiplicator;
380 if (asClone == null) {
381 asClone = true;
382 }
383 if (!this.options.useClones) {
384 asClone = false;
385 }
386 now = Date.now();
387 livetime = 0;
388 ttlMultiplicator = 1000;
389 if (ttl === 0) {
390 livetime = 0;
391 } else if (ttl) {
392 livetime = now + (ttl * ttlMultiplicator);
393 } else {
394 if (this.options.stdTTL === 0) {
395 livetime = this.options.stdTTL;
396 } else {
397 livetime = now + (this.options.stdTTL * ttlMultiplicator);
398 }
399 }
400 return oReturn = {
401 t: livetime,
402 v: asClone ? clone(value) : value
403 };
404 };
405
406 NodeCache.prototype._unwrap = function(value, asClone) {
407 if (asClone == null) {
408 asClone = true;
409 }
410 if (!this.options.useClones) {
411 asClone = false;
412 }
413 if (value.v != null) {
414 if (asClone) {
415 return clone(value.v);
416 } else {
417 return value.v;
418 }
419 }
420 return null;
421 };
422
423 NodeCache.prototype._getKeyLength = function(key) {
424 return key.length;
425 };
426
427 NodeCache.prototype._getValLength = function(value) {
428 if (_isString(value)) {
429 return value.length;
430 } else if (this.options.forceString) {
431 return JSON.stringify(value).length;
432 } else if (_isArray(value)) {
433 return this.options.arrayValueSize * value.length;
434 } else if (_isNumber(value)) {
435 return 8;
436 } else if (_isObject(value)) {
437 return this.options.objectValueSize * _size(value);
438 } else {
439 return 0;
440 }
441 };
442
443 NodeCache.prototype._error = function(type, data, cb) {
444 var error;
445 if (data == null) {
446 data = {};
447 }
448 error = new Error();
449 error.name = type;
450 error.errorcode = type;
451 error.message = this.ERRORS[type] != null ? this.ERRORS[type](data) : "-";
452 error.data = data;
453 if (cb && _isFunction(cb)) {
454 cb(error, null);
455 } else {
456 return error;
457 }
458 };
459
460 NodeCache.prototype._initErrors = function() {
461 var _errMsg, _errT, ref;
462 this.ERRORS = {};
463 ref = this._ERRORS;
464 for (_errT in ref) {
465 _errMsg = ref[_errT];
466 this.ERRORS[_errT] = _template(_errMsg);
467 }
468 };
469
470 NodeCache.prototype._ERRORS = {
471 "ENOTFOUND": "Key `<%= key %>` not found",
472 "EKEYTYPE": "The key argument has to be of type `string` or `number`. Found: `<%= type %>`",
473 "EKEYSTYPE": "The keys argument has to be an array."
474 };
475
476 return NodeCache;
477
478 })(EventEmitter);
479
480}).call(this);