Skip to content

Commit

Permalink
adding ttl method
Browse files Browse the repository at this point in the history
  • Loading branch information
mac- committed May 26, 2016
1 parent fb36444 commit 8225d34
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 16 deletions.
15 changes: 11 additions & 4 deletions README.md
Expand Up @@ -62,6 +62,13 @@ The del method is used to remove a specific item from the cache:
* key - required, a unique string that is used to access the value
* returns true if there was an entry that was successfully removed from the cache

The ttl method is used to get the number of seconds until a specific item expires in the cache:

ttl(key:String)

* key - required, a unique string that is used to access the value
* returns a number representing the number of seconds until the item expires

The clear method is used to empty the entire cache:

clear()
Expand All @@ -73,12 +80,12 @@ The following methods can be called to get stats on the cache:
currentSize()

The following are publically available properties:

maxSize (Number, defaults to 500) - The max number of items that can live in the cache
canPutWhenFull (Boolean, defaults to false) - When trying to put to a full cache, if set to true, eidetic will remove the least recently used entry to make room for the one you are currently storing. If set to false, the storing of the entry will fail.

Valid options to pass to the constructor:

maxSize (see above)
canPutWhenFull (see above)
logger (an instance of your own logging object)
Expand All @@ -93,12 +100,12 @@ Here is an example:
var cache = new Cache(options);

cache.put('key', {my: 'obj'}, 60, true);

var cachedValue = cache.get('key'); // {my: 'obj'}
cache.del('key');

cachedValue = cache.get('key'); // undefined


# License

Expand Down
19 changes: 15 additions & 4 deletions lib/eidetic.js
@@ -1,9 +1,10 @@
var clone = require('clone');

module.exports = function Cache(options) {

options = options || {};
var self = this,
maxDurationSeconds = parseInt('0x7FFFFFFF', 16) / 1000, // setTimeout uses a 32bit integer to store its delay value, so anything above that (24.8551 days) will cause the timeout to execute immediately
clone = require('clone'),
hits = 0,
total = 0,
logger = options.logger || {log:function(){},trace:function(){},debug:function(){},info:function(){},warn:function(){},error:function(){}},
Expand Down Expand Up @@ -77,13 +78,14 @@ module.exports = function Cache(options) {
throw new Error('Missing or invalid parameter: value');
}

durationSeconds = (typeof durationSeconds === 'number') ? Math.max(0, Math.min(maxDurationSeconds, durationSeconds)) : 1; // must be between 0 and 2147483.647 seconds

var existingTimeoutId;
if (durationSeconds === 0 && cachedItems.hasOwnProperty(key)) {
existingTimeoutId = cachedItems[key].timeoutId;
durationSeconds = cachedItems[key].durationSeconds - Math.ceil((new Date() - cachedItems[key].lastModified) / 1000);
}

durationSeconds = (durationSeconds) ? Math.max(0, Math.min(maxDurationSeconds, durationSeconds)) : 1; // must be between 0 and 2147483.647 seconds

if (Object.keys(cachedItems).length >= self.maxSize &&
cachedItems[key] === undefined &&
!self.canPutWhenFull) {
Expand All @@ -94,12 +96,13 @@ module.exports = function Cache(options) {
if (cachedItems[key] !== undefined && cachedItems[key].timeoutId !== undefined && !existingTimeoutId) {
clearTimeout(cachedItems[key].timeoutId);
}

cachedItems[key] = {
value: clone(value),
durationSeconds: durationSeconds,
useSlidingExpiration: useSlidingExpiration,
lastAccessed: new Date(),
lastModified: new Date(),
timeoutId: (existingTimeoutId) ? existingTimeoutId : setTimeout(function() {
self.del(key);
}, durationSeconds * 1000)
Expand All @@ -125,6 +128,14 @@ module.exports = function Cache(options) {
return wasDeleted;
};

this.ttl = function(key) {
if (!cachedItems.hasOwnProperty(key) || !cachedItems[key].timeoutId) {
return 0;
}
var lastDate = (cachedItems[key].useSlidingExpiration) ? cachedItems[key].lastAccessed : cachedItems[key].lastModified;
return cachedItems[key].durationSeconds - Math.ceil((new Date() - lastDate) / 1000);
};

this.clear = function() {
logger.log('Clearing entire cache');
for (var prop in cachedItems) {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -4,7 +4,7 @@
"contributors": [
"Mac Angell <mac.ang311@gmail.com>"
],
"version": "0.3.1",
"version": "0.4.0",
"dependencies": {
"clone": "0.2.x"
},
Expand Down
2 changes: 1 addition & 1 deletion test/jshint/config.json
Expand Up @@ -18,7 +18,7 @@
"unused": true,
"strict": false,
"trailing": true,
"maxparams": 0,
"maxparams": 5,
"maxdepth": 0,
"maxstatements": 0,
"maxcomplexity": 0,
Expand Down
84 changes: 78 additions & 6 deletions test/unit/eidetic.tests.js
Expand Up @@ -20,7 +20,7 @@ var assert = require('assert'),

describe('Eidetic Cache', function() {
describe('stats', function() {

it('should give accurate stats on the cache', function(done) {

cache = new Cache({maxSize: 2});
Expand Down Expand Up @@ -48,7 +48,7 @@ describe('Eidetic Cache', function() {
});
});
describe('get()', function() {

it('should get an undefined value when there is no value in the cache', function(done) {

cache = new Cache();
Expand All @@ -63,7 +63,7 @@ describe('Eidetic Cache', function() {
cache = new Cache();
cache.put('key', testValue);
var value = cache.get('key');

assert.deepEqual(value, testValue, 'The cached value should be equal to the test value');
done();
});
Expand All @@ -75,7 +75,7 @@ describe('Eidetic Cache', function() {
var value = cache.get('key');

value.fnord = 'hacky';

assert.deepEqual(cache.get('key'), testValue, 'The cached value should be equal to the test value');
done();
});
Expand All @@ -99,7 +99,7 @@ describe('Eidetic Cache', function() {
});
});
describe('clear()', function() {

it('should clear the entire cache', function(done) {

cache = new Cache();
Expand All @@ -113,7 +113,7 @@ describe('Eidetic Cache', function() {
});
});
describe('del()', function() {

it('should remove entry from cache', function(done) {

cache = new Cache();
Expand All @@ -139,6 +139,78 @@ describe('Eidetic Cache', function() {
done();
});
});
describe('ttl()', function() {

it('should get a ttl of 0 when cached item does not exist', function(done) {

cache = new Cache();
cache.put('key', testValue);
setTimeout(function() {
var ttl = cache.ttl('key');
assert.strictEqual(ttl, 0, 'The value should be 0');

var value = cache.get('key');

assert.strictEqual(value, undefined, 'The value should be undefined');
done();
}, 1300)
});
it('should get a ttl when cached item exists', function(done) {

cache = new Cache();
cache.put('key', testValue, 10);
var ttl = cache.ttl('key');
assert.strictEqual(ttl, 10, 'The value should be 10');
done();
});
it('should get a ttl after some time when cached item exists', function(done) {

cache = new Cache();
cache.put('key', testValue, 10);
setTimeout(function() {
var ttl = cache.ttl('key');
assert.strictEqual(ttl, 9, 'The value should be 9');
done();
}, 500);
});
it('should get a new ttl when an item is updated after some time', function(done) {

cache = new Cache();
cache.put('key', testValue, 10);
setTimeout(function() {
cache.put('key', testValue, 10);
var ttl = cache.ttl('key');
assert.strictEqual(ttl, 10, 'The value should be 10');
done();
}, 500);
});
it('should get a same ttl when an item is updated with a 0 duration after some time', function(done) {

cache = new Cache();
cache.put('key', testValue, 10);
setTimeout(function() {
cache.put('key', testValue, 0);
var ttl = cache.ttl('key');
assert.strictEqual(ttl, 9, 'The value should be 9');
done();
}, 500);
});
it('should get a new ttl when an item is fetched with a sliding expiration after some time', function(done) {

cache = new Cache();
cache.put('key', testValue, 10, true);
setTimeout(function() {
cache.get('key');
var ttl = cache.ttl('key');
assert.strictEqual(ttl, 10, 'The value should be 10');
setTimeout(function() {
var ttl = cache.ttl('key');
assert.strictEqual(ttl, 9, 'The value should be 9');
done();
}, 500);
}, 500);
});
});
describe('put()', function() {
it('should throw an error when passing invalid arguments to put()', function(done) {

Expand Down

0 comments on commit 8225d34

Please sign in to comment.