1 var Proxy = require("node-proxy"), object = require("./object"), merge = object.merge, functions = require("./functions"), misc = require("./misc"); 2 3 var comb = exports; 4 var handlerMaker = function (obj) { 5 return { 6 getOwnPropertyDescriptor:function (name) { 7 var desc = Object.getOwnPropertyDescriptor(obj, name); 8 // a trapping proxy's properties must always be configurable 9 if (desc !== undefined) { 10 desc.configurable = true; 11 } 12 return desc; 13 }, 14 getPropertyDescriptor:function (name) { 15 var desc = Object.getPropertyDescriptor(obj, name); // not in ES5 16 // a trapping proxy's properties must always be configurable 17 if (desc !== undefined) { 18 desc.configurable = true; 19 } 20 return desc; 21 }, 22 getOwnPropertyNames:function () { 23 return Object.getOwnPropertyNames(obj); 24 }, 25 getPropertyNames:function () { 26 return Object.getPropertyNames(obj); // not in ES5 27 }, 28 defineProperty:function (name, desc) { 29 Object.defineProperty(obj, name, desc); 30 }, 31 delete:function (name) { 32 return delete obj[name]; 33 }, 34 fix:function () { 35 if (Object.isFrozen(obj)) { 36 var result = {}; 37 Object.getOwnPropertyNames(obj).forEach(function (name) { 38 result[name] = Object.getOwnPropertyDescriptor(obj, name); 39 }); 40 return result; 41 } 42 // As long as obj is not frozen, the proxy won't allow itself to be fixed 43 return undefined; // will cause a TypeError to be thrown 44 }, 45 46 has:function (name) { 47 return name in obj; 48 }, 49 hasOwn:function (name) { 50 return ({}).hasOwnProperty.call(obj, name); 51 }, 52 get:function (receiver, name) { 53 return obj[name]; 54 }, 55 set:function (receiver, name, val) { 56 obj[name] = val; 57 return true; 58 }, // bad behavior when set fails in non-strict mode 59 enumerate:function () { 60 var result = []; 61 for (var name in obj) { 62 result.push(name); 63 } 64 return result; 65 }, 66 keys:function () { 67 return Object.keys(obj); 68 } 69 70 }; 71 }; 72 73 var noSuchMethodHandler = function (obj, handler) { 74 return { 75 get:function (receiver, name) { 76 return obj[name] ? obj[name] : handler.call(obj, name); 77 } 78 } 79 }; 80 81 /** 82 * Creates a proxy for an object. 83 * @param obj object to proxy 84 * @param {Object} opts object with methods to define on the handler. 85 * @memberOf comb 86 */ 87 comb.handlerProxy = function (obj, opts) { 88 opts = opts || {}; 89 return Proxy.create(merge(handlerMaker(obj), opts)); 90 }; 91 92 /** 93 * Creates a method missing proxy for an object. 94 * <b>NOTE:</b> This method does not gurantee that the property will be used as a function call. 95 * 96 * @example 97 * 98 * var x = {hello:function () {return "hello"}, world:"world"}; 99 * var xHandler = comb.methodMissing(x, function (m) { 100 * //you can do more interesting stuff in here! 101 * return function () { 102 * return [m].concat(comb.argsToArray(arguments)); 103 * } 104 * }); 105 * xHandler.hello(); //=> "hello" 106 * xHandler.world //=> "world" 107 * xHandler.someMethod("hello", "world"); //=> [ 'someMethod', 'hello', 'world' ] 108 * 109 * @param {Object} obj object to wrap with a method missing proxy 110 * @param {Function} handler handle to call when a property is missing 111 * @param {Object} opts prototype to assign to the proxy 112 * @memberOf comb 113 * @returns {Proxy} a proxy 114 */ 115 comb.methodMissing = function (obj, handler, proto) { 116 proto = proto || {}; 117 return Proxy.create(merge(handlerMaker(obj), noSuchMethodHandler(obj, handler)), object.isHash(proto) ? proto : proto.prototype); 118 }; 119 120 /** 121 * Determines if the object is a proxy or not. 122 * 123 * @param {Anything} obj object to test 124 * @memberOf comb 125 * @returns {Boolean} true if it is a proxy false otherwise 126 */ 127 comb.isProxy = function (obj) { 128 var undef; 129 return obj !== undef && obj !== null && Proxy.isProxy(obj); 130 } 131 132 /** 133 * Creates a function proxy for an object. 134 * 135 * @example 136 * 137 * //create an object that can use properties or as a function through the new operator 138 * var MyObject = comb.define(null, { 139 * instance : { 140 * hello : "hello", 141 * constructor : function(){ 142 * this.args = comb.argsToArray(arguments); 143 * } 144 * } 145 * }); 146 * 147 * //NOTE: this will not work properly for native objects like Date. 148 * var createNewMyObject = function(){ 149 * try { 150 * p = new MyObject(); 151 * } catch (ignore) { 152 * //ignore the error because its probably from missing arguments 153 * } 154 * //Now lets take care of arguments supplied!!! 155 * return MyObject.apply(p, comb.argsToArray(arguments)); 156 * }; 157 * 158 * //This example creates an object with a world property but its not a function! 159 * var handle = comb.createFunctionWrapper({world : "world"}, createNewMyObject, createNewMyObject); 160 * 161 * handle.world //=> "world" 162 * var a = handle(1); 163 * a.hello; //=>"hello" 164 * a.args; //=> [1]; 165 * a = new handle(1,2); 166 * a.hello; //=>"hello" 167 * a.args; //=> [1,2]; 168 * @param obj the object to proxy 169 * @param {Function} handler the handler to call when the object is used as a function 170 * @param {Function} constructTrap the funciton to use when using new on the object 171 * @param {Object} opts the prototype of the object. 172 * @memberOf comb 173 */ 174 comb.createFunctionWrapper = function (obj, handler, constructTrap, opts) { 175 var args = misc.argsToArray(arguments), ret; 176 if (args.length != 4) { 177 opts = object.isHash(args[args.length - 1]) ? args.pop() : null; 178 constructTrap = functions.isFunction(args[args.length - 1]) ? args.pop() : null; 179 handler = functions.isFunction(args[args.length - 1]) ? args.pop() : null; 180 } 181 if (misc.isUndefined(obj)) throw new Error("obj required when using create function wrapper"); 182 if (functions.isFunction(constructTrap) && !functions.isFunction(handler)) { 183 ret = Proxy.createFunction(handlerMaker(obj), constructTrap); 184 } else { 185 ret = Proxy.createFunction(handlerMaker(obj), handler, constructTrap); 186 } 187 188 189 if (opts) { 190 Proxy.setPrototype(ret, object.isHash(opts) ? opts : opts.prototype); 191 } 192 return ret; 193 }; 194 195 196 comb.Proxy = Proxy;