utils.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. 'use strict';
  2. /*!
  3. * Module dependencies.
  4. */
  5. var Buffer = require('safe-buffer').Buffer;
  6. var RegExpClone = require('regexp-clone');
  7. /**
  8. * Clones objects
  9. *
  10. * @param {Object} obj the object to clone
  11. * @param {Object} options
  12. * @return {Object} the cloned object
  13. * @api private
  14. */
  15. var clone = exports.clone = function clone(obj, options) {
  16. if (obj === undefined || obj === null)
  17. return obj;
  18. if (Array.isArray(obj))
  19. return exports.cloneArray(obj, options);
  20. if (obj.constructor) {
  21. if (/ObjectI[dD]$/.test(obj.constructor.name)) {
  22. return 'function' == typeof obj.clone
  23. ? obj.clone()
  24. : new obj.constructor(obj.id);
  25. }
  26. if (obj.constructor.name === 'ReadPreference') {
  27. return new obj.constructor(obj.mode, clone(obj.tags, options));
  28. }
  29. if ('Binary' == obj._bsontype && obj.buffer && obj.value) {
  30. return 'function' == typeof obj.clone
  31. ? obj.clone()
  32. : new obj.constructor(obj.value(true), obj.sub_type);
  33. }
  34. if ('Date' === obj.constructor.name || 'Function' === obj.constructor.name)
  35. return new obj.constructor(+obj);
  36. if ('RegExp' === obj.constructor.name)
  37. return RegExpClone(obj);
  38. if ('Buffer' === obj.constructor.name)
  39. return exports.cloneBuffer(obj);
  40. }
  41. if (isObject(obj))
  42. return exports.cloneObject(obj, options);
  43. if (obj.valueOf)
  44. return obj.valueOf();
  45. };
  46. /*!
  47. * ignore
  48. */
  49. exports.cloneObject = function cloneObject(obj, options) {
  50. var minimize = options && options.minimize;
  51. var ret = {};
  52. var hasKeys;
  53. var val;
  54. var k;
  55. for (k in obj) {
  56. val = clone(obj[k], options);
  57. if (!minimize || ('undefined' !== typeof val)) {
  58. hasKeys || (hasKeys = true);
  59. ret[k] = val;
  60. }
  61. }
  62. return minimize
  63. ? hasKeys && ret
  64. : ret;
  65. };
  66. exports.cloneArray = function cloneArray(arr, options) {
  67. var ret = [];
  68. for (var i = 0, l = arr.length; i < l; i++)
  69. ret.push(clone(arr[i], options));
  70. return ret;
  71. };
  72. /**
  73. * process.nextTick helper.
  74. *
  75. * Wraps the given `callback` in a try/catch. If an error is
  76. * caught it will be thrown on nextTick.
  77. *
  78. * node-mongodb-native had a habit of state corruption when
  79. * an error was immediately thrown from within a collection
  80. * method (find, update, etc) callback.
  81. *
  82. * @param {Function} [callback]
  83. * @api private
  84. */
  85. exports.tick = function tick(callback) {
  86. if ('function' !== typeof callback) return;
  87. return function() {
  88. // callbacks should always be fired on the next
  89. // turn of the event loop. A side benefit is
  90. // errors thrown from executing the callback
  91. // will not cause drivers state to be corrupted
  92. // which has historically been a problem.
  93. var args = arguments;
  94. soon(function() {
  95. callback.apply(this, args);
  96. });
  97. };
  98. };
  99. /**
  100. * Merges `from` into `to` without overwriting existing properties.
  101. *
  102. * @param {Object} to
  103. * @param {Object} from
  104. * @api private
  105. */
  106. exports.merge = function merge(to, from) {
  107. var keys = Object.keys(from),
  108. i = keys.length,
  109. key;
  110. while (i--) {
  111. key = keys[i];
  112. if ('undefined' === typeof to[key]) {
  113. to[key] = from[key];
  114. } else {
  115. if (exports.isObject(from[key])) {
  116. merge(to[key], from[key]);
  117. } else {
  118. to[key] = from[key];
  119. }
  120. }
  121. }
  122. };
  123. /**
  124. * Same as merge but clones the assigned values.
  125. *
  126. * @param {Object} to
  127. * @param {Object} from
  128. * @api private
  129. */
  130. exports.mergeClone = function mergeClone(to, from) {
  131. var keys = Object.keys(from),
  132. i = keys.length,
  133. key;
  134. while (i--) {
  135. key = keys[i];
  136. if ('undefined' === typeof to[key]) {
  137. to[key] = clone(from[key]);
  138. } else {
  139. if (exports.isObject(from[key])) {
  140. mergeClone(to[key], from[key]);
  141. } else {
  142. to[key] = clone(from[key]);
  143. }
  144. }
  145. }
  146. };
  147. /**
  148. * Read pref helper (mongo 2.2 drivers support this)
  149. *
  150. * Allows using aliases instead of full preference names:
  151. *
  152. * p primary
  153. * pp primaryPreferred
  154. * s secondary
  155. * sp secondaryPreferred
  156. * n nearest
  157. *
  158. * @param {String} pref
  159. */
  160. exports.readPref = function readPref(pref) {
  161. switch (pref) {
  162. case 'p':
  163. pref = 'primary';
  164. break;
  165. case 'pp':
  166. pref = 'primaryPreferred';
  167. break;
  168. case 's':
  169. pref = 'secondary';
  170. break;
  171. case 'sp':
  172. pref = 'secondaryPreferred';
  173. break;
  174. case 'n':
  175. pref = 'nearest';
  176. break;
  177. }
  178. return pref;
  179. };
  180. /**
  181. * Read Concern helper (mongo 3.2 drivers support this)
  182. *
  183. * Allows using string to specify read concern level:
  184. *
  185. * local 3.2+
  186. * available 3.6+
  187. * majority 3.2+
  188. * linearizable 3.4+
  189. * snapshot 4.0+
  190. *
  191. * @param {String|Object} concern
  192. */
  193. exports.readConcern = function readConcern(concern) {
  194. if ('string' === typeof concern) {
  195. switch (concern) {
  196. case 'l':
  197. concern = 'local';
  198. break;
  199. case 'a':
  200. concern = 'available';
  201. break;
  202. case 'm':
  203. concern = 'majority';
  204. break;
  205. case 'lz':
  206. concern = 'linearizable';
  207. break;
  208. case 's':
  209. concern = 'snapshot';
  210. break;
  211. }
  212. concern = { level: concern };
  213. }
  214. return concern;
  215. };
  216. /**
  217. * Object.prototype.toString.call helper
  218. */
  219. var _toString = Object.prototype.toString;
  220. exports.toString = function(arg) {
  221. return _toString.call(arg);
  222. };
  223. /**
  224. * Determines if `arg` is an object.
  225. *
  226. * @param {Object|Array|String|Function|RegExp|any} arg
  227. * @return {Boolean}
  228. */
  229. var isObject = exports.isObject = function(arg) {
  230. return '[object Object]' == exports.toString(arg);
  231. };
  232. /**
  233. * Determines if `arg` is an array.
  234. *
  235. * @param {Object}
  236. * @return {Boolean}
  237. * @see nodejs utils
  238. */
  239. exports.isArray = function(arg) {
  240. return Array.isArray(arg) ||
  241. 'object' == typeof arg && '[object Array]' == exports.toString(arg);
  242. };
  243. /**
  244. * Object.keys helper
  245. */
  246. exports.keys = Object.keys || function(obj) {
  247. var keys = [];
  248. for (var k in obj) if (obj.hasOwnProperty(k)) {
  249. keys.push(k);
  250. }
  251. return keys;
  252. };
  253. /**
  254. * Basic Object.create polyfill.
  255. * Only one argument is supported.
  256. *
  257. * Based on https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/create
  258. */
  259. exports.create = 'function' == typeof Object.create
  260. ? Object.create
  261. : create;
  262. function create(proto) {
  263. if (arguments.length > 1) {
  264. throw new Error('Adding properties is not supported');
  265. }
  266. function F() {}
  267. F.prototype = proto;
  268. return new F;
  269. }
  270. /**
  271. * inheritance
  272. */
  273. exports.inherits = function(ctor, superCtor) {
  274. ctor.prototype = exports.create(superCtor.prototype);
  275. ctor.prototype.constructor = ctor;
  276. };
  277. /**
  278. * nextTick helper
  279. * compat with node 0.10 which behaves differently than previous versions
  280. */
  281. var soon = exports.soon = 'function' == typeof setImmediate
  282. ? setImmediate
  283. : process.nextTick;
  284. /**
  285. * Clones the contents of a buffer.
  286. *
  287. * @param {Buffer} buff
  288. * @return {Buffer}
  289. */
  290. exports.cloneBuffer = function(buff) {
  291. var dupe = Buffer.alloc(buff.length);
  292. buff.copy(dupe, 0, 0, buff.length);
  293. return dupe;
  294. };
  295. /**
  296. * Check if this object is an arguments object
  297. *
  298. * @param {Any} v
  299. * @return {Boolean}
  300. */
  301. exports.isArgumentsObject = function(v) {
  302. return Object.prototype.toString.call(v) === '[object Arguments]';
  303. };