Skip to content
Will Webberley edited this page Jul 27, 2017 · 6 revisions

Transforms

Transforms were introduced in CENode 3.0.13.

Transforms are a feature of CENode that allow instances to change their own properties based on certain conditions. They are similar to Rules, but are slightly more complex and flexible.

Overview

At the core of a transform is the transform function, which is a short JavaScript snippet that is evaluated in order to update a particular instance. Individual instances can use an instance of transform in order to implement it. Usually, a specific transform instance is specific to another type of instance, though this doesn't have to be the case.

A single instance can use multiple transforms, which are executed "in order", allowing you to chain transforms. This is useful if the input to a particular function is dependent on the output of another.

Transforms are useful if you sometimes find you are writing lots of repetitive CE or if rules aren't suitable for your particular use case.

Security and context

Transforms makes use of JavaScript's eval function. In many scenarios, using eval can be dangerous.

As such, when running on the server, CENode sandboxes the environment to only the direct properties of the instance the transform is applied to (which is accessible through the this keyword). Additionally, individual functions must complete within 30ms, require()s are not permitted, and the code is run in a separate VM to the main program.

On a web browser, the function is again limited to the scope of the instance (which, again, is accessible through this) and browser "sandboxing" (particularly in browsers such as Chrome and Firefox) further increases the security.

Due to some security concerns around use of eval, transforms are disabled by default in new CENode instances. However, they can be enabled at any time after node instantiation by running:

node.transformEngine.enable();

Using the transform and transformer concepts

The transform and transformer concepts (in the core CENode model) make it easy to use transforms in your own implementations.

In this example, we will have a person concept which has two values we explicitly define: first name and last name. We can use a transform to automatically generate a full name when one of the first or last name is updated.

Before starting we need to make sure transforms are enabled on our CENode instance:

node.transformEngine.enable();

First, we declare the person concept and specify that it is a transformer (allowing it to use transforms without needing to explicitly declare it):

conceptualise a ~ person ~ P that is a transformer and has the value V as ~ first name ~ and has the value W as ~ last name ~ and has the value X as ~ full name ~

Instead of extending transformer, we could directly add ... and ~ uses ~ the transform T ... to the person concept. It is up to you.

Now we can create the transform that instances of person can use:

there is a transform named t1 that has 'full name' as output and has 'this.first_name + " " + this.last_name' as transform function

And finally we can instantiate a new person to see the effect:

there is a person named p1 that has Jane as first name and has Smith as last name and uses the transform t1

Now, if we inspect the p1 instance, we can verify the transform worked:

node.instances.p1.full_name
// "Jane Smith"

Now imagine we update Jane's last name:

the person p1 has Jones as last name

If we again inspect the p1 instance, we can verify that the full name attribute was re-evaluated:

node.instances.p1.full_name
// "Jane Jones"

Using a transform policy

Directly extending the transformer concept is useful in one-off situations, but can be a pain if you want to create lots of instances of the same type that all use the same transform - since it'd involve attaching the transform individually to each instance you create.

An alternative is to use a transform policy, which leverages CENode's agent to add the appropriate transforms on your behalf, meaning that you only need to declare the use of a transform at the concept level, rather than on individual instances.

In this example, we will use a transform to generate a human-readable date property on instances of timestamp. In the core CENode model, the timestamp concept already inherits from transformer, and so there isn't much more work for us to do.

Firstly, we need to make sure that there is an agent active on our node instance so that it is able to observe and act on the policy, and that transforms are enabled:

node.attachAgent();
node.transformEngine.enable();

Next we instantiate a new transform instance that will be applied to all of our timestamp instances:

there is a transform named t1 that has date as output and has 'new Date(parseInt(this.name))' as transform function

And finally we create a transform policy, which instructs the agent to apply transform t1 to all instances of type timestamp:

there is a transform policy named p1 that has timestamp as transform type and has true as enabled and uses the transform t1

Now, if we generate a timestamp as usual:

there is a timestamp named 715263762315

We can see that our transform policy, together with the transform itself, is able to generate us a nice-looking date representing the timestamp:

node.getInstanceByName('715263762315').date
// Mon Aug 31 1992 13:22:42 GMT+0100 (BST)

Clone this wiki locally