Comparing version 0.0.8 to 0.1.0
236
index.js
@@ -1,235 +0,1 @@ | ||
/*! | ||
* goldfish | ||
* Copyright(c) 2012 Ian Hansen <ian@supershabam.com> | ||
* MIT Licensed | ||
*/ | ||
"use strict"; | ||
var util = require('util') | ||
, events = require('events') | ||
, NULL | ||
, EVICT_REASONS | ||
; | ||
// @TODO get rid of NULL, no longer needed | ||
// @TODO allow keys to be more than just strings. Objects 'n' such | ||
EVICT_REASONS = { | ||
CAPACITY: 'capacity', | ||
EXPIRED: 'expired', | ||
MANUAL: 'manual' | ||
}; | ||
/** | ||
* options: | ||
* + populate - function to run when getting a value that doesn't exist in the cache | ||
* + expires - time in miliseconds that a key can stay in the cache (null for no expiration) | ||
* + capacity - maximum number of items in the cache, older items will be evicted to make space (null for no max) | ||
* + cleanup - time in miliseconds between running the cleanup function to expire old items | ||
*/ | ||
function Goldfish(options) { | ||
options = options || {}; | ||
this._populate = options.populate || null; | ||
this._expires = options.expires || null; | ||
this._capacity = options.capacity || null; | ||
this._cleanupPeriod = options.cleanup || 60000; | ||
this._cache = {}; | ||
this._newest = NULL; | ||
this._oldest = NULL; | ||
this._size = 0; | ||
this._cleanupInterval = null; | ||
this._fetching = {}; | ||
// magical thisness | ||
this._cleanup = this._cleanup.bind(this); | ||
} | ||
util.inherits(Goldfish, events.EventEmitter); | ||
/** | ||
* key - hashable value to fetch from cache or run populate function with | ||
* options: | ||
* + populate - override the populate function provided | ||
* + refresh - boolean whether or not to touch the key value so that it doesn't expire or get evicted for space (default: true) | ||
*/ | ||
Goldfish.prototype.get = function(key, cb, options) { | ||
options = options || {}; | ||
var populate = options.populate || this._populate | ||
, refresh = options.refresh ? true : false | ||
, expiresTime = this._expires ? this._expires + (new Date().getTime()) : -Infinity | ||
; | ||
// before reading from local cache, expire this key if it's too old | ||
if (this._cache.hasOwnProperty(key) && this._cache[key].refreshed < expiresTime) { | ||
this._evict(key, EVICT_REASONS.expired); | ||
} | ||
// comence cache retreival | ||
if (this._cache.hasOwnProperty(key)) { | ||
if (refresh) this._refresh(key); | ||
// do callback on next cpu event (allow caller to operate truely asynchronously) | ||
process.nextTick(cb.bind({}, null, this._cache[key].value)); | ||
} else { | ||
this._fetch(populate, key, cb); | ||
} | ||
}; | ||
/** | ||
* force the eviction of a key | ||
* key - hashable value to evict from local cache | ||
*/ | ||
Goldfish.prototype.evict = function(key, silent) { | ||
if (this._cache.hasOwnProperty(key)) { | ||
this._evict(key, EVICT_REASONS.MANUAL, silent); | ||
} | ||
}; | ||
Goldfish.prototype._fetch = function(populate, key, cb) { | ||
var self = this; | ||
if (this._fetching.hasOwnProperty(key)) { | ||
this._fetching[key].push(cb); | ||
} else { | ||
this._fetching[key] = [cb]; | ||
populate(key, function handlePopulateResponse(err, value) { | ||
var i; | ||
// add to cache if we got a value | ||
if (!err) self._insert(key, value); | ||
// notify everybody waiting on this result (error or value) | ||
for(i=0; i < self._fetching[key].length; ++i) { | ||
self._fetching[key][i](err, value); | ||
} | ||
// clear fetchers | ||
delete self._fetching[key]; | ||
}); | ||
} | ||
}; | ||
Goldfish.prototype._insert = function(key, value) { | ||
var entry | ||
, capacity = this._capacity ? this._capacity : Infinity | ||
; | ||
entry = { | ||
key: key, | ||
value: value, | ||
refreshed: new Date().getTime() | ||
}; | ||
this._pushEntry(entry); | ||
this._cache[key] = entry; | ||
++this._size; | ||
// evict oldest if we are now over capacity | ||
if (this._size > capacity) { | ||
this._evict(this._oldest.key, EVICT_REASONS.CAPACITY); | ||
} | ||
// start the cleanup thread if not already running | ||
// @TODO only run if evict time is set | ||
if(!this._cleanupInterval) { | ||
this._cleanupInterval = setInterval(this._cleanup, this._cleanupPeriod); | ||
} | ||
}; | ||
Goldfish.prototype._evict = function(key, reason, silent) { | ||
var entry = this._cache[key] | ||
, silent = silent ? true : false | ||
, env | ||
; | ||
if(!entry) return; | ||
this._pullEntry(entry); | ||
delete this._cache[key]; | ||
--this._size; | ||
env = { | ||
reason: reason, | ||
key: key, | ||
value: entry.value, | ||
refreshed: entry.refreshed | ||
}; | ||
// let people know about this eviction | ||
if(!silent) { | ||
this.emit('evict', env); | ||
this.emit('evict:' + env.reason, env); | ||
} | ||
// stop cleanup thread if we are now empty | ||
if(this._size === 0) { | ||
clearInterval(this._cleanupInterval); | ||
this._cleanupInterval = null; | ||
} | ||
}; | ||
Goldfish.prototype._pullEntry = function(entry) { | ||
// remove from middle | ||
if (entry.older !== NULL && entry.newer !== NULL) { | ||
entry.older.newer = entry.newer; | ||
entry.newer.older = entry.older; | ||
} | ||
// remove single entry | ||
if (this._oldest === entry && this._newest === entry) { | ||
this._oldest = NULL; | ||
this._newest = NULL; | ||
} | ||
// remove oldest in a series | ||
if (this._oldest === entry) { | ||
this._oldest = entry.newer; | ||
entry.newer.older = NULL; | ||
} | ||
// remove newest in a series | ||
if (this._newest === entry) { | ||
this._newest = entry.older; | ||
entry.older.newer = NULL; | ||
} | ||
}; | ||
Goldfish.prototype._pushEntry = function(entry) { | ||
// first entry if there is no newest | ||
if (this._newest === NULL) { | ||
this._newest = entry; | ||
this._oldest = entry; | ||
entry.newer = NULL; | ||
entry.older = NULL; | ||
} | ||
// adding to the front | ||
else { | ||
this._newest.newer = entry; | ||
entry.older = this._newest; | ||
entry.newer = NULL; | ||
this._newest = entry; | ||
} | ||
}; | ||
Goldfish.prototype._refresh = function(key) { | ||
var entry = this._cache[key]; | ||
this._pullEntry(entry); | ||
this._pushEntry(entry); | ||
entry.refreshed = new Date().getTime(); | ||
}; | ||
// starting from the oldest, remove while timestamp is expired | ||
Goldfish.prototype._cleanup = function() { | ||
var maxElapsed = this._expires ? this._expires : Infinity | ||
, now = new Date().getTime() | ||
; | ||
while (this._oldest && (now - this._oldest.refreshed) > maxElapsed) { | ||
this._evict(this._oldest.key, EVICT_REASONS.EXPIRED); | ||
} | ||
}; | ||
module.exports = Goldfish; | ||
module.exports = require('./lib/goldfish.js'); |
@@ -5,3 +5,3 @@ { | ||
"description": "Evented in-memory cache", | ||
"version": "0.0.8", | ||
"version": "0.1.0", | ||
"repository": { | ||
@@ -12,10 +12,20 @@ "type": "git", | ||
"dependencies": { | ||
"jsosort": "0.0.0", | ||
"moment": "~2.0.0" | ||
}, | ||
"devDependencies": { | ||
"mocha": "~1.4.0" | ||
"mocha": "~1.8.2", | ||
"grunt": "~0.4.0", | ||
"grunt-contrib-coffee": "~0.6.0", | ||
"grunt-contrib-clean": "~0.4.0", | ||
"chai": "~1.5.0", | ||
"sinon-chai": "~2.3.1", | ||
"sinon": "~1.6.0", | ||
"coffee-script": "~1.6.1", | ||
"async": "~0.2.6" | ||
}, | ||
"optionalDependencies": {}, | ||
"scripts": { | ||
"test": "./node_modules/.bin/mocha" | ||
"test": "grunt test" | ||
} | ||
} |
@@ -1,8 +0,33 @@ | ||
goldfish | ||
Goldfish | ||
======== | ||
[![Build Status](https://secure.travis-ci.org/SuperShabam/goldfish.png?branch=master)](http://travis-ci.org/SuperShabam/goldfish) | ||
Goldfish - the forgetful in-memory cache | ||
Evented JavaScript in-memory cache | ||
```javascript | ||
// _, _, | ||
// .' ( .-' / | ||
// _/..._'. .' / | ||
// .-'` ` '-./ _.' | ||
// ( o) ;= <_ | ||
// '-.,\\__ __.-;`\ '. | ||
// \) |`\ \) '. \ | ||
// \_/ jgs '-._\ | ||
// ` | ||
``` | ||
[![Build Status](https://secure.travis-ci.org/supershabam/goldfish.png?branch=master)](http://travis-ci.org/supershabam/goldfish) | ||
[![endorse](http://api.coderwall.com/supershabam/endorsecount.png)](http://coderwall.com/supershabam) | ||
Options | ||
======= | ||
```javascript | ||
Goldfish({ | ||
populate: // fn(arg1, arg2, ..., cb) | ||
expires: // (optional) Integer - miliseconds before a cache item is expired (default = Infinity) | ||
remind: // (optional) Boolean - refresh expire time on fetch (default = false) | ||
capacity: // (optional) Integer - max number of items to have in the cache (default = Infinity) | ||
}); | ||
``` | ||
Example | ||
@@ -35,14 +60,8 @@ ======= | ||
// listen for any evictions | ||
cache.on('evict', function(evict) { | ||
console.log(evict.key); // the key of the item being evicted | ||
console.log(evict.value); // the value of key that is being removed from the cache | ||
console.log(evict.reason); // the reason the eviction occured (manual, capacity, expired) | ||
cache.on('evict', function(entry) { | ||
console.log(entry.args); // Array - the args passed to populate resulting in this entry | ||
console.log(evict.result); // Array - the results from populate | ||
}); | ||
// can also listen for specific types of evictions | ||
cache.on('evict:manual', function(evict) { | ||
console.log(evict.key); | ||
}); | ||
cache.evict('test'); | ||
// clear the cache | ||
cache.clear(); | ||
``` | ||
@@ -54,4 +73,13 @@ | ||
**get#hit** O(1) | ||
**get#miss** O(1) + O(populate) | ||
**evict** O(1) | ||
**expire** O(1) | ||
**get#miss** O(1) + Populate() | ||
**clear** O(n) | ||
Changelog | ||
========= | ||
0.1.0 | ||
----- | ||
Complete disregard for the previous api. Don't blindly update. | ||
Smaller, simpler api. | ||
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Non-existent author
Supply chain riskThe package was published by an npm account that no longer exists.
Found 1 instance in 1 package
22375
12
84
0
2
9
201
1
1
+ Addedjsosort@0.0.0
+ Addedmoment@~2.0.0
+ Addedjsosort@0.0.0(transitive)
+ Addedmoment@2.0.0(transitive)