@util.js/privates 0.41.6 → 0.41.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Privates.js +231 -0
- package/package.json +6 -2
- package/lib/Privates.js +0 -192
- package/test/PrivatesTest.js +0 -202
package/dist/Privates.js
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault");
|
|
4
|
+
var _weakMap = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/weak-map"));
|
|
5
|
+
var _concat = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/concat"));
|
|
6
|
+
var _forEach = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/for-each"));
|
|
7
|
+
var _entries = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/object/entries"));
|
|
8
|
+
var _bind = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/bind"));
|
|
9
|
+
var _getPrototypeOf = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/object/get-prototype-of"));
|
|
10
|
+
var _filter = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/filter"));
|
|
11
|
+
var _sort = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/sort"));
|
|
12
|
+
var _getOwnPropertyNames = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/object/get-own-property-names"));
|
|
13
|
+
var _map = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/map"));
|
|
14
|
+
var _getOwnPropertySymbols = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/object/get-own-property-symbols"));
|
|
15
|
+
var _indexOf = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/index-of"));
|
|
16
|
+
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/slicedToArray"));
|
|
17
|
+
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/classCallCheck"));
|
|
18
|
+
var _createClass2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/createClass"));
|
|
19
|
+
var RethrownError = require("@util.js/errors/RethrownError");
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* JavaScript utility class for private member variables.
|
|
23
|
+
*
|
|
24
|
+
* This class decreases some of the boilerplate of the
|
|
25
|
+
* [WeakMap]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap}
|
|
26
|
+
* pattern as described by
|
|
27
|
+
* ["Private Variables in JavaScript with ES6 WeakMaps"]{@link https://modernweb.com/private-variables-in-javascript-with-es6-weakmaps/}
|
|
28
|
+
* and
|
|
29
|
+
* ["Privates In ES2015 Javascript Classes"]{@link https://ilikekillnerds.com/2015/09/privates-in-es2015-javascript-classes/}.
|
|
30
|
+
* This utility handles shallow copying at construction.
|
|
31
|
+
*
|
|
32
|
+
* Private fields may become a part of the ECMA standard. The following is a
|
|
33
|
+
* StackOverflow answer to the question
|
|
34
|
+
* ["Private properties in JavaScript ES6 classes"]{@link https://stackoverflow.com/a/52237988}:
|
|
35
|
+
* "Private fields are being implemented in the ECMA standard. You can start
|
|
36
|
+
* using them today with babel 7 and stage 3 preset." Wouldn't private fields
|
|
37
|
+
* make this class obsolete? Not necessarily. If each of your privates is a
|
|
38
|
+
* function (e.g., you want to lazy load all member variables), this class has
|
|
39
|
+
* convenience methods for that.
|
|
40
|
+
*
|
|
41
|
+
* @public
|
|
42
|
+
* @class Privates
|
|
43
|
+
*/
|
|
44
|
+
module.exports = /*#__PURE__*/function () {
|
|
45
|
+
function Privates() {
|
|
46
|
+
(0, _classCallCheck2["default"])(this, Privates);
|
|
47
|
+
this.privates = new _weakMap["default"]();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Calls `this.get(thiz, property)()`.
|
|
52
|
+
*
|
|
53
|
+
* @public
|
|
54
|
+
* @function Privates#call
|
|
55
|
+
*/
|
|
56
|
+
return (0, _createClass2["default"])(Privates, [{
|
|
57
|
+
key: "call",
|
|
58
|
+
value: function call(thiz, property) {
|
|
59
|
+
try {
|
|
60
|
+
// this.get(thiz, property)() does not work all the time unless we
|
|
61
|
+
// call bind, which we do in the set method. See
|
|
62
|
+
// https://javascript.info/object-methods.
|
|
63
|
+
return this.get(thiz, property)();
|
|
64
|
+
} catch (e) {
|
|
65
|
+
var _context, _context2;
|
|
66
|
+
var propertyStr = getPropertyErrorString(property);
|
|
67
|
+
throw new RethrownError(e, (0, _concat["default"])(_context = (0, _concat["default"])(_context2 = "privates.call(".concat(thiz, ", ")).call(_context2, propertyStr, ") failed. ")).call(_context, e.message));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}, {
|
|
71
|
+
key: "get",
|
|
72
|
+
value: function get(thiz, property) {
|
|
73
|
+
try {
|
|
74
|
+
return this.getProps(thiz)[property];
|
|
75
|
+
} catch (e) {
|
|
76
|
+
var _context3, _context4;
|
|
77
|
+
var propertyStr = getPropertyErrorString(property);
|
|
78
|
+
throw new RethrownError(e, (0, _concat["default"])(_context3 = (0, _concat["default"])(_context4 = "privates.get(".concat(thiz, ", ")).call(_context4, propertyStr, ") failed. ")).call(_context3, e.message));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}, {
|
|
82
|
+
key: "getCallProxy",
|
|
83
|
+
value: function getCallProxy(thiz) {
|
|
84
|
+
return new Proxy(this.getProps(thiz), {
|
|
85
|
+
get: callProp
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}, {
|
|
89
|
+
key: "getProps",
|
|
90
|
+
value: function getProps(thiz) {
|
|
91
|
+
try {
|
|
92
|
+
var properties = this.privates.get(thiz);
|
|
93
|
+
if (!properties) this.privates.set(thiz, properties = {});
|
|
94
|
+
return properties;
|
|
95
|
+
} catch (e) {
|
|
96
|
+
if (thiz == null) throw new RethrownError(e, "thiz cannot be null or undefined.");
|
|
97
|
+
throw e;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}, {
|
|
101
|
+
key: "lambdaize",
|
|
102
|
+
value: function lambdaize(properties) {
|
|
103
|
+
try {
|
|
104
|
+
var _context5;
|
|
105
|
+
var lambdaProperties = {};
|
|
106
|
+
(0, _forEach["default"])(_context5 = (0, _entries["default"])(properties)).call(_context5, function (_ref) {
|
|
107
|
+
var _ref2 = (0, _slicedToArray2["default"])(_ref, 2),
|
|
108
|
+
property = _ref2[0],
|
|
109
|
+
value = _ref2[1];
|
|
110
|
+
return lambdaProperties[property] = function () {
|
|
111
|
+
return value;
|
|
112
|
+
};
|
|
113
|
+
});
|
|
114
|
+
return lambdaProperties;
|
|
115
|
+
} catch (e) {
|
|
116
|
+
if (properties == null) throw new RethrownError(e, "properties cannot be null or undefined.");
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}, {
|
|
120
|
+
key: "lazyLoad",
|
|
121
|
+
value: function lazyLoad(thiz, property, lazyLoadFunction) {
|
|
122
|
+
if (typeof lazyLoadFunction !== "function") throw new TypeError("lazyLoadFunction (".concat(lazyLoadFunction, ") must be a function."));
|
|
123
|
+
var value;
|
|
124
|
+
var loaded = false;
|
|
125
|
+
this.set(thiz, property, function () {
|
|
126
|
+
if (!loaded) {
|
|
127
|
+
value = lazyLoadFunction();
|
|
128
|
+
loaded = true;
|
|
129
|
+
}
|
|
130
|
+
return value;
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}, {
|
|
134
|
+
key: "lazyLoadProps",
|
|
135
|
+
value: function lazyLoadProps(thiz) {
|
|
136
|
+
var t = this;
|
|
137
|
+
for (var _len = arguments.length, lazilyLoadedProperties = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
|
138
|
+
lazilyLoadedProperties[_key - 1] = arguments[_key];
|
|
139
|
+
}
|
|
140
|
+
(0, _forEach["default"])(lazilyLoadedProperties).call(lazilyLoadedProperties, function (currentProperties) {
|
|
141
|
+
var _context6;
|
|
142
|
+
(0, _forEach["default"])(_context6 = (0, _entries["default"])(currentProperties)).call(_context6, function (_ref3) {
|
|
143
|
+
var _ref4 = (0, _slicedToArray2["default"])(_ref3, 2),
|
|
144
|
+
property = _ref4[0],
|
|
145
|
+
lazyLoadFunction = _ref4[1];
|
|
146
|
+
return t.lazyLoad(thiz, property, lazyLoadFunction);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}, {
|
|
151
|
+
key: "set",
|
|
152
|
+
value: function set(thiz, property, newValue) {
|
|
153
|
+
try {
|
|
154
|
+
this.getProps(thiz)[property] = newValue;
|
|
155
|
+
} catch (e) {
|
|
156
|
+
var _context7, _context8, _context9;
|
|
157
|
+
var propertyStr = getPropertyErrorString(property);
|
|
158
|
+
throw new RethrownError(e, (0, _concat["default"])(_context7 = (0, _concat["default"])(_context8 = (0, _concat["default"])(_context9 = "privates.set(".concat(thiz, ", ")).call(_context9, propertyStr, ", ")).call(_context8, newValue, ") failed. ")).call(_context7, e.message));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}, {
|
|
162
|
+
key: "setProps",
|
|
163
|
+
value: function setProps(thiz) {
|
|
164
|
+
var t = this;
|
|
165
|
+
for (var _len2 = arguments.length, properties = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
|
|
166
|
+
properties[_key2 - 1] = arguments[_key2];
|
|
167
|
+
}
|
|
168
|
+
(0, _forEach["default"])(properties).call(properties, function (currentProperties) {
|
|
169
|
+
var _context0, _context1;
|
|
170
|
+
(0, _forEach["default"])(_context0 = (0, _entries["default"])(currentProperties)).call(_context0, function (_ref5) {
|
|
171
|
+
var _ref6 = (0, _slicedToArray2["default"])(_ref5, 2),
|
|
172
|
+
property = _ref6[0],
|
|
173
|
+
value = _ref6[1];
|
|
174
|
+
return t.set(thiz, property, value);
|
|
175
|
+
});
|
|
176
|
+
(0, _forEach["default"])(_context1 = getAllMethods(currentProperties)).call(_context1, function (method) {
|
|
177
|
+
var _context10;
|
|
178
|
+
return t.set(thiz, method, (0, _bind["default"])(_context10 = currentProperties[method]).call(_context10, currentProperties));
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}, {
|
|
183
|
+
key: "subset",
|
|
184
|
+
value: function subset(thiz) {
|
|
185
|
+
var object = {};
|
|
186
|
+
for (var _len3 = arguments.length, properties = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
|
|
187
|
+
properties[_key3 - 1] = arguments[_key3];
|
|
188
|
+
}
|
|
189
|
+
for (var i = 0; i < properties.length; i++) object[properties[i]] = this.get(thiz, properties[i]);
|
|
190
|
+
return object;
|
|
191
|
+
}
|
|
192
|
+
}]);
|
|
193
|
+
}();
|
|
194
|
+
function getPropertyErrorString(property) {
|
|
195
|
+
// https://stackoverflow.com/a/7772724
|
|
196
|
+
if (typeof property === "string" || property instanceof String) return "\"".concat(property, "\"");
|
|
197
|
+
return "".concat(property);
|
|
198
|
+
}
|
|
199
|
+
function callProp(obj, property) {
|
|
200
|
+
try {
|
|
201
|
+
return obj[property]();
|
|
202
|
+
} catch (e) {
|
|
203
|
+
throw new RethrownError(e, "Calling \"".concat(property, "\" as a function failed."));
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// https://stackoverflow.com/questions/31054910/get-functions-methods-of-a-class
|
|
208
|
+
function getAllMethods(obj) {
|
|
209
|
+
if (!(0, _getPrototypeOf["default"])(obj)) return [];
|
|
210
|
+
var props = [];
|
|
211
|
+
do {
|
|
212
|
+
var _context11, _context12, _context13, _context14;
|
|
213
|
+
var l = (0, _filter["default"])(_context11 = (0, _sort["default"])(_context12 = (0, _concat["default"])(_context13 = (0, _getOwnPropertyNames["default"])(obj)).call(_context13, (0, _map["default"])(_context14 = (0, _getOwnPropertySymbols["default"])(obj)).call(_context14, function (s) {
|
|
214
|
+
return s.toString();
|
|
215
|
+
}))).call(_context12)).call(_context11, function (p, i, arr) {
|
|
216
|
+
return typeof obj[p] === "function" &&
|
|
217
|
+
// only the methods
|
|
218
|
+
p !== "constructor" && (
|
|
219
|
+
// not the constructor
|
|
220
|
+
i === 0 || p !== arr[i - 1]) &&
|
|
221
|
+
// not overriding in this prototype
|
|
222
|
+
(0, _indexOf["default"])(props).call(props, p) === -1;
|
|
223
|
+
} // not overridden in a child
|
|
224
|
+
);
|
|
225
|
+
props = (0, _concat["default"])(props).call(props, l);
|
|
226
|
+
} while ((obj = (0, _getPrototypeOf["default"])(obj)) &&
|
|
227
|
+
// walk-up the prototype chain
|
|
228
|
+
(0, _getPrototypeOf["default"])(obj) // not the the Object prototype methods (hasOwnProperty, etc...)
|
|
229
|
+
);
|
|
230
|
+
return props;
|
|
231
|
+
}
|
package/package.json
CHANGED
|
@@ -2,9 +2,13 @@
|
|
|
2
2
|
"author": "Chris Topher <c@creemama.com>",
|
|
3
3
|
"dependencies": {
|
|
4
4
|
"@babel/runtime-corejs3": "7.29.7",
|
|
5
|
-
"@util.js/errors": "0.41.
|
|
5
|
+
"@util.js/errors": "0.41.8"
|
|
6
6
|
},
|
|
7
7
|
"description": "JavaScript utility class for private member variables",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"index.js"
|
|
11
|
+
],
|
|
8
12
|
"keywords": [
|
|
9
13
|
"field",
|
|
10
14
|
"fields",
|
|
@@ -29,5 +33,5 @@
|
|
|
29
33
|
"type": "git",
|
|
30
34
|
"url": "https://github.com/creemama/utiljs/tree/master/packages/utiljs-privates"
|
|
31
35
|
},
|
|
32
|
-
"version": "0.41.
|
|
36
|
+
"version": "0.41.8"
|
|
33
37
|
}
|
package/lib/Privates.js
DELETED
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const RethrownError = require("@util.js/errors/RethrownError");
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* JavaScript utility class for private member variables.
|
|
7
|
-
*
|
|
8
|
-
* This class decreases some of the boilerplate of the
|
|
9
|
-
* [WeakMap]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap}
|
|
10
|
-
* pattern as described by
|
|
11
|
-
* ["Private Variables in JavaScript with ES6 WeakMaps"]{@link https://modernweb.com/private-variables-in-javascript-with-es6-weakmaps/}
|
|
12
|
-
* and
|
|
13
|
-
* ["Privates In ES2015 Javascript Classes"]{@link https://ilikekillnerds.com/2015/09/privates-in-es2015-javascript-classes/}.
|
|
14
|
-
* This utility handles shallow copying at construction.
|
|
15
|
-
*
|
|
16
|
-
* Private fields may become a part of the ECMA standard. The following is a
|
|
17
|
-
* StackOverflow answer to the question
|
|
18
|
-
* ["Private properties in JavaScript ES6 classes"]{@link https://stackoverflow.com/a/52237988}:
|
|
19
|
-
* "Private fields are being implemented in the ECMA standard. You can start
|
|
20
|
-
* using them today with babel 7 and stage 3 preset." Wouldn't private fields
|
|
21
|
-
* make this class obsolete? Not necessarily. If each of your privates is a
|
|
22
|
-
* function (e.g., you want to lazy load all member variables), this class has
|
|
23
|
-
* convenience methods for that.
|
|
24
|
-
*
|
|
25
|
-
* @public
|
|
26
|
-
* @class Privates
|
|
27
|
-
*/
|
|
28
|
-
module.exports = class Privates {
|
|
29
|
-
constructor() {
|
|
30
|
-
this.privates = new WeakMap();
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Calls `this.get(thiz, property)()`.
|
|
35
|
-
*
|
|
36
|
-
* @public
|
|
37
|
-
* @function Privates#call
|
|
38
|
-
*/
|
|
39
|
-
call(thiz, property) {
|
|
40
|
-
try {
|
|
41
|
-
// this.get(thiz, property)() does not work all the time unless we
|
|
42
|
-
// call bind, which we do in the set method. See
|
|
43
|
-
// https://javascript.info/object-methods.
|
|
44
|
-
return this.get(thiz, property)();
|
|
45
|
-
} catch (e) {
|
|
46
|
-
const propertyStr = getPropertyErrorString(property);
|
|
47
|
-
throw new RethrownError(
|
|
48
|
-
e,
|
|
49
|
-
`privates.call(${thiz}, ${propertyStr}) failed. ${e.message}`,
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
get(thiz, property) {
|
|
55
|
-
try {
|
|
56
|
-
return this.getProps(thiz)[property];
|
|
57
|
-
} catch (e) {
|
|
58
|
-
const propertyStr = getPropertyErrorString(property);
|
|
59
|
-
throw new RethrownError(
|
|
60
|
-
e,
|
|
61
|
-
`privates.get(${thiz}, ${propertyStr}) failed. ${e.message}`,
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
getCallProxy(thiz) {
|
|
67
|
-
return new Proxy(this.getProps(thiz), { get: callProp });
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
getProps(thiz) {
|
|
71
|
-
try {
|
|
72
|
-
let properties = this.privates.get(thiz);
|
|
73
|
-
if (!properties) this.privates.set(thiz, (properties = {}));
|
|
74
|
-
return properties;
|
|
75
|
-
} catch (e) {
|
|
76
|
-
if (thiz == null)
|
|
77
|
-
throw new RethrownError(e, `thiz cannot be null or undefined.`);
|
|
78
|
-
throw e;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
lambdaize(properties) {
|
|
83
|
-
try {
|
|
84
|
-
const lambdaProperties = {};
|
|
85
|
-
Object.entries(properties).forEach(
|
|
86
|
-
([property, value]) => (lambdaProperties[property] = () => value),
|
|
87
|
-
);
|
|
88
|
-
return lambdaProperties;
|
|
89
|
-
} catch (e) {
|
|
90
|
-
if (properties == null)
|
|
91
|
-
throw new RethrownError(e, `properties cannot be null or undefined.`);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
lazyLoad(thiz, property, lazyLoadFunction) {
|
|
96
|
-
if (typeof lazyLoadFunction !== "function")
|
|
97
|
-
throw new TypeError(
|
|
98
|
-
`lazyLoadFunction (${lazyLoadFunction}) must be a function.`,
|
|
99
|
-
);
|
|
100
|
-
let value;
|
|
101
|
-
let loaded = false;
|
|
102
|
-
this.set(thiz, property, () => {
|
|
103
|
-
if (!loaded) {
|
|
104
|
-
value = lazyLoadFunction();
|
|
105
|
-
loaded = true;
|
|
106
|
-
}
|
|
107
|
-
return value;
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
lazyLoadProps(thiz, ...lazilyLoadedProperties) {
|
|
112
|
-
const t = this;
|
|
113
|
-
lazilyLoadedProperties.forEach((currentProperties) => {
|
|
114
|
-
Object.entries(currentProperties).forEach(
|
|
115
|
-
([property, lazyLoadFunction]) =>
|
|
116
|
-
t.lazyLoad(thiz, property, lazyLoadFunction),
|
|
117
|
-
);
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
set(thiz, property, newValue) {
|
|
122
|
-
try {
|
|
123
|
-
this.getProps(thiz)[property] = newValue;
|
|
124
|
-
} catch (e) {
|
|
125
|
-
const propertyStr = getPropertyErrorString(property);
|
|
126
|
-
throw new RethrownError(
|
|
127
|
-
e,
|
|
128
|
-
`privates.set(${thiz}, ${propertyStr}, ${newValue}) failed. ${e.message}`,
|
|
129
|
-
);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
setProps(thiz, ...properties) {
|
|
134
|
-
const t = this;
|
|
135
|
-
properties.forEach((currentProperties) => {
|
|
136
|
-
Object.entries(currentProperties).forEach(([property, value]) =>
|
|
137
|
-
t.set(thiz, property, value),
|
|
138
|
-
);
|
|
139
|
-
getAllMethods(currentProperties).forEach((method) =>
|
|
140
|
-
t.set(thiz, method, currentProperties[method].bind(currentProperties)),
|
|
141
|
-
);
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
subset(thiz, ...properties) {
|
|
146
|
-
const object = {};
|
|
147
|
-
for (let i = 0; i < properties.length; i++)
|
|
148
|
-
object[properties[i]] = this.get(thiz, properties[i]);
|
|
149
|
-
return object;
|
|
150
|
-
}
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
function getPropertyErrorString(property) {
|
|
154
|
-
// https://stackoverflow.com/a/7772724
|
|
155
|
-
if (typeof property === "string" || property instanceof String)
|
|
156
|
-
return `"${property}"`;
|
|
157
|
-
return `${property}`;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
function callProp(obj, property) {
|
|
161
|
-
try {
|
|
162
|
-
return obj[property]();
|
|
163
|
-
} catch (e) {
|
|
164
|
-
throw new RethrownError(e, `Calling "${property}" as a function failed.`);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// https://stackoverflow.com/questions/31054910/get-functions-methods-of-a-class
|
|
169
|
-
function getAllMethods(obj) {
|
|
170
|
-
if (!Object.getPrototypeOf(obj)) return [];
|
|
171
|
-
|
|
172
|
-
let props = [];
|
|
173
|
-
|
|
174
|
-
do {
|
|
175
|
-
const l = Object.getOwnPropertyNames(obj)
|
|
176
|
-
.concat(Object.getOwnPropertySymbols(obj).map((s) => s.toString()))
|
|
177
|
-
.sort()
|
|
178
|
-
.filter(
|
|
179
|
-
(p, i, arr) =>
|
|
180
|
-
typeof obj[p] === "function" && // only the methods
|
|
181
|
-
p !== "constructor" && // not the constructor
|
|
182
|
-
(i === 0 || p !== arr[i - 1]) && // not overriding in this prototype
|
|
183
|
-
props.indexOf(p) === -1, // not overridden in a child
|
|
184
|
-
);
|
|
185
|
-
props = props.concat(l);
|
|
186
|
-
} while (
|
|
187
|
-
(obj = Object.getPrototypeOf(obj)) && // walk-up the prototype chain
|
|
188
|
-
Object.getPrototypeOf(obj) // not the the Object prototype methods (hasOwnProperty, etc...)
|
|
189
|
-
);
|
|
190
|
-
|
|
191
|
-
return props;
|
|
192
|
-
}
|
package/test/PrivatesTest.js
DELETED
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const { expect } = require("chai");
|
|
4
|
-
const Privates = require("..");
|
|
5
|
-
const { RethrownError } = require("@util.js/errors");
|
|
6
|
-
|
|
7
|
-
const ExampleUsingGet = (function () {
|
|
8
|
-
const privates = new Privates();
|
|
9
|
-
|
|
10
|
-
class ExampleUsingGet {
|
|
11
|
-
constructor(options) {
|
|
12
|
-
privates.setProps(this, options);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
getProperty1() {
|
|
16
|
-
return property1(this);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
getProperty2() {
|
|
20
|
-
return property2(this);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function get(thiz, privatePart) {
|
|
25
|
-
return privates.get(thiz, privatePart);
|
|
26
|
-
}
|
|
27
|
-
function property1(thiz) {
|
|
28
|
-
return get(thiz, "property1");
|
|
29
|
-
}
|
|
30
|
-
function property2(thiz) {
|
|
31
|
-
return get(thiz, "property2");
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return ExampleUsingGet;
|
|
35
|
-
})();
|
|
36
|
-
|
|
37
|
-
const ExampleUsingCall = (function () {
|
|
38
|
-
const privates = new Privates();
|
|
39
|
-
|
|
40
|
-
class ExampleUsingCall {
|
|
41
|
-
constructor(options) {
|
|
42
|
-
privates.setProps(this, options);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
getProperty1() {
|
|
46
|
-
return property1(this);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
getProperty2() {
|
|
50
|
-
return property2(this);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function call(thiz, privatePart) {
|
|
55
|
-
return privates.call(thiz, privatePart);
|
|
56
|
-
}
|
|
57
|
-
function property1(thiz) {
|
|
58
|
-
return call(thiz, "property1");
|
|
59
|
-
}
|
|
60
|
-
function property2(thiz) {
|
|
61
|
-
return call(thiz, "property2");
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return ExampleUsingCall;
|
|
65
|
-
})();
|
|
66
|
-
|
|
67
|
-
describe("Privates", () => {
|
|
68
|
-
it("should hide member variables using Privates#get", () => {
|
|
69
|
-
const example = new ExampleUsingGet({
|
|
70
|
-
property1: "a",
|
|
71
|
-
property2: 2,
|
|
72
|
-
});
|
|
73
|
-
expect(example.property1).to.be.undefined;
|
|
74
|
-
expect(example.property2).to.be.undefined;
|
|
75
|
-
expect(example.getProperty1()).to.eql("a");
|
|
76
|
-
expect(example.getProperty2()).to.eql(2);
|
|
77
|
-
});
|
|
78
|
-
it("should hide member variables using Privates#call", () => {
|
|
79
|
-
const example = new ExampleUsingCall({
|
|
80
|
-
property1: () => "a",
|
|
81
|
-
property2: () => 2,
|
|
82
|
-
});
|
|
83
|
-
expect(example.property1).to.be.undefined;
|
|
84
|
-
expect(example.property2).to.be.undefined;
|
|
85
|
-
expect(example.getProperty1()).to.eql("a");
|
|
86
|
-
expect(example.getProperty2()).to.eql(2);
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
describe("Privates#call", () => {
|
|
91
|
-
const privates = new Privates();
|
|
92
|
-
it("should correctly call privates that are functions", () => {
|
|
93
|
-
const thiz = {};
|
|
94
|
-
|
|
95
|
-
// Test a lambda function.
|
|
96
|
-
privates.set(thiz, "lambda", () => "lambdaObj");
|
|
97
|
-
expect(privates.call(thiz, "lambda")).to.eql("lambdaObj");
|
|
98
|
-
|
|
99
|
-
// Test a declared function.
|
|
100
|
-
function func() {
|
|
101
|
-
return "funcObj";
|
|
102
|
-
}
|
|
103
|
-
privates.set(thiz, "function", func);
|
|
104
|
-
expect(privates.call(thiz, "function")).to.eql("funcObj");
|
|
105
|
-
|
|
106
|
-
// Test a class function.
|
|
107
|
-
class CallClass {
|
|
108
|
-
delegate() {
|
|
109
|
-
return "classObj";
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
call() {
|
|
113
|
-
return this.delegate();
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
const c = new CallClass();
|
|
117
|
-
privates.set(thiz, "classBound", c.call.bind(c));
|
|
118
|
-
|
|
119
|
-
// Test an object as a property.
|
|
120
|
-
const key = { a: "a" };
|
|
121
|
-
privates.set(thiz, key, () => "objectAsProperty");
|
|
122
|
-
expect(privates.call(thiz, key)).to.eql("objectAsProperty");
|
|
123
|
-
|
|
124
|
-
// Test a symbol as a property.
|
|
125
|
-
const symbol = Symbol("symbol");
|
|
126
|
-
privates.set(thiz, symbol, () => "symbolAsProperty");
|
|
127
|
-
expect(privates.call(thiz, symbol)).to.eql("symbolAsProperty");
|
|
128
|
-
|
|
129
|
-
// Test a number as a property.
|
|
130
|
-
privates.set(thiz, -5, () => "numberAsProperty");
|
|
131
|
-
expect(privates.call(thiz, -5)).to.eql("numberAsProperty");
|
|
132
|
-
|
|
133
|
-
// Test a boolean as a property.
|
|
134
|
-
privates.set(thiz, false, () => "booleanAsProperty");
|
|
135
|
-
expect(privates.call(thiz, false)).to.eql("booleanAsProperty");
|
|
136
|
-
|
|
137
|
-
// Test undefined as a property.
|
|
138
|
-
privates.set(thiz, undefined, () => "undefinedAsProperty");
|
|
139
|
-
expect(privates.call(thiz, undefined)).to.eql("undefinedAsProperty");
|
|
140
|
-
|
|
141
|
-
// Test null as a property.
|
|
142
|
-
privates.set(thiz, null, () => "nullAsProperty");
|
|
143
|
-
expect(privates.call(thiz, null)).to.eql("nullAsProperty");
|
|
144
|
-
});
|
|
145
|
-
it("should fail with invalid arguments", () => {
|
|
146
|
-
const thiz = {};
|
|
147
|
-
|
|
148
|
-
expect(() => privates.call()).to.throw(RethrownError);
|
|
149
|
-
// eslint-disable-next-line no-useless-call
|
|
150
|
-
expect(() => privates.call(null, "nonexistent")).to.throw(RethrownError);
|
|
151
|
-
expect(() => privates.call(thiz)).to.throw(RethrownError);
|
|
152
|
-
expect(() => privates.call(thiz, "nonexistent")).to.throw(RethrownError);
|
|
153
|
-
|
|
154
|
-
// Test an unbound function failing.
|
|
155
|
-
class CallClass {
|
|
156
|
-
delegate() {
|
|
157
|
-
return "classObj";
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
call() {
|
|
161
|
-
return this.delegate();
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
const c = new CallClass();
|
|
165
|
-
privates.set(thiz, "classBound", c.call);
|
|
166
|
-
expect(() => privates.call(thiz, "classBound")).to.throw(RethrownError);
|
|
167
|
-
|
|
168
|
-
// Test a private that is not a function.
|
|
169
|
-
privates.set(thiz, "notafunction", "string");
|
|
170
|
-
expect(() => privates.call(thiz, "notafunction")).to.throw(RethrownError);
|
|
171
|
-
});
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
describe("Privates#lambdaize", () => {
|
|
175
|
-
const privates = new Privates();
|
|
176
|
-
it("should convert an object's properties to lambda properties", () => {
|
|
177
|
-
const obj = privates.lambdaize({
|
|
178
|
-
a: 0,
|
|
179
|
-
b: 1,
|
|
180
|
-
});
|
|
181
|
-
expect(obj.a()).to.eql(0);
|
|
182
|
-
expect(obj.b()).to.eql(1);
|
|
183
|
-
|
|
184
|
-
expect(privates.lambdaize(true)).to.eql({});
|
|
185
|
-
expect(privates.lambdaize(1)).to.eql({});
|
|
186
|
-
expect(Object.keys(privates.lambdaize("string"))).to.eql([
|
|
187
|
-
"0",
|
|
188
|
-
"1",
|
|
189
|
-
"2",
|
|
190
|
-
"3",
|
|
191
|
-
"4",
|
|
192
|
-
"5",
|
|
193
|
-
]);
|
|
194
|
-
expect(privates.lambdaize(Symbol("string"))).to.eql({});
|
|
195
|
-
expect(privates.lambdaize({})).to.eql({});
|
|
196
|
-
});
|
|
197
|
-
it("should fail with invalid arguments", () => {
|
|
198
|
-
expect(() => privates.lambdaize()).to.throw(RethrownError);
|
|
199
|
-
expect(() => privates.lambdaize(null)).to.throw(RethrownError);
|
|
200
|
-
expect(() => privates.lambdaize(undefined)).to.throw(RethrownError);
|
|
201
|
-
});
|
|
202
|
-
});
|