Skip to content

Commit

Permalink
Allow importing and exporting to JSON
Browse files Browse the repository at this point in the history
  • Loading branch information
cooperka committed Jun 19, 2017
1 parent 4be8981 commit 84f2b2e
Show file tree
Hide file tree
Showing 3 changed files with 210 additions and 0 deletions.
12 changes: 12 additions & 0 deletions README.md
Expand Up @@ -97,6 +97,18 @@ which should print

* Returns all the cache keys

### exportJson = function()

* Returns a JSON string representing all the cache data
* Any timeoutCallbacks will be ignored

### importJson = function(string)

* Merges all the data from a previous call to `export` into the cache
* Any existing entries before an `import` will remain in the cache (or be overwritten)
* Any entries that would have expired since being exported will expire upon being imported (but their callbacks will not be invoked)
* Returns the new size of the cache

### Cache = function()

* Cache constructor
Expand Down
41 changes: 41 additions & 0 deletions index.js
Expand Up @@ -127,6 +127,47 @@ function Cache () {
this.keys = function() {
return Object.keys(_cache);
};

this.exportJson = function() {
var plainJsCache = {};

// Discard the `timeout` property.
// Note: JSON doesn't support `NaN`, so convert it to `'NaN'`.
for (var key in _cache) {
var record = _cache[key];
plainJsCache[key] = {
value: record.value,
expire: record.expire || 'NaN',
};
}

return JSON.stringify(plainJsCache);
};

this.importJson = function(jsonToImport) {
var cacheToImport = JSON.parse(jsonToImport);
var currTime = Date.now();

for (var key in cacheToImport) {
if (cacheToImport.hasOwnProperty(key)) {
var record = cacheToImport[key];

// This could possibly be `'NaN'` if there's no expiry set.
var remainingTime = record.expire - currTime;

if (remainingTime <= 0) {
// Delete any record that might exist with the same key.
this.del(key);
continue;
}

// Remaining time is either positive, or `'NaN'`.
this.put(key, record.value, remainingTime > 0 ? remainingTime : undefined);
}
}

return this.size();
};
}

module.exports = new Cache();
Expand Down
157 changes: 157 additions & 0 deletions test.js
Expand Up @@ -637,6 +637,163 @@ describe('node-cache', function() {
});
});

describe('export()', function() {
var START_TIME = 10000;

var BASIC_EXPORT = JSON.stringify({
key: {
value: 'value',
expire: START_TIME + 1000,
},
});

before(function() {
cache.debug(false);
});

beforeEach(function() {
clock.tick(START_TIME);
});

it('should return an empty object given an empty cache', function() {
expect(cache.exportJson()).to.equal(JSON.stringify({}));
});

it('should return a single record after adding a single item to the cache', function() {
cache.put('key', 'value', 1000);
expect(cache.exportJson()).to.equal(BASIC_EXPORT);
});

it('should return multiple records with expiry', function() {
cache.put('key1', 'value1');
cache.put('key2', 'value2', 1000);
expect(cache.exportJson()).to.equal(JSON.stringify({
key1: {
value: 'value1',
expire: 'NaN',
},
key2: {
value: 'value2',
expire: START_TIME + 1000,
},
}));
});

it('should update when a key in the cache expires', function() {
cache.put('key', 'value', 1000);
expect(cache.exportJson()).to.equal(BASIC_EXPORT);
clock.tick(999);
expect(cache.exportJson()).to.equal(BASIC_EXPORT);
clock.tick(1);
expect(cache.exportJson()).to.equal(JSON.stringify({}));
});
});

describe('import()', function() {
var START_TIME = 10000;

var BASIC_EXPORT = JSON.stringify({
key: {
value: 'value',
expire: START_TIME + 1000,
},
});

before(function() {
cache.debug(false);
});

beforeEach(function() {
clock.tick(START_TIME);
});

it('should import an empty object into an empty cache', function() {
var exportedJson = cache.exportJson();

cache.clear();
cache.importJson(exportedJson);

expect(cache.exportJson()).to.equal(JSON.stringify({}));
});

it('should import records into an empty cache', function() {
cache.put('key1', 'value1');
cache.put('key2', 'value2', 1000);
var exportedJson = cache.exportJson();

cache.clear();
cache.importJson(exportedJson);

expect(cache.exportJson()).to.equal(JSON.stringify({
key1: {
value: 'value1',
expire: 'NaN',
},
key2: {
value: 'value2',
expire: START_TIME + 1000,
},
}));
});

it('should import records into an already-existing cache', function() {
cache.put('key1', 'value1');
cache.put('key2', 'value2', 1000);
var exportedJson = cache.exportJson();

cache.put('key1', 'changed value', 5000);
cache.put('key3', 'value3', 500);

cache.importJson(exportedJson);

expect(cache.exportJson()).to.equal(JSON.stringify({
key1: {
value: 'value1',
expire: 'NaN',
},
key2: {
value: 'value2',
expire: START_TIME + 1000,
},
key3: {
value: 'value3',
expire: START_TIME + 500,
},
}));
});

it('should import with updated expire times', function() {
cache.put('key1', 'value1', 500);
cache.put('key2', 'value2', 1000);
var exportedJson = cache.exportJson();

var tickAmount = 750;
clock.tick(tickAmount);

cache.importJson(exportedJson);

expect(cache.exportJson()).to.equal(JSON.stringify({
key2: {
value: 'value2',
expire: START_TIME + tickAmount + 250,
},
}));
});

it('should return the new size', function() {
cache.put('key1', 'value1', 500);
var exportedJson = cache.exportJson();

cache.clear();
cache.put('key2', 'value2', 1000);
expect(cache.size()).to.equal(1);

var size = cache.importJson(exportedJson);
expect(size).to.equal(2);
expect(cache.size()).to.equal(2);
});
});

describe('Cache()', function() {
it('should return a new cache instance when called', function() {
var cache1 = new Cache(),
Expand Down

0 comments on commit 84f2b2e

Please sign in to comment.