scarlet
Version:
The simple fast javascript interceptor for methods and properties.
1,769 lines (1,478 loc) • 75.4 kB
JavaScript
!function(e){if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.scarlet=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
module.exports = _dereq_("./lib/scarlet.js");
},{"./lib/scarlet.js":16}],2:[function(_dereq_,module,exports){
var assert = _dereq_("assert");
function Enumerable() {
"use strict";
var self = this;
self.__typename__ = "scarlet.lib.extensions.Enumerable";
self.arrayFor = function(array, callback) {
for (var i = 0; i < array.length; i++) {
callback(array[i], i, array);
}
};
self.funcFor = function(object, callback) {
for (var key in object) {
callback(object[key], key, object);
}
};
self.stringFor = function(string, callback) {
self.arrayFor(string.split(""), function(chr, index) {
callback(chr, index, string);
});
};
self.allEach = function(object, callback) {
Object.getOwnPropertyNames(object).forEach(function(property) {
callback(object[property], property, object);
});
};
self.forEach = function(object, callback) {
if (object) {
var resolve = self.funcFor;
if (object instanceof Function) {
resolve = self.funcFor;
} else if (typeof object == "string") {
resolve = self.stringFor;
} else if (typeof object.length == "number") {
resolve = self.arrayFor;
}
resolve(object, callback);
}
};
self.any = function(object, predicateCallback) {
var isTrue = false;
self.forEach(object, function(element, index) {
if (!predicateCallback(element, index))
return;
isTrue = true;
return;
});
return isTrue;
};
self.where = function(object, predicateCallback) {
var results = [];
self.forEach(object, function(element) {
if (predicateCallback(element))
results.push(element);
});
return results;
};
self.first = function(object, predicateCallback) {
if (typeof(predicateCallback) == "undefined" && typeof(object) != "undefined" && object.length > 0) {
return object[0];
}
var results = self.where(object, predicateCallback);
if (results.length > 0)
return results[0];
return null;
};
self.mapSeries = function(functions, onEach, onComplete) {
assert(typeof functions.length === "number", "Object to map must be an array");
if (!this)
return new self.mapSeries(functions, onEach, onComplete);
var results = [];
var thisContext = this;
var functionNumber = 0;
var next = function() {
var nextfunction = functions[functionNumber];
functionNumber++;
return nextfunction;
};
var finalResult = null;
thisContext._proceed = function(error, result) {
if (result !== undefined)
results.push(result);
var nextfunction = next();
if (nextfunction)
onEach(error, nextfunction, thisContext._proceed);
else if (onComplete)
finalResult = onComplete(error, results);
return finalResult;
};
thisContext._proceed();
};
}
module.exports = new Enumerable();
},{"assert":17}],3:[function(_dereq_,module,exports){
var util = _dereq_("util");
var inspect = util.inspect;
var assert = _dereq_("assert");
var object = _dereq_("./object");
var ll = function(val) {
self.log(inspect(val));
};
function Logger() {
"use strict";
var self = this;
self.DEBUG = 4;
self.INFO = 3;
self.WARN = 2;
self.ERROR = 1;
self.NONE = 0;
self.logLevel = self.NONE;
self.log = console.log;
var getFunctionName = function(func) {
if (typeof(func) == "string")
return func;
var ret = func.toString();
ret = ret.substr("function ".length);
ret = ret.substr(0, ret.indexOf("("));
if (ret === "" || ret === null || typeof(ret) === "undefined")
ret = "function<anonymous>";
return ret;
};
var getParamNames = function(func) {
var ret = "";
if (typeof(func) === "string") {
ret += func;
} else if (typeof(func) !== "string") {
ret = func.toString();
}
if (ret == "[object Object]")
ret += "";
var firstBracketIndex = ret.indexOf("(");
var lastBracketIndex = ret.indexOf(")");
if (firstBracketIndex === -1 || lastBracketIndex === -1)
return [];
ret = ret.slice(ret.indexOf("(") + 1, ret.indexOf(")"));
return ret.split(",");
};
self.print = function(type, obj, func, msg, args) {
if (args === null)
args = "";
else
args = "\n" + inspect(args).replace(/\n/g, "\n");
if (typeof(msg) == "object" || typeof(msg) == "function")
msg = inspect(msg, {
depth: 10,
showHidden: true
});
if (typeof(func) == "string" && !object.isNull(obj) && !object.isNull(obj[func])) {
var funcName = func;
var actualFunc = obj[func];
self.log(type + getFunctionName(obj) + "::" + funcName + "(" + getParamNames(actualFunc).join(",") + ") - " + msg + args + "\n");
return;
}
if (!object.isNull(obj) && object.isNull(func)) {
self.log(type + getFunctionName(func) + "(" + getParamNames(obj).join(",") + ") - " + msg + args + "\n");
return;
}
if (object.isNull(obj) && !object.isNull(func)) {
self.log(type + getFunctionName(func) + "(" + getParamNames(func).join(",") + ") - " + msg + args + "\n");
return;
}
if (!object.isNull(obj) && !object.isNull(func)) {
self.log(type + getFunctionName(obj) + "::" + getFunctionName(func) + "(" + getParamNames(func).join(",") + ") - " + msg + args + "\n");
return;
}
};
self.debug = function(obj, func, msg, args) {
if (self.logLevel == self.DEBUG)
self.print("DEBUG @ [" + new Date().toString() + "] -> ", obj, func, msg, args);
};
self.info = function(obj, func, msg, args) {
if (self.logLevel >= self.INFO)
self.print("INFO @ [" + new Date().toString() + "] -> ", obj, func, msg, args);
};
self.warn = function(obj, func, msg, args) {
if (self.logLevel >= self.WARN)
self.print("WARN @ [" + new Date().toString() + "] -> ", obj, func, msg, args);
};
self.error = function(obj, func, msg, args) {
if (self.logLevel >= self.ERROR)
self.print("ERROR @ [" + new Date().toString() + "] -> ", obj, func, msg, args);
};
}
module.exports = new Logger();
},{"./object":4,"assert":17,"util":25}],4:[function(_dereq_,module,exports){
function ObjectExtended() {
"use strict";
var self = this;
var enumerable = _dereq_("./enumerable");
self.__typename__ = "scarlet.lib.extensions.Object";
self.has = function(obj, key) {
return hasOwnProperty.call(obj, key);
};
self.isObject = function(obj) {
return obj instanceof Object;
};
self.isFunction = function(obj) {
return obj instanceof Function;
};
self.isNull = function(obj) {
if (typeof(obj) === "object")
return obj === null;
return typeof(obj) === "undefined";
};
self.inherit = function(child, parent) {
child.__super__ = parent;
child.prototype = Object.create(parent.prototype, {
constructor: {
value: child,
writable: true,
enumerable: false,
configurable: true
}
});
};
self.name = function(obj) {
if (!obj)
return "undefined";
if (obj.name)
return obj.name;
if (obj.constructor) {
if (obj.constructor.name)
return obj.constructor.name;
}
var funcNameRegex = /function\s([^(]{1,})\(/;
var results = (funcNameRegex).exec((obj).toString());
if ((results && results.length > 1))
return results[1].trim();
if (obj instanceof Function)
return "Function";
return "Object";
};
self.objectHasFunction = function(object, objectFunction) {
for (var property in object) {
if (object[property] == objectFunction)
return true;
}
return false;
};
self.extend = function(fromObject, toObject) {
for (var property in fromObject) {
toObject[property] = fromObject[property];
}
};
}
module.exports = new ObjectExtended();
},{"./enumerable":2}],5:[function(_dereq_,module,exports){
var assert = _dereq_("assert");
var ScarletTrace = function(proceed, info) {
"use strict";
assert(info, "info=null");
assert(proceed, "proceed=null");
var self = this;
self.args = info.args;
self.result = info.result;
self.memberName = info.memberName;
self.hasResult = typeof(info.result) !== "undefined";
self.hasArgs = typeof(info.args) !== "undefined";
self.argsEmpty = info.args !== null && typeof(info.args) !== "undefined" && info.args.length === 0;
self.traceTo = function(io) {
var formattedResult = (!self.hasResult) ? "void" : info.result;
var formattedName = (!info.memberName) ? "" : info.memberName;
var formattedArgs = ((!self.hasArgs) || (self.argsEmpty)) ? "" : JSON.stringify(info.args);
io(formattedName + "(" + formattedArgs + "):" + formattedResult);
};
};
module.exports = ScarletTrace;
},{"assert":17}],6:[function(_dereq_,module,exports){
var assert = _dereq_("assert");
var Invocation = _dereq_("./invocation");
var enumerable = _dereq_("../extensions/enumerable");
var ProxyInterceptor = _dereq_("../proxies/proxy-interceptor");
module.exports = function Interceptor() {
"use strict";
var self = this;
self.proxy = null;
self.interceptors = [];
self.__typename__ = "scarlet.lib.interceptors.Interceptor";
self.intercept = function(typeOrInstance, memberName, replaceTypeCallback) {
assert(typeOrInstance);
assert(replaceTypeCallback);
self.proxy = new ProxyInterceptor(typeOrInstance, memberName);
self.proxy.intercept(whenProxyCalled, replaceTypeCallback);
return self;
};
self.using = function(interceptor) {
assert(self.proxy, "Please make sure you are intercepting something first");
self.interceptors.push(interceptor);
return self;
};
var whenProxyCalled = function(invocationName, invocationMethod, args) {
assert(self.interceptors.length > 0, "Please make sure you add an interceptor");
var thisContext = this;
var invocation = new Invocation(thisContext, invocationName, invocationMethod, args);
callEachInterceptor(thisContext,
invocation,
function(error, interceptorResults) {
if (error) throw error;
invocation.result = invocation.proceed();
if (interceptorResults.length > 0)
invocation.result = interceptorResults[interceptorResults.length - 1];
return invocation.result;
});
return invocation.result;
};
var callEachInterceptor = function(thisContext, invocation, onAllCalled) {
enumerable.mapSeries(self.interceptors,
function(error, nextInterceptor, callback) {
if (error && numberOfParameters(nextInterceptor) < 3)
return callback(error);
if (numberOfParameters(nextInterceptor) === 1)
return nextInterceptor.call(thisContext, callback);
if (numberOfParameters(nextInterceptor) === 2)
return nextInterceptor.call(thisContext, invocation, callback);
return nextInterceptor.call(thisContext, error, invocation, callback);
},
onAllCalled);
};
};
var numberOfParameters = function(functionWithParameters) {
if (typeof functionWithParameters !== "function")
return 0;
return functionWithParameters.length;
};
},{"../extensions/enumerable":2,"../proxies/proxy-interceptor":11,"./invocation":7,"assert":17}],7:[function(_dereq_,module,exports){
var assert = _dereq_("assert");
var objectExt = _dereq_("../extensions/object");
module.exports = function Invocation(context, invocationName, invocationMethod, args) {
"use strict";
assert(invocationMethod, "invocationMethod == null");
var self = this;
if (!args)
args = [];
/**
* The original arguments passed into the function being intercepted
*
* @category Invocation Attributes
* @type {Object} - the argument object based into objects
*/
self.args = args;
/**
* The reference to self for the original/called methd
*
* @category Invocation Attributes
* @type {Object}
*/
self.context = context;
/**
* The result of the method being intercepted
*
* @category Invocation Attributes
* @type {Any}
*/
self.result = null;
/**
* The method being intercepted
*
* @category Invocation Attributes
* @type {Function}
*/
self.method = invocationMethod;
/**
* Gets the name of the intercepted context
*
* @category Invocation Attributes
* @type {String}
*/
self.contextName = function() {
return objectExt.name(self.context);
};
/**
* Gets the name of the intercepted member
*
* @category Invocation Attributes
* @type {String}
*/
self.memberName = function() {
if (invocationName)
return invocationName;
return objectExt.name(self.method);
};
/**
* The start date time when the method was invoked
*
* @category Invocation Attributes
* @type {Date}
*/
self.executionStartDate = null;
/**
* The end date time when the method was invoked
*
* @category Invocation Attributes
* @type {Date}
*/
self.executionEndDate = null;
/**
* Calls the intercepted method
*
* @category Invocation Attributes
* @method proceed
* @return Function|Object of the result of the original method call
*/
self.proceed = function() {
var parameters = Array.prototype.slice.call(self.args);
self.executionStartDate = new Date();
self.result = self.method.apply(self.context, parameters);
self.executionEndDate = new Date();
return self.result;
};
};
},{"../extensions/object":4,"assert":17}],8:[function(_dereq_,module,exports){
(function (__dirname){
var path = _dereq_("path");
var assert = _dereq_("assert");
function PluginManager() {
"use strict";
var self = this;
self.directoryPath = __dirname + "/../../../";
self.setDirectory = function(directoryPath) {
self.directoryPath = directoryPath;
};
self.load = function(scarlet, pluginDirectoryPath) {
assert(scarlet);
var fullPath = path.normalize(self.directoryPath + pluginDirectoryPath);
var ScarletPlugin = _dereq_(fullPath);
var pluginObject = new ScarletPlugin(scarlet);
pluginObject.initialize();
return pluginObject;
};
}
module.exports = PluginManager;
}).call(this,"/lib/plugins")
},{"assert":17,"path":23}],9:[function(_dereq_,module,exports){
var assert = _dereq_("assert");
module.exports = function ProxyFunction(actualFunction) {
"use strict";
assert(actualFunction);
this.__typename__ = "scarlet.lib.proxies.ProxyFunction";
this.wrap = function(whenCalled, replaceFunctionCallback) {
assert(whenCalled);
var proxiedFunction = function() {
var args = Array.prototype.slice.call(arguments);
return whenCalled.call(this,
actualFunction.name,
actualFunction,
args);
};
if (replaceFunctionCallback)
replaceFunctionCallback(proxiedFunction);
return this;
};
this.unwrap = function() {
return actualFunction;
};
};
},{"assert":17}],10:[function(_dereq_,module,exports){
var assert = _dereq_("assert");
var ProxyMethod = _dereq_("./proxy-method");
var ProxyMetadata = _dereq_("./proxy-metadata");
var ProxyProperty = _dereq_("./proxy-property");
var enumerable = _dereq_("../extensions/enumerable");
module.exports = function ProxyInstance(instance) {
"use strict";
if (!(this instanceof ProxyInstance))
return new ProxyInstance(instance);
assert(instance);
var proxies = [];
this.__typename__ = "scarlet.lib.proxies.ProxyInstance";
this.wrap = function(whenCalled) {
assert(whenCalled);
proxyEachInstanceMember(instance, function(proxy) {
proxy.wrap(whenCalled);
proxies.push(proxy);
});
return this;
};
this.unwrap = function() {
enumerable.forEach(proxies, function(proxy) {
proxy.unwrap();
});
return this;
};
var proxyEachInstanceMember = function(instanceToProxy, onEach) {
if (!onEach) return;
enumerable.forEach(instanceToProxy, function(member, memberName) {
assert(memberName);
var proxyMetadata = new ProxyMetadata(instanceToProxy, memberName).ensureShadow();
if (!proxyMetadata.reflection.isAllowed()) return;
if (proxyMetadata.reflection.isMethod())
return onEach(new ProxyMethod(instanceToProxy, memberName));
if (proxyMetadata.reflection.isProperty())
return onEach(new ProxyProperty(instanceToProxy, memberName));
});
};
};
},{"../extensions/enumerable":2,"./proxy-metadata":12,"./proxy-method":13,"./proxy-property":14,"assert":17}],11:[function(_dereq_,module,exports){
var assert = _dereq_("assert");
var ProxyMethod = _dereq_("./proxy-method");
var proxyMetadata = _dereq_("./proxy-metadata");
var ProxyProperty = _dereq_("./proxy-property");
var ProxyInstance = _dereq_("./proxy-instance");
var ProxyFunction = _dereq_("./proxy-function");
var ProxyPrototype = _dereq_("./proxy-prototype");
module.exports = function ProxyInterceptor(typeOrInstance, memberName) {
"use strict";
assert(typeOrInstance);
this.proxy = null;
this.__typename__ = "scarlet.lib.proxies.ProxyInterceptor";
this.intercept = function(whenCalledCallback, replaceClassCallback) {
assert(whenCalledCallback);
assert(replaceClassCallback);
proxyMetadata(typeOrInstance, memberName).ensureShadow();
this.proxy = proxyForTypeOrInstance().wrap(whenCalledCallback, replaceClassCallback);
};
this.release = function() {
if (this.proxy)
this.proxy.unwrap();
};
var proxyForTypeOrInstance = function() {
if (memberName)
return proxyForMember();
if (typeOrInstance.prototype)
return new ProxyPrototype(typeOrInstance);
if (typeof typeOrInstance === "function")
return new ProxyFunction(typeOrInstance);
return new ProxyInstance(typeOrInstance);
};
var proxyForMember = function() {
if (!memberName) return;
if (typeof typeOrInstance[memberName] === "function")
return new ProxyMethod(typeOrInstance, memberName);
return new ProxyProperty(typeOrInstance, memberName);
};
};
},{"./proxy-function":9,"./proxy-instance":10,"./proxy-metadata":12,"./proxy-method":13,"./proxy-property":14,"./proxy-prototype":15,"assert":17}],12:[function(_dereq_,module,exports){
var assert = _dereq_("assert");
var object = _dereq_("../extensions/object");
var logger = _dereq_("../extensions/logger");
module.exports = function ProxyMetadata(instanceOrType, memberName) {
"use strict";
if (!(this instanceof ProxyMetadata))
return new ProxyMetadata(instanceOrType, memberName);
assert(instanceOrType);
this.__typename__ = "scarlet.lib.proxies.ProxyMetadata";
this.reflection = {};
this.hasShadow = function() {
return object.has(instanceOrType, "__scarlet__");
};
this.ensureShadow = function() {
if (!this.hasShadow())
instanceOrType.__scarlet__ = {};
logger.debug(ProxyMetadata, "ensureShadow", "Shadow Object Created", [instanceOrType]);
return this;
};
this.reflection.isAllowed = function() {
var result = memberName != "__scarlet__";
logger.debug(ProxyMetadata, "isAllowed", "Is Allowed For Proxy?", [result]);
return result;
};
this.reflection.isMethod = function() {
var result = !object.isNull(instanceOrType) && !object.isNull(memberName) && !object.isNull(instanceOrType[memberName]) && object.isFunction(instanceOrType[memberName]);
logger.info(ProxyMetadata, "isMethod", "Is Method?", [result]);
return result;
};
this.reflection.isFunction = function() {
var result = object.isNull(memberName) && object.isFunction(instanceOrType);
logger.info(ProxyMetadata, "isFunction", "Is Function?", [result]);
return result;
};
this.reflection.isProperty = function() {
var result = !object.isFunction(instanceOrType[memberName]);
logger.info(ProxyMetadata, "isProperty", "Is Property?", [result]);
return result;
};
this.reflection.isInstance = function() {
this.isFunction();
var result = object.isNull(memberName) && object.isObject(instanceOrType);
logger.info(ProxyMetadata, "isInstance", "Is Instance?", [result]);
return result;
};
this.reflection.isPrototype = function() {
var result = object.isNull(memberName) && object.isFunction(instanceOrType) && !object.isNull(instanceOrType.prototype);
logger.info(ProxyMetadata, "isPrototype", "Is Prototype?", [result]);
return result;
};
this.reflection.hasMember = function() {
return !object.isNull(memberName);
};
return this;
};
},{"../extensions/logger":3,"../extensions/object":4,"assert":17}],13:[function(_dereq_,module,exports){
var assert = _dereq_("assert");
module.exports = function ProxyMethod(methodThisContext, methodName) {
"use strict";
assert(methodName);
assert(methodThisContext);
var actualMethod = methodThisContext[methodName];
this.__typename__ = "scarlet.lib.proxies.ProxyMethod";
this.wrap = function(whenCalled) {
assert(whenCalled);
methodThisContext[methodName] = function() {
var args = Array.prototype.slice.call(arguments);
return whenCalled.call(methodThisContext,
methodName,
actualMethod,
args);
};
return this;
};
this.unwrap = function() {
methodThisContext[methodName] = actualMethod;
return this;
};
};
},{"assert":17}],14:[function(_dereq_,module,exports){
var assert = _dereq_("assert");
module.exports = function ProxyProperty(properyThisContext, propertyName) {
"use strict";
assert(propertyName);
assert(properyThisContext);
var actualValue = properyThisContext[propertyName];
this.__typename__ = "scarlet.lib.proxies.ProxyProperty";
this.wrap = function(whenCalled) {
assert(whenCalled);
Object.defineProperty(
properyThisContext,
propertyName, {
get: function() {
return invokeWhenCalledForGet(whenCalled);
},
set: function(value) {
invokeWhenCalledForSet(whenCalled, value);
}
});
return this;
};
var invokeWhenCalledForGet = function(whenCalled) {
return whenCalled.call(properyThisContext,
propertyName,
function() {
return actualValue;
});
};
var invokeWhenCalledForSet = function(whenCalled, value) {
return whenCalled.call(
properyThisContext,
propertyName,
function() {
actualValue = value;
});
};
this.unwrap = function() {
delete properyThisContext[propertyName];
properyThisContext[propertyName] = actualValue;
return this;
};
};
},{"assert":17}],15:[function(_dereq_,module,exports){
var util = _dereq_("util");
var assert = _dereq_("assert");
var object = _dereq_("../extensions/object");
var proxyMetadata = _dereq_("./proxy-metadata");
var proxyInstance = _dereq_("./proxy-instance");
var enumerable = _dereq_("../extensions/enumerable");
module.exports = function ProxyPrototype(type) {
"use strict";
assert(type);
assert(type.prototype);
var self = this;
self.whenCalled = null;
self.__typename__ = "scarlet.lib.proxies.ProxyPrototype";
self.wrap = function(whenCalled, replaceClassCallback) {
assert(whenCalled);
self.whenCalled = whenCalled;
util.inherits(inheritor, type);
if (replaceClassCallback)
replaceClassCallback(inheritor);
return self;
};
self.unwrap = function() {
return type;
};
var inheritor = function() {
var thisContext = this || {};
var callArguments = Array.prototype.slice.call(arguments);
var proceed = buildProceed(thisContext);
return self.whenCalled.call(
type.name,
thisContext,
proceed,
callArguments);
};
var buildProceed = function(thisContext) {
return function() {
var callArguments = Array.prototype.slice.call(arguments);
var result = type.apply(thisContext, callArguments);
initializeShadow(thisContext);
return result;
};
};
var initializeShadow = function(thisContext) {
if (proxyMetadata(thisContext).hasShadow())
return;
proxyMetadata(thisContext).ensureShadow();
if (!object.objectHasFunction(thisContext, inheritor))
proxyInstance(thisContext).wrap(self.whenCalled);
};
};
},{"../extensions/enumerable":2,"../extensions/object":4,"./proxy-instance":10,"./proxy-metadata":12,"assert":17,"util":25}],16:[function(_dereq_,module,exports){
var util = _dereq_("util");
var events = _dereq_("events");
var assert = _dereq_("assert");
var logger = _dereq_("./extensions/logger");
var Interceptor = _dereq_("./interceptors/interceptor");
var PluginManager = _dereq_("./plugins/plugin-manager");
var ScarletTrace = _dereq_("./extensions/scarletTrace");
/**
For creating a new instance of Scarlet
@namespace scarlet.lib
@method ctor
@param {array|string} pluginArr
@return scarlet.lib.Scarlet
@example
var Scarlet = require("scarlet");
var scarlet = new Scarlet();
// A function that does addition
function add(arg1, arg2){
return arg1 + arg2;
}
// Log arguments and result of add
add = scarlet
.intercept(add)
.using(function(info, method, args){
console.log("Adding '" + args[0] + "'' and '" + args[1] + "'");
var result = method.call(this, info, method, args);
console.log("Result is '" + result + "'");
return result;
}).proxy();
add(1,2); // Output -> Adding '1' and '2'\n Result is '3'
add(3,5); // Output -> Adding '3' and '5'\n Result is '8'
*/
function Scarlet(pluginArr) {
"use strict";
if (!(this instanceof Scarlet))
return new Scarlet(pluginArr);
var interceptor = null;
var pluginManager = new PluginManager();
var self = this;
self.plugins = {};
self.__typename__ = "scarlet.lib.Scarlet";
/**
Method for proxying types, functions and instances
@method intercept
@param {object} typeOrInstance
@return {scarlet.lib.Scarlet}
@chainable
@example
var Scarlet = require("scarlet");
var scarlet = new Scarlet();
// Type for that we would like to intercept
function MyClass(){
var self = this;
self.myMethod = function(arg1, arg2){
return arg1 + arg2;
};
}
// First instantiate the type
var instance = new MyClass();
// Scarlet will only intercept the instance
instance = scarlet
.intercept(instance)
.using(function(info, method, args){
return method.call(this, info, method, args);
}).proxy();
// Invoke
var result = instance.myMethod(1,2);
*/
self.intercept = function(typeOrInstance, memberName) {
assert(typeOrInstance, "Please make sure you supply a typeOrInstance parameter. eg. scarlet.intercept(MyFunc, scarlet.type.asInstance());");
logger.info(Scarlet, "intercept", "For Type Or Instance", [typeOrInstance]);
interceptor = new Interceptor();
interceptor.observable = typeOrInstance;
interceptor.intercept(typeOrInstance,
memberName,
function(observable) {
interceptor.observable = observable;
});
interceptor.using(self.beforeEventEmitterInterceptor);
return self;
};
/**
Method for chaining interceptors onto a proxied type or function
@method using
@param {Function} callback
@return {scarlet.lib.Scarlet}
@chainable
@example
var Scarlet = require("scarlet");
var scarlet = new Scarlet();
// Type for that we would like to intercept
function MyClass(){
var self = this;
self.myMethod = function(arg1, arg2){
return arg1 + arg2;
};
}
// First instantiate the type
var instance = new MyClass();
// Scarlet will only intercept the instance
instance = scarlet
.intercept(instance)
.using(function(info, method, args){ // Interceptor 1
return method.call(this, info, method, args);
})
.using(function(info, method, args){ // Interceptor 2
return method.call(this, info, method, args);
})
.using(function(info, method, args){ // Interceptor 3
return method.call(this, info, method, args);
}).proxy();
// Invoke
var result = instance.myMethod(1,2);
*/
self.using = function(callback) {
assert(callback);
assert(interceptor);
logger.info(Scarlet, "using", "Using Interceptor", [callback]);
interceptor.using(callback);
return self;
};
self.beforeEventEmitterInterceptor = function(info, proceed) {
assert(interceptor);
self.emit("before", info);
proceed();
};
self.afterEventEmitterInterceptor = function(error, info, proceed) {
assert(interceptor);
if (error !== undefined && error !== null) {
info.error = error;
self.emit("error", info);
throw error;
}
var result = null;
try {
result = proceed();
self.emit("after", info);
} catch (exception) {
info.error = exception;
self.emit("error", info);
throw exception;
}
self.emit("done", info);
return result;
};
/**
Method for retrieving a reference to a proxy type, this is for types that need to be instantiated using 'new'
@method proxy
@return {Object}
*/
self.proxy = function() {
assert(interceptor);
assert(interceptor.observable);
logger.info(Scarlet, "proxy", "As Proxied Type Or Instance", [interceptor.observable]);
interceptor.using(self.afterEventEmitterInterceptor);
return interceptor.observable;
};
/**
Method for loading a plugin into scarlet
@param {String} pluginPath
@method load
@return {scarlet.lib.Scarlet}
*/
self.load = function(pluginPath) {
assert(pluginPath);
pluginManager.load(self, pluginPath);
return self;
};
self.interceptQuery = function(proceed, info) {
return new ScarletTrace(proceed, info);
};
var initializePlugins = function() {
if (typeof(pluginArr) === "string")
pluginArr = [pluginArr];
if (pluginArr) {
if (pluginArr.length) {
pluginArr.forEach(function(plugin) {
self.load(plugin);
});
}
}
};
self.on("error", function(error) {
logger.error(Scarlet, "Event", "error event", [error]);
});
initializePlugins();
events.EventEmitter.call(self);
}
util.inherits(Scarlet, events.EventEmitter);
module.exports = Scarlet;
},{"./extensions/logger":3,"./extensions/scarletTrace":5,"./interceptors/interceptor":6,"./plugins/plugin-manager":8,"assert":17,"events":20,"util":25}],17:[function(_dereq_,module,exports){
// http://wiki.commonjs.org/wiki/Unit_Testing/1.0
//
// THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8!
//
// Originally from narwhal.js (http://narwhaljs.org)
// Copyright (c) 2009 Thomas Robinson <280north.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the 'Software'), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// when used in node, this will actually load the util module we depend on
// versus loading the builtin util module as happens otherwise
// this is a bug in node module loading as far as I am concerned
var util = _dereq_('util/');
var pSlice = Array.prototype.slice;
var hasOwn = Object.prototype.hasOwnProperty;
// 1. The assert module provides functions that throw
// AssertionError's when particular conditions are not met. The
// assert module must conform to the following interface.
var assert = module.exports = ok;
// 2. The AssertionError is defined in assert.
// new assert.AssertionError({ message: message,
// actual: actual,
// expected: expected })
assert.AssertionError = function AssertionError(options) {
this.name = 'AssertionError';
this.actual = options.actual;
this.expected = options.expected;
this.operator = options.operator;
if (options.message) {
this.message = options.message;
this.generatedMessage = false;
} else {
this.message = getMessage(this);
this.generatedMessage = true;
}
var stackStartFunction = options.stackStartFunction || fail;
if (Error.captureStackTrace) {
Error.captureStackTrace(this, stackStartFunction);
}
else {
// non v8 browsers so we can have a stacktrace
var err = new Error();
if (err.stack) {
var out = err.stack;
// try to strip useless frames
var fn_name = stackStartFunction.name;
var idx = out.indexOf('\n' + fn_name);
if (idx >= 0) {
// once we have located the function frame
// we need to strip out everything before it (and its line)
var next_line = out.indexOf('\n', idx + 1);
out = out.substring(next_line + 1);
}
this.stack = out;
}
}
};
// assert.AssertionError instanceof Error
util.inherits(assert.AssertionError, Error);
function replacer(key, value) {
if (util.isUndefined(value)) {
return '' + value;
}
if (util.isNumber(value) && (isNaN(value) || !isFinite(value))) {
return value.toString();
}
if (util.isFunction(value) || util.isRegExp(value)) {
return value.toString();
}
return value;
}
function truncate(s, n) {
if (util.isString(s)) {
return s.length < n ? s : s.slice(0, n);
} else {
return s;
}
}
function getMessage(self) {
return truncate(JSON.stringify(self.actual, replacer), 128) + ' ' +
self.operator + ' ' +
truncate(JSON.stringify(self.expected, replacer), 128);
}
// At present only the three keys mentioned above are used and
// understood by the spec. Implementations or sub modules can pass
// other keys to the AssertionError's constructor - they will be
// ignored.
// 3. All of the following functions must throw an AssertionError
// when a corresponding condition is not met, with a message that
// may be undefined if not provided. All assertion methods provide
// both the actual and expected values to the assertion error for
// display purposes.
function fail(actual, expected, message, operator, stackStartFunction) {
throw new assert.AssertionError({
message: message,
actual: actual,
expected: expected,
operator: operator,
stackStartFunction: stackStartFunction
});
}
// EXTENSION! allows for well behaved errors defined elsewhere.
assert.fail = fail;
// 4. Pure assertion tests whether a value is truthy, as determined
// by !!guard.
// assert.ok(guard, message_opt);
// This statement is equivalent to assert.equal(true, !!guard,
// message_opt);. To test strictly for the value true, use
// assert.strictEqual(true, guard, message_opt);.
function ok(value, message) {
if (!value) fail(value, true, message, '==', assert.ok);
}
assert.ok = ok;
// 5. The equality assertion tests shallow, coercive equality with
// ==.
// assert.equal(actual, expected, message_opt);
assert.equal = function equal(actual, expected, message) {
if (actual != expected) fail(actual, expected, message, '==', assert.equal);
};
// 6. The non-equality assertion tests for whether two objects are not equal
// with != assert.notEqual(actual, expected, message_opt);
assert.notEqual = function notEqual(actual, expected, message) {
if (actual == expected) {
fail(actual, expected, message, '!=', assert.notEqual);
}
};
// 7. The equivalence assertion tests a deep equality relation.
// assert.deepEqual(actual, expected, message_opt);
assert.deepEqual = function deepEqual(actual, expected, message) {
if (!_deepEqual(actual, expected)) {
fail(actual, expected, message, 'deepEqual', assert.deepEqual);
}
};
function _deepEqual(actual, expected) {
// 7.1. All identical values are equivalent, as determined by ===.
if (actual === expected) {
return true;
} else if (util.isBuffer(actual) && util.isBuffer(expected)) {
if (actual.length != expected.length) return false;
for (var i = 0; i < actual.length; i++) {
if (actual[i] !== expected[i]) return false;
}
return true;
// 7.2. If the expected value is a Date object, the actual value is
// equivalent if it is also a Date object that refers to the same time.
} else if (util.isDate(actual) && util.isDate(expected)) {
return actual.getTime() === expected.getTime();
// 7.3 If the expected value is a RegExp object, the actual value is
// equivalent if it is also a RegExp object with the same source and
// properties (`global`, `multiline`, `lastIndex`, `ignoreCase`).
} else if (util.isRegExp(actual) && util.isRegExp(expected)) {
return actual.source === expected.source &&
actual.global === expected.global &&
actual.multiline === expected.multiline &&
actual.lastIndex === expected.lastIndex &&
actual.ignoreCase === expected.ignoreCase;
// 7.4. Other pairs that do not both pass typeof value == 'object',
// equivalence is determined by ==.
} else if (!util.isObject(actual) && !util.isObject(expected)) {
return actual == expected;
// 7.5 For all other Object pairs, including Array objects, equivalence is
// determined by having the same number of owned properties (as verified
// with Object.prototype.hasOwnProperty.call), the same set of keys
// (although not necessarily the same order), equivalent values for every
// corresponding key, and an identical 'prototype' property. Note: this
// accounts for both named and indexed properties on Arrays.
} else {
return objEquiv(actual, expected);
}
}
function isArguments(object) {
return Object.prototype.toString.call(object) == '[object Arguments]';
}
function objEquiv(a, b) {
if (util.isNullOrUndefined(a) || util.isNullOrUndefined(b))
return false;
// an identical 'prototype' property.
if (a.prototype !== b.prototype) return false;
//~~~I've managed to break Object.keys through screwy arguments passing.
// Converting to array solves the problem.
if (isArguments(a)) {
if (!isArguments(b)) {
return false;
}
a = pSlice.call(a);
b = pSlice.call(b);
return _deepEqual(a, b);
}
try {
var ka = objectKeys(a),
kb = objectKeys(b),
key, i;
} catch (e) {//happens when one is a string literal and the other isn't
return false;
}
// having the same number of owned properties (keys incorporates
// hasOwnProperty)
if (ka.length != kb.length)
return false;
//the same set of keys (although not necessarily the same order),
ka.sort();
kb.sort();
//~~~cheap key test
for (i = ka.length - 1; i >= 0; i--) {
if (ka[i] != kb[i])
return false;
}
//equivalent values for every corresponding key, and
//~~~possibly expensive deep test
for (i = ka.length - 1; i >= 0; i--) {
key = ka[i];
if (!_deepEqual(a[key], b[key])) return false;
}
return true;
}
// 8. The non-equivalence assertion tests for any deep inequality.
// assert.notDeepEqual(actual, expected, message_opt);
assert.notDeepEqual = function notDeepEqual(actual, expected, message) {
if (_deepEqual(actual, expected)) {
fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual);
}
};
// 9. The strict equality assertion tests strict equality, as determined by ===.
// assert.strictEqual(actual, expected, message_opt);
assert.strictEqual = function strictEqual(actual, expected, message) {
if (actual !== expected) {
fail(actual, expected, message, '===', assert.strictEqual);
}
};
// 10. The strict non-equality assertion tests for strict inequality, as
// determined by !==. assert.notStrictEqual(actual, expected, message_opt);
assert.notStrictEqual = function notStrictEqual(actual, expected, message) {
if (actual === expected) {
fail(actual, expected, message, '!==', assert.notStrictEqual);
}
};
function expectedException(actual, expected) {
if (!actual || !expected) {
return false;
}
if (Object.prototype.toString.call(expected) == '[object RegExp]') {
return expected.test(actual);
} else if (actual instanceof expected) {
return true;
} else if (expected.call({}, actual) === true) {
return true;
}
return false;
}
function _throws(shouldThrow, block, expected, message) {
var actual;
if (util.isString(expected)) {
message = expected;
expected = null;
}
try {
block();
} catch (e) {
actual = e;
}
message = (expected && expected.name ? ' (' + expected.name + ').' : '.') +
(message ? ' ' + message : '.');
if (shouldThrow && !actual) {
fail(actual, expected, 'Missing expected exception' + message);
}
if (!shouldThrow && expectedException(actual, expected)) {
fail(actual, expected, 'Got unwanted exception' + message);
}
if ((shouldThrow && actual && expected &&
!expectedException(actual, expected)) || (!shouldThrow && actual)) {
throw actual;
}
}
// 11. Expected to throw an error:
// assert.throws(block, Error_opt, message_opt);
assert.throws = function(block, /*optional*/error, /*optional*/message) {
_throws.apply(this, [true].concat(pSlice.call(arguments)));
};
// EXTENSION! This is annoying to write outside this module.
assert.doesNotThrow = function(block, /*optional*/message) {
_throws.apply(this, [false].concat(pSlice.call(arguments)));
};
assert.ifError = function(err) { if (err) {throw err;}};
var objectKeys = Object.keys || function (obj) {
var keys = [];
for (var key in obj) {
if (hasOwn.call(obj, key)) keys.push(key);
}
return keys;
};
},{"util/":19}],18:[function(_dereq_,module,exports){
module.exports = function isBuffer(arg) {
return arg && typeof arg === 'object'
&& typeof arg.copy === 'function'
&& typeof arg.fill === 'function'
&& typeof arg.readUInt8 === 'function';
}
},{}],19:[function(_dereq_,module,exports){
(function (process,global){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var formatRegExp = /%[sdj%]/g;
exports.format = function(f) {
if (!isString(f)) {
var objects = [];
for (var i = 0; i < arguments.length; i++) {
objects.push(inspect(arguments[i]));
}
return objects.join(' ');
}
var i = 1;
var args = arguments;
var len = args.length;
var str = String(f).replace(formatRegExp, function(x) {
if (x === '%%') return '%';
if (i >= len) return x;
switch (x) {
case '%s': return String(args[i++]);
case '%d': return Number(args[i++]);
case '%j':
try {
return JSON.stringify(args[i++]);
} catch (_) {
return '[Circular]';
}
default:
return x;
}
});
for (var x = args[i]; i < len; x = args[++i]) {
if (isNull(x) || !isObject(x)) {
str += ' ' + x;
} else {
str += ' ' + inspect(x);
}
}
return str;
};
// Mark that a method should not be used.
// Returns a modified function which warns once by default.
// If --no-deprecation is set, then it is a no-op.
exports.deprecate = function(fn, msg) {
// Allow for deprecating things in the process of starting up.
if (isUndefined(global.process)) {
return function() {
return exports.deprecate(fn, msg).apply(this, arguments);
};
}
if (process.noDeprecation === true) {
return fn;
}
var warned = false;
function deprecated() {
if (!warned) {
if (process.throwDeprecation) {
throw new Error(msg);
} else if (process.traceDeprecation) {
console.trace(msg);
} else {
console.error(msg);
}
warned = true;
}
return fn.apply(this, arguments);
}
return deprecated;
};
var debugs = {};
var debugEnviron;
exports.debuglog = function(set) {
if (isUndefined(debugEnviron))
debugEnviron = process.env.NODE_DEBUG || '';
set = set.toUpperCase();
if (!debugs[set]) {
if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) {
var pid = process.pid;
debugs[set] = function() {
var msg = exports.format.apply(exports, arguments);
console.error('%s %d: %s', set, pid, msg);
};
} else {
debugs[set] = function() {};
}
}
return debugs[set];
};
/**
* Echos the value of a value. Trys to print the value out
* in the best way possible given the different types.
*
* @param {Object} obj The object to print out.
* @param {Object} opts Optional options object that alters the output.
*/
/* legacy: obj, showHidden, depth, colors*/
function inspect(obj, opts) {
// default options
var ctx = {
seen: [],
stylize: stylizeNoColor
};
// legacy...
if (arguments.length >= 3) ctx.depth = arguments[2];
if (arguments.length >= 4) ctx.colors = arguments[3];
if (isBoolean(opts)) {
// legacy...
ctx.showHidden = opts;
} else if (opts) {
// got an "options" object
exports._extend(ctx, opts);
}
// set default options
if (isUndefined(ctx.showHidden)) ctx.showHidden = false;
if (isUndefined(ctx.depth)) ctx.depth = 2;
if (isUndefined(ctx.colors)) ctx.colors = false;
if (isUndefined(ctx.customInspect)) ctx.customInspect = true;
if (ctx.colors) ctx.stylize = stylizeWithColor;
return formatValue(ctx, obj, ctx.depth);
}
exports.inspect = inspect;
// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
inspect.colors = {
'bold' : [1, 22],
'italic' : [3, 23],
'underline' : [4, 24],
'inverse' : [7, 27],
'white' : [37, 39],
'grey' : [90, 39],
'black' : [30, 39],
'blue' : [34, 39],
'cyan' : [36, 39],
'green' : [32, 39],
'magenta' : [35, 39],
'red' : [31, 39],
'yellow' : [33, 39]
};
// Don't use 'blue' not visible on cmd.exe
inspect.styles = {
'special': 'cyan',
'number': 'yellow',
'boolean': 'yellow',
'undefined': 'grey',
'null': 'bold',
'string': 'green',
'date': 'magenta',
// "name": intentionally not styling
'regexp': 'red'
};
function stylizeWithColor(str, styleType) {
var style = inspect.styles[styleType];
if (style) {
return '\u001b[' + inspect.colors[style][0] + 'm' + str +
'\u001b[' + inspect.colors[style][1] + 'm';
} else {
return str;
}
}
function stylizeNoColor(str, styleType) {
return str;
}
function arrayToHash(array) {
var hash = {};
array.forEach(function(val, idx) {
hash[val] = true;
});
return hash;
}
function formatValue(ctx, value, recurseTimes) {
// Provide a hook for user-specified inspect functions.
// Check that value is an object with an inspect function on it
if (ctx.customInspect &&
value &&
isFunction(value.inspect) &&
// Filter out the util module, it's inspect function is special
value.inspect !== exports.inspect &&
// Also filter out any prototype objects using the circular check.
!(value.constructor && value.constructor.prototype === value)) {
var ret = value.inspect(recurseTimes, ctx);
if (!isString(ret)) {
ret = formatValue(ctx, ret, recurseTimes);
}
return ret;
}
// Primitive types cannot have properties
var primitive = formatPrimitive(ctx, value);
if (primitive) {
return primitive;
}
// Look up the keys of the object.
var keys = Object.keys(value);
var visibleKeys = arrayToHash(keys);
if (ctx.showHidden) {
keys = Object.getOwnPropertyNames(value);
}
// IE doesn't make error fields non-enumerable
// http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx
if (isError(value)
&& (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) {
return formatError(value);
}
// Some type of object without properties can be shortcutted.
if (keys.length === 0) {
if (isFunction(value)) {
var name = value.name ? ': ' + value.name : '';
return ctx.stylize('[Function' + name + ']', 'special');
}
if (isRegExp(value)) {
return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
}
if (isDate(value)) {
return ctx.stylize(Date.prototype.toString.call(value), 'date');
}
if (isError(value)) {
return formatError(value);
}
}
var base = '', array = false, braces = ['{', '}'];
// Make Array say that they are Array
if (isArray(value)) {
array = true;
braces = ['[', ']'];
}
// Make functions say that they are functions
if (isFunction(value)) {
var n = value.name ? ': ' + value.name : '';
base = ' [Function' + n + ']';
}
// Make RegExps say that they are RegExps
if (isRegExp(value)) {
base = ' ' + RegExp.prototype.toString.call(value);
}
// Make dates with properties first say the date
if (isDate(value)) {
base = ' ' + Date.prototype.toUTCString.call(value);
}
// Make error with message first say the error
if (isError(value)) {
base = ' ' + formatError(value);
}
if (keys.length === 0 && (!array || value.length == 0)) {
return braces[0] + base + braces[1];
}
if (recurseTimes < 0) {
if (isRegExp(value)) {
return ctx.styl