File: /var/dev/nowruzgan/rest/node_modules/rttc/lib/hydrate.js
/**
* Module dependencies
*/
var _ = require('lodash');
/**
* Eval stringified functions at the top-level or within value (use `typeSchema` to know
* where to expect functions-- the "lamda" type)
*
* @param {*} value
* @param {*} typeSchema
* @return {*}
*/
module.exports = function hydrate (value, typeSchema) {
if (_.isUndefined(typeSchema)) {
throw new Error('rttc.hydrate() requires a 2nd argument (`typeSchema`).');
}
// Deserialize any lamda functions that exist in the provided input value,
// including at the top level.
//
// If this is a lamda type, or something which MIGHT contain a lamda type
// (i.e. nested array or dictionary type schema), we must recursively iterate over the
// type schema looking for lamda types, and when we find them, parse input values as
// stringified functions, converting them to hydrated JavaScript functions.
//
// But otherwise, we just go ahead and bail.
if (typeSchema !== 'lamda' &&
(!_.isObject(typeSchema) || _.isEqual(typeSchema, []) || _.isEqual(typeSchema, {}))) {
return value;
}
return (function parseLamdaInputValues(val, keysSoFar){
var typeHere = keysSoFar.length > 0 ? _.get(typeSchema, keysSoFar.join('.')) : typeSchema;
// If this is supposed to be an array or dictionary, recursively traverse the
// next leg of the type schema
//
// (note that we don't need to worry about circular refs because we've already
// ensured JSON serializability above)
if (_.isArray(typeHere)) {
// if the actual value does not have an array here as expected,
// just stop looking for lamdas this direction (there obviously aren't any,
// and it's not the job of this function to catch any validation issues)
if (!_.isArray(val)) {
return val;
}
// Special case for array generic (`[]`)
if (typeHere.length === 0) {
return val;
}
// Since a type schema array will only have one item, we must iterate over
// the actual value:
return _.reduce(val, function (memo, unused, index){
memo.push(parseLamdaInputValues(val[index], keysSoFar.concat('0') ));
return memo;
}, []);
}
else if (_.isObject(typeHere)){
// if the actual value does not have a dictionary here as expected,
// just stop looking for lamdas this direction (there obviously aren't any,
// and it's not the job of this function to catch any validation issues)
if (!_.isObject(val)) {
return val;
}
// Special case for dictionary generic (`{}`)
if (_.keys(typeHere).length === 0) {
return val;
}
return _.reduce(typeHere, function (memo, unused, subKey){
// If the key from the type schema contains `.`, then fail with an error.
if ((''+subKey).match(/\./)) {
throw new Error('Keys containing dots (`.`) are not currently supported in the type schema for `rttc.hydrate`.');
}
memo[subKey] = parseLamdaInputValues(val[subKey], keysSoFar.concat(subKey));
return memo;
}, {});
}
// If this is supposed to be a lamda, and the actual value is a string,
// parse a function out of it. If anything goes wrong, just pass the value
// through as-is.
else if (typeHere === 'lamda' && _.isString(val)) {
try {
var fn;
// If the lamda string begins with "function", then we'll assume it's a
// complete, stringified function.
if (val.match(/^\s*function/)){
eval('fn='+val);
}
// If the lamda string doesn't begin with "function", then we'll assume it
// is a function body, and build a machine `fn` out of it (assumes standard
// `fn` function signature)
else {
eval('fn=function(inputs, exits, env){'+val+'}');
}
return fn;
}
catch (e){
// Could not parse usable lamda function from provided string-
// so just pass the value through as-is.
return val;
}
}
// Otherwise, just return what we've got
return val;
})(value, []);
};