timed-cache
Advanced tools
Comparing version 1.1.0 to 1.1.1
{ | ||
"name": "timed-cache", | ||
"version": "1.1.0", | ||
"version": "1.1.1", | ||
"main": "cache.js", | ||
@@ -25,7 +25,3 @@ "authors": [ | ||
"tests" | ||
], | ||
"devDependencies": { | ||
"requirejs": "~2.1.16", | ||
"jasmine": "~2.2.1" | ||
} | ||
] | ||
} |
88
cache.js
/** | ||
* //////////////////////////////////////// | ||
* //////////// Cache module ///////////// | ||
* //////////////////////////////////////// | ||
* /////////////////////////////////////// | ||
* //////////// Cache module ///////////// | ||
* /////////////////////////////////////// | ||
* | ||
@@ -16,36 +16,33 @@ * This module offers object caching mechanisms for | ||
(function (name, definition) { | ||
/* istanbul ignore next */ | ||
if (typeof define === 'function' && define.amd) { | ||
// Defining the module in an AMD fashion. | ||
define(definition); | ||
// Defining the module in an AMD fashion. | ||
define(definition); | ||
} else if (typeof module !== 'undefined' && module.exports) { | ||
// Exporting the module for Node.js/io.js. | ||
module.exports = definition(); | ||
// Exporting the module for Node.js/io.js. | ||
module.exports = definition(); | ||
} else { | ||
var gl = this; | ||
var instance = definition(); | ||
var old = gl[name]; | ||
var gl = this; | ||
var instance = definition(); | ||
var old = gl[name]; | ||
/** | ||
* Allowing to scope the module | ||
* avoiding global namespace pollution. | ||
*/ | ||
instance.noConflict = function () { | ||
gl[name] = old; | ||
return instance; | ||
}; | ||
// Exporting the module in the global | ||
// namespace in a browser context. | ||
gl[name] = instance; | ||
/** | ||
* Allowing to scope the module | ||
* avoiding global namespace pollution. | ||
*/ | ||
instance.noConflict = function () { | ||
gl[name] = old; | ||
return instance; | ||
}; | ||
// Exporting the module in the global | ||
// namespace in a browser context. | ||
gl[name] = instance; | ||
} | ||
})('Cache', function () { | ||
var cache = {}; | ||
/** | ||
* Returns whether the given object | ||
* is of type `object`. | ||
* We use an object literal ads the | ||
* internal storage. | ||
*/ | ||
var isObject = function (obj) { | ||
return typeof obj === 'object' && !!obj; | ||
}; | ||
var cache = {}; | ||
@@ -58,17 +55,10 @@ /** | ||
var has = function (obj, key) { | ||
return obj !== null && Object.prototype.hasOwnProperty.call(obj, key); | ||
return (obj !== null && Object.prototype.hasOwnProperty.call(obj, key)); | ||
}; | ||
/** | ||
* Retrieve the names of an object's own properties. | ||
* Delegates to ECMAScript 5's native `Object.keys` | ||
* Cache constructor. | ||
* @param {*} options the `options` object | ||
* holder used by the cache implementation. | ||
*/ | ||
var keys = function (obj) { | ||
if (!isObject(obj)) return []; | ||
if (Object.keys) return Object.keys(obj); | ||
var keys = []; | ||
for (var key in obj) if (has(obj, key)) keys.push(key); | ||
return keys; | ||
}; | ||
var Cache = function (options) { | ||
@@ -82,2 +72,5 @@ // The default cached objects expiration | ||
options.defaultTtl : 60 * 1000; | ||
// A prefix used to forbid access to internal properties | ||
// of the object storage. | ||
this.prefix = '__cache__'; | ||
}; | ||
@@ -90,6 +83,6 @@ | ||
var serialize = function (key) { | ||
if (isObject(key)) { | ||
return (JSON.stringify(key)); | ||
if (typeof key !== 'string') { | ||
return (this.prefix + JSON.stringify(key)); | ||
} | ||
return (key); | ||
return (this.prefix + key); | ||
}; | ||
@@ -134,6 +127,3 @@ | ||
var value = cache[serialize(key)]; | ||
if (value) { | ||
return (value.data); | ||
} | ||
return (value && value.data); | ||
}; | ||
@@ -173,6 +163,6 @@ | ||
Cache.prototype.size = function () { | ||
return keys(cache).length; | ||
return Object.keys(cache).length; | ||
}; | ||
return Cache; | ||
}); | ||
return (Cache); | ||
}); |
@@ -1,1 +0,1 @@ | ||
!function(a,b){if("function"==typeof define&&define.amd)define(b);else if("undefined"!=typeof module&&module.exports)module.exports=b();else{var c=this,d=b(),e=c[a];d.noConflict=function(){return c[a]=e,d},c[a]=d}}("Cache",function(){var a={},b=function(a){return"object"==typeof a&&!!a},c=function(a,b){return null!==a&&Object.prototype.hasOwnProperty.call(a,b)},d=function(a){if(!b(a))return[];if(Object.keys)return Object.keys(a);var d=[];for(var e in a)c(a,e)&&d.push(e);return d},e=function(a){this.defaultTtl=a&&a.defaultTtl?a.defaultTtl:6e4},f=function(a){return b(a)?JSON.stringify(a):a};return e.prototype.put=function(b,c,d){var e=(d?d.ttl:void 0)||this.defaultTtl,g=(d?d.callback:void 0)||function(){},h=f(b),i=this,j=a[h];j&&clearTimeout(j.handle);var k=setTimeout(function(){i.remove(b)},e);a[h]={handle:k,data:c,callback:g}},e.prototype.get=function(b){var c=a[f(b)];if(c)return c.data},e.prototype.remove=function(b){var c=f(b),d=a[c];d&&(clearTimeout(d.handle),delete a[c],d.callback(b,d.data))},e.prototype.clear=function(){for(var b in a)c(a,b)&&clearTimeout(a[b].handle);a={}},e.prototype.size=function(){return d(a).length},e}); | ||
!function(t,e){if("function"==typeof define&&define.amd)define(e);else if("undefined"!=typeof module&&module.exports)module.exports=e();else{var n=this,o=e(),i=n[t];o.noConflict=function(){return n[t]=i,o},n[t]=o}}("Cache",function(){var u={},t=function(t){this.defaultTtl=t&&t.defaultTtl?t.defaultTtl:6e4,this.prefix="__cache__"},c=function(t){return"string"!=typeof t?this.prefix+JSON.stringify(t):this.prefix+t};return t.prototype.put=function(t,e,n){var o=(n?n.ttl:void 0)||this.defaultTtl,i=(n?n.callback:void 0)||function(){},r=c(t),a=this,l=u[r];l&&clearTimeout(l.handle);var f=setTimeout(function(){a.remove(t)},o);u[r]={handle:f,data:e,callback:i}},t.prototype.get=function(t){var e=u[c(t)];return e&&e.data},t.prototype.remove=function(t){var e=c(t),n=u[e];n&&(clearTimeout(n.handle),delete u[e],n.callback(t,n.data))},t.prototype.clear=function(){for(var t in u)n=t,null!==(e=u)&&Object.prototype.hasOwnProperty.call(e,n)&&clearTimeout(u[t].handle);var e,n;u={}},t.prototype.size=function(){return Object.keys(u).length},t}); |
@@ -10,40 +10,32 @@ module.exports = function (grunt) { | ||
grunt.initConfig({ | ||
clean: { | ||
dist: 'dist/*' | ||
}, | ||
jshint: { | ||
dist: { | ||
src: ['*.js'] | ||
}, | ||
test: { | ||
src: ['tests/*.js'] | ||
} | ||
}, | ||
uglify: { | ||
dist: { | ||
src: 'cache.js', | ||
dest: 'dist/cache.min.js' | ||
} | ||
}, | ||
jasmine: { | ||
dist: { | ||
options: { | ||
specs: ['tests/cache-spec.js'], | ||
template: require('grunt-template-jasmine-requirejs'), | ||
templateOptions: { | ||
requireConfig: { | ||
buildPath: '../' | ||
} | ||
}, | ||
junit: { | ||
path: 'reports/junit/jasmine' | ||
} | ||
} | ||
} | ||
} | ||
clean: { | ||
dist: 'dist/*' | ||
}, | ||
jshint: { | ||
dist: { | ||
src: ['*.js'] | ||
}, | ||
test: { | ||
src: ['tests/*.js'] | ||
} | ||
}, | ||
uglify: { | ||
dist: { | ||
src: 'cache.js', | ||
dest: 'dist/cache.min.js' | ||
} | ||
}, | ||
mochaTest: { | ||
test: { | ||
src: ['tests/**/*.js'], | ||
options: { | ||
timeout: 3000 | ||
} | ||
} | ||
} | ||
}); | ||
// Registering the tasks. | ||
grunt.registerTask('test', ['jasmine']); | ||
grunt.registerTask('test', ['mochaTest']); | ||
grunt.registerTask('default', ['clean', 'jshint', 'uglify', 'test']); | ||
}; |
{ | ||
"name": "timed-cache", | ||
"version": "1.1.0", | ||
"version": "1.1.1", | ||
"description": "A minimalist time-based caching system.", | ||
@@ -13,3 +13,3 @@ "main": "cache.js", | ||
"build": "grunt", | ||
"prepublish": "npm run build" | ||
"prepublishOnly": "npm run build" | ||
}, | ||
@@ -27,12 +27,13 @@ "keywords": [ | ||
"devDependencies": { | ||
"grunt": "^0.4.5", | ||
"grunt-contrib-clean": "^0.6.0", | ||
"grunt-contrib-copy": "^0.8.0", | ||
"grunt-contrib-jasmine": "^0.9.2", | ||
"grunt-contrib-jshint": "^0.11.2", | ||
"grunt-contrib-uglify": "^0.9.1", | ||
"grunt-template-jasmine-requirejs": "^0.2.3", | ||
"load-grunt-tasks": "^3.1.0", | ||
"time-grunt": "^1.2.1" | ||
"expect": "^23.6.0", | ||
"grunt": "^1.0.3", | ||
"grunt-contrib-clean": "^2.0.0", | ||
"grunt-contrib-copy": "^1.0.0", | ||
"grunt-contrib-jshint": "^2.0.0", | ||
"grunt-contrib-uglify": "^4.0.0", | ||
"grunt-mocha-test": "^0.13.3", | ||
"load-grunt-tasks": "3.1.0", | ||
"mocha": "^5.2.0", | ||
"time-grunt": "^1.4.0" | ||
} | ||
} |
@@ -6,4 +6,6 @@ <p align="center"> | ||
## Cache storage | ||
[![Build Status](https://travis-ci.org/HQarroum/timed-cache.svg?branch=master)](https://travis-ci.org/HQarroum/timed-cache) [![Code Climate](https://codeclimate.com/repos/55e34093e30ba072de0013d2/badges/acc2df5cc7f78c301ad9/gpa.svg)](https://codeclimate.com/repos/55e34093e30ba072de0013d2/feed) | ||
[![Build Status](https://travis-ci.org/HQarroum/timed-cache.svg?branch=master)](https://travis-ci.org/HQarroum/timed-cache) | ||
[![CodeFactor](https://www.codefactor.io/repository/github/hqarroum/timed-cache/badge)](https://www.codefactor.io/repository/github/hqarroum/timed-cache) | ||
A minimalist time-based caching system. | ||
@@ -13,3 +15,3 @@ | ||
Current version: **1.1.0** | ||
Current version: **1.1.1** | ||
@@ -76,3 +78,3 @@ Lead Maintainer: [Halim Qarroum](mailto:hqm.post@gmail.com) | ||
It is also possible to use an object as a key as long as it is serializable using `JSON.stringify` : | ||
It is also possible to use an object as a key as long as it is serializable using [`JSON.stringify`](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/JSON/stringify) : | ||
@@ -106,2 +108,17 @@ ```javascript | ||
### Element removal | ||
It is possible to remove a cache entry before its time-to-live is reached, by using the `.remove` primitive : | ||
```javascript | ||
cache.put('foo', 'bar', { | ||
callback: function (key, value) { | ||
console.log(key, value, 'removed !'); | ||
} | ||
}); | ||
cache.remove('foo'); | ||
``` | ||
In this case, the callback passed to a `.put` will be called if the user removed the inserted entry. | ||
### Building | ||
@@ -108,0 +125,0 @@ |
@@ -1,140 +0,176 @@ | ||
define(['cache'], function (Cache) { | ||
var expect = require('expect'); | ||
var Cache = require('../cache.js'); | ||
var cache = new Cache(); | ||
/** | ||
* Configuration test plan. | ||
*/ | ||
describe('Cache storage', function () { | ||
var cache; | ||
/** | ||
* Configuration test plan. | ||
* On each test, we create a new cache storage. | ||
*/ | ||
describe('Cache storage', function () { | ||
beforeEach(function () { | ||
cache = new Cache(); | ||
}); | ||
/** | ||
* Insertion test. | ||
*/ | ||
it('should be able to insert and retrieve new key/value pair', function () { | ||
cache.put('foo', 'bar'); | ||
cache.put('foo', 'baz'); | ||
cache.put('foo', { foo: 'bar' }); | ||
cache.put('bar', 'baz'); | ||
cache.put('bar', { bar: 'baz' }); | ||
/** | ||
* Insertion test. | ||
*/ | ||
it('should be able to insert and retrieve new key/value pair', function () { | ||
cache.put('foo', 'bar'); | ||
cache.put('foo', 'baz'); | ||
cache.put('foo', { foo: 'bar' }); | ||
cache.put('bar', 'baz'); | ||
cache.put('bar', { bar: 'baz' }); | ||
expect(cache.get('foo')).toEqual({ foo: 'bar' }); | ||
expect(cache.get('bar')).toEqual({ bar: 'baz' }); | ||
}); | ||
expect(cache.get('foo')).toEqual({ foo: 'bar' }); | ||
expect(cache.get('bar')).toEqual({ bar: 'baz' }); | ||
}); | ||
/** | ||
* Object key insertion. | ||
*/ | ||
it('should be able to insert and retrieve key objects', function () { | ||
cache.put({ foo: 'bar' }, { foo: 'baz'}); | ||
cache.put({ bar: 'baz' }, { bar: 'foo' }); | ||
/** | ||
* Object key insertion. | ||
*/ | ||
it('should be able to insert and retrieve key objects', function () { | ||
cache.put({ foo: 'bar' }, { foo: 'baz'}); | ||
cache.put({ bar: 'baz' }, { bar: 'foo' }); | ||
expect(cache.get({ foo: 'bar' })).toEqual({ foo: 'baz' }); | ||
expect(cache.get({ bar: 'baz' })).toEqual({ bar: 'foo' }); | ||
}); | ||
expect(cache.get({ foo: 'bar' })).toEqual({ foo: 'baz' }); | ||
expect(cache.get({ bar: 'baz' })).toEqual({ bar: 'foo' }); | ||
}); | ||
/** | ||
* Cache entry removal. | ||
*/ | ||
it('should be able to remove cache entries by key', function () { | ||
cache.put('hello', 'world'); | ||
cache.put({ key: 'hello' }, 'world'); | ||
/** | ||
* Cache entry removal. | ||
*/ | ||
it('should be able to remove cache entries by key', function () { | ||
cache.put('hello', 'world'); | ||
cache.put({ key: 'hello' }, 'world'); | ||
// Removing entries | ||
cache.remove('hello'); | ||
cache.remove({ key: 'hello' }); | ||
// Removing entries | ||
cache.remove('hello'); | ||
cache.remove({ key: 'hello' }); | ||
expect(cache.get('hello')).toBe(undefined); | ||
expect(cache.get({ key: 'hello' })).toBe(undefined); | ||
}); | ||
/** | ||
* Iterables as keys. | ||
*/ | ||
it('should be able to insert iterables as keys', function () { | ||
var object = { foo: 'bar', bar: 'baz' }; | ||
var array = [1, 'a', 3, 4]; | ||
// Inserting values. | ||
cache.put({ foo: 'bar', bar: 'baz' }, 'foo'); | ||
cache.put([1, 'a', 3, 4], 'bar'); | ||
cache.put('foo', 'bar'); | ||
cache.put(1, 'bar'); | ||
// Testing presence of iterable keys. | ||
expect(cache.get({ foo: 'bar', bar: 'baz' })).toEqual('foo'); | ||
expect(cache.get(object)).toEqual('foo'); | ||
expect(cache.get([1, 'a', 3, 4])).toEqual('bar'); | ||
expect(cache.get([1, 'a', 4, 3])).not.toEqual('bar'); | ||
expect(cache.get(array)).toEqual('bar'); | ||
expect(cache.get('foo')).toEqual('bar'); | ||
expect(cache.get(1)).toEqual('bar'); | ||
}); | ||
/** | ||
* Cache entry removal notification. | ||
*/ | ||
it('should be able to get notified when an entry has been removed', function () { | ||
cache.put('hello', 'world', { | ||
callback: function (k, v) { | ||
expect(k).toBe('hello'); | ||
expect(v).toBe('world'); | ||
expect(cache.get('hello')).toBe(undefined); | ||
expect(cache.get({ key: 'hello' })).toBe(undefined); | ||
} | ||
}); | ||
cache.remove('hello'); | ||
}); | ||
/** | ||
* Cache entry removal notification. | ||
*/ | ||
it('should be able to get notified when an entry has been removed', function () { | ||
cache.put('hello', 'world', { | ||
callback: function (k, v) { | ||
expect(k).toBe('hello'); | ||
expect(v).toBe('world'); | ||
expect(cache.get('hello')).toBe(undefined); | ||
} | ||
}); | ||
cache.remove('hello'); | ||
}); | ||
/** | ||
* Cache clearance. | ||
*/ | ||
it('should be able to be cleared', function () { | ||
// Inserting 1000 elements. | ||
for (var i = 0; i < 1000; ++i) { | ||
cache.put('foo' + i, 'bar'); | ||
} | ||
// Clearing the cache. | ||
cache.clear(); | ||
// Testing the final size. | ||
expect(cache.size()).toBe(0); | ||
}); | ||
}); | ||
/** | ||
* Cache clearance. | ||
*/ | ||
it('should be able to be cleared', function () { | ||
// Inserting 1000 elements. | ||
for (var i = 0; i < 1000; ++i) { | ||
cache.put('foo' + i, 'bar'); | ||
} | ||
// Clearing the cache. | ||
cache.clear(); | ||
// Testing the final size. | ||
expect(cache.size()).toBe(0); | ||
}); | ||
describe('Time-based cache', function () { | ||
var cache; | ||
/** | ||
* On each test, we create a new cache storage. | ||
*/ | ||
beforeEach(function () { | ||
cache = new Cache(); | ||
}); | ||
describe('Time-based cache', function () { | ||
/** | ||
* The elements time-to-live. | ||
*/ | ||
var ttl = 1 * 1000; | ||
/** | ||
* The elements time-to-live. | ||
*/ | ||
var ttl = 2 * 1000; | ||
/** | ||
* Cache insertion using a TTL. | ||
*/ | ||
it('should be able to set a ttl on a cached object', function (done) { | ||
cache.put('foo', 'bar', { ttl: ttl }); | ||
/** | ||
* Cache insertion using a TTL. | ||
*/ | ||
it('should be able to set a ttl on a cached object', function (done) { | ||
cache.put('foo', 'bar', { ttl: ttl }); | ||
// Awaiting for the element to be evicted. | ||
setTimeout(function () { | ||
expect(cache.get('foo')).toBe(undefined); | ||
done(); | ||
}, ttl + 1); | ||
}); | ||
// Awaiting for the element to be evicted. | ||
setTimeout(function () { | ||
expect(cache.get('foo')).toBeUndefined(); | ||
done(); | ||
}, ttl + 1); | ||
}); | ||
/** | ||
* Cache insertion using the default TTL. | ||
*/ | ||
it('should be able to use the default ttl on a cached object', function (done) { | ||
var originalTtl = cache.defaultTtl; | ||
/** | ||
* Cache insertion using the default TTL. | ||
*/ | ||
it('should be able to use the default ttl on a cached object', function (done) { | ||
var originalTtl = cache.defaultTtl; | ||
// Updating the default ttl. | ||
cache.defaultTtl = ttl; | ||
// Updating the default ttl. | ||
cache.defaultTtl = ttl; | ||
// Inserting an element. | ||
cache.put('foobar', 'baz'); | ||
// Inserting an element. | ||
cache.put('foobar', 'baz'); | ||
// Awaiting for the element to be evicted. | ||
setTimeout(function () { | ||
expect(cache.get('foobar')).toBe(undefined); | ||
// Restoring the default cached elements ttl. | ||
cache.defaultTtl = originalTtl; | ||
done(); | ||
}, cache.defaultTtl + 1); | ||
}); | ||
// Awaiting for the element to be evicted. | ||
setTimeout(function () { | ||
expect(cache.get('foobar')).toBeUndefined(); | ||
// Restoring the default cached elements ttl. | ||
cache.defaultTtl = originalTtl; | ||
/** | ||
* Callback upon element eviction. | ||
*/ | ||
it('should be able to call back a function upon element eviction', function (done) { | ||
// We use the second as a unit of measure for | ||
// the elasped time to avoid delays caused by the | ||
// Javascript event loop. | ||
var time = function () { return Math.floor(Date.now() / 1000); }; | ||
var start = time(); | ||
cache.put('foobar', 'baz', { | ||
ttl: ttl, | ||
callback: function () { | ||
expect(time()).toBe(start + (ttl / 1000)); | ||
done(); | ||
}, cache.defaultTtl + 1); | ||
} | ||
}); | ||
/** | ||
* Callback upon element eviction. | ||
*/ | ||
it('should be able to call back a function upon element eviction', function (done) { | ||
// We use the second as a unit of measure for | ||
// the elasped time to avoid delays caused by the | ||
// Javascript event loop. | ||
var time = function () { return Math.floor(Date.now() / 1000); }; | ||
var start = time(); | ||
cache.put('foobar', 'baz', { | ||
ttl: ttl, | ||
callback: function () { | ||
expect(time()).toBe(start + (ttl / 1000)); | ||
done(); | ||
} | ||
}); | ||
}); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
155
0
18685
10
9
358