-
Notifications
You must be signed in to change notification settings - Fork 0
/
Dcor.js
206 lines (179 loc) · 8.39 KB
/
Dcor.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
/**********************************************************
Dcor.js - JavaScript function decoration based on AOP
A simple JavaScript library for decorating functions with common and useful cross-cutting concerns like
logging, authorization, caching, etc.
It is heavily based on closures and functional aspects of JavaScript.
Alan Zavari
May 2014
Note: While the library has been tested in browser,
it's not tested on Node.js yet.
Dcor predefined aspects:
logged: Allows logging function calls along with the passed arguments and the return values
secured: Secures a function by ignoring (or breaking) unauthorized calls
cached: Stores the function return value to cache (memory) and use it for later calls
performanceLogged: Measures the runtime duration of the function
singleton: Makes the function singleton. All future instances will be the same.
safeConstructor: Protects constructor functions against NOT using `new`. Any call will be considered as an instantiation.
async: Makes synchronous functions asynchronous to prevent UI blocking
***********************************************************/
(function (global, exporter) {
// export
if (typeof module !== 'undefined') {
module.exports = exporter(); //Node.js
} else {
global.Dcor = exporter(); //browser
}
})(this, function () {
var undefined, //Guard against variable named "undefined" being declared in global context.
Dcor = {
name: "Dcor.js",
version: "1.0"
};
Dcor.decorate = function (fnNamePattern, decors, namespaces) {
//TODO: check for duplicate decors
//no namespaces: global ns
if (!namespaces || namespaces.length === 0) namespaces = [(function () {
return this;
}).call()]; //doesn't work in strict mode
//iterate over all namespaces
namespaces.forEach(function (ns) {
for (var key in ns) {
if (key == "localStorage" || key == "sessionStorage" || key == "caches") continue; //prevent causing error when running locally (for testing)
if (typeof ns[key] == 'function' && ns.hasOwnProperty(key) && key.match(fnNamePattern)) {
decors.forEach(function (decor) {
appyDecor(ns[key], key, decor, ns);
console.log(key);
});
}
}
});
};
Dcor.dedecorate = null; //TODO: remove decors from functions
function appyDecor(fn, fnName, decor, ns) {
fn.__decoratedWith = decor; //for informational purposes
ns[fnName] = decorFactory(fn, fnName, decor, ns); //overwrite original function with proper wrapper function
ns[fnName].__fnName = fnName;
ns[fnName].__fn = fn;
ns[fnName].__decoratedWith = fn.__decoratedWith;
//__fnOriginal is always the original function.
//it's only used to be passed to callbacks for informational purposes
if (fn.__fnOriginal) ns[fnName].__fnOriginal = fn.__fnOriginal;
else {
ns[fnName].__fnOriginal = fn;
fn.__fnOriginal = fn; //for the original function itself
}
}
function decorFactory(fn, fnName, decor, ns) {
if (decor.type === "logged") {
return function () {
//logged
//var result = ns[fnName].__fn.apply(this, arguments);
var result = fn.apply(this, arguments);
if (decor.callback) decor.callback(fnName, arguments, result, fn.__fnOriginal.toString(), ns);
return result;
};
} else if (decor.type === "secured") {
return function () {
//secured
if (!decor.callback || (decor.callback && decor.callback(fnName, arguments, null, fn.__fnOriginal.toString(), ns))) return fn.apply(this, arguments); //authorized to run function
};
} else if (decor.type === "cached") {
return function () {
//cached
fn.__cache = fn.__cache || {};
//var argsStr = Array.prototype.slice.call(arguments).join(",");
var argsStr = ""; //no arguments
for (var i = 0; i < arguments.length; i++) {
if (typeof arguments[i] == "object") argsStr += JSON.stringify(arguments[i]) + ",";
else argsStr += arguments[i].toString() + ",";
}
if (fn.__cache[argsStr] != null) {
if (decor.callback) decor.callback(true, fnName, arguments, fn.__cache[argsStr], fn.__fnOriginal.toString(), ns);
return fn.__cache[argsStr];
} else {
var result = fn.apply(this, arguments);
if (decor.callback) decor.callback(false, fnName, arguments, result, fn.__fnOriginal.toString(), ns);
return fn.__cache[argsStr] = result;
}
};
} else if (decor.type === "performanceLogged") {
return function () {
//performanceLogged
var startTime = (new Date()).getTime(),
result = fn.apply(this, arguments),
duration = (new Date()).getTime() - startTime;
if (decor.callback) decor.callback(duration, fnName, arguments, result, fn.__fnOriginal.toString(), ns);
return result;
};
} else if (decor.type === "singleton") {
return function () {
//singleton
if (fn.__singletonInstance) return fn.__singletonInstance;
fn.__singletonInstance = this;
return fn.apply(this, arguments);
};
} else if (decor.type === "safeConstructor") { //'new' keyword will be used even if the function is called normally
return function () {
//safeConstructor
if (!(this instanceof fn)) //called as a function (without 'new')
return new(Function.prototype.bind.apply(fn, Array.prototype.concat.apply([null], arguments)));
else //instanciated with 'new' keyword
return fn.apply(this, arguments);
};
} else if (decor.type === "async") {
return function () {
//async
var that = this,
args = arguments;
setTimeout(function () {
var result = fn.apply(that, args);
if (decor.callback) decor.callback(fnName, args, result, fn.__fnOriginal.toString(), ns);
}, 0);
};
}
}
//easy-access functions with predefined decor types
Dcor.logged = function (fnNamePattern, decorCallback, namespaces) {
return Dcor.decorate(fnNamePattern, [{
type: "logged",
callback: decorCallback
}], namespaces);
};
Dcor.secured = function (fnNamePattern, decorCallback, namespaces) {
return Dcor.decorate(fnNamePattern, [{
type: "secured",
callback: decorCallback
}], namespaces);
};
Dcor.cached = function (fnNamePattern, decorCallback, namespaces) {
return Dcor.decorate(fnNamePattern, [{
type: "cached",
callback: decorCallback
}], namespaces);
};
Dcor.performanceLogged = function (fnNamePattern, decorCallback, namespaces) {
return Dcor.decorate(fnNamePattern, [{
type: "performanceLogged",
callback: decorCallback
}], namespaces);
};
Dcor.singleton = function (fnNamePattern, decorCallback, namespaces) {
return Dcor.decorate(fnNamePattern, [{
type: "singleton",
callback: decorCallback
}], namespaces);
};
Dcor.safeConstructor = function (fnNamePattern, decorCallback, namespaces) {
return Dcor.decorate(fnNamePattern, [{
type: "safeConstructor",
callback: decorCallback
}], namespaces);
};
Dcor.async = function (fnNamePattern, decorCallback, namespaces) {
return Dcor.decorate(fnNamePattern, [{
type: "async",
callback: decorCallback
}], namespaces);
};
return Dcor;
});