getPathInfo.js 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. /*!
  2. * Chai - getPathInfo utility
  3. * Copyright(c) 2012-2014 Jake Luer <jake@alogicalparadox.com>
  4. * MIT Licensed
  5. */
  6. var hasProperty = require('./hasProperty');
  7. /**
  8. * ### .getPathInfo(path, object)
  9. *
  10. * This allows the retrieval of property info in an
  11. * object given a string path.
  12. *
  13. * The path info consists of an object with the
  14. * following properties:
  15. *
  16. * * parent - The parent object of the property referenced by `path`
  17. * * name - The name of the final property, a number if it was an array indexer
  18. * * value - The value of the property, if it exists, otherwise `undefined`
  19. * * exists - Whether the property exists or not
  20. *
  21. * @param {String} path
  22. * @param {Object} object
  23. * @returns {Object} info
  24. * @name getPathInfo
  25. * @api public
  26. */
  27. module.exports = function getPathInfo(path, obj) {
  28. var parsed = parsePath(path),
  29. last = parsed[parsed.length - 1];
  30. var info = {
  31. parent: parsed.length > 1 ? _getPathValue(parsed, obj, parsed.length - 1) : obj,
  32. name: last.p || last.i,
  33. value: _getPathValue(parsed, obj),
  34. };
  35. info.exists = hasProperty(info.name, info.parent);
  36. return info;
  37. };
  38. /*!
  39. * ## parsePath(path)
  40. *
  41. * Helper function used to parse string object
  42. * paths. Use in conjunction with `_getPathValue`.
  43. *
  44. * var parsed = parsePath('myobject.property.subprop');
  45. *
  46. * ### Paths:
  47. *
  48. * * Can be as near infinitely deep and nested
  49. * * Arrays are also valid using the formal `myobject.document[3].property`.
  50. * * Literal dots and brackets (not delimiter) must be backslash-escaped.
  51. *
  52. * @param {String} path
  53. * @returns {Object} parsed
  54. * @api private
  55. */
  56. function parsePath (path) {
  57. var str = path.replace(/([^\\])\[/g, '$1.[')
  58. , parts = str.match(/(\\\.|[^.]+?)+/g);
  59. return parts.map(function (value) {
  60. var re = /^\[(\d+)\]$/
  61. , mArr = re.exec(value);
  62. if (mArr) return { i: parseFloat(mArr[1]) };
  63. else return { p: value.replace(/\\([.\[\]])/g, '$1') };
  64. });
  65. }
  66. /*!
  67. * ## _getPathValue(parsed, obj)
  68. *
  69. * Helper companion function for `.parsePath` that returns
  70. * the value located at the parsed address.
  71. *
  72. * var value = getPathValue(parsed, obj);
  73. *
  74. * @param {Object} parsed definition from `parsePath`.
  75. * @param {Object} object to search against
  76. * @param {Number} object to search against
  77. * @returns {Object|Undefined} value
  78. * @api private
  79. */
  80. function _getPathValue (parsed, obj, index) {
  81. var tmp = obj
  82. , res;
  83. index = (index === undefined ? parsed.length : index);
  84. for (var i = 0, l = index; i < l; i++) {
  85. var part = parsed[i];
  86. if (tmp) {
  87. if ('undefined' !== typeof part.p)
  88. tmp = tmp[part.p];
  89. else if ('undefined' !== typeof part.i)
  90. tmp = tmp[part.i];
  91. if (i == (l - 1)) res = tmp;
  92. } else {
  93. res = undefined;
  94. }
  95. }
  96. return res;
  97. }