Skip to content

Commit ff41b74

Browse files
author
Hugo Ribeiro
committed
refactor(utils): moving clone to helpers/clone
Since clone has some dependencies from utils, also: - specialProperties to helpers/specialProperties; - isMongooseObject to helpers/isMongooseObject; - getFunctionName to helpers/getFunctionName; - isBsonType to helpers/isBsonType; - isObject to helpers/isObject. I'd resisted to temptation to refactor methods, with two exception: - util.object.vals changed by Object.values; - utils.each changed forEach. Updated reference from utils to helpers at: - lib/browserDocument.js; - lib/cast.js; - lib/drivers/node-mongodb-native/collection.js; - lib/helpers/common.js; - lib/helpers/schema/getIndexes.js; - lib/options/PopulateOptions.js; - lib/options/SchemaTypeOptions.js; - lib/options/removeOptions.js; - lib/options/saveOptions.js; - lib/queryhelpers.js; - lib/schema/mixed.js; - lib/types/map.js.
1 parent ca52ead commit ff41b74

24 files changed

+648
-223
lines changed

lib/browserDocument.js

+8-10
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const Schema = require('./schema');
1111
const ObjectId = require('./types/objectid');
1212
const ValidationError = MongooseError.ValidationError;
1313
const applyHooks = require('./helpers/model/applyHooks');
14-
const utils = require('./utils');
14+
const isObject = require('./helpers/isObject');
1515

1616
/**
1717
* Document constructor.
@@ -30,7 +30,7 @@ function Document(obj, schema, fields, skipId, skipInit) {
3030
return new Document(obj, schema, fields, skipId, skipInit);
3131
}
3232

33-
if (utils.isObject(schema) && !schema.instanceOfSchema) {
33+
if (isObject(schema) && !schema.instanceOfSchema) {
3434
schema = new Schema(schema);
3535
}
3636

@@ -85,14 +85,12 @@ Document.events = new EventEmitter();
8585

8686
Document.$emitter = new EventEmitter();
8787

88-
utils.each(
89-
['on', 'once', 'emit', 'listeners', 'removeListener', 'setMaxListeners',
90-
'removeAllListeners', 'addListener'],
91-
function(emitterFn) {
92-
Document[emitterFn] = function() {
93-
return Document.$emitter[emitterFn].apply(Document.$emitter, arguments);
94-
};
95-
});
88+
['on', 'once', 'emit', 'listeners', 'removeListener', 'setMaxListeners',
89+
'removeAllListeners', 'addListener'].forEach(function(emitterFn) {
90+
Document[emitterFn] = function() {
91+
return Document.$emitter[emitterFn].apply(Document.$emitter, arguments);
92+
};
93+
});
9694

9795
/*!
9896
* Module exports.

lib/cast.js

+8-7
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ const castTextSearch = require('./schema/operators/text');
1010
const get = require('./helpers/get');
1111
const isOperator = require('./helpers/query/isOperator');
1212
const util = require('util');
13-
const utils = require('./utils');
13+
const isObject = require('./helpers/isObject');
14+
const isMongooseObject = require('./helpers/isMongooseObject');
1415

1516
const ALLOWED_GEOWITHIN_GEOJSON_TYPES = ['Polygon', 'MultiPolygon'];
1617

@@ -139,7 +140,7 @@ module.exports = function cast(schema, obj, options, context) {
139140
continue;
140141
}
141142

142-
if (utils.isObject(val)) {
143+
if (isObject(val)) {
143144
// handle geo schemas that use object notation
144145
// { loc: { long: Number, lat: Number }
145146

@@ -203,7 +204,7 @@ module.exports = function cast(schema, obj, options, context) {
203204
context: context
204205
});
205206
}
206-
if (utils.isMongooseObject(value.$geometry)) {
207+
if (isMongooseObject(value.$geometry)) {
207208
value.$geometry = value.$geometry.toObject({
208209
transform: false,
209210
virtuals: false
@@ -212,7 +213,7 @@ module.exports = function cast(schema, obj, options, context) {
212213
value = value.$geometry.coordinates;
213214
} else if (geo === '$geoWithin') {
214215
if (value.$geometry) {
215-
if (utils.isMongooseObject(value.$geometry)) {
216+
if (isMongooseObject(value.$geometry)) {
216217
value.$geometry = value.$geometry.toObject({ virtuals: false });
217218
}
218219
const geoWithinType = value.$geometry.type;
@@ -224,7 +225,7 @@ module.exports = function cast(schema, obj, options, context) {
224225
} else {
225226
value = value.$box || value.$polygon || value.$center ||
226227
value.$centerSphere;
227-
if (utils.isMongooseObject(value)) {
228+
if (isMongooseObject(value)) {
228229
value = value.toObject({ virtuals: false });
229230
}
230231
}
@@ -325,7 +326,7 @@ module.exports = function cast(schema, obj, options, context) {
325326
function _cast(val, numbertype, context) {
326327
if (Array.isArray(val)) {
327328
val.forEach(function(item, i) {
328-
if (Array.isArray(item) || utils.isObject(item)) {
329+
if (Array.isArray(item) || isObject(item)) {
329330
return _cast(item, numbertype, context);
330331
}
331332
val[i] = numbertype.castForQueryWrapper({ val: item, context: context });
@@ -336,7 +337,7 @@ function _cast(val, numbertype, context) {
336337
while (nearLen--) {
337338
const nkey = nearKeys[nearLen];
338339
const item = val[nkey];
339-
if (Array.isArray(item) || utils.isObject(item)) {
340+
if (Array.isArray(item) || isObject(item)) {
340341
_cast(item, numbertype, context);
341342
val[nkey] = item;
342343
} else {

lib/drivers/node-mongodb-native/collection.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,8 @@ function format(obj, sub, color) {
276276
return obj;
277277
}
278278

279-
let x = require('../../utils').clone(obj, {transform: false});
279+
const clone = require('../../helpers/clone');
280+
let x = clone(obj, {transform: false});
280281

281282
if (x.constructor.name === 'Binary') {
282283
x = 'BinData(' + x.sub_type + ', "' + x.toString('base64') + '")';

lib/helpers/clone.js

+135
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
'use strict';
2+
3+
4+
const cloneRegExp = require('regexp-clone');
5+
const Decimal = require('../types/decimal128');
6+
const ObjectId = require('../types/objectid');
7+
const specialProperties = require('./specialProperties');
8+
const isMongooseObject = require('./isMongooseObject');
9+
const getFunctionName = require('./getFunctionName');
10+
const isBsonType = require('./isBsonType');
11+
const isObject = require('./isObject');
12+
const symbols = require('./symbols');
13+
14+
15+
/*!
16+
* Object clone with Mongoose natives support.
17+
*
18+
* If options.minimize is true, creates a minimal data object. Empty objects and undefined values will not be cloned. This makes the data payload sent to MongoDB as small as possible.
19+
*
20+
* Functions are never cloned.
21+
*
22+
* @param {Object} obj the object to clone
23+
* @param {Object} options
24+
* @param {Boolean} isArrayChild true if cloning immediately underneath an array. Special case for minimize.
25+
* @return {Object} the cloned object
26+
* @api private
27+
*/
28+
29+
function clone(obj, options, isArrayChild) {
30+
if (obj == null) {
31+
return obj;
32+
}
33+
34+
if (Array.isArray(obj)) {
35+
return cloneArray(obj, options);
36+
}
37+
38+
if (isMongooseObject(obj)) {
39+
// Single nested subdocs should apply getters later in `applyGetters()`
40+
// when calling `toObject()`. See gh-7442, gh-8295
41+
if (options && options._skipSingleNestedGetters && obj.$isSingleNested) {
42+
options = Object.assign({}, options, { getters: false });
43+
}
44+
if (options && options.json && typeof obj.toJSON === 'function') {
45+
return obj.toJSON(options);
46+
}
47+
return obj.toObject(options);
48+
}
49+
50+
if (obj.constructor) {
51+
switch (getFunctionName(obj.constructor)) {
52+
case 'Object':
53+
return cloneObject(obj, options, isArrayChild);
54+
case 'Date':
55+
return new obj.constructor(+obj);
56+
case 'RegExp':
57+
return cloneRegExp(obj);
58+
default:
59+
// ignore
60+
break;
61+
}
62+
}
63+
64+
if (obj instanceof ObjectId) {
65+
return new ObjectId(obj.id);
66+
}
67+
68+
if (isBsonType(obj, 'Decimal128')) {
69+
if (options && options.flattenDecimals) {
70+
return obj.toJSON();
71+
}
72+
return Decimal.fromString(obj.toString());
73+
}
74+
75+
if (!obj.constructor && isObject(obj)) {
76+
// object created with Object.create(null)
77+
return cloneObject(obj, options, isArrayChild);
78+
}
79+
80+
if (obj[symbols.schemaTypeSymbol]) {
81+
return obj.clone();
82+
}
83+
84+
// If we're cloning this object to go into a MongoDB command,
85+
// and there's a `toBSON()` function, assume this object will be
86+
// stored as a primitive in MongoDB and doesn't need to be cloned.
87+
if (options && options.bson && typeof obj.toBSON === 'function') {
88+
return obj;
89+
}
90+
91+
if (obj.valueOf != null) {
92+
return obj.valueOf();
93+
}
94+
95+
return cloneObject(obj, options, isArrayChild);
96+
}
97+
module.exports = clone;
98+
99+
/*!
100+
* ignore
101+
*/
102+
103+
function cloneObject(obj, options, isArrayChild) {
104+
const minimize = options && options.minimize;
105+
const ret = {};
106+
let hasKeys;
107+
108+
for (const k in obj) {
109+
if (specialProperties.has(k)) {
110+
continue;
111+
}
112+
113+
// Don't pass `isArrayChild` down
114+
const val = clone(obj[k], options);
115+
116+
if (!minimize || (typeof val !== 'undefined')) {
117+
if (minimize === false && typeof val === 'undefined') {
118+
delete ret[k];
119+
} else {
120+
hasKeys || (hasKeys = true);
121+
ret[k] = val;
122+
}
123+
}
124+
}
125+
126+
return minimize && !isArrayChild ? hasKeys && ret : ret;
127+
}
128+
129+
function cloneArray(arr, options) {
130+
const ret = [];
131+
for (let i = 0, l = arr.length; i < l; i++) {
132+
ret.push(clone(arr[i], options, true));
133+
}
134+
return ret;
135+
}

lib/helpers/common.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
const Decimal128 = require('../types/decimal128');
88
const ObjectId = require('../types/objectid');
9-
const utils = require('../utils');
9+
const isMongooseObject = require('./isMongooseObject');
1010

1111
exports.flatten = flatten;
1212
exports.modifiedPaths = modifiedPaths;
@@ -17,7 +17,7 @@ exports.modifiedPaths = modifiedPaths;
1717

1818
function flatten(update, path, options, schema) {
1919
let keys;
20-
if (update && utils.isMongooseObject(update) && !Buffer.isBuffer(update)) {
20+
if (update && isMongooseObject(update) && !Buffer.isBuffer(update)) {
2121
keys = Object.keys(update.toObject({ transform: false, virtuals: false }));
2222
} else {
2323
keys = Object.keys(update || {});
@@ -78,7 +78,7 @@ function modifiedPaths(update, path, result) {
7878
let val = update[key];
7979

8080
result[path + key] = true;
81-
if (utils.isMongooseObject(val) && !Buffer.isBuffer(val)) {
81+
if (isMongooseObject(val) && !Buffer.isBuffer(val)) {
8282
val = val.toObject({ transform: false, virtuals: false });
8383
}
8484
if (shouldFlatten(val)) {

lib/helpers/getFunctionName.js

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
'use strict';
2+
3+
module.exports = function(fn) {
4+
if (fn.name) {
5+
return fn.name;
6+
}
7+
return (fn.toString().trim().match(/^function\s*([^\s(]+)/) || [])[1];
8+
};

lib/helpers/isBsonType.js

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
'use strict';
2+
3+
const get = require('./get');
4+
5+
/*!
6+
* Get the bson type, if it exists
7+
*/
8+
9+
function isBsonType(obj, typename) {
10+
return get(obj, '_bsontype', void 0) === typename;
11+
}
12+
13+
module.exports = isBsonType;

lib/helpers/isMongooseObject.js

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
'use strict';
2+
3+
/*!
4+
* Returns if `v` is a mongoose object that has a `toObject()` method we can use.
5+
*
6+
* This is for compatibility with libs like Date.js which do foolish things to Natives.
7+
*
8+
* @param {any} v
9+
* @api private
10+
*/
11+
12+
module.exports = function(v) {
13+
if (v == null) {
14+
return false;
15+
}
16+
17+
return v.$__ != null || // Document
18+
v.isMongooseArray || // Array or Document Array
19+
v.isMongooseBuffer || // Buffer
20+
v.$isMongooseMap; // Map
21+
};

lib/helpers/isObject.js

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
'use strict';
2+
3+
/*!
4+
* Determines if `arg` is an object.
5+
*
6+
* @param {Object|Array|String|Function|RegExp|any} arg
7+
* @api private
8+
* @return {Boolean}
9+
*/
10+
11+
module.exports = function(arg) {
12+
if (Buffer.isBuffer(arg)) {
13+
return true;
14+
}
15+
return Object.prototype.toString.call(arg) === '[object Object]';
16+
};

lib/helpers/schema/getIndexes.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use strict';
22

33
const get = require('../get');
4-
const utils = require('../../utils');
4+
const helperIsObject = require('../isObject');
55

66
/*!
77
* Gather all indexes defined in the schema, including single nested,
@@ -62,7 +62,7 @@ module.exports = function getIndexes(schema) {
6262

6363
if (index !== false && index !== null && index !== undefined) {
6464
const field = {};
65-
const isObject = utils.isObject(index);
65+
const isObject = helperIsObject(index);
6666
const options = isObject ? index : {};
6767
const type = typeof index === 'string' ? index :
6868
isObject ? index.type :

lib/helpers/specialProperties.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
'use strict';
2+
3+
module.exports = new Set(['__proto__', 'constructor', 'prototype']);

lib/options/PopulateOptions.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict';
22

3-
const utils = require('../utils');
3+
const clone = require('../helpers/clone');
44

55
class PopulateOptions {
66
constructor(obj) {
@@ -9,7 +9,7 @@ class PopulateOptions {
99
if (obj == null) {
1010
return;
1111
}
12-
obj = utils.clone(obj);
12+
obj = clone(obj);
1313
Object.assign(this, obj);
1414
if (typeof obj.subPopulate === 'object') {
1515
this.populate = obj.subPopulate;

lib/options/SchemaTypeOptions.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict';
22

3-
const utils = require('../utils');
3+
const clone = require('../helpers/clone');
44

55
/**
66
* The options defined on a schematype.
@@ -19,7 +19,7 @@ class SchemaTypeOptions {
1919
if (obj == null) {
2020
return this;
2121
}
22-
Object.assign(this, utils.clone(obj));
22+
Object.assign(this, clone(obj));
2323
}
2424
}
2525

0 commit comments

Comments
 (0)