Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 108 additions & 72 deletions lib/twill.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,96 +66,132 @@ function _pointcut(handle) {
},
remove: function (name) {
var orig = target[name];
return function (func) {
return function () {
handle.aspects.push({name: name, orig: orig});
delete target[name];
};
}
};
}

module.exports = {
aspect: function (target, advice) {
if (!target) {
throw "missing target object to aspect";
}

if (!advice) {
throw "missing advice";
function _fastWeave(handle) {
return {
add: {},
before: function(name, method) {
_pointcut(handle).before(name)(method);
},
after: function(name, method) {
_pointcut(handle).after(name)(method);
},
around: function(name, method) {
_pointcut(handle).around(name)(method);
},
remove: function(name) {
if (typeof handle.target[name] === 'undefined') {
throw new Error(name + " not defined");
}
_pointcut(handle).remove(name)();
},
exception: function(name, method) {
_pointcut(handle).exception(name)(method);
}
};
}

var functions = _funcs(target),
handle = {
target: target,
aspects: [],
add: []
},
weave = {
before: {},
after: {},
around: {},
add: {},
remove: {},
exception: {},
all: {
before: function (method) {
var funcs = _funcs(target);

funcs.forEach(function (f) {
_pointcut(handle).before(f)(function () {
method(f, arguments);
});
function _weave(handle) {
var target = handle.target,
functions = _funcs(target),
weave = {
before: {},
after: {},
around: {},
add: {},
remove: {},
exception: {},
all: {
before: function (method) {
var funcs = _funcs(target);

funcs.forEach(function (f) {
_pointcut(handle).before(f)(function () {
method(f, arguments);
});
},
after: function (method) {
var funcs = _funcs(target);

funcs.forEach(function (f) {
_pointcut(handle).after(f)(function () {
method(f, arguments);
});
});
},
after: function (method) {
var funcs = _funcs(target);

funcs.forEach(function (f) {
_pointcut(handle).after(f)(function () {
method(f, arguments);
});
},
around: function (method) {
var funcs = _funcs(target);

funcs.forEach(function (f) {
_pointcut(handle).around(f)(function (args, orig) {
return method(f, args, orig);
});
});
},
around: function (method) {
var funcs = _funcs(target);

funcs.forEach(function (f) {
_pointcut(handle).around(f)(function (args, orig) {
return method(f, args, orig);
});
},
exception: function (method) {
var funcs = _funcs(target);

funcs.forEach(function (f) {
_pointcut(handle).around(f)(function () {
return method.apply(target, arguments);
});
});
},
exception: function (method) {
var funcs = _funcs(target);

funcs.forEach(function (f) {
_pointcut(handle).around(f)(function () {
return method.apply(target, arguments);
});
}
});
}
};
}
};
functions.forEach(function (f) {
weave.before[f] = _pointcut(handle).before(f);
weave.after[f] = _pointcut(handle).after(f);
weave.around[f] = _pointcut(handle).around(f);
weave.exception[f] = _pointcut(handle).exception(f);
weave.remove[f] = _pointcut(handle).remove(f);
});
return weave;
};

functions.forEach(function (f) {
weave.before[f] = _pointcut(handle).before(f);
weave.after[f] = _pointcut(handle).after(f);
weave.around[f] = _pointcut(handle).around(f);
weave.exception[f] = _pointcut(handle).exception(f);
weave.remove[f] = _pointcut(handle).remove(f);
});
function _aspect(target, advice, getWeave) {
if (!target) {
throw "missing target object to aspect";
}

advice.apply(target, [weave]);
if (!advice) {
throw "missing advice";
}

_funcs(weave.add).forEach(function (f) {
if (target[f]) {
throw "attempt to add a method that already existed";
}
target[f] = weave.add[f];
handle.add.push(f);
});
var handle = {
target: target,
aspects: [],
add: []
},
weave = getWeave(handle);
advice.apply(target, [weave]);

return _handles.push(handle) - 1;
_funcs(weave.add).forEach(function (f) {
if (target[f]) {
throw "attempt to add a method that already existed";
}
target[f] = weave.add[f];
handle.add.push(f);
});

return _handles.push(handle) - 1;
}

module.exports = {
fastAspect: function(target, advice) {
return _aspect(target, advice, _fastWeave);
},

aspect: function (target, advice) {
return _aspect(target, advice, _weave);
},

unweave: function (id) {
Expand Down
46 changes: 46 additions & 0 deletions test/aspect-fast-add.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
var twill = require("../lib/twill");

exports["when aspecting in a new method"] = {
"it can add a new method" : function (test) {
var target = {};

twill.fastAspect(target, function (weave) {
weave.add.asdf = function () {
test.done();
};
});

target.asdf();
},

"it throws an exception if the method already exists" : function (test) {
var target = {
woohoo: function () {}
};

try {
twill.fastAspect(target, function (weave) {
weave.add.woohoo = function () {
};
});
} catch (e) {
test.done();
}
},

"it can unweave the new method" : function (test) {
var target = {},
aspect = twill.fastAspect(target, function (weave) {
weave.add.foo = function () {};
weave.add.bar = function () {};
weave.add.baz = function () {};
});

twill.unweave(aspect);

test.equal(undefined, target.foo);
test.equal(undefined, target.bar);
test.equal(undefined, target.baz);
test.done();
}
};
96 changes: 96 additions & 0 deletions test/aspect-fast-after.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
var twill = require("../lib/twill");

exports["when aspecting after a method"] = {
"it calls the aspect after the actual" : function (test) {
var msg = "",
target = {
f: function () {
msg += "hello ";
}
};

twill.fastAspect(target, function (weave) {
weave.after('f', function () {
msg += "world";
});
});

target.f();

test.equal("hello world", msg);
test.done();
},

"it passes the arguments to the aspect method and the orig method" : function (test) {
var target = {
stuff: function (a, b, c) {
test.equal(a, "1");
test.equal(b, "2");
test.equal(c, "3");
test.done();
}
};

twill.fastAspect(target, function (weave) {
weave.after('stuff', function (x, y, z) {
test.equal(x, "1");
test.equal(y, "2");
test.equal(z, "3");
});
});

target.stuff("1", "2", "3");
},

"the context for both the aspect method and the original are the target method" : function (test) {
var target = {
merp: function () {
test.strictEqual(this, target);
test.done();
}
};

twill.fastAspect(target, function (weave) {
weave.after('merp', function () {
test.strictEqual(this, target);
});
});

target.merp();
},

"it still returns the value of the original method" : function (test) {
var target = {
doubleRainbow: function () {
return "OMG OMG OMG OMG OMG";
}
};

twill.fastAspect(target, function (weave) {
weave.after('doubleRainbow', function () {
return "meh";
});
});

test.equal("OMG OMG OMG OMG OMG", target.doubleRainbow());
test.done();
},

"it can unweave the methods added after" : function (test) {
var untouched = true,
target = {
soap: function () {
}
};

twill.unweave(twill.fastAspect(target, function (weave) {
weave.after('soap', function () {
untouched = false;
});
}));

target.soap();
test.ok(untouched);
test.done();
}
};
Loading