UNPKG

7.75 kBJavaScriptView Raw
1'use strict';
2
3var util = require('util');
4var define = require('define-property');
5var isObj = require('isobject');
6
7/**
8 * Expose class utils: `cu`
9 */
10
11var cu = exports = module.exports;
12
13cu.isObject = function isObject(val) {
14 return isObj(val) || typeof val === 'function';
15};
16
17/**
18 * Returns true if an array has any of the given elements, or an
19 * object has any of the give keys.
20 *
21 * ```js
22 * cu.has(['a', 'b', 'c'], 'c');
23 * //=> true
24 *
25 * cu.has(['a', 'b', 'c'], ['c', 'z']);
26 * //=> true
27 *
28 * cu.has({a: 'b', c: 'd'}, ['c', 'z']);
29 * //=> true
30 * ```
31 * @param {Object} `obj`
32 * @param {String|Array} `val`
33 * @return {Boolean}
34 * @api public
35 */
36
37cu.has = function has(obj, val) {
38 val = cu.arrayify(val);
39 var len = val.length;
40
41 if (cu.isObject(obj)) {
42 for (var key in obj) {
43 if (val.indexOf(key) > -1) {
44 return true;
45 }
46 }
47
48 var keys = cu.nativeKeys(obj);
49 return cu.has(keys, val);
50 }
51
52 if (Array.isArray(obj)) {
53 var arr = obj;
54 while (len--) {
55 if (arr.indexOf(val[len]) > -1) {
56 return true;
57 }
58 }
59 return false;
60 }
61
62 throw new TypeError('expected an array or object.');
63};
64
65/**
66 * Returns true if an array or object has all of the given values.
67 *
68 * ```js
69 * cu.hasAll(['a', 'b', 'c'], 'c');
70 * //=> true
71 *
72 * cu.hasAll(['a', 'b', 'c'], ['c', 'z']);
73 * //=> false
74 *
75 * cu.hasAll({a: 'b', c: 'd'}, ['c', 'z']);
76 * //=> false
77 * ```
78 * @param {Object|Array} `val`
79 * @param {String|Array} `values`
80 * @return {Boolean}
81 * @api public
82 */
83
84cu.hasAll = function hasAll(val, values) {
85 values = cu.arrayify(values);
86 var len = values.length;
87 while (len--) {
88 if (!cu.has(val, values[len])) {
89 return false;
90 }
91 }
92 return true;
93};
94
95/**
96 * Cast the given value to an array.
97 *
98 * ```js
99 * cu.arrayify('foo');
100 * //=> ['foo']
101 *
102 * cu.arrayify(['foo']);
103 * //=> ['foo']
104 * ```
105 *
106 * @param {String|Array} `val`
107 * @return {Array}
108 * @api public
109 */
110
111cu.arrayify = function arrayify(val) {
112 return val ? (Array.isArray(val) ? val : [val]) : []
113};
114
115/**
116 * Noop
117 */
118
119cu.noop = function noop() {
120 return;
121};
122
123/**
124 * Returns the first argument passed to the function.
125 */
126
127cu.identity = function identity(val) {
128 return val;
129};
130
131/**
132 * Returns true if a value has a `contructor`
133 *
134 * ```js
135 * cu.hasConstructor({});
136 * //=> true
137 *
138 * cu.hasConstructor(Object.create(null));
139 * //=> false
140 * ```
141 * @param {Object} `value`
142 * @return {Boolean}
143 * @api public
144 */
145
146cu.hasConstructor = function hasConstructor(val) {
147 return cu.isObject(val) && typeof val.constructor !== 'undefined';
148};
149
150/**
151 * Get the native `ownPropertyNames` from the constructor of the
152 * given `object`. An empty array is returned if the object does
153 * not have a constructor.
154 *
155 * ```js
156 * cu.nativeKeys({a: 'b', b: 'c', c: 'd'})
157 * //=> ['a', 'b', 'c']
158 *
159 * cu.nativeKeys(function(){})
160 * //=> ['length', 'caller']
161 * ```
162 *
163 * @param {Object} `obj` Object that has a `constructor`.
164 * @return {Array} Array of keys.
165 * @api public
166 */
167
168cu.nativeKeys = function nativeKeys(val) {
169 if (!cu.hasConstructor(val)) return [];
170 return Object.getOwnPropertyNames(val);
171};
172
173/**
174 * Returns property descriptor `key` if it's an "own" property
175 * of the given object.
176 *
177 * ```js
178 * function App() {}
179 * Object.defineProperty(App.prototype, 'count', {
180 * get: function() {
181 * return Object.keys(this).length;
182 * }
183 * });
184 * cu.getDescriptor(App.prototype, 'count');
185 * // returns:
186 * // {
187 * // get: [Function],
188 * // set: undefined,
189 * // enumerable: false,
190 * // configurable: false
191 * // }
192 * ```
193 *
194 * @param {Object} `obj`
195 * @param {String} `key`
196 * @return {Object} Returns descriptor `key`
197 * @api public
198 */
199
200cu.getDescriptor = function getDescriptor(obj, key) {
201 if (!cu.isObject(obj)) {
202 throw new TypeError('expected an object.');
203 }
204 if (typeof key !== 'string') {
205 throw new TypeError('expected key to be a string.');
206 }
207 return Object.getOwnPropertyDescriptor(obj, key);
208};
209
210/**
211 * Copy a descriptor from one object to another.
212 *
213 * ```js
214 * function App() {}
215 * Object.defineProperty(App.prototype, 'count', {
216 * get: function() {
217 * return Object.keys(this).length;
218 * }
219 * });
220 * var obj = {};
221 * cu.copyDescriptor(obj, App.prototype, 'count');
222 * ```
223 * @param {Object} `receiver`
224 * @param {Object} `provider`
225 * @param {String} `name`
226 * @return {Object}
227 * @api public
228 */
229
230cu.copyDescriptor = function copyDescriptor(receiver, provider, name) {
231 if (!cu.isObject(receiver)) {
232 throw new TypeError('expected receiving object to be an object.');
233 }
234 if (!cu.isObject(provider)) {
235 throw new TypeError('expected providing object to be an object.');
236 }
237 if (typeof name !== 'string') {
238 throw new TypeError('expected name to be a string.');
239 }
240 var val = cu.getDescriptor(provider, name);
241 if (val) define(receiver, name, val);
242};
243
244/**
245 * Copy static properties, prototype properties, and descriptors
246 * from one object to another.
247 *
248 * @param {Object} `receiver`
249 * @param {Object} `provider`
250 * @param {String|Array} `omit` One or more properties to omit
251 * @return {Object}
252 * @api public
253 */
254
255cu.copy = function copy(receiver, provider, omit) {
256 if (!cu.isObject(receiver)) {
257 throw new TypeError('expected receiving object to be an object.');
258 }
259 if (!cu.isObject(provider)) {
260 throw new TypeError('expected providing object to be an object.');
261 }
262 var props = Object.getOwnPropertyNames(provider);
263 var keys = Object.keys(provider);
264 var len = props.length, key;
265 omit = cu.arrayify(omit);
266
267 while (len--) {
268 key = props[len];
269
270 if (cu.has(keys, key)) {
271 define(receiver, key, provider[key]);
272 } else if (!(key in receiver) && !cu.has(omit, key)) {
273 cu.copyDescriptor(receiver, provider, key);
274 }
275 }
276};
277
278/**
279 * Inherit the static properties, prototype properties, and descriptors
280 * from of an object.
281 *
282 * @param {Object} `receiver`
283 * @param {Object} `provider`
284 * @param {String|Array} `omit` One or more properties to omit
285 * @return {Object}
286 * @api public
287 */
288
289cu.inherit = function inherit(receiver, provider, omit) {
290 if (!cu.isObject(receiver)) {
291 throw new TypeError('expected receiving object to be an object.');
292 }
293 if (!cu.isObject(provider)) {
294 throw new TypeError('expected providing object to be an object.');
295 }
296
297 var keys = [];
298 for (var key in provider) {
299 keys.push(key);
300 define(receiver, key, provider[key]);
301 }
302
303 keys = keys.concat(cu.arrayify(omit));
304
305 var a = provider.prototype || provider;
306 var b = receiver.prototype || receiver;
307 cu.copy(b, a, keys);
308};
309
310/**
311 * Returns a function for extending the static properties,
312 * prototype properties, and descriptors from the `Parent`
313 * constructor onto `Child` constructors.
314 *
315 * ```js
316 * var extend = cu.extend(Parent);
317 * Parent.extend(Child);
318 *
319 * // optional methods
320 * Parent.extend(Child, {
321 * foo: function() {},
322 * bar: function() {}
323 * });
324 * ```
325 * @param {Function} `Parent` Parent ctor
326 * @param {Function} `Child` Child ctor
327 * @param {Object} `proto` Optionally pass additional prototype properties to inherit.
328 * @return {Object}
329 * @api public
330 */
331
332cu.extend = function extend(Parent) {
333 if (typeof Parent !== 'function') {
334 throw new TypeError('expected Parent to be a function.');
335 }
336
337 return function (Ctor, proto) {
338 if (typeof Ctor !== 'function') {
339 throw new TypeError('expected Ctor to be a function.');
340 }
341
342 util.inherits(Ctor, Parent);
343
344 for (var key in Parent) {
345 Ctor[key] = Parent[key];
346 }
347
348 if (typeof proto === 'object') {
349 var obj = Object.create(proto);
350
351 for (var k in obj) {
352 Ctor.prototype[k] = obj[k];
353 }
354 }
355 Ctor.extend = cu.extend(Ctor);
356 };
357};