Major reorganization and partial refactoring

- Move out main modules from `lib` folder
- Introduce `normalizer` based configurations, and convert primitive and regular handlers into thos normalizers (can be found in lib/normalizers folder). Custom normalizers can be provided at run time via `normaizer` option
- Provide `plain` module which does not load any extensions or normalizers. Any extensions that have to be used should be required upfront and normalizers should be provided directly
- Remove `method` option, instead `methods` and `methods-plan` modules are provided which generate descriptors for lazy created memoized methods
- `profile` is no longer extension. To be used it should be required directly
- Improve logic for `async` handling
- Take out `max` extensionLRU logic into external `lru-queue` package
- Remove `context` option
- Remove possibility to access original arguments when resolvers are used
- Assure expected length of memoized functions
This commit is contained in:
Mariusz Nowak
2014-04-27 12:11:06 +02:00
parent ec114973d3
commit eb72d16bf6
61 changed files with 1424 additions and 1481 deletions

8
.lint
View File

@@ -1,10 +1,9 @@
@root
module
es5
indent 2
maxlen 80
maxlen 100
tabs
ass
@@ -14,8 +13,7 @@ nomen
./lib/_base.js
bitwise
./lib/ext/max-age.js
bitwise
./ext/max-age.js
predef+ setTimeout, clearTimeout
./lib/ext/max.js
@@ -25,8 +23,6 @@ predef+ setTimeout, clearTimeout
./test/index.js
predef+ setTimeout
./test/ext/max-age.js
predef+ setTimeout

1
.testignore Normal file
View File

@@ -0,0 +1 @@
/benchmark

View File

@@ -8,7 +8,7 @@
var forEach = require('es5-ext/object/for-each')
, pad = require('es5-ext/string/#/pad')
, memoizee = require('../lib')
, memoizee = require('..')
, underscore = require('underscore').memoize
, lodash = require('lodash').memoize
, lruCache = require('lru-cache')

140
ext/async.js Normal file
View File

@@ -0,0 +1,140 @@
// Support for asynchronous functions
'use strict';
var aFrom = require('es5-ext/array/from')
, mixin = require('es5-ext/object/mixin')
, defineLength = require('es5-ext/function/_define-length')
, nextTick = require('next-tick')
, slice = Array.prototype.slice
, apply = Function.prototype.apply, create = Object.create
, hasOwnProperty = Object.prototype.hasOwnProperty;
require('../lib/registered-extensions').async = function (tbi, conf) {
var waiting = create(null), cache = create(null)
, base = conf.memoized, original = conf.original
, currentCallback, currentContext, currentArgs;
// Initial
conf.memoized = defineLength(function (arg) {
var args = arguments, last = args[args.length - 1];
if (typeof last === 'function') {
currentCallback = last;
args = slice.call(args, 0, -1);
}
return base.apply(currentContext = this, currentArgs = args);
}, base);
try { mixin(conf.memoized, base); } catch (ignore) {}
// From cache (sync)
conf.on('get', function (id) {
var cb, context, args;
if (!currentCallback) return;
// Unresolved
if (waiting[id]) {
if (typeof waiting[id] === 'function') {
waiting[id] = [waiting[id], currentCallback];
} else {
waiting[id].push(currentCallback);
}
currentCallback = null;
return;
}
// Resolved, assure next tick invocation
cb = currentCallback;
context = currentContext;
args = currentArgs;
currentCallback = currentContext = currentArgs = null;
nextTick(function () {
var data;
if (hasOwnProperty.call(cache, id)) {
data = cache[id];
conf.emit('getasync', id);
apply.call(cb, data.context, data.args);
} else {
// Purged in a meantime, we shouldn't rely on cached value, recall
currentCallback = cb;
currentContext = context;
currentArgs = args;
base.apply(context, args);
}
});
});
// Not from cache
conf.original = function () {
var args, cb, origCb, result;
if (!currentCallback) return apply.call(original, this, arguments);
args = aFrom(arguments);
cb = function self(err) {
var cb, args, id = self.id;
if (id == null) {
// Shouldn't happen, means async callback was called sync way
nextTick(apply.bind(self, this, arguments));
return;
}
delete self.id;
cb = waiting[id];
delete waiting[id];
args = aFrom(arguments);
if (conf.has(id)) {
if (err) {
conf.delete(id);
} else {
cache[id] = { context: this, args: args };
conf.emit('setasync', id, (typeof cb === 'function') ? 1 : cb.length);
}
}
if (typeof cb === 'function') {
result = apply.call(cb, this, args);
} else {
cb.forEach(function (cb) {
result = apply.call(cb, this, args);
}, this);
}
return result;
};
origCb = currentCallback;
currentCallback = currentContext = currentArgs = null;
args.push(cb);
result = apply.call(original, this, args);
cb.cb = origCb;
currentCallback = cb;
return result;
};
// After not from cache call
conf.on('set', function (id) {
if (!currentCallback) {
conf.delete(id);
return;
}
waiting[id] = currentCallback.cb;
delete currentCallback.cb;
currentCallback.id = id;
currentCallback = null;
});
// On delete
conf.on('delete', function (id) {
var result;
// If false, we don't have value yet, so we assume that intention is not
// to memoize this call. After value is obtained we don't cache it but
// gracefully pass to callback
if (hasOwnProperty.call(waiting, id)) return;
if (!cache[id]) return;
result = cache[id];
delete cache[id];
conf.emit('deleteasync', id, result);
});
// On clear
conf.on('clear', function () {
var oldCache = cache;
cache = create(null);
conf.emit('clearasync', oldCache);
});
};

27
ext/dispose.js Normal file
View File

@@ -0,0 +1,27 @@
// Call dispose callback on each cache purge
'use strict';
var callable = require('es5-ext/object/valid-callable')
, forEach = require('es5-ext/object/for-each')
, extensions = require('../lib/registered-extensions')
, slice = Array.prototype.slice, apply = Function.prototype.apply;
extensions.dispose = function (dispose, conf, options) {
var del;
callable(dispose);
if (options.async && extensions.async) {
conf.on('deleteasync', del = function (id, result) {
apply.call(dispose, null, slice.call(result.args, 1));
});
conf.on('clearasync', function (cache) {
forEach(cache, function (result, id) { del(id, result); });
});
return;
}
conf.on('delete', del = function (id, result) { dispose(result); });
conf.on('clear', function (cache) {
forEach(cache, function (result, id) { del(id, result); });
});
};

64
ext/max-age.js Normal file
View File

@@ -0,0 +1,64 @@
// Timeout cached values
'use strict';
var forEach = require('es5-ext/object/for-each')
, timeout = require('timers-ext/valid-timeout')
, extensions = require('../lib/registered-extensions')
, max = Math.max, min = Math.min, create = Object.create;
extensions.maxAge = function (maxAge, conf, options) {
var timeouts, postfix, preFetchAge, preFetchTimeouts;
maxAge = timeout(maxAge);
if (!maxAge) return;
timeouts = create(null);
postfix = (options.async && extensions.async) ? 'async' : '';
conf.on('set' + postfix, function (id) {
timeouts[id] = setTimeout(function () { conf.clear(id); }, maxAge);
if (!preFetchTimeouts) return;
if (preFetchTimeouts[id]) clearTimeout(preFetchTimeouts[id]);
preFetchTimeouts[id] = setTimeout(function () {
delete preFetchTimeouts[id];
}, preFetchAge);
});
conf.on('delete' + postfix, function (id) {
clearTimeout(timeouts[id]);
delete timeouts[id];
if (!preFetchTimeouts) return;
clearTimeout(preFetchTimeouts[id]);
delete preFetchTimeouts[id];
});
if (options.preFetch) {
if ((options.preFetch === true) || isNaN(options.preFetch)) {
preFetchAge = 0.333;
} else {
preFetchAge = max(min(Number(options.preFetch), 1), 0);
}
if (preFetchAge) {
preFetchTimeouts = {};
preFetchAge = (1 - preFetchAge) * maxAge;
conf.on('get' + postfix, function (id, args, context) {
if (!preFetchTimeouts[id]) {
preFetchTimeouts[id] = setTimeout(function () {
delete preFetchTimeouts[id];
conf.delete(id);
conf.memoized.apply(context, args);
}, 0);
}
});
}
}
conf.on('clear' + postfix, function () {
forEach(timeouts, function (id) { clearTimeout(id); });
timeouts = {};
if (preFetchTimeouts) {
forEach(preFetchTimeouts, function (id) { clearTimeout(id); });
preFetchTimeouts = {};
}
});
};

26
ext/max.js Normal file
View File

@@ -0,0 +1,26 @@
// Limit cache size, LRU (least recently used) algorithm.
'use strict';
var toPosInteger = require('es5-ext/number/to-pos-integer')
, lruQueue = require('lru-queue')
, extensions = require('../lib/registered-extensions');
extensions.max = function (max, conf, options) {
var postfix, queue, hit;
max = toPosInteger(max);
if (!max) return;
queue = lruQueue(max);
postfix = (options.async && extensions.async) ? 'async' : '';
conf.on('set' + postfix, hit = function (id) {
id = queue.hit(id);
if (id === undefined) return;
conf.delete(id);
});
conf.on('get' + postfix, hit);
conf.on('delete' + postfix, queue.delete);
conf.on('clear' + postfix, queue.clear);
};

39
ext/ref-counter.js Normal file
View File

@@ -0,0 +1,39 @@
// Reference counter, useful for garbage collector like functionality
'use strict';
var d = require('d')
, extensions = require('../lib/registered-extensions')
, create = Object.create, defineProperties = Object.defineProperties;
extensions.refCounter = function (ignore, conf, options) {
var cache, postfix;
cache = create(null);
postfix = (options.async && extensions.async) ? 'async' : '';
conf.on('set' + postfix, function (id, length) { cache[id] = length || 1; });
conf.on('get' + postfix, function (id) { ++cache[id]; });
conf.on('delete' + postfix, function (id) { delete cache[id]; });
conf.on('clear' + postfix, function () { cache = {}; });
defineProperties(conf.memoized, {
deleteRef: d(function () {
var id = conf.get(arguments);
if (id === null) return null;
if (!cache[id]) return null;
if (!--cache[id]) {
conf.delete(id);
return true;
}
return false;
}),
getRefCount: d(function () {
var id = conf.get(arguments);
if (id === null) return 0;
if (!cache[id]) return 0;
return cache[id];
})
});
};

35
index.js Normal file
View File

@@ -0,0 +1,35 @@
'use strict';
var normalizeOpts = require('es5-ext/object/normalize-options')
, resolveLength = require('./lib/resolve-length')
, plain = require('./plain');
module.exports = function (fn/*, options*/) {
var options = normalizeOpts(arguments[1]), length;
if (!options.normalizer && !options.serialize) {
length = options.length = resolveLength(options.length, fn.length, options.async);
if (length === 0) {
options.normalizer = require('./lib/normalizers/0');
} else if (options.primitive) {
if (length === false) {
options.normalizer = require('./lib/normalizers/primitive');
} else if (length > 1) {
options.normalizer = require('./lib/normalizers/get-primitive-fixed')(length);
}
} else {
if (length === false) options.normalizer = require('./lib/normalizers/get-regular')();
else if (length === 1) options.normalizer = require('./lib/normalizers/get-regular-1')();
else options.normalizer = require('./lib/normalizers/get-regular-fixed')(length);
}
}
// Assure extensions
if (options.async) require('./ext/async');
if (options.dispose) require('./ext/dispose');
if (options.maxAge) require('./ext/max-age');
if (options.max) require('./ext/max');
if (options.refCounter) require('./ext/ref-counter');
return plain(fn, options);
};

View File

@@ -1,57 +0,0 @@
// To be used internally, memoize factory
'use strict';
var callable = require('es5-ext/object/valid-callable')
, forEach = require('es5-ext/object/for-each')
, ee = require('event-emitter')
, ext;
module.exports = exports = function (core) {
return function self(fn/*, options */) {
var options, length, get, clear, conf;
callable(fn);
options = Object(arguments[1]);
// Do not memoize already memoized function
if (fn.memoized && !options.force) return fn;
if (ext.method && (options.method != null)) {
return ext.method(options.method, options, fn, self);
}
conf = ee({ memoize: self, fn: fn });
// Normalize length
if (isNaN(options.length)) {
length = fn.length;
// Special case
if (options.async && ext.async) --length;
} else {
length = (options.length === false) ? false : (options.length >>> 0);
}
core(conf, length, options);
forEach(ext, function (fn, name) {
if (fn.force) fn(conf, options);
else if (options[name]) fn(options[name], conf, options);
});
fn = conf.fn;
get = conf.get;
clear = conf.clear;
conf.memoized.clear = function () { clear(get(arguments)); };
conf.memoized.clearAll = function () {
conf.emit('purgeall');
conf.clearAll();
};
conf.memoized.memoized = true;
conf.emit('ready');
return conf.memoized;
};
};
ext = exports.ext = {};

144
lib/configure-map.js Normal file
View File

@@ -0,0 +1,144 @@
'use strict';
var toArray = require('es5-ext/array/to-array')
, customError = require('es5-ext/error/custom')
, defineLength = require('es5-ext/function/_define-length')
, callable = require('es5-ext/object/valid-callable')
, d = require('d')
, ee = require('event-emitter').methods
, slice = Array.prototype.slice
, apply = Function.prototype.apply, call = Function.prototype.call
, create = Object.create, hasOwnProperty = Object.prototype.hasOwnProperty
, defineProperties = Object.defineProperties
, on = ee.on, emit = ee.emit, resolveArgs;
resolveArgs = function (args) {
return this.map(function (r, i) {
return r ? r(args[i]) : args[i];
}).concat(slice.call(args, this.length));
};
module.exports = function (original, length, options) {
var cache = create(null), conf, memLength, get, set, del, clear
, getListeners, setListeners, deleteListeners, memoized, resolve, resolvers;
if (length !== false) memLength = length;
else if (isNaN(original.length)) memLength = 1;
else memLength = original.length;
if (options.normalizer) {
get = callable(options.normalizer.get);
if (options.normalizer.set !== undefined) {
set = callable(options.normalizer.set);
del = callable(options.normalizer.delete);
clear = callable(options.normalizer.clear);
} else {
set = get;
}
} else if (options.serialize) {
set = get = (function (serialize) {
return function (args) { return serialize.apply(null, args); };
}(callable(options.serialize)));
}
if (options.resolvers != null) {
resolvers = toArray(options.resolvers);
resolvers.forEach(function (r) {
if (r != null) callable(r);
});
resolve = resolveArgs.bind(resolvers);
}
if (get) {
memoized = defineLength(function (arg) {
var id, result, args = arguments;
if (resolve) args = resolve(args);
id = get(args);
if (id !== null) {
if (hasOwnProperty.call(cache, id)) {
if (getListeners) conf.emit('get', id, args, this);
return cache[id];
}
}
if (args.length === 1) result = call.call(original, this, arg);
else result = apply.call(original, this, args);
if (id === null) {
id = get(args);
if (id !== null) throw customError("Circular invocation", 'CIRCULAR_INVOCATION');
id = set(args);
} else if (hasOwnProperty.call(cache, id)) {
throw customError("Circular invocation", 'CIRCULAR_INVOCATION');
}
cache[id] = result;
if (setListeners) conf.emit('set', id);
return result;
}, memLength);
} else {
memoized = function (arg) {
var result, args = arguments;
if (resolve) {
args = resolve(arguments);
arg = args[0];
}
if (hasOwnProperty.call(cache, arg)) {
if (getListeners) conf.emit('get', arg, args, this);
return cache[arg];
}
if (args.length === 1) result = call.call(original, this, arg);
else result = apply.call(original, this, args);
if (hasOwnProperty.call(cache, arg)) {
throw customError("Circular invocation", 'CIRCULAR_INVOCATION');
}
cache[arg] = result;
if (setListeners) conf.emit('set', arg);
return result;
};
}
conf = {
original: original,
memoized: memoized,
get: function (args) {
if (resolve) args = resolve(args);
if (get) return get(args);
return args[0];
},
has: function (id) { return hasOwnProperty.call(cache, id); },
delete: function (id) {
var result;
if (!hasOwnProperty.call(cache, id)) return;
if (del) del(id);
result = cache[id];
delete cache[id];
if (deleteListeners) conf.emit('delete', id, result);
},
clear: function () {
var oldCache = cache;
if (clear) clear();
cache = create(null);
conf.emit('clear', oldCache);
},
on: function (type, listener) {
if (type === 'get') getListeners = true;
else if (type === 'set') setListeners = true;
else if (type === 'delete') deleteListeners = true;
return on.call(this, type, listener);
},
emit: emit,
updateEnv: function () { original = conf.original; }
};
defineProperties(memoized, {
__memoized__: d(true),
delete: d(get ? defineLength(function (arg) {
var id, args = arguments;
if (resolve) args = resolve(args);
id = get(args);
if (id === null) return;
conf.delete(id);
}, memLength) : function (arg) {
if (resolve) arg = resolve(arguments)[0];
return conf.delete(arg);
}),
clear: d(conf.clear)
});
return conf;
};

View File

@@ -1,20 +0,0 @@
'use strict';
var forEach = require('es5-ext/object/for-each')
, callable = require('es5-ext/object/valid-callable')
, lazy = require('d/lazy')
, a = [], b = [];
module.exports = function (fn) {
var index = a.indexOf(callable(fn));
if (index !== -1) return b[index];
index = a.push(fn);
return (b[index - 1] = function (props) {
forEach(props, function (desc, name) {
var value = callable(desc.value);
desc.value = function (options) { return fn(value.bind(this), options); };
});
return lazy(props);
});
};

View File

@@ -1,111 +0,0 @@
// Support for asynchronous functions
'use strict';
var aFrom = require('es5-ext/array/from')
, last = require('es5-ext/array/#/last')
, isArguments = require('es5-ext/function/is-arguments')
, forEach = require('es5-ext/object/for-each')
, isCallable = require('es5-ext/object/is-callable')
, nextTick = require('next-tick')
, isArray = Array.isArray, slice = Array.prototype.slice
, apply = Function.prototype.apply;
require('../_base').ext.async = function (ignore, conf) {
var cache, purge;
cache = conf.async = {};
(function (org) {
var value, cb, initContext, initArgs, fn, resolver;
conf.on('init', function (id) {
value.id = id;
cache[id] = cb ? [cb] : [];
});
conf.on('hit', function (id, syncArgs, syncCtx) {
if (!cb) {
return;
}
if (isArray(cache[id])) {
cache[id].push(cb);
} else {
nextTick(function (cb, id, ctx, args) {
if (cache[id]) {
conf.emit('hitasync', id, syncArgs, syncCtx);
apply.call(cb, this.context, this);
} else {
// Purged in a meantime, we shouldn't rely on cached value, recall
fn.apply(ctx, args);
}
}.bind(cache[id], cb, id, initContext, initArgs));
initContext = initArgs = null;
}
});
conf.fn = function () {
var args, asyncArgs;
args = arguments;
asyncArgs = aFrom(args);
asyncArgs.push(value = function self(err) {
var i, cb, waiting, res;
if (self.id == null) {
// Shouldn't happen, means async callback was called sync way
nextTick(apply.bind(self, this, arguments));
return;
}
waiting = cache[self.id];
if (conf.cache.hasOwnProperty(self.id)) {
if (err) {
delete cache[self.id];
conf.clear(self.id);
} else {
arguments.context = this;
cache[self.id] = arguments;
conf.emit('initasync', self.id, waiting.length);
}
} else {
delete cache[self.id];
}
for (i = 0; (cb = waiting[i]); ++i) {
res = apply.call(cb, this, arguments);
}
return res;
});
return apply.call(org, this, asyncArgs);
};
fn = conf.memoized;
resolver = function (args) {
cb = last.call(args);
if (isCallable(cb)) return slice.call(args, 0, -1);
cb = null;
return args;
};
conf.memoized = function () {
return fn.apply(initContext = this, initArgs = resolver(arguments));
};
forEach(fn, function (value, name) {
conf.memoized[name] = function () {
return fn[name].apply(this, resolver(arguments));
};
});
}(conf.fn));
conf.on('purge', purge = function (id) {
// If false, we don't have value yet, so we assume that intention is not
// to memoize this call. After value is obtained we don't cache it but
// gracefully pass to callback
if (isArguments(cache[id])) {
conf.emit('purgeasync', id);
delete cache[id];
}
});
conf.on('purgeall', function () {
forEach(conf.async, function (value, id) { purge(id); });
});
};

View File

@@ -1,33 +0,0 @@
// Call dispose callback on each cache purge
'use strict';
var callable = require('es5-ext/object/valid-callable')
, forEach = require('es5-ext/object/for-each')
, ext = require('../_base').ext
, slice = Array.prototype.slice;
ext.dispose = function (dispose, conf, options) {
var clear, async, context;
callable(dispose);
async = (options.async && ext.async);
context = options.context;
conf.on('purge' + (async ? 'async' : ''), clear = async ? function (id) {
var value = conf.async[id];
delete conf.cache[id];
dispose.apply(context, slice.call(value, 1));
} : function (id) {
var value = conf.cache[id];
delete conf.cache[id];
dispose.call(context, value);
});
if (!async) {
conf.on('purgeall', function () {
forEach(conf.cache, function (value, id) { clear(id); });
});
}
};

View File

@@ -1,76 +0,0 @@
// Timeout cached values
'use strict';
var isNumber = require('es5-ext/number/is-number')
, forEach = require('es5-ext/object/for-each')
, nextTick = require('next-tick')
, ext = require('../_base').ext
, max = Math.max, min = Math.min;
ext.maxAge = function (maxAge, conf, options) {
var cache, async, preFetchAge, preFetchCache;
maxAge = maxAge >>> 0;
if (!maxAge) {
return;
}
cache = {};
async = options.async && ext.async;
conf.on('init' + (async ? 'async' : ''), function (id) {
cache[id] = setTimeout(function () { conf.clear(id); }, maxAge);
if (preFetchCache) {
preFetchCache[id] = setTimeout(function () { delete preFetchCache[id]; },
preFetchAge);
}
});
conf.on('purge' + (async ? 'async' : ''), function (id) {
clearTimeout(cache[id]);
if (preFetchCache && preFetchCache[id]) {
clearTimeout(preFetchCache[id]);
delete preFetchCache[id];
}
delete cache[id];
});
if (options.preFetch) {
if (isNumber(options.preFetch)) {
preFetchAge = max(min(Number(options.preFetch), 1), 0);
} else {
preFetchAge = 0.333;
}
if (preFetchAge) {
preFetchCache = {};
preFetchAge = (1 - preFetchAge) * maxAge;
conf.on('hit' + (async ? 'async' : ''), function (id, args, ctx) {
if (!preFetchCache[id]) {
preFetchCache[id] = true;
nextTick(function () {
if (preFetchCache[id] === true) {
delete preFetchCache[id];
conf.clear(id);
conf.memoized.apply(ctx, args);
}
});
}
});
}
}
if (!async) {
conf.on('purgeall', function () {
forEach(cache, function (id) {
clearTimeout(id);
});
cache = {};
if (preFetchCache) {
forEach(preFetchCache, function (id) {
clearTimeout(id);
});
preFetchCache = {};
}
});
}
};

View File

@@ -1,62 +0,0 @@
// Limit cache size, LRU (least recently used) algorithm.
'use strict';
var ext = require('../_base').ext;
ext.max = function (max, conf, options) {
var index, base, size, queue, map, async;
max = max >>> 0;
if (!max) {
return;
}
index = -1;
base = size = 0;
queue = {};
map = {};
async = options.async && ext.async;
conf.on('init' + (async ? 'async' : ''), function (id) {
queue[++index] = id;
map[id] = index;
++size;
if (size > max) {
conf.clear(queue[base]);
}
});
conf.on('hit' + (async ? 'async' : ''), function (id) {
var oldIndex = map[id];
queue[++index] = id;
map[id] = index;
delete queue[oldIndex];
if (base === oldIndex) {
while (!queue.hasOwnProperty(++base)) continue; //jslint: skip
}
});
conf.on('purge' + (async ? 'async' : ''), function (id) {
var oldIndex = map[id];
delete queue[oldIndex];
--size;
if (base === oldIndex) {
if (!size) {
index = -1;
base = 0;
} else {
while (!queue.hasOwnProperty(++base)) continue; //jslint: skip
}
}
});
if (!async) {
conf.on('purgeall', function () {
index = -1;
base = size = 0;
queue = {};
map = {};
});
}
};

View File

@@ -1,42 +0,0 @@
// Memoized methods factory
'use strict';
var copy = require('es5-ext/object/copy')
, defineProperty = Object.defineProperty;
require('../_base').ext.method = function (method, options, fn, configure) {
var name, descriptor, props, prop, selfName;
name = selfName = String(method);
descriptor = {
enumerable: (options.enumerable == null) ? false :
Boolean(options.enumerable),
configurable: (options.configurable == null) ? true :
Boolean(options.configurable),
writable: (options.writable == null) ? true :
Boolean(options.writable)
};
props = {};
prop = props[selfName] = copy(descriptor);
delete prop.writable;
if (options.protoDeep != null) {
if (typeof options.protoDeep === 'boolean') {
if (options.protoDeep) name = '_' + name + '_';
} else {
name = String(options.protoDeep);
}
}
options = copy(options);
delete options.method;
delete options.protoDeep;
prop.get = function () {
if ((name !== selfName) && this.hasOwnProperty(name)) return this[name];
options.context = this;
descriptor.value = configure(fn.bind(this), options);
defineProperty(this, name, descriptor);
return this[name];
};
return props;
};

View File

@@ -1,40 +0,0 @@
// Reference counter, useful for garbage collector like functionality
'use strict';
var ext = require('../_base').ext;
ext.refCounter = function (ignore, conf, options) {
var cache, async;
cache = {};
async = options.async && ext.async;
conf.on('init' + (async ? 'async' : ''), async ? function (id, length) {
cache[id] = length;
} : function (id) { cache[id] = 1; });
conf.on('hit' + (async ? 'async' : ''), function (id) { ++cache[id]; });
conf.on('purge' + (async ? 'async' : ''), function (id) {
delete cache[id];
});
if (!async) {
conf.on('purgeall', function () { cache = {}; });
}
conf.memoized.clearRef = function () {
var id = conf.get(arguments);
if (cache.hasOwnProperty(id)) {
if (!--cache[id]) {
conf.clear(id);
return true;
}
return false;
}
return null;
};
conf.memoized.getRefCount = function () {
var id = conf.get(arguments);
if (!cache.hasOwnProperty(id)) return 0;
return cache[id];
};
};

View File

@@ -1,42 +0,0 @@
// Normalize arguments before passing them to underlying function
'use strict';
var toArray = require('es5-ext/array/to-array')
, forEach = require('es5-ext/object/for-each')
, callable = require('es5-ext/object/valid-callable')
, slice = Array.prototype.slice
, resolve;
resolve = function (args) {
return this.map(function (r, i) {
return r ? r(args[i]) : args[i];
}).concat(slice.call(args, this.length));
};
require('../_base').ext.resolvers = function (resolvers, conf) {
var resolver;
resolver = toArray(resolvers);
resolver.forEach(function (r) {
((r == null) || callable(r));
});
resolver = resolve.bind(resolver);
(function (fn) {
conf.memoized = function () {
var value;
conf.memoized.args = arguments;
value = fn.apply(this, resolver(arguments));
delete conf.memoized.args;
return value;
};
forEach(fn, function (value, name) {
conf.memoized[name] = function () {
return fn[name].apply(this, resolver(arguments));
};
});
}(conf.memoized));
};

View File

@@ -1,23 +0,0 @@
// Provides memoize with all options
'use strict';
var regular = require('./regular')
, primitive = require('./primitive')
, call = Function.prototype.call;
// Order is significant!
require('./ext/dispose');
require('./ext/resolvers');
require('./ext/async');
require('./ext/ref-counter');
require('./ext/method');
require('./ext/max-age');
require('./ext/max');
module.exports = function (fn/* options */) {
var options = Object(arguments[1]);
return call.call((options.primitive || options.serialize) ?
primitive : regular, this, fn, options);
};

15
lib/methods.js Normal file
View File

@@ -0,0 +1,15 @@
'use strict';
var forEach = require('es5-ext/object/for-each')
, callable = require('es5-ext/object/valid-callable')
, lazy = require('d/lazy');
module.exports = function (memoize) {
return function (props) {
forEach(props, function (desc, name) {
var fn = callable(desc.value);
desc.value = function (options) { return memoize(fn.bind(this), options); };
});
return lazy(props);
};
};

5
lib/normalizers/0.js Normal file
View File

@@ -0,0 +1,5 @@
'use strict';
var k = require('es5-ext/function/constant');
module.exports = { get: k('') };

View File

@@ -0,0 +1,12 @@
'use strict';
module.exports = function (length) {
if (!length) {
return { get: function () { return ''; } };
}
return { get: function (args) {
var id = String(args[0]), i = 0, l = length;
while (--l) { id += '\u0001' + args[++i]; }
return id;
} };
};

View File

@@ -0,0 +1,30 @@
'use strict';
var indexOf = require('es5-ext/array/#/e-index-of');
module.exports = function () {
var lastId = 0, argsMap = [], cache = [];
return {
get: function (args) {
var index = indexOf.call(argsMap, args[0]);
return (index === -1) ? null : cache[index];
},
set: function (args) {
argsMap.push(args[0]);
cache.push(++lastId);
return lastId;
},
delete: function (id) {
var index = indexOf.call(cache, id);
if (index !== -1) {
argsMap.splice(index, 1);
cache.splice(index, 1);
}
},
clear: function () {
argsMap = [];
cache = [];
lastId = 0;
}
};
};

View File

@@ -0,0 +1,72 @@
'use strict';
var indexOf = require('es5-ext/array/#/e-index-of')
, create = Object.create;
module.exports = function (length) {
var lastId = 0, map = [[], []], cache = create(null);
return {
get: function (args) {
var index = 0, set = map, i;
while (index < (length - 1)) {
i = indexOf.call(set[0], args[index]);
if (i === -1) return null;
set = set[1][i];
++index;
}
i = indexOf.call(set[0], args[index]);
if (i === -1) return null;
return set[1][i] || null;
},
set: function (args) {
var index = 0, set = map, i;
while (index < (length - 1)) {
i = indexOf.call(set[0], args[index]);
if (i === -1) {
i = set[0].push(args[index]) - 1;
set[1].push([[], []]);
}
set = set[1][i];
++index;
}
i = indexOf.call(set[0], args[index]);
if (i === -1) {
i = set[0].push(args[index]) - 1;
}
set[1][i] = ++lastId;
cache[lastId] = args;
return lastId;
},
delete: function (id) {
var index = 0, set = map, i, path = [], args = cache[id];
while (index < (length - 1)) {
i = indexOf.call(set[0], args[index]);
if (i === -1) {
return;
}
path.push(set, i);
set = set[1][i];
++index;
}
i = indexOf.call(set[0], args[index]);
if (i === -1) {
return;
}
id = set[1][i];
set[0].splice(i, 1);
set[1].splice(i, 1);
while (!set[0].length && path.length) {
i = path.pop();
set = path.pop();
set[0].splice(i, 1);
set[1].splice(i, 1);
}
delete cache[id];
},
clear: function () {
map = [[], []];
cache = create(null);
lastId = 0;
}
};
};

View File

@@ -0,0 +1,89 @@
'use strict';
var indexOf = require('es5-ext/array/#/e-index-of')
, create = Object.create;
module.exports = function () {
var lastId = 0, map = [], cache = create(null);
return {
get: function (args) {
var index = 0, set = map, i, length = args.length;
if (length === 0) return set[length] || null;
if ((set = set[length])) {
while (index < (length - 1)) {
i = indexOf.call(set[0], args[index]);
if (i === -1) return null;
set = set[1][i];
++index;
}
i = indexOf.call(set[0], args[index]);
if (i === -1) return null;
return set[1][i] || null;
}
return null;
},
set: function (args) {
var index = 0, set = map, i, length = args.length;
if (length === 0) {
set[length] = ++lastId;
} else {
if (!set[length]) {
set[length] = [[], []];
}
set = set[length];
while (index < (length - 1)) {
i = indexOf.call(set[0], args[index]);
if (i === -1) {
i = set[0].push(args[index]) - 1;
set[1].push([[], []]);
}
set = set[1][i];
++index;
}
i = indexOf.call(set[0], args[index]);
if (i === -1) {
i = set[0].push(args[index]) - 1;
}
set[1][i] = ++lastId;
}
cache[lastId] = args;
return lastId;
},
delete: function (id) {
var index = 0, set = map, i, args = cache[id], length = args.length
, path = [];
if (length === 0) {
delete set[length];
} else if ((set = set[length])) {
while (index < (length - 1)) {
i = indexOf.call(set[0], args[index]);
if (i === -1) {
return;
}
path.push(set, i);
set = set[1][i];
++index;
}
i = indexOf.call(set[0], args[index]);
if (i === -1) {
return;
}
id = set[1][i];
set[0].splice(i, 1);
set[1].splice(i, 1);
while (!set[0].length && path.length) {
i = path.pop();
set = path.pop();
set[0].splice(i, 1);
set[1].splice(i, 1);
}
}
delete cache[id];
},
clear: function () {
map = [];
cache = create(null);
lastId = 0;
}
};
};

View File

@@ -0,0 +1,9 @@
'use strict';
module.exports = { get: function (args) {
var id, i, length = args.length;
if (!length) return '\u0002';
id = String(args[i = 0]);
while (--length) id += '\u0001' + args[++i];
return id;
} };

View File

@@ -1,90 +0,0 @@
// Memoize working in primitive mode
'use strict';
var customError = require('es5-ext/error/custom')
, callable = require('es5-ext/object/valid-callable')
, hasListeners = require('event-emitter/has-listeners')
, serialize0 = function () { return ''; }
, serialize1 = function (args) { return args[0]; }
, apply = Function.prototype.apply, call = Function.prototype.call
, serializeN;
serializeN = function (args) {
var id = '', i, length = args.length;
if (length) {
id += args[i = 0];
while (--length) id += '\u0001' + args[++i];
} else {
id = '\u0002';
}
return id;
};
module.exports = require('./_base')(function (conf, length, options) {
var get, cache = conf.cache = {}, fn
, hitListeners, initListeners, purgeListeners, serialize;
if (options.serialize) {
serialize = callable(options.serialize);
get = conf.get = function (args) { return serialize.apply(this, args); };
} else if (length === 1) {
get = conf.get = serialize1;
} else if (length === false) {
get = conf.get = serializeN;
} else if (length) {
get = conf.get = function (args) {
var id = String(args[0]), i = 0, l = length;
while (--l) { id += '\u0001' + args[++i]; }
return id;
};
} else {
get = conf.get = serialize0;
}
conf.memoized = (length === 1) ? function (id) {
var value;
if (cache.hasOwnProperty(id)) {
if (hitListeners) conf.emit('hit', id, arguments, this);
return cache[id];
}
if (arguments.length === 1) value = call.call(fn, this, id);
else value = apply.call(fn, this, arguments);
if (cache.hasOwnProperty(id)) {
throw customError("Circular invocation", 'CIRCULAR_INVOCATION');
}
cache[id] = value;
if (initListeners) conf.emit('init', id);
return value;
} : function () {
var id = get(arguments), value;
if (cache.hasOwnProperty(id)) {
if (hitListeners) conf.emit('hit', id, arguments, this);
return cache[id];
}
value = apply.call(conf.fn, this, arguments);
if (cache.hasOwnProperty(id)) {
throw customError("Circular invocation", 'CIRCULAR_INVOCATION');
}
cache[id] = value;
if (initListeners) conf.emit('init', id);
return value;
};
conf.clear = function (id) {
if (cache.hasOwnProperty(id)) {
if (purgeListeners) conf.emit('purge', id);
delete cache[id];
}
};
conf.clearAll = function () { cache = conf.cache = {}; };
conf.once('ready', function () {
fn = conf.fn;
hitListeners = hasListeners(conf, 'hit');
initListeners = hasListeners(conf, 'init');
purgeListeners = hasListeners(conf, 'purge');
});
});

View File

@@ -0,0 +1 @@
'use strict';

View File

@@ -1,246 +0,0 @@
// Memoize working in object mode (supports any type of arguments)
'use strict';
var customError = require('es5-ext/error/custom')
, indexOf = require('es5-ext/array/#/e-index-of')
, hasListeners = require('event-emitter/has-listeners')
, apply = Function.prototype.apply;
// Results are saved internally within array matrix:
// [0] -> Result of calling function with no arguments
// [1] -> Matrix that keeps results when function is called with one argument
// [1][0] -> Array of arguments with which
// function have been called
// [1][1] -> Array of results that matches [1][0] array
// [2] -> Matrix that keeps results when function is called with two arguments
// [2][0] -> Array of first (of two) arguments with which
// function have been called
// [2][1] -> Matrixes that keeps results for two arguments function calls
// Each matrix matches first argument found in [2][0]
// [2][1][x][0] -> Array of second arguments with which
// function have been called.
// [2][1][x][1] -> Array of results that matches [2][1][x][0]
// arguments array
// ...and so on
module.exports = require('./_base')(function (conf, length) {
var map, map1, map2, get, set, clear, count, fn
, hitListeners, initListeners, purgeListeners
, cache = conf.cache = {}, argsCache;
if (length === 0) {
map = null;
get = conf.get = function () { return map; };
set = function () { return ((map = 1)); };
clear = function () { map = null; };
conf.clearAll = function () {
map = null;
cache = conf.cache = {};
};
} else {
count = 0;
if (length === 1) {
map1 = [];
map2 = [];
get = conf.get = function (args) {
var index = indexOf.call(map1, args[0]);
return (index === -1) ? null : map2[index];
};
set = function (args) {
map1.push(args[0]);
map2.push(++count);
return count;
};
clear = function (id) {
var index = indexOf.call(map2, id);
if (index !== -1) {
map1.splice(index, 1);
map2.splice(index, 1);
}
};
conf.clearAll = function () {
map1 = [];
map2 = [];
cache = conf.cache = {};
};
} else if (length === false) {
map = [];
argsCache = {};
get = conf.get = function (args) {
var index = 0, set = map, i, length = args.length;
if (length === 0) return set[length] || null;
if ((set = set[length])) {
while (index < (length - 1)) {
i = indexOf.call(set[0], args[index]);
if (i === -1) return null;
set = set[1][i];
++index;
}
i = indexOf.call(set[0], args[index]);
if (i === -1) return null;
return set[1][i] || null;
}
return null;
};
set = function (args) {
var index = 0, set = map, i, length = args.length;
if (length === 0) {
set[length] = ++count;
} else {
if (!set[length]) {
set[length] = [[], []];
}
set = set[length];
while (index < (length - 1)) {
i = indexOf.call(set[0], args[index]);
if (i === -1) {
i = set[0].push(args[index]) - 1;
set[1].push([[], []]);
}
set = set[1][i];
++index;
}
i = indexOf.call(set[0], args[index]);
if (i === -1) {
i = set[0].push(args[index]) - 1;
}
set[1][i] = ++count;
}
argsCache[count] = args;
return count;
};
clear = function (id) {
var index = 0, set = map, i, args = argsCache[id], length = args.length
, path = [];
if (length === 0) {
delete set[length];
} else if ((set = set[length])) {
while (index < (length - 1)) {
i = indexOf.call(set[0], args[index]);
if (i === -1) {
return;
}
path.push(set, i);
set = set[1][i];
++index;
}
i = indexOf.call(set[0], args[index]);
if (i === -1) {
return;
}
id = set[1][i];
set[0].splice(i, 1);
set[1].splice(i, 1);
while (!set[0].length && path.length) {
i = path.pop();
set = path.pop();
set[0].splice(i, 1);
set[1].splice(i, 1);
}
}
delete argsCache[id];
};
conf.clearAll = function () {
map = [];
cache = conf.cache = {};
argsCache = {};
};
} else {
map = [[], []];
argsCache = {};
get = conf.get = function (args) {
var index = 0, set = map, i;
while (index < (length - 1)) {
i = indexOf.call(set[0], args[index]);
if (i === -1) return null;
set = set[1][i];
++index;
}
i = indexOf.call(set[0], args[index]);
if (i === -1) return null;
return set[1][i] || null;
};
set = function (args) {
var index = 0, set = map, i;
while (index < (length - 1)) {
i = indexOf.call(set[0], args[index]);
if (i === -1) {
i = set[0].push(args[index]) - 1;
set[1].push([[], []]);
}
set = set[1][i];
++index;
}
i = indexOf.call(set[0], args[index]);
if (i === -1) {
i = set[0].push(args[index]) - 1;
}
set[1][i] = ++count;
argsCache[count] = args;
return count;
};
clear = function (id) {
var index = 0, set = map, i, path = [], args = argsCache[id];
while (index < (length - 1)) {
i = indexOf.call(set[0], args[index]);
if (i === -1) {
return;
}
path.push(set, i);
set = set[1][i];
++index;
}
i = indexOf.call(set[0], args[index]);
if (i === -1) {
return;
}
id = set[1][i];
set[0].splice(i, 1);
set[1].splice(i, 1);
while (!set[0].length && path.length) {
i = path.pop();
set = path.pop();
set[0].splice(i, 1);
set[1].splice(i, 1);
}
delete argsCache[id];
};
conf.clearAll = function () {
map = [[], []];
cache = conf.cache = {};
argsCache = {};
};
}
}
conf.memoized = function () {
var id = get(arguments), value;
if (id != null) {
if (hitListeners) conf.emit('hit', id, arguments, this);
return cache[id];
}
value = apply.call(fn, this, arguments);
id = get(arguments);
if (id != null) {
throw customError("Circular invocation", 'CIRCULAR_INVOCATION');
}
id = set(arguments);
cache[id] = value;
if (initListeners) conf.emit('init', id);
return value;
};
conf.clear = function (id) {
if (cache.hasOwnProperty(id)) {
if (purgeListeners) conf.emit('purge', id);
clear(id);
delete cache[id];
}
};
conf.once('ready', function () {
fn = conf.fn;
hitListeners = hasListeners(conf, 'hit');
initListeners = hasListeners(conf, 'init');
purgeListeners = hasListeners(conf, 'purge');
});
});

15
lib/resolve-length.js Normal file
View File

@@ -0,0 +1,15 @@
'use strict';
var toPosInt = require('es5-ext/number/to-pos-integer');
module.exports = function (optsLength, fnLength, isAsync) {
var length;
if (isNaN(optsLength)) {
length = fnLength;
if (!(length >= 0)) return 1;
if (isAsync && length) return length - 1;
return length;
}
if (optsLength === false) return false;
return toPosInt(optsLength);
};

3
methods-plain.js Normal file
View File

@@ -0,0 +1,3 @@
'use strict';
module.exports = require('./lib/methods')(require('./plain'));

3
methods.js Normal file
View File

@@ -0,0 +1,3 @@
'use strict';
module.exports = require('./lib/methods')(require('./'));

View File

@@ -1,15 +1,8 @@
{
"name": "memoizee",
"version": "0.3.0",
"description": "Complete memoize/cache solution. Works with any type and length of function arguments",
"main": "lib",
"scripts": {
"test": "node node_modules/tad/bin/tad lib"
},
"repository": {
"type": "git",
"url": "git://github.com/medikoo/memoize.git"
},
"description": "Memoize/cache",
"author": "Mariusz Nowak <medikoo@medikoo.com> (http://www.medikoo.com/)",
"keywords": [
"memoize",
"memoizer",
@@ -26,22 +19,23 @@
"garbage",
"collector"
],
"bugs": {
"email": "medikoo+memoize@medikoo.com",
"url": "https://github.com/medikoo/memoize/issues"
},
"engines": {
"node": ">=0.4"
"repository": {
"type": "git",
"url": "git://github.com/medikoo/memoize.git"
},
"dependencies": {
"d": "~0.1.1",
"es5-ext": "~0.10.2",
"event-emitter": "0.3.x",
"next-tick": "~0.2.2"
"event-emitter": "~0.3.1",
"lru-queue": "0.1.x",
"next-tick": "~0.2.2",
"timers-ext": "0.1.x"
},
"devDependencies": {
"tad": "~0.1.21"
},
"author": "Mariusz Nowak <medikoo+memoize@medikoo.com> (http://www.medikoo.com/)",
"scripts": {
"test": "node node_modules/tad/bin/tad"
},
"license": "MIT"
}

37
plain.js Normal file
View File

@@ -0,0 +1,37 @@
// To be used internally, memoize factory
'use strict';
var callable = require('es5-ext/object/valid-callable')
, forEach = require('es5-ext/object/for-each')
, extensions = require('./lib/registered-extensions')
, configure = require('./lib/configure-map')
, resolveLength = require('./lib/resolve-length')
, hasOwnProperty = Object.prototype.hasOwnProperty;
module.exports = function self(fn/*, options */) {
var options, length, conf;
callable(fn);
options = Object(arguments[1]);
// Do not memoize already memoized function
if (hasOwnProperty.call(fn, '__memoized__') && !options.force) return fn;
// Resolve length;
length = resolveLength(options.length, fn.length, options.async && extensions.async);
// Configure cache map
conf = configure(fn, length, options);
// Bind eventual extensions
forEach(extensions, function (fn, name) {
if (options[name]) fn(options[name], conf, options);
});
if (self.__profiler__) self.__profiler__(conf);
conf.updateEnv();
return conf.memoized;
};

View File

@@ -5,18 +5,16 @@
var partial = require('es5-ext/function/#/partial')
, forEach = require('es5-ext/object/for-each')
, pad = require('es5-ext/string/#/pad')
, memoize = require('./plain')
, max = Math.max
, stats = exports.statistics = {};
, stats = exports.statistics = {}, ext;
require('../_base').ext.profile = ext = function (conf) {
Object.defineProperty(memoize, '__profiler__', function (conf) {
var id, stack, data;
stack = (new Error()).stack;
if (!stack.split('\n').slice(3).some(function (line) {
if (!stack || !stack.split('\n').slice(3).some(function (line) {
if ((line.indexOf('/memoizee/') === -1) &&
(line.indexOf('/es5-ext/') === -1) &&
(line.indexOf('/next/lib/fs/_memoize-watcher') === -1) &&
(line.indexOf(' (native)') === -1)) {
id = line.replace(/\n/g, "\\n").trim();
return true;
@@ -25,15 +23,12 @@ require('../_base').ext.profile = ext = function (conf) {
id = 'unknown';
}
if (!stats[id]) {
stats[id] = { initial: 0, cached: 0 };
}
if (!stats[id]) stats[id] = { initial: 0, cached: 0 };
data = stats[id];
conf.on('init', function () { ++data.initial; });
conf.on('hit', function () { ++data.cached; });
};
ext.force = true;
conf.on('set', function () { ++data.initial; });
conf.on('get', function () { ++data.cached; });
});
exports.log = function () {
var initial, cached, ordered, ipad, cpad, ppad, toPrc, log;

View File

@@ -1,18 +0,0 @@
'use strict';
module.exports = function (t, a) {
var mfn, m = t(function (conf) {
conf.get = function () { return 'test'; };
conf.memoized = function (x, y, z) { return conf.fn(z, y, x); };
conf.clear = function () {};
conf.clearAll = function () {};
});
a(typeof m, 'function', "Returns");
mfn = m(function (x, y, z) { return [x, y, z]; });
a(typeof mfn, 'function', "Generates");
a.deep(mfn(3, 7, 11), [11, 7, 3], "Works");
a(mfn.memoized, true, "Marked as memoized");
a(m(mfn), mfn, "Do not memoize memoized");
};

View File

@@ -1,10 +1,8 @@
'use strict';
var memoize = require('../../lib')
var memoize = require('../..')
, nextTick = require('next-tick');
require('../../lib/ext/dispose');
module.exports = function () {
return {
"Regular": {
@@ -58,7 +56,7 @@ module.exports = function () {
a(i, 2, "Init Called #2");
a(invoked, 7, "Cb Called #2");
mfn.clear(3, 7);
mfn.delete(3, 7);
a(mfn(3, 7, function (err, res) {
++invoked;
@@ -70,8 +68,8 @@ module.exports = function () {
}), u, "Again B: Initial");
nextTick(function () {
a(i, 3, "Init After clear");
a(invoked, 9, "Cb After clear");
a(i, 3, "Init After delete");
a(invoked, 9, "Cb After delete");
d();
});
});
@@ -164,7 +162,7 @@ module.exports = function () {
nextTick(function () {
a(i, 2, "Again Called #2");
mfn.clear(3, 7);
mfn.delete(3, 7);
a(mfn(3, 7, function (err, res) {
a.deep([err, res], [null, 10], "Again: Result");
@@ -174,7 +172,7 @@ module.exports = function () {
}), u, "Again B: Initial");
nextTick(function () {
a(i, 3, "Call After clear");
a(i, 3, "Call After delete");
d();
});
});

View File

@@ -1,6 +1,6 @@
'use strict';
var memoize = require('../../lib')
var memoize = require('../..')
, nextTick = require('next-tick');
module.exports = function () {
@@ -15,15 +15,15 @@ module.exports = function () {
mfn(5, 8);
mfn(12, 4);
a.deep(value, [], "Pre");
mfn.clear(5, 8);
mfn.delete(5, 8);
a.deep(value, [13], "#1");
value = [];
mfn.clear(12, 4);
mfn.delete(12, 4);
a.deep(value, [16], "#2");
value = [];
mfn(77, 11);
mfn.clearAll();
mfn.clear();
a.deep(value, [10, 88], "Clear all");
x = {};
@@ -31,35 +31,12 @@ module.exports = function () {
mfn = memoize(function () { return x; },
{ dispose: function (val) { invoked = val; } });
mfn.clear();
a(invoked, false, "No args: Post invalid clear");
mfn.delete();
a(invoked, false, "No args: Post invalid delete");
mfn();
a(invoked, false, "No args: Post cache");
mfn.clear();
a(invoked, x, "No args: Pre clear");
},
"Method": function (a) {
var fn, value = [];
fn = function (x, y) { return x + y; };
Object.defineProperties(value, memoize(fn, {
method: 'mfn',
dispose: function (val) { this.push(val); }
}));
value.mfn(3, 7);
value.mfn(5, 8);
value.mfn(12, 4);
a.deep(value, [], "Pre");
value.mfn.clear(5, 8);
a.deep(value, [13], "#1");
value.length = 0;
value.mfn.clear(12, 4);
a.deep(value, [16], "#2");
value.length = 0;
value.mfn(77, 11);
value.mfn.clearAll();
a.deep(value, [10, 88], "Clear all");
mfn.delete();
a(invoked, x, "No args: Pre delete");
},
"Ref counter": function (a) {
var mfn, fn, value = [];
@@ -72,17 +49,17 @@ module.exports = function () {
mfn(12, 4);
a.deep(value, [], "Pre");
mfn(5, 8);
mfn.clearRef(5, 8);
mfn.deleteRef(5, 8);
a.deep(value, [], "Pre");
mfn.clearRef(5, 8);
mfn.deleteRef(5, 8);
a.deep(value, [13], "#1");
value = [];
mfn.clearRef(12, 4);
mfn.deleteRef(12, 4);
a.deep(value, [16], "#2");
value = [];
mfn(77, 11);
mfn.clearAll();
mfn.clear();
a.deep(value, [10, 88], "Clear all");
},
"Async": function (a, d) {
@@ -99,15 +76,15 @@ module.exports = function () {
mfn(5, 8, function () {
mfn(12, 4, function () {
a.deep(value, [], "Pre");
mfn.clear(5, 8);
mfn.delete(5, 8);
a.deep(value, [13], "#1");
value = [];
mfn.clear(12, 4);
mfn.delete(12, 4);
a.deep(value, [16], "#2");
value = [];
mfn(77, 11, function () {
mfn.clearAll();
mfn.clear();
a.deep(value, [10, 88], "Clear all");
d();
});
@@ -126,15 +103,15 @@ module.exports = function () {
mfn(5, 8);
mfn(12, 4);
a.deep(value, [], "Pre");
mfn.clear(5, 8);
mfn.delete(5, 8);
a.deep(value, [13], "#1");
value = [];
mfn.clear(12, 4);
mfn.delete(12, 4);
a.deep(value, [16], "#2");
value = [];
mfn(77, 11);
mfn.clearAll();
mfn.clear();
a.deep(value, [10, 88], "Clear all");
},
"Ref counter": function (a) {
@@ -148,17 +125,17 @@ module.exports = function () {
mfn(12, 4);
a.deep(value, [], "Pre");
mfn(5, 8);
mfn.clearRef(5, 8);
mfn.deleteRef(5, 8);
a.deep(value, [], "Pre");
mfn.clearRef(5, 8);
mfn.deleteRef(5, 8);
a.deep(value, [13], "#1");
value = [];
mfn.clearRef(12, 4);
mfn.deleteRef(12, 4);
a.deep(value, [16], "#2");
value = [];
mfn(77, 11);
mfn.clearAll();
mfn.clear();
a.deep(value, [10, 88], "Clear all");
},
"Async": function (a, d) {
@@ -175,15 +152,15 @@ module.exports = function () {
mfn(5, 8, function () {
mfn(12, 4, function () {
a.deep(value, [], "Pre");
mfn.clear(5, 8);
mfn.delete(5, 8);
a.deep(value, [13], "#1");
value = [];
mfn.clear(12, 4);
mfn.delete(12, 4);
a.deep(value, [16], "#2");
value = [];
mfn(77, 11, function () {
mfn.clearAll();
mfn.clear();
a.deep(value, [10, 88], "Clear all");
d();
});

View File

@@ -1,8 +1,10 @@
'use strict';
var memoize = require('../../lib')
var memoize = require('../..')
, nextTick = require('next-tick');
require('../../ext/async');
module.exports = function () {
return {
"Regular": {

View File

@@ -1,6 +1,6 @@
'use strict';
var memoize = require('../../lib')
var memoize = require('../..')
, nextTick = require('next-tick');
module.exports = function () {
@@ -31,31 +31,31 @@ module.exports = function () {
a(mfn(5, 8), 13, "Result B #3");
a(i, 3, "Called B #3");
a(mfn(77, 11), 88, "Result D #1"); // Clear 12, 4
a(mfn(77, 11), 88, "Result D #1"); // Delete 12, 4
a(i, 4, "Called D #1");
a(mfn(5, 8), 13, "Result B #4");
a(i, 4, "Called B #4");
a(mfn(12, 4), 16, "Result C #2"); // Clear 3, 7
a(mfn(12, 4), 16, "Result C #2"); // Delete 3, 7
a(i, 5, "Called C #2");
a(mfn(3, 7), 10, "Result #5"); // Clear 77, 11
a(mfn(3, 7), 10, "Result #5"); // Delete 77, 11
a(i, 6, "Called #5");
a(mfn(77, 11), 88, "Result D #2"); // Clear 5, 8
a(mfn(77, 11), 88, "Result D #2"); // Delete 5, 8
a(i, 7, "Called D #2");
a(mfn(12, 4), 16, "Result C #3");
a(i, 7, "Called C #3");
a(mfn(5, 8), 13, "Result B #5"); // Clear 3, 7
a(mfn(5, 8), 13, "Result B #5"); // Delete 3, 7
a(i, 8, "Called B #5");
a(mfn(77, 11), 88, "Result D #3");
a(i, 8, "Called D #3");
mfn.clear(77, 11);
mfn.delete(77, 11);
a(mfn(77, 11), 88, "Result D #4");
a(i, 9, "Called D #4");
mfn.clearAll();
mfn.clear();
a(mfn(5, 8), 13, "Result B #6");
a(i, 10, "Called B #6");
a(mfn(77, 11), 88, "Result D #5");
@@ -141,13 +141,13 @@ module.exports = function () {
"Result D #3");
a(i, 8, "Called D #3");
mfn.clear(77, 11);
mfn.delete(77, 11);
a(mfn(77, 11, function (err, res) {
a.deep([err, res], [null, 88],
"Result D #4");
a(i, 9, "Called D #4");
mfn.clearAll();
mfn.clear();
a(mfn(5, 8, function (err, res) {
a.deep([err, res], [null, 13],
"Result B #6");
@@ -207,31 +207,31 @@ module.exports = function () {
a(mfn(5, 8), 13, "Result B #3");
a(i, 3, "Called B #3");
a(mfn(77, 11), 88, "Result D #1"); // Clear 12, 4
a(mfn(77, 11), 88, "Result D #1"); // Delete 12, 4
a(i, 4, "Called D #1");
a(mfn(5, 8), 13, "Result B #4");
a(i, 4, "Called B #4");
a(mfn(12, 4), 16, "Result C #2"); // Clear 3, 7
a(mfn(12, 4), 16, "Result C #2"); // Delete 3, 7
a(i, 5, "Called C #2");
a(mfn(3, 7), 10, "Result #5"); // Clear 77, 11
a(mfn(3, 7), 10, "Result #5"); // Delete 77, 11
a(i, 6, "Called #5");
a(mfn(77, 11), 88, "Result D #2"); // Clear 5, 8
a(mfn(77, 11), 88, "Result D #2"); // Delete 5, 8
a(i, 7, "Called D #2");
a(mfn(12, 4), 16, "Result C #3");
a(i, 7, "Called C #3");
a(mfn(5, 8), 13, "Result B #5"); // Clear 3, 7
a(mfn(5, 8), 13, "Result B #5"); // Delete 3, 7
a(i, 8, "Called B #5");
a(mfn(77, 11), 88, "Result D #3");
a(i, 8, "Called D #3");
mfn.clear(77, 11);
mfn.delete(77, 11);
a(mfn(77, 11), 88, "Result D #4");
a(i, 9, "Called D #4");
mfn.clearAll();
mfn.clear();
a(mfn(5, 8), 13, "Result B #6");
a(i, 10, "Called B #6");
a(mfn(77, 11), 88, "Result D #5");
@@ -317,13 +317,13 @@ module.exports = function () {
"Result D #3");
a(i, 8, "Called D #3");
mfn.clear(77, 11);
mfn.delete(77, 11);
a(mfn(77, 11, function (err, res) {
a.deep([err, res], [null, 88],
"Result D #4");
a(i, 9, "Called D #4");
mfn.clearAll();
mfn.clear();
a(mfn(5, 8, function (err, res) {
a.deep([err, res], [null, 13],
"Result B #6");

View File

@@ -1,34 +0,0 @@
'use strict';
var memoize = require('../../lib');
module.exports = function () {
return {
"No descriptor": function (a) {
var x = {}, i = 0, fn = function () {
++i;
return this;
};
Object.defineProperties(x, memoize(fn, { method: 'foo' }));
a(x.foo(), x, "Context");
a(x.foo(), x, "Method");
a(i, 1, "Cached");
},
"Descriptor": function (a) {
var x = {}, i = 0, fn = function () {
++i;
return this;
};
Object.defineProperties(x, memoize(fn,
{ method: 'foo', writable: false }));
a(x.foo(), x, "Context");
a.deep(Object.getOwnPropertyDescriptor(x, 'foo'),
{ enumerable: false, configurable: true, writable: false,
value: x.foo });
a(x.foo(), x, "Method");
a(i, 1, "Cached");
}
};
};

View File

@@ -1,6 +1,6 @@
'use strict';
var memoize = require('../../lib')
var memoize = require('../..')
, nextTick = require('next-tick');
module.exports = function () {
@@ -8,18 +8,18 @@ module.exports = function () {
"Regular": function (a) {
var i = 0, fn = function (x, y, z) { ++i; return x + y + z; }, mfn;
mfn = memoize(fn, { refCounter: true });
a(mfn.clearRef(3, 5, 7), null, "Clear before");
a(mfn.deleteRef(3, 5, 7), null, "Delete before");
a(mfn(3, 5, 7), 15, "Initial");
a(mfn(3, 5, 7), 15, "Cache");
a(mfn.clearRef(3, 5, 7), false, "Clear #1");
a(mfn.deleteRef(3, 5, 7), false, "Delete #1");
mfn(3, 5, 7);
a(mfn.clearRef(3, 5, 7), false, "Clear #2");
a(mfn.deleteRef(3, 5, 7), false, "Delete #2");
mfn(3, 5, 7);
a(mfn.clearRef(3, 5, 7), false, "Clear #3");
a(mfn.deleteRef(3, 5, 7), false, "Delete #3");
mfn(3, 5, 7);
a(i, 1, "Not cleared");
a(mfn.clearRef(3, 5, 7), false, "Clear #4");
a(mfn.clearRef(3, 5, 7), true, "Clear final");
a(i, 1, "Not deleteed");
a(mfn.deleteRef(3, 5, 7), false, "Delete #4");
a(mfn.deleteRef(3, 5, 7), true, "Delete final");
mfn(3, 5, 7);
a(i, 2, "Restarted");
mfn(3, 5, 7);
@@ -37,7 +37,7 @@ module.exports = function () {
mfn = memoize(fn, { async: true, refCounter: true });
a(mfn.clearRef(3, 7), null, "Clear ref before");
a(mfn.deleteRef(3, 7), null, "Delete ref before");
a(mfn(3, 7, function (err, res) {
a.deep([err, res], [null, 10], "Result #1");
@@ -68,10 +68,10 @@ module.exports = function () {
nextTick(function () {
a(i, 2, "Again Called #2");
a(mfn.clearRef(3, 7), false, "Clear ref #1");
a(mfn.clearRef(3, 7), false, "Clear ref #2");
a(mfn.clearRef(3, 7), false, "Clear ref #3");
a(mfn.clearRef(3, 7), true, "Clear ref Final");
a(mfn.deleteRef(3, 7), false, "Delete ref #1");
a(mfn.deleteRef(3, 7), false, "Delete ref #2");
a(mfn.deleteRef(3, 7), false, "Delete ref #3");
a(mfn.deleteRef(3, 7), true, "Delete ref Final");
a(mfn(3, 7, function (err, res) {
a.deep([err, res], [null, 10], "Again: Result");
@@ -81,7 +81,7 @@ module.exports = function () {
}), u, "Again B: Initial");
nextTick(function () {
a(i, 3, "Call After clear");
a(i, 3, "Call After delete");
d();
});
});
@@ -90,18 +90,18 @@ module.exports = function () {
"Primitive": function (a) {
var i = 0, fn = function (x, y, z) { ++i; return x + y + z; }, mfn;
mfn = memoize(fn, { primitive: true, refCounter: true });
a(mfn.clearRef(3, 5, 7), null, "Clear before");
a(mfn.deleteRef(3, 5, 7), null, "Delete before");
a(mfn(3, 5, 7), 15, "Initial");
a(mfn(3, 5, 7), 15, "Cache");
a(mfn.clearRef(3, 5, 7), false, "Clear #1");
a(mfn.deleteRef(3, 5, 7), false, "Delete #1");
mfn(3, 5, 7);
a(mfn.clearRef(3, 5, 7), false, "Clear #2");
a(mfn.deleteRef(3, 5, 7), false, "Delete #2");
mfn(3, 5, 7);
a(mfn.clearRef(3, 5, 7), false, "Clear #3");
a(mfn.deleteRef(3, 5, 7), false, "Delete #3");
mfn(3, 5, 7);
a(i, 1, "Not cleared");
a(mfn.clearRef(3, 5, 7), false, "Clear #4");
a(mfn.clearRef(3, 5, 7), true, "Clear final");
a(i, 1, "Not deleteed");
a(mfn.deleteRef(3, 5, 7), false, "Delete #4");
a(mfn.deleteRef(3, 5, 7), true, "Delete final");
mfn(3, 5, 7);
a(i, 2, "Restarted");
mfn(3, 5, 7);
@@ -119,7 +119,7 @@ module.exports = function () {
mfn = memoize(fn, { async: true, primitive: true, refCounter: true });
a(mfn.clearRef(3, 7), null, "Clear ref before");
a(mfn.deleteRef(3, 7), null, "Delete ref before");
a(mfn(3, 7, function (err, res) {
a.deep([err, res], [null, 10], "Result #1");
@@ -150,10 +150,10 @@ module.exports = function () {
nextTick(function () {
a(i, 2, "Again Called #2");
a(mfn.clearRef(3, 7), false, "Clear ref #1");
a(mfn.clearRef(3, 7), false, "Clear ref #2");
a(mfn.clearRef(3, 7), false, "Clear ref #3");
a(mfn.clearRef(3, 7), true, "Clear ref Final");
a(mfn.deleteRef(3, 7), false, "Delete ref #1");
a(mfn.deleteRef(3, 7), false, "Delete ref #2");
a(mfn.deleteRef(3, 7), false, "Delete ref #3");
a(mfn.deleteRef(3, 7), true, "Delete ref Final");
a(mfn(3, 7, function (err, res) {
a.deep([err, res], [null, 10], "Again: Result");
@@ -163,7 +163,7 @@ module.exports = function () {
}), u, "Again B: Initial");
nextTick(function () {
a(i, 3, "Call After clear");
a(i, 3, "Call After delete");
d();
});
});

View File

@@ -1,47 +0,0 @@
'use strict';
var aFrom = require('es5-ext/array/from')
, memoize = require('../../lib');
module.exports = function (a) {
return {
"Original arguments": function (a) {
var fn, mfn, x = {};
fn = function (x, y) { x = y; return aFrom(mfn.args); };
mfn = memoize(fn, { resolvers: [] });
a.deep(mfn(23, 'raz', x), [23, 'raz', x]);
},
"Resolvers": function () {
var i = 0, fn, r;
fn = memoize(function () { ++i; return arguments; },
{ length: 3, resolvers: [Boolean, String] });
return {
"No args": function () {
i = 0;
a.deep(aFrom(r = fn()), [false, 'undefined'], "First");
a(fn(), r, "Second");
a(fn(), r, "Third");
a(i, 1, "Called once");
},
"Some Args": function () {
var x = {};
i = 0;
a.deep(aFrom(r = fn(0, 34, x, 45)), [false, '34', x, 45],
"First");
a(fn(0, 34, x, 22), r, "Second");
a(fn(0, 34, x, false), r, "Third");
a(i, 1, "Called once");
return {
"Other": function () {
a.deep(aFrom(r = fn(1, 34, x, 34)),
[true, '34', x, 34], "Second");
a(fn(1, 34, x, 89), r, "Third");
a(i, 2, "Called once");
}
};
}
};
}
};
};

View File

@@ -91,8 +91,7 @@ module.exports = function (t, a) {
};
},
"Serialize": function () {
var i = 0, fn = function () { ++i; return join.call(arguments, '|'); }
, mfn;
var i = 0, fn = function () { ++i; return join.call(arguments, '|'); }, mfn;
mfn = t(fn, { serialize: Boolean });
a(mfn(false, 'raz'), 'false|raz', "#1");
a(mfn(0, 'dwa'), 'false|raz', "#2");
@@ -132,13 +131,6 @@ module.exports = function (t, a) {
}
};
},
"Original arguments": function (a) {
var fn, mfn, x = {};
fn = function (x, y) { x = y; return aFrom(mfn.args); };
mfn = t(fn, { resolvers: [] });
a.deep(mfn(23, 'raz', x), [23, 'raz', x]);
},
"Resolvers": function () {
var i = 0, fn, r;
fn = t(function () { ++i; return arguments; },
@@ -184,11 +176,11 @@ module.exports = function (t, a) {
mfn = t(fn);
mfn(1, x, 3);
mfn(1, x, 4);
mfn.clear(1, x, 4);
mfn.delete(1, x, 4);
mfn(1, x, 3);
mfn(1, x, 3);
a(i, 1, "Pre clear");
mfn.clear(1, x, 3);
mfn.delete(1, x, 3);
mfn(1, x, 3);
a(i, 2, "After clear");
@@ -198,7 +190,7 @@ module.exports = function (t, a) {
mfn(1, x, 3);
mfn();
mfn();
mfn.clear();
mfn.delete();
mfn(1, x, 3);
a(i, 1, "Proper no arguments clear");
},
@@ -216,7 +208,7 @@ module.exports = function (t, a) {
fn(1, x, 3);
fn(1, x, 4);
a(i, 2, "Pre clear");
fn.clearAll();
fn.clear();
fn(1, x, 3);
fn(1, x, 4);
fn(1, x, 3);
@@ -224,34 +216,6 @@ module.exports = function (t, a) {
a(i, 4, "After clear");
}
},
"Method": {
"No descriptor": function (a) {
var x = {}, i = 0, fn = function () {
++i;
return this;
};
Object.defineProperties(x, t(fn, { method: 'foo' }));
a(x.foo(), x, "Context");
a(x.foo(), x, "Method");
a(i, 1, "Cached");
},
"Descriptor": function (a) {
var x = {}, i = 0, fn = function () {
++i;
return this;
};
Object.defineProperties(x, t(fn,
{ method: 'foo', writable: false }));
a(x.foo(), x, "Context");
a.deep(Object.getOwnPropertyDescriptor(x, 'foo'),
{ enumerable: false, configurable: true, writable: false,
value: x.foo });
a(x.foo(), x, "Method");
a(i, 1, "Cached");
}
},
"Primitive": {
"No args": function (a) {
var i = 0, fn = function () { ++i; return arguments[0]; }, mfn;
@@ -283,7 +247,7 @@ module.exports = function (t, a) {
a(mfn(y, 'bar', 'zeta'), 'foobarzeta', "#1");
a(mfn('foo', 'bar', 'zeta'), 'foobarzeta', "#2");
a(i, 1, "Called once");
mfn.clear('foo', { toString: function () { return 'bar'; } },
mfn.delete('foo', { toString: function () { return 'bar'; } },
'zeta');
a(mfn(y, 'bar', 'zeta'), 'foobarzeta', "#3");
a(i, 2, "Called twice");
@@ -293,18 +257,18 @@ module.exports = function (t, a) {
"Regular": function (a) {
var i = 0, fn = function (x, y, z) { ++i; return x + y + z; }, mfn;
mfn = t(fn, { refCounter: true });
a(mfn.clearRef(3, 5, 7), null, "Clear before");
a(mfn.deleteRef(3, 5, 7), null, "Clear before");
a(mfn(3, 5, 7), 15, "Initial");
a(mfn(3, 5, 7), 15, "Cache");
a(mfn.clearRef(3, 5, 7), false, "Clear #1");
a(mfn.deleteRef(3, 5, 7), false, "Clear #1");
mfn(3, 5, 7);
a(mfn.clearRef(3, 5, 7), false, "Clear #2");
a(mfn.deleteRef(3, 5, 7), false, "Clear #2");
mfn(3, 5, 7);
a(mfn.clearRef(3, 5, 7), false, "Clear #3");
a(mfn.deleteRef(3, 5, 7), false, "Clear #3");
mfn(3, 5, 7);
a(i, 1, "Not cleared");
a(mfn.clearRef(3, 5, 7), false, "Clear #4");
a(mfn.clearRef(3, 5, 7), true, "Clear final");
a(mfn.deleteRef(3, 5, 7), false, "Clear #4");
a(mfn.deleteRef(3, 5, 7), true, "Clear final");
mfn(3, 5, 7);
a(i, 2, "Restarted");
mfn(3, 5, 7);
@@ -313,18 +277,18 @@ module.exports = function (t, a) {
"Primitive": function (a) {
var i = 0, fn = function (x, y, z) { ++i; return x + y + z; }, mfn;
mfn = t(fn, { primitive: true, refCounter: true });
a(mfn.clearRef(3, 5, 7), null, "Clear before");
a(mfn.deleteRef(3, 5, 7), null, "Clear before");
a(mfn(3, 5, 7), 15, "Initial");
a(mfn(3, 5, 7), 15, "Cache");
a(mfn.clearRef(3, 5, 7), false, "Clear #1");
a(mfn.deleteRef(3, 5, 7), false, "Clear #1");
mfn(3, 5, 7);
a(mfn.clearRef(3, 5, 7), false, "Clear #2");
a(mfn.deleteRef(3, 5, 7), false, "Clear #2");
mfn(3, 5, 7);
a(mfn.clearRef(3, 5, 7), false, "Clear #3");
a(mfn.deleteRef(3, 5, 7), false, "Clear #3");
mfn(3, 5, 7);
a(i, 1, "Not cleared");
a(mfn.clearRef(3, 5, 7), false, "Clear #4");
a(mfn.clearRef(3, 5, 7), true, "Clear final");
a(mfn.deleteRef(3, 5, 7), false, "Clear #4");
a(mfn.deleteRef(3, 5, 7), true, "Clear final");
mfn(3, 5, 7);
a(i, 2, "Restarted");
mfn(3, 5, 7);
@@ -383,7 +347,7 @@ module.exports = function (t, a) {
a(i, 2, "Init Called #2");
a(invoked, 7, "Cb Called #2");
mfn.clear(3, 7);
mfn.delete(3, 7);
a(mfn(3, 7, function (err, res) {
++invoked;
@@ -414,7 +378,7 @@ module.exports = function (t, a) {
mfn = t(fn, { async: true, refCounter: true });
a(mfn.clearRef(3, 7), null, "Clear ref before");
a(mfn.deleteRef(3, 7), null, "Clear ref before");
a(mfn(3, 7, function (err, res) {
a.deep([err, res], [null, 10], "Result #1");
@@ -445,10 +409,10 @@ module.exports = function (t, a) {
nextTick(function () {
a(i, 2, "Again Called #2");
a(mfn.clearRef(3, 7), false, "Clear ref #1");
a(mfn.clearRef(3, 7), false, "Clear ref #2");
a(mfn.clearRef(3, 7), false, "Clear ref #3");
a(mfn.clearRef(3, 7), true, "Clear ref Final");
a(mfn.deleteRef(3, 7), false, "Clear ref #1");
a(mfn.deleteRef(3, 7), false, "Clear ref #2");
a(mfn.deleteRef(3, 7), false, "Clear ref #3");
a(mfn.deleteRef(3, 7), true, "Clear ref Final");
a(mfn(3, 7, function (err, res) {
a.deep([err, res], [null, 10], "Again: Result");
@@ -551,7 +515,7 @@ module.exports = function (t, a) {
nextTick(function () {
a(i, 2, "Again Called #2");
mfn.clear(3, 7);
mfn.delete(3, 7);
a(mfn(3, 7, function (err, res) {
a.deep([err, res], [null, 10], "Again: Result");
@@ -579,7 +543,7 @@ module.exports = function (t, a) {
mfn = t(fn, { async: true, primitive: true, refCounter: true });
a(mfn.clearRef(3, 7), null, "Clear ref before");
a(mfn.deleteRef(3, 7), null, "Clear ref before");
a(mfn(3, 7, function (err, res) {
a.deep([err, res], [null, 10], "Result #1");
@@ -610,10 +574,10 @@ module.exports = function (t, a) {
nextTick(function () {
a(i, 2, "Again Called #2");
a(mfn.clearRef(3, 7), false, "Clear ref #1");
a(mfn.clearRef(3, 7), false, "Clear ref #2");
a(mfn.clearRef(3, 7), false, "Clear ref #3");
a(mfn.clearRef(3, 7), true, "Clear ref Final");
a(mfn.deleteRef(3, 7), false, "Clear ref #1");
a(mfn.deleteRef(3, 7), false, "Clear ref #2");
a(mfn.deleteRef(3, 7), false, "Clear ref #3");
a(mfn.deleteRef(3, 7), true, "Clear ref Final");
a(mfn(3, 7, function (err, res) {
a.deep([err, res], [null, 10], "Again: Result");
@@ -916,11 +880,11 @@ module.exports = function (t, a) {
a(mfn(77, 11), 88, "Result D #3");
a(i, 8, "Called D #3");
mfn.clear(77, 11);
mfn.delete(77, 11);
a(mfn(77, 11), 88, "Result D #4");
a(i, 9, "Called D #4");
mfn.clearAll();
mfn.clear();
a(mfn(5, 8), 13, "Result B #6");
a(i, 10, "Called B #6");
a(mfn(77, 11), 88, "Result D #5");
@@ -1006,13 +970,13 @@ module.exports = function (t, a) {
"Result D #3");
a(i, 8, "Called D #3");
mfn.clear(77, 11);
mfn.delete(77, 11);
a(mfn(77, 11, function (err, res) {
a.deep([err, res], [null, 88],
"Result D #4");
a(i, 9, "Called D #4");
mfn.clearAll();
mfn.clear();
a(mfn(5, 8, function (err, res) {
a.deep([err, res], [null, 13],
"Result B #6");
@@ -1092,11 +1056,11 @@ module.exports = function (t, a) {
a(mfn(77, 11), 88, "Result D #3");
a(i, 8, "Called D #3");
mfn.clear(77, 11);
mfn.delete(77, 11);
a(mfn(77, 11), 88, "Result D #4");
a(i, 9, "Called D #4");
mfn.clearAll();
mfn.clear();
a(mfn(5, 8), 13, "Result B #6");
a(i, 10, "Called B #6");
a(mfn(77, 11), 88, "Result D #5");
@@ -1182,13 +1146,13 @@ module.exports = function (t, a) {
"Result D #3");
a(i, 8, "Called D #3");
mfn.clear(77, 11);
mfn.delete(77, 11);
a(mfn(77, 11, function (err, res) {
a.deep([err, res], [null, 88],
"Result D #4");
a(i, 9, "Called D #4");
mfn.clearAll();
mfn.clear();
a(mfn(5, 8, function (err, res) {
a.deep([err, res], [null, 13],
"Result B #6");
@@ -1234,15 +1198,15 @@ module.exports = function (t, a) {
mfn(5, 8);
mfn(12, 4);
a.deep(value, [], "Pre");
mfn.clear(5, 8);
mfn.delete(5, 8);
a.deep(value, [13], "#1");
value = [];
mfn.clear(12, 4);
mfn.delete(12, 4);
a.deep(value, [16], "#2");
value = [];
mfn(77, 11);
mfn.clearAll();
mfn.clear();
a.deep(value, [10, 88], "Clear all");
x = {};
@@ -1250,11 +1214,11 @@ module.exports = function (t, a) {
mfn = t(function () { return x; },
{ dispose: function (val) { invoked = val; } });
mfn.clear();
mfn.delete();
a(invoked, false, "No args: Post invalid clear");
mfn();
a(invoked, false, "No args: Post cache");
mfn.clear();
mfn.delete();
a(invoked, x, "No args: Pre clear");
},
"Ref counter": function (a) {
@@ -1268,17 +1232,17 @@ module.exports = function (t, a) {
mfn(12, 4);
a.deep(value, [], "Pre");
mfn(5, 8);
mfn.clearRef(5, 8);
mfn.deleteRef(5, 8);
a.deep(value, [], "Pre");
mfn.clearRef(5, 8);
mfn.deleteRef(5, 8);
a.deep(value, [13], "#1");
value = [];
mfn.clearRef(12, 4);
mfn.deleteRef(12, 4);
a.deep(value, [16], "#2");
value = [];
mfn(77, 11);
mfn.clearAll();
mfn.clear();
a.deep(value, [10, 88], "Clear all");
},
"Async": function (a, d) {
@@ -1295,15 +1259,15 @@ module.exports = function (t, a) {
mfn(5, 8, function () {
mfn(12, 4, function () {
a.deep(value, [], "Pre");
mfn.clear(5, 8);
mfn.delete(5, 8);
a.deep(value, [13], "#1");
value = [];
mfn.clear(12, 4);
mfn.delete(12, 4);
a.deep(value, [16], "#2");
value = [];
mfn(77, 11, function () {
mfn.clearAll();
mfn.clear();
a.deep(value, [10, 88], "Clear all");
d();
});
@@ -1322,15 +1286,15 @@ module.exports = function (t, a) {
mfn(5, 8);
mfn(12, 4);
a.deep(value, [], "Pre");
mfn.clear(5, 8);
mfn.delete(5, 8);
a.deep(value, [13], "#1");
value = [];
mfn.clear(12, 4);
mfn.delete(12, 4);
a.deep(value, [16], "#2");
value = [];
mfn(77, 11);
mfn.clearAll();
mfn.clear();
a.deep(value, [10, 88], "Clear all");
},
"Ref counter": function (a) {
@@ -1344,17 +1308,17 @@ module.exports = function (t, a) {
mfn(12, 4);
a.deep(value, [], "Pre");
mfn(5, 8);
mfn.clearRef(5, 8);
mfn.deleteRef(5, 8);
a.deep(value, [], "Pre");
mfn.clearRef(5, 8);
mfn.deleteRef(5, 8);
a.deep(value, [13], "#1");
value = [];
mfn.clearRef(12, 4);
mfn.deleteRef(12, 4);
a.deep(value, [16], "#2");
value = [];
mfn(77, 11);
mfn.clearAll();
mfn.clear();
a.deep(value, [10, 88], "Clear all");
},
"Async": function (a, d) {
@@ -1371,15 +1335,15 @@ module.exports = function (t, a) {
mfn(5, 8, function () {
mfn(12, 4, function () {
a.deep(value, [], "Pre");
mfn.clear(5, 8);
mfn.delete(5, 8);
a.deep(value, [13], "#1");
value = [];
mfn.clear(12, 4);
mfn.delete(12, 4);
a.deep(value, [16], "#2");
value = [];
mfn(77, 11, function () {
mfn.clearAll();
mfn.clear();
a.deep(value, [10, 88], "Clear all");
d();
});

77
test/lib/configure-map.js Normal file
View File

@@ -0,0 +1,77 @@
'use strict';
var aFrom = require('es5-ext/array/from')
, memoize = require('../..');
module.exports = function () {
return {
"One arg": function (a) {
var i = 0, fn = function (x) { ++i; return x; }, mfn
, y = { toString: function () { return 'foo'; } };
mfn = memoize(fn, { primitive: true });
a(mfn(y), y, "#1");
a(mfn('foo'), y, "#2");
a(i, 1, "Called once");
},
"Clear cache": function (a) {
var i = 0, fn = function (x, y, z) { ++i; return x + y + z; }, mfn
, y = { toString: function () { return 'foo'; } };
mfn = memoize(fn, { primitive: true });
a(mfn(y, 'bar', 'zeta'), 'foobarzeta', "#1");
a(mfn('foo', 'bar', 'zeta'), 'foobarzeta', "#2");
a(i, 1, "Called once");
mfn.delete('foo', { toString: function () { return 'bar'; } },
'zeta');
a(mfn(y, 'bar', 'zeta'), 'foobarzeta', "#3");
a(i, 2, "Called twice");
},
"Circular": function (a) {
var i = 0, fn;
fn = memoize(function (x) {
if (++i < 2) fn(x);
});
a.throws(function () {
fn('foo');
}, 'CIRCULAR_INVOCATION');
i = 0;
fn = memoize(function (x, y) {
if (++i < 2) fn(x, y);
});
a.throws(function () {
fn('foo', 'bar');
}, 'CIRCULAR_INVOCATION');
},
"Resolvers": function () {
var i = 0, fn, r;
fn = memoize(function () { ++i; return arguments; },
{ length: 3, resolvers: [Boolean, String] });
return {
"No args": function (a) {
i = 0;
a.deep(aFrom(r = fn()), [false, 'undefined'], "First");
a(fn(), r, "Second");
a(fn(), r, "Third");
a(i, 1, "Called once");
},
"Some Args": function (a) {
var x = {};
i = 0;
a.deep(aFrom(r = fn(0, 34, x, 45)), [false, '34', x, 45], "First");
a(fn(0, 34, x, 22), r, "Second");
a(fn(0, 34, x, false), r, "Third");
a(i, 1, "Called once");
return {
"Other": function (a) {
a.deep(aFrom(r = fn(1, 34, x, 34)),
[true, '34', x, 34], "Second");
a(fn(1, 34, x, 89), r, "Third");
a(i, 2, "Called once");
}
};
}
};
}
};
};

View File

@@ -1,10 +1,10 @@
'use strict';
var d = require('d')
, memoize = require('../lib/regular');
, memoize = require('../..');
require('../lib/ext/dispose');
require('../lib/ext/ref-counter');
require('../ext/dispose');
require('../ext/ref-counter');
module.exports = function (t, a) {
var value = [], obj = {};
@@ -21,16 +21,16 @@ module.exports = function (t, a) {
obj.someFn(12, 4);
a.deep(value, [], "Pre");
obj.someFn(5, 8);
obj.someFn.clearRef(5, 8);
obj.someFn.deleteRef(5, 8);
a.deep(value, [], "Pre");
obj.someFn.clearRef(5, 8);
obj.someFn.deleteRef(5, 8);
a.deep(value, [13], "#1");
value = [];
obj.someFn.clearRef(12, 4);
obj.someFn.deleteRef(12, 4);
a.deep(value, [16], "#2");
value = [];
obj.someFn(77, 11);
obj.someFn.clearAll();
a.deep(value, [10, 88], "Clear all");
obj.someFn.clear();
a.deep(value, [10, 88], "Clear");
};

32
test/lib/normalizers/0.js Normal file
View File

@@ -0,0 +1,32 @@
'use strict';
var memoize = require('../../..');
module.exports = {
"": function (a) {
var i = 0, fn = function () { ++i; return 3; };
fn = memoize(fn);
a(fn(), 3, "First");
a(fn(1), 3, "Second");
a(fn(5), 3, "Third");
a(i, 1, "Called once");
},
"Delete": function (a) {
var i = 0, fn, mfn, x = {};
fn = function (a, b, c) {
return a + (++i);
};
mfn = memoize(fn, { length: 0 });
a(mfn(3), 4, "Init");
a(mfn(5), 4, "Other");
a(i, 1, "Pre clear");
mfn.delete(6, x, 1);
a(i, 1, "After clear");
a(mfn(6, x, 1), 8, "Reinit");
a(i, 2, "Reinit count");
a(mfn(3, x, 1), 8, "Reinit Cached");
a(i, 2, "Reinit count");
}
};

View File

@@ -0,0 +1,43 @@
'use strict';
var memoize = require('../../..');
module.exports = {
"": function (a) {
var i = 0, fn = function (x, y, z) { ++i; return x + y + z; }, mfn
, y = { toString: function () { return 'foo'; } };
mfn = memoize(fn, { primitive: true });
a(mfn(y, 'bar', 'zeta'), 'foobarzeta', "#1");
a(mfn('foo', 'bar', 'zeta'), 'foobarzeta', "#2");
a(i, 1, "Called once");
},
"Delete": function (a) {
var i = 0, fn = function (x, y, z) { ++i; return x + y + z; }, mfn
, y = { toString: function () { return 'foo'; } };
mfn = memoize(fn, { primitive: true });
a(mfn(y, 'bar', 'zeta'), 'foobarzeta', "#1");
a(mfn('foo', 'bar', 'zeta'), 'foobarzeta', "#2");
a(i, 1, "Called once");
mfn.delete('foo', { toString: function () { return 'bar'; } },
'zeta');
a(mfn(y, 'bar', 'zeta'), 'foobarzeta', "#3");
a(i, 2, "Called twice");
},
"Clear": function (a) {
var i = 0, fn;
fn = memoize(function (x) {
if (++i < 2) fn(x);
});
a.throws(function () {
fn('foo');
}, 'CIRCULAR_INVOCATION');
i = 0;
fn = memoize(function (x, y) {
if (++i < 2) fn(x, y);
});
a.throws(function () {
fn('foo', 'bar');
}, 'CIRCULAR_INVOCATION');
}
};

View File

@@ -0,0 +1,56 @@
'use strict';
var memoize = require('../../..');
module.exports = {
"": function (t, a) {
var i = 0, fn = function (x) { ++i; return x; };
fn = memoize(fn);
return {
"No arg": function () {
i = 0;
a(fn(), undefined, "First");
a(fn(), undefined, "Second");
a(fn(), undefined, "Third");
a(i, 1, "Called once");
},
"Arg": function () {
var x = {};
i = 0;
a(fn(x, 8), x, "First");
a(fn(x, 4), x, "Second");
a(fn(x, 2), x, "Third");
a(i, 1, "Called once");
},
"Other Arg": function () {
var x = {};
i = 0;
a(fn(x, 2), x, "First");
a(fn(x, 9), x, "Second");
a(fn(x, 3), x, "Third");
a(i, 1, "Called once");
}
};
},
"Delete": function (a) {
var i = 0, fn, mfn, x = {};
fn = function (a, b, c) {
return a + (++i);
};
mfn = memoize(fn, { length: 1 });
a(mfn(3), 4, "Init");
a(mfn(4, x, 1), 6, "Init #2");
mfn.delete(4);
a(mfn(3, x, 1), 4, "Cached");
mfn(3, x, 1);
a(i, 2, "Pre clear");
mfn.delete(3, x, 1);
a(i, 2, "After clear");
a(mfn(3, x, 1), 6, "Reinit");
a(i, 3, "Reinit count");
a(mfn(3, x, 1), 6, "Reinit Cached");
a(i, 3, "Reinit count");
}
};

View File

@@ -0,0 +1,91 @@
'use strict';
var memoize = require('../../..');
module.exports = {
"": function (a) {
var i = 0, fn = function (x, y, z) { ++i; return [x, y, z]; }, r;
fn = memoize(fn);
return {
"No args": function () {
i = 0;
a.deep(r = fn(), [undefined, undefined, undefined], "First");
a(fn(), r, "Second");
a(fn(), r, "Third");
a(i, 1, "Called once");
},
"Some Args": function () {
var x = {};
i = 0;
a.deep(r = fn(x, 8), [x, 8, undefined], "First");
a(fn(x, 8), r, "Second");
a(fn(x, 8), r, "Third");
a(i, 1, "Called once");
return {
"Other": function () {
a.deep(r = fn(x, 5), [x, 5, undefined], "Second");
a(fn(x, 5), r, "Third");
a(i, 2, "Called once");
}
};
},
"Full stuff": function () {
var x = {};
i = 0;
a.deep(r = fn(x, 8, 23, 98), [x, 8, 23], "First");
a(fn(x, 8, 23, 43), r, "Second");
a(fn(x, 8, 23, 9), r, "Third");
a(i, 1, "Called once");
return {
"Other": function () {
a.deep(r = fn(x, 23, 8, 13), [x, 23, 8], "Second");
a(fn(x, 23, 8, 22), r, "Third");
a(i, 2, "Called once");
}
};
}
};
},
"Delete": function (a) {
var i = 0, fn, mfn, x = {};
fn = function (a, b, c) {
return a + (++i);
};
mfn = memoize(fn);
a(mfn(3, x, 1), 4, "Init");
a(mfn(4, x, 1), 6, "Init #2");
mfn.delete(4, x, 1);
a(mfn(3, x, 1), 4, "Cached");
mfn(3, x, 1);
a(i, 2, "Pre clear");
mfn.delete(3, x, 1);
a(i, 2, "After clear");
a(mfn(3, x, 1), 6, "Reinit");
a(i, 3, "Reinit count");
a(mfn(3, x, 1), 6, "Reinit Cached");
a(i, 3, "Reinit count");
},
"Clear": function (a) {
var i = 0, fn, x = {};
fn = function () {
++i;
return arguments;
};
fn = memoize(fn, { length: 3 });
fn(1, x, 3);
fn(1, x, 4);
fn(1, x, 3);
fn(1, x, 4);
a(i, 2, "Pre clear");
fn.clear();
fn(1, x, 3);
fn(1, x, 4);
fn(1, x, 3);
fn(1, x, 4);
a(i, 4, "After clear");
}
};

View File

@@ -0,0 +1,59 @@
'use strict';
var aFrom = require('es5-ext/array/from')
, memoize = require('../../..');
module.exports = function () {
return {
"": function (a) {
var i = 0, fn = function () { ++i; return arguments; }, r;
fn = memoize(fn, { length: false });
return {
"No args": function () {
i = 0;
a.deep(aFrom(r = fn()), [], "First");
a(fn(), r, "Second");
a(fn(), r, "Third");
a(i, 1, "Called once");
},
"Some Args": function () {
var x = {};
i = 0;
a.deep(aFrom(r = fn(x, 8)), [x, 8], "First");
a(fn(x, 8), r, "Second");
a(fn(x, 8), r, "Third");
a(i, 1, "Called once");
},
"Many args": function () {
var x = {};
i = 0;
a.deep(aFrom(r = fn(x, 8, 23, 98)), [x, 8, 23, 98], "First");
a(fn(x, 8, 23, 98), r, "Second");
a(fn(x, 8, 23, 98), r, "Third");
a(i, 1, "Called once");
}
};
},
"Delete": function (a) {
var i = 0, fn, mfn, x = {};
fn = function (a, b, c) {
return a + (++i);
};
mfn = memoize(fn, { length: false });
a(mfn(3, x, 1), 4, "Init");
a(mfn(4, x, 1), 6, "Init #2");
mfn.delete(4, x, 1);
a(mfn(3, x, 1), 4, "Cached");
mfn(3, x, 1);
a(i, 2, "Pre clear");
mfn.delete(3, x, 1);
a(i, 2, "After clear");
a(mfn(3, x, 1), 6, "Reinit");
a(i, 3, "Reinit count");
a(mfn(3, x, 1), 6, "Reinit Cached");
a(i, 3, "Reinit count");
}
};
};

View File

@@ -0,0 +1,18 @@
'use strict';
var memoize = require('../../..')
, join = Array.prototype.join;
module.exports = function (a) {
var i = 0, fn = function () { ++i; return join.call(arguments, '|'); }
, y = { toString: function () { return 'foo'; } }, mfn;
mfn = memoize(fn, { primitive: true, length: false });
a(mfn(y, 'bar', 'zeta'), 'foo|bar|zeta', "#1");
a(mfn('foo', 'bar', 'zeta'), 'foo|bar|zeta', "#2");
a(i, 1, "Called once");
a(mfn(y, 'bar'), 'foo|bar', "#3");
a(i, 2, "Called twice");
a(mfn(y, 'bar'), 'foo|bar', "#4");
a(i, 2, "Called twice #2");
};

View File

@@ -0,0 +1,6 @@
'use strict';
module.exports = function (t, a) {
require('../../ext/async');
a(typeof t.async, 'function');
};

View File

@@ -0,0 +1,10 @@
'use strict';
module.exports = function (t, a) {
a(t(1, 2), 1, "Options");
a(t(1, 2, true), 1, "Options: Async ");
a(t(undefined, 2), 2, "Function");
a(t(undefined, 2, true), 1, "Function: Async");
a(t(undefined, undefined, false), 1, "Unknown");
a(t(undefined, undefined, true), 1, "Unknown: async");
};

34
test/methods-plain.js Normal file
View File

@@ -0,0 +1,34 @@
'use strict';
var d = require('d');
require('../ext/dispose');
require('../ext/ref-counter');
module.exports = function (t, a) {
var value = [], obj = {};
Object.defineProperties(obj, t({
someFn: d(function (x, y) { a(this, obj); return x + y; },
{ refCounter: true,
dispose: function (val) { value.push(val); } })
}));
obj = Object.create(obj);
obj.someFn(3, 7);
obj.someFn(5, 8);
obj.someFn(12, 4);
a.deep(value, [], "Pre");
obj.someFn(5, 8);
obj.someFn.deleteRef(5, 8);
a.deep(value, [], "Pre");
obj.someFn.deleteRef(5, 8);
a.deep(value, [13], "#1");
value = [];
obj.someFn.deleteRef(12, 4);
a.deep(value, [16], "#2");
value = [];
obj.someFn(77, 11);
obj.someFn.clear();
a.deep(value, [10, 88], "Clear all");
};

31
test/methods.js Normal file
View File

@@ -0,0 +1,31 @@
'use strict';
var d = require('d');
module.exports = function (t, a) {
var value = [], obj = {};
Object.defineProperties(obj, t({
someFn: d(function (x, y) { a(this, obj); return x + y; },
{ refCounter: true,
dispose: function (val) { value.push(val); } })
}));
obj = Object.create(obj);
obj.someFn(3, 7);
obj.someFn(5, 8);
obj.someFn(12, 4);
a.deep(value, [], "Pre");
obj.someFn(5, 8);
obj.someFn.deleteRef(5, 8);
a.deep(value, [], "Pre");
obj.someFn.deleteRef(5, 8);
a.deep(value, [13], "#1");
value = [];
obj.someFn.deleteRef(12, 4);
a.deep(value, [16], "#2");
value = [];
obj.someFn(77, 11);
obj.someFn.clear();
a.deep(value, [10, 88], "Clear all");
};

29
test/plain.js Normal file
View File

@@ -0,0 +1,29 @@
'use strict';
module.exports = function (t) {
return {
"": function (a) {
var i = 0, fn = function (x) { ++i; return x; }, mfn
, y = { toString: function () { return 'foo'; } };
mfn = t(fn, { primitive: true });
a(typeof mfn, 'function', "Returns");
a(mfn.__memoized__, true, "Marked");
a(t(mfn), mfn, "Do not memoize memoized");
a(mfn(y), y, "#1");
a(mfn('foo'), y, "#2");
a(i, 1, "Called once");
},
"Clear cache": function (a) {
var i = 0, fn = function (x, y, z) { ++i; return x + y + z; }, mfn
, y = { toString: function () { return 'foo'; } };
mfn = t(fn, { primitive: true });
a(mfn(y, 'bar', 'zeta'), 'foobarzeta', "#1");
a(mfn('foo', 'bar', 'zeta'), 'foobarzeta', "#2");
a(i, 1, "Called once");
mfn.delete('foo', { toString: function () { return 'bar'; } },
'zeta');
a(mfn(y, 'bar', 'zeta'), 'foobarzeta', "#3");
a(i, 2, "Called twice");
}
};
};

View File

@@ -1,72 +0,0 @@
'use strict';
var join = Array.prototype.join;
module.exports = function (t) {
return {
"No args": function (a) {
var i = 0, fn = function () { ++i; return arguments[0]; }, mfn;
mfn = t(fn, { primitive: true });
a(mfn('ble'), 'ble', "#1");
a(mfn({}), 'ble', "#2");
a(i, 1, "Called once");
},
"One arg": function (a) {
var i = 0, fn = function (x) { ++i; return x; }, mfn
, y = { toString: function () { return 'foo'; } };
mfn = t(fn, { primitive: true });
a(mfn(y), y, "#1");
a(mfn('foo'), y, "#2");
a(i, 1, "Called once");
},
"Many args": function (a) {
var i = 0, fn = function (x, y, z) { ++i; return x + y + z; }, mfn
, y = { toString: function () { return 'foo'; } };
mfn = t(fn, { primitive: true });
a(mfn(y, 'bar', 'zeta'), 'foobarzeta', "#1");
a(mfn('foo', 'bar', 'zeta'), 'foobarzeta', "#2");
a(i, 1, "Called once");
},
"Many args: Length false": function (a) {
var i = 0, fn = function () { ++i; return join.call(arguments, '|'); }
, y = { toString: function () { return 'foo'; } }, mfn;
mfn = t(fn, { primitive: true, length: false });
a(mfn(y, 'bar', 'zeta'), 'foo|bar|zeta', "#1");
a(mfn('foo', 'bar', 'zeta'), 'foo|bar|zeta', "#2");
a(i, 1, "Called once");
a(mfn(y, 'bar'), 'foo|bar', "#3");
a(i, 2, "Called twice");
a(mfn(y, 'bar'), 'foo|bar', "#4");
a(i, 2, "Called twice #2");
},
"Clear cache": function (a) {
var i = 0, fn = function (x, y, z) { ++i; return x + y + z; }, mfn
, y = { toString: function () { return 'foo'; } };
mfn = t(fn, { primitive: true });
a(mfn(y, 'bar', 'zeta'), 'foobarzeta', "#1");
a(mfn('foo', 'bar', 'zeta'), 'foobarzeta', "#2");
a(i, 1, "Called once");
mfn.clear('foo', { toString: function () { return 'bar'; } },
'zeta');
a(mfn(y, 'bar', 'zeta'), 'foobarzeta', "#3");
a(i, 2, "Called twice");
},
"Circular": function (a) {
var i = 0, fn;
fn = t(function (x) {
if (++i < 2) fn(x);
});
a.throws(function () {
fn('foo');
}, 'CIRCULAR_INVOCATION');
i = 0;
fn = t(function (x, y) {
if (++i < 2) fn(x, y);
});
a.throws(function () {
fn('foo', 'bar');
}, 'CIRCULAR_INVOCATION');
}
};
};

View File

@@ -1,223 +0,0 @@
'use strict';
var aFrom = require('es5-ext/array/from');
module.exports = function (t, a) {
return {
"0": function () {
var i = 0, fn = function () { ++i; return 3; };
fn = t(fn);
a(fn(), 3, "First");
a(fn(1), 3, "Second");
a(fn(5), 3, "Third");
a(i, 1, "Called once");
},
"1": function () {
var i = 0, fn = function (x) { ++i; return x; };
fn = t(fn);
return {
"No arg": function () {
i = 0;
a(fn(), undefined, "First");
a(fn(), undefined, "Second");
a(fn(), undefined, "Third");
a(i, 1, "Called once");
},
"Arg": function () {
var x = {};
i = 0;
a(fn(x, 8), x, "First");
a(fn(x, 4), x, "Second");
a(fn(x, 2), x, "Third");
a(i, 1, "Called once");
},
"Other Arg": function () {
var x = {};
i = 0;
a(fn(x, 2), x, "First");
a(fn(x, 9), x, "Second");
a(fn(x, 3), x, "Third");
a(i, 1, "Called once");
}
};
},
"3": function () {
var i = 0, fn = function (x, y, z) { ++i; return [x, y, z]; }, r;
fn = t(fn);
return {
"No args": function () {
i = 0;
a.deep(r = fn(), [undefined, undefined, undefined], "First");
a(fn(), r, "Second");
a(fn(), r, "Third");
a(i, 1, "Called once");
},
"Some Args": function () {
var x = {};
i = 0;
a.deep(r = fn(x, 8), [x, 8, undefined], "First");
a(fn(x, 8), r, "Second");
a(fn(x, 8), r, "Third");
a(i, 1, "Called once");
return {
"Other": function () {
a.deep(r = fn(x, 5), [x, 5, undefined], "Second");
a(fn(x, 5), r, "Third");
a(i, 2, "Called once");
}
};
},
"Full stuff": function () {
var x = {};
i = 0;
a.deep(r = fn(x, 8, 23, 98), [x, 8, 23], "First");
a(fn(x, 8, 23, 43), r, "Second");
a(fn(x, 8, 23, 9), r, "Third");
a(i, 1, "Called once");
return {
"Other": function () {
a.deep(r = fn(x, 23, 8, 13), [x, 23, 8], "Second");
a(fn(x, 23, 8, 22), r, "Third");
a(i, 2, "Called once");
}
};
}
};
},
"Dynamic": function () {
var i = 0, fn = function () { ++i; return arguments; }, r;
fn = t(fn, { length: false });
return {
"No args": function () {
i = 0;
a.deep(aFrom(r = fn()), [], "First");
a(fn(), r, "Second");
a(fn(), r, "Third");
a(i, 1, "Called once");
},
"Some Args": function () {
var x = {};
i = 0;
a.deep(aFrom(r = fn(x, 8)), [x, 8], "First");
a(fn(x, 8), r, "Second");
a(fn(x, 8), r, "Third");
a(i, 1, "Called once");
},
"Many args": function () {
var x = {};
i = 0;
a.deep(aFrom(r = fn(x, 8, 23, 98)), [x, 8, 23, 98], "First");
a(fn(x, 8, 23, 98), r, "Second");
a(fn(x, 8, 23, 98), r, "Third");
a(i, 1, "Called once");
}
};
},
"Clear Cache": {
"Specific": function () {
var i = 0, fn, mfn, x = {};
fn = function (a, b, c) {
return a + (++i);
};
return {
"0": function (a) {
mfn = t(fn, { length: 0 });
a(mfn(3), 4, "Init");
a(mfn(5), 4, "Other");
a(i, 1, "Pre clear");
mfn.clear(6, x, 1);
a(i, 1, "After clear");
a(mfn(6, x, 1), 8, "Reinit");
a(i, 2, "Reinit count");
a(mfn(3, x, 1), 8, "Reinit Cached");
a(i, 2, "Reinit count");
},
"1": function (a) {
i = 0;
mfn = t(fn, { length: 1 });
a(mfn(3), 4, "Init");
a(mfn(4, x, 1), 6, "Init #2");
mfn.clear(4);
a(mfn(3, x, 1), 4, "Cached");
mfn(3, x, 1);
a(i, 2, "Pre clear");
mfn.clear(3, x, 1);
a(i, 2, "After clear");
a(mfn(3, x, 1), 6, "Reinit");
a(i, 3, "Reinit count");
a(mfn(3, x, 1), 6, "Reinit Cached");
a(i, 3, "Reinit count");
},
"3": function (a) {
i = 0;
mfn = t(fn);
a(mfn(3, x, 1), 4, "Init");
a(mfn(4, x, 1), 6, "Init #2");
mfn.clear(4, x, 1);
a(mfn(3, x, 1), 4, "Cached");
mfn(3, x, 1);
a(i, 2, "Pre clear");
mfn.clear(3, x, 1);
a(i, 2, "After clear");
a(mfn(3, x, 1), 6, "Reinit");
a(i, 3, "Reinit count");
a(mfn(3, x, 1), 6, "Reinit Cached");
a(i, 3, "Reinit count");
},
"Any": function (a) {
i = 0;
mfn = t(fn, { length: false });
a(mfn(3, x, 1), 4, "Init");
a(mfn(4, x, 1), 6, "Init #2");
mfn.clear(4, x, 1);
a(mfn(3, x, 1), 4, "Cached");
mfn(3, x, 1);
a(i, 2, "Pre clear");
mfn.clear(3, x, 1);
a(i, 2, "After clear");
a(mfn(3, x, 1), 6, "Reinit");
a(i, 3, "Reinit count");
a(mfn(3, x, 1), 6, "Reinit Cached");
a(i, 3, "Reinit count");
}
};
},
"All": function (a) {
var i = 0, fn, x = {};
fn = function () {
++i;
return arguments;
};
fn = t(fn, { length: 3 });
fn(1, x, 3);
fn(1, x, 4);
fn(1, x, 3);
fn(1, x, 4);
a(i, 2, "Pre clear");
fn.clearAll();
fn(1, x, 3);
fn(1, x, 4);
fn(1, x, 3);
fn(1, x, 4);
a(i, 4, "After clear");
}
},
"Circular": function (a) {
var i = 0, fn;
fn = t(function (x) {
if (++i < 2) fn(x);
});
a.throws(function () {
fn(34);
}, 'CIRCULAR_INVOCATION');
}
};
};