chai/lib/utils/getPathValue.js
2012-03-18 18:05:55 -04:00

102 lines
2.4 KiB
JavaScript

/**
* Chai - getPathValue utility
* Copyright(c) 2012 Jake Luer <jake@alogicalparadox.com>
* @see https://github.com/logicalparadox/filtr
* MIT Licensed
*/
/**
* # .getPathValue(path, object)
*
* This allows the retrieval of values in an
* object given a string path.
*
* var obj = {
* prop1: {
* arr: ['a', 'b', 'c']
* , str: 'Hello'
* }
* , prop2: {
* arr: [ { nested: 'Universe' } ]
* , str: 'Hello again!'
* }
* }
*
* The following would be the results.
*
* getPathValue('prop1.str', obj); // Hello
* getPathValue('prop1.att[2]', obj); // b
* getPathValue('prop2.arr[0].nested', obj); // Universe
*
* @param {String} path
* @param {Object} object
* @returns {Object} value or `undefined`
* @api public
*/
var getPathValue = module.exports = function (path, obj) {
var parsed = parsePath(path);
return _getPathValue(parsed, obj);
};
/*!
* ## parsePath(path)
*
* Helper function used to parse string object
* paths. Use in conjunction with `_getPathValue`.
*
* var parsed = parsePath('myobject.property.subprop');
*
* ### Paths:
*
* * Can be as near infinitely deep and nested
* * Arrays are also valid using the formal `myobject.document[3].property`.
*
* @param {String} path
* @returns {Object} parsed
* @api private
*/
function parsePath (path) {
var parts = path.split('.').filter(Boolean);
return parts.map(function (value) {
var re = /([A-Za-z0-9]+)\[(\d+)\]$/
, mArr = re.exec(value)
, val;
if (mArr) val = { p: mArr[1], i: parseFloat(mArr[2]) };
return val || value;
});
};
/**
* ## _getPathValue(parsed, obj)
*
* Helper companion function for `.parsePath` that returns
* the value located at the parsed address.
*
* var value = getPathValue(parsed, obj);
*
* @param {Object} parsed definition from `parsePath`.
* @param {Object} object to search against
* @returns {Object|Undefined} value
* @api private
*/
function _getPathValue (parsed, obj) {
var tmp = obj
, res;
for (var i = 0, l = parsed.length; i < l; i++) {
var part = parsed[i];
if (tmp) {
if ('object' === typeof part && tmp[part.p]) {
tmp = tmp[part.p][part.i];
} else {
tmp = tmp[part];
}
if (i == (l - 1)) res = tmp;
} else {
res = undefined;
}
}
return res;
};