JavaScript micro library to model trees that evaluate arbitrary code when accessing its nodes.
The tree is modeled as a plain JavaScript object where each node has an arbitrary getter function. This allows to have a representation of a tree where a subtree is generated on the fly when a node is accessed.
Let's start by doing the most simple case, the identity case:
var root = Ceibo.create({
foo: {
bar: 'baz';
}
});
console.log(root.foo.bar); // "baz"
You can create special node types called descriptors that allow you to respond to node access:
var root = Ceibo.create({
foo: {
isDescriptor: true,
get() {
return 'bar';
}
}
});
console.log(root.foo); // "bar"
As you can see, a descriptor is a JavaScript object that has a isDescriptor
attribute.
You can define a get
method or you can declare a value
attribute, then the
value attribute is going to be used as is:
var root = Ceibo.create({
foo: {
isDescriptor: true,
value(answer) {
return `The answer to life, the universe and everything is ${answer}`;
}
}
});
console.log(root.foo('42')); // "The answer to life, the universe and everything is 42"
descriptors can inspect and mutate the target object by defining a setup
method:
var tree = Ceibo.create({
foo: {
isDescriptor: true,
get() {
return 'bar';
},
setup(target, keyName) {
Ceibo.defineProperty(target, keyName.toUpperCase(), 'generated property');
}
}
});
console.log(tree.foo); // "bar"
console.log(tree.FOO); // "generated property"
Note that Ceibo trees are read-only, so you cannot reassign attributes:
var root = Ceibo.create({
foo: 'bar';
});
root.foo = 'baz'; // => throws an error!
You can redefine how each value type is processed when the Ceibo tree is created:
function buildString(node, blueprintKey, value, defaultBuilder) {
return defaultBuilder(node, blueprintKey, `Cuack ${value}`);
}
var root = Ceibo.create(
{
foo: 'first value'
},
{
builder: {
string: buildString
}
}
);
console.log(root.foo); // "Cuack first value"
Redefine how plain objects are processed to generate custom attributes:
function buildObject(node, blueprintKey, blueprint /*, defaultBuilder */) {
var value = {
generatedProperty: 'generated property'
};
// define current key and assign the new object
Ceibo.defineProperty(node, blueprintKey, value);
// continue to build the tree recursively
return [value, blueprint];
}
var root = Ceibo.create(
{
foo: {
bar: 'baz'
}
},
{
builder: {
object: buildObject
}
}
);
console.log(tree.generatedProperty); // "generated property"
console.log(tree.foo.generatedProperty); // "generated property"
console.log(tree.foo.bar); // "baz"
You can navigate to parent nodes
var tree = Ceibo.create({
foo: {
bar: {
baz: 'a value'
}
}
});
console.log(Ceibo.parent(tree.foo.bar).bar.baz); // "a value"
You can assign custom parents to trees
var parentTree = Ceibo.create({ foo: 'value' });
var childTree = Ceibo.create({ bar: 'another value' }, { parent: parentTree });
console.log(Ceibo.parent(childTree).foo); // "value"
Descriptor's get
function receive the key
when evaluated
var descriptor = {
isDescriptor: true,
get: function(key) {
return key;
}
};
var root = Ceibo.create({
foo: descriptor,
bar: descriptor
});
console.log(root.foo); // "foo"
console.log(root.bar); // "bar"
Ceibo's nodes store some meta data, you can access said meta data using
Ceibo.meta
function.
var descriptor = {
isDescriptor: true,
get: function(key) {
var keys = [key];
var node = this;
var meta;
do {
meta = Ceibo.meta(node);
keys.unshift(meta.key);
} while(node = Ceibo.parent(node));
return keys;
}
};
var tree = Ceibo.create({
foo: {
bar: {
baz: {
qux: descriptor
}
}
}
});
console.log(tree.foo.bar.baz.qux); // ['root', 'foo', 'bar', 'baz', 'qux']
Ceibo is licensed under the MIT license.
See LICENSE for the full license text.