falcor
Version:
A JavaScript library for efficient data fetching.
139 lines (123 loc) • 4.13 kB
JavaScript
var followReference = require("./../get/followReference");
var clone = require("./../get/util/clone");
var isExpired = require("./../get/util/isExpired");
var promote = require("./../lru/promote");
var $ref = require("./../types/ref");
var $atom = require("./../types/atom");
var $error = require("./../types/error");
module.exports = function getValueSync(model, simplePath, noClone) {
var root = model._root.cache;
var len = simplePath.length;
var optimizedPath = [];
var shorted = false, shouldShort = false;
var depth = 0;
var key, i, next = root, curr = root, out = root, type, ref, refNode;
var found = true;
var expired = false;
while (next && depth < len) {
key = simplePath[depth++];
if (key !== null) {
next = curr[key];
optimizedPath[optimizedPath.length] = key;
}
if (!next) {
out = undefined;
shorted = true;
found = false;
break;
}
type = next.$type;
// A materialized item. There is nothing to deref to.
if (type === $atom && next.value === undefined) {
out = undefined;
found = false;
shorted = depth < len;
break;
}
// Up to the last key we follow references, ensure that they are not
// expired either.
if (depth < len) {
if (type === $ref) {
// If the reference is expired then we need to set expired to
// true.
if (isExpired(next)) {
expired = true;
out = undefined;
break;
}
ref = followReference(model, root, root, next, next.value);
refNode = ref[0];
// The next node is also set to undefined because nothing
// could be found, this reference points to nothing, so
// nothing must be returned.
if (!refNode) {
out = void 0;
next = void 0;
found = false;
break;
}
type = refNode.$type;
next = refNode;
optimizedPath = ref[1].slice(0);
}
if (type) {
break;
}
}
// If there is a value, then we have great success, else, report an undefined.
else {
out = next;
}
curr = next;
}
if (depth < len && !expired) {
// Unfortunately, if all that follows are nulls, then we have not shorted.
for (i = depth; i < len; ++i) {
if (simplePath[depth] !== null) {
shouldShort = true;
break;
}
}
// if we should short or report value. Values are reported on nulls.
if (shouldShort) {
shorted = true;
out = void 0;
} else {
out = next;
}
for (i = depth; i < len; ++i) {
if (simplePath[i] !== null) {
optimizedPath[optimizedPath.length] = simplePath[i];
}
}
}
// promotes if not expired
if (out && type) {
if (isExpired(out)) {
out = void 0;
} else {
promote(model._root, out);
}
}
// if (out && out.$type === $error && !model._treatErrorsAsValues) {
if (out && type === $error && !model._treatErrorsAsValues) {
/* eslint-disable no-throw-literal */
throw {
path: depth === len ? simplePath : simplePath.slice(0, depth),
value: out.value
};
/* eslint-enable no-throw-literal */
} else if (out && model._boxed) {
out = Boolean(type) && !noClone ? clone(out) : out;
} else if (!out && model._materialized) {
out = {$type: $atom};
} else if (out) {
out = out.value;
}
return {
value: out,
shorted: shorted,
optimizedPath: optimizedPath,
found: found
};
};