--- title: Model Transformation API category: Infusion --- The **model transformation** framework is a core part of Infusion and allows you to transform an input [model](FrameworkConcepts.md#model-objects) (JSON structure) based on a set of rules. The result will be a new model, built according to the rules specified and the input model. Many kinds of transformation can also have their *inverses* computed and operated automatically. Any model transformation can be supplied as part of a [model relay](ModelRelay.md#explicit-model-relay-style) rule registered in a [fluid.modelComponent](ComponentConfigurationOptions.md#model-components)'s `modelRelay` block - the system will then constantly operate the transformation rule whenever there is a model change to keep the linked model in sync. Note that the modelRelay system does **not** support `xxxPath` style inputs, and steer towards the use of SingleTransform rather than full transforms. For more information, see the documentation on [model relay](ModelRelay.md). The user may also operate model transformation rules manually by use of the [`fluid.model.transformWithRules` API](#fluidmodeltransformwithrulessource-rules-options). ## List of available transformations Below is a list of all the available transformations in the framework. For details on each, see the individual description in the [Transformation Functions](ModelTransformationAPI.md#transformation-functions) section. * [fluid.transforms.value](ModelTransformationAPI.md#output-a-value-given-as-input-fluidtransformsvalue-and-fluidtransformsidentity) * [fluid.transforms.identity](ModelTransformationAPI.md#output-a-value-given-as-input-fluidtransformsvalue-and-fluidtransformsidentity) * [fluid.transforms.literalValue](ModelTransformationAPI.md#literal-value-fluidtransformsliteralvalue) * [fluid.transforms.stringToNumber](ModelTransformationAPI.html#fluidtransformsstringtonumber) * [fluid.transforms.numberToString](ModelTransformationAPI.html#fluidtransformsnumbertostring) * [fluid.transforms.count](ModelTransformationAPI.html#count-length-of-array-fluidtransformscount) * [fluid.transforms.round](ModelTransformationAPI.html#round-a-floating-point-number-fluidtransformsround) * [fluid.transforms.delete](ModelTransformationAPI.html#delete-path-from-the-output-fluidtransformsdelete) * [fluid.transforms.firstValue](ModelTransformationAPI.html#get-first-value-of-array-fluidtransformsfirstvalue) * [fluid.transforms.linearScale](ModelTransformationAPI.html#scale-value-with-optional-offset-fluidtransformslinearscale) * [fluid.transforms.binaryOp](ModelTransformationAPI.html#binary-operation-fluidtransformsbinaryop) * [fluid.transforms.condition](ModelTransformationAPI.html#conditional-transform-fluidtransformscondition) * [fluid.transforms.valueMapper](ModelTransformationAPI.html#mapping-based-on-input-value-fluidtransformsvaluemapper) * [fluid.transforms.quantize](ModelTransformationAPI.html#mapping-a-continuous-range-into-discrete-values-fluidtransformsquantize) * [fluid.transforms.inRange](ModelTransformationAPI.html#check-whether-a-number-is-in-a-given-range-fluidtransformsinrange) * [fluid.transforms.arrayToSetMembership](ModelTransformationAPI.html#fluidtransformsarraytosetmembership) * [fluid.transforms.setMembershipToArray](ModelTransformationAPI.html#fluidtransformssetmembershiptoarray) * [fluid.transforms.indexArrayByKey](ModelTransformationAPI.html#creates-an-object-indexed-with-keys-from-array-entries-fluidtransformsindexarraybykey) * [fluid.transforms.deindexIntoArrayByKey](ModelTransformationAPI.md#create-an-object-indexed-with-keys-from-array-entries-fluidtransformsdeindexintoarraybykey) * [fluid.transforms.indexOf](ModelTransformationAPI.html#get-the-index-of-an-element-in-an-array-fluidtransformsindexof) * [fluid.transforms.dereference](ModelTransformationAPI.html#get-the-value-at-an-index-of-array-fluidtransformsdereference) * [fluid.transforms.stringTemplate](ModelTransformationAPI.html#create-string-from-template-fluidtransformsstringtemplate) * [fluid.transforms.free](ModelTransformationAPI.html#use-any-globally-available-function-as-transform-fluidtransformsfree) * [fluid.transforms.stringToBoolean](ModelTransformationAPI.html#fluidtransformsstringtoboolean) * [fluid.transforms.booleanToString](ModelTransformationAPI.html#fluidtransformsbooleantostring) * [fluid.transforms.JSONstringToObject](ModelTransformationAPI.html#fluidtransformsjsonstringtoobject) * [fluid.transforms.objectToJSONString](ModelTransformationAPI.html#fluidtransformsobjecttojsonstring) * [fluid.transforms.stringToDate](ModelTransformationAPI.html#fluidtransformsstringtodate) * [fluid.transforms.dateToString](ModelTransformationAPI.html#fluidtransformsdatetostring) * [fluid.transforms.dateTimeToString](ModelTransformationAPI.html#fluidtransformsdatetimetostring) ## fluid.model.transformWithRules(source, rules[, options]) Function to manually apply transformation rules to a source model. It will return the transformed source model. * `source {Object}` The input model to transform - this will not be modified * `rules {Transform}` A transformation rules object containing instructions on how to transform the model (see [below](#structure-of-a-transformation-document) for more information) * `options {Object}` A set of options governing the kind of transformation to be operated. At present this may contain the values: * `isomorphic {Boolean}` If `true` indicating that the output model is to be governed by the same schema found in the input model, or * `flatSchema {Object: String -> String}` Holding a flat schema object which consists of a hash of EL path specifications with wildcards, to the string values "array"/"object" defining the schema to be used to construct missing trunk values. This option is not part of the official API and might be subject to change. * Returns: `{Object}` The transformed model ## Structure of a transformation document The top-level structure of a full transformation document `{Transform}` is keyed by output [EL paths](FrameworkConcepts.md#el-paths) and looks like this: ```snippet { : OR {SingleTransform}, : OR {SingleTransform} // ... } ``` A `{SingleTransform}` record is as follows: ```snippet { transform: { "type": // ... } } ``` where `transform-type` is the name of a registered transformation function. This will be a [function grade](FunctionGrades.md) derived from the base grade `fluid.transformFunction`. Each transformation function can accept arbitrary additional options on the level of the "type" key, but many are derived from the grade `fluid.standardInputTransformFunction` which accept a standard input value named `input` (or path named `inputPath`) and/or derived from the grade `fluid.standardOutputTransformFunction` which accepts an output path named `outputPath`. In general you should consult the below documentation for each transform function to see the names and interpretations of its options. Many transforms will themselves accept configuration of type `{SingleTransform}` in their options, leading transform documents to have a recursive structure. ### Reserved words The model transformation system contains some reserved words - words which have a special meaning when used as JSON keys. In general, the reserved words of the model transformation system are: | key | Reserved in | |:----|:------------| | `transform` | ***everywhere*** | | `literalValue` | ***everywhere*** | | `type` | inside all `transform` blocks | | `inputPath` | inside all `standardInputTransformFunctions` | | `input` | inside all `standardInputTransformFunctions`, `fluid.transforms.linearScale` | | `outputPath` | inside all `standardOutputTransformFunctions`, `fluid.transforms.delete`, `fluid.transforms.valueMapper` (match and nomatch directives) | | `values` | `fluid.transforms.firstValue` | | `defaultInputPath` | `fluid.transforms.valueMapper` (top level) | | `defaultOutputPath` | `fluid.transforms.valueMapper` (top level) | | `defaultOutputValue` | `fluid.transforms.valueMapper` (top level) | | `match` | `fluid.transforms.valueMapper` (top level) | | `partialMatches` | `fluid.transforms.valueMapper` (inside `match`) | | `inputValue` | `fluid.transforms.valueMapper` (inside `match`) | | `outputValue` | `fluid.transforms.valueMapper` (inside `match`/`noMatch`) | | `outputUndefinedValue` | `fluid.transforms.valueMapper` (inside `match`/`noMatch`) | | `left` | `fluid.transforms.binaryOp` | | `right` | `fluid.transforms.binaryOp` | | `operator` | `fluid.transforms.binaryOp` | | `condition` | `fluid.transforms.condition` | | `true` | `fluid.transforms.condition` | | `false` | `fluid.transforms.condition` | | `factor` | `fluid.transforms.linearScale` | | `offset` | `fluid.transforms.linearScale` | | `ranges` | `fluid.transforms.quantize` | | `upperBound` | (inside entries of) `fluid.transforms.quantize` | | `output` | (inside entries of) `fluid.transforms.quantize` | | `min` | `fluid.transforms.inRange` | | `max` | `fluid.transforms.inRange` | | `key` | `fluid.transforms.indexArrayByKey`, `fluid.transforms.deindexIntoArrayByKey` | | `innerValue` | `fluid.transforms.indexArrayByKey`, `fluid.transforms.deindexIntoArrayByKey` | | `array` | `fluid.transforms.indexOf`, `fluid.transforms.dereference` | | `notFound` | `fluid.transforms.indexOf`, `fluid.transforms.dereference` | | `offset` | `fluid.transforms.indexOf`, `fluid.transforms.dereference` | | `template` | `fluid.transforms.stringTemplate` | | `terms` | `fluid.transforms.stringTemplate` | | `func` | `fluid.transforms.free` | | `args` | `fluid.transforms.free` | | `presentValue` | `fluid.transforms.arrayToSetMembership`, `fluid.transforms.setMembershipToArray` | | `missingValue` | `fluid.transforms.arrayToSetMembership`, `fluid.transforms.setMembershipToArray` | | `options` | `fluid.transforms.arrayToSetMembership`, `fluid.transforms.setMembershipToArray` | Besides these, most transformations have further reserved words. These are briefly listed here, with the transformation(s) they belong to. They will be more fully described for each relevant transformation. ## Grades of transformations Transformation can be registered with different grades (or types), which define how they handle inputs and outputs. The standard base grades recognized by the framework as follows: ### standardInputTransformFunction These transformations take a single input which can be defined in two ways: * `input`: As a constant or nested transform function * `inputPath`: As a path reference to the input (model) by using the key inputPath. If both keys are used in a transform declaration, the value found at the `inputPath` will be used if something is found there. If not, the transformer will default to using the value defined by `input`. More details are given on `input` and `inputPath` later in this document. ### standardOutputTransformFunction: These transformations only outputs a single value. The value of the output depends on the transformation, but the path to output can be defined by: * `outputPath`: An EL path to where the transformation should output its value to. The output path provided here is always relative to the current path. See section on building the output document structure, found further down. ### standardTransformFunction: These transformations satisfy the requirements of both [`standardInputTransformFunction`](ModelTransformationAPI.html#standardinputtransformfunction) and [`standardOutputTransformFunction`](ModelTransformationAPI.html#standardoutputtransformfunction) ### multiInputTransformFunction: These transformations allow for multiple inputs. In their default blocks, besides the gradename, they require an `inputVariables` key. The value of this should be a hash of key-value pairs, where the key is the variable name and the value is the default value to assign to the variable in case no matching constant or path is found. For each of the variables declared, the system will look up the source-model path defined by Path in the transform and the declared in the transform. They will behave in the same way as `input` and `inputPath`, where the path takes priority , so the non-path value can be used as fallback if nothing found at the path. An example of a multiInputTransformFunction declaration: ```javascript fluid.defaults("fluid.transforms.weirdScale", { "gradeNames": [ "fluid.multiInputTransformFunction" ], "inputVariables": { "factor": 1, "randomSeed": 0 } }); ``` The above declaration defines two input variables; `factor` and `randomSeed`. Taking as an example the `factor`, the first thing that will be looked up in the transform is the value of `factorPath`. If it is defined, this path will be looked up in the source-model and this will be assigned to factor if found. If it is not found, the value (expanded if necessary) of the "factor" key of the transform will be used if found. If neither is found, the default value of 1 will be assigned to factor. The inputVariables will be available to fluid.transforms.weirdScale as the first argument in the form of an object keyed by variable names with the values as described above. ### Combining standardInputTransformFunction and multiInputTransformFunction: A transformation can have both the grades `standardInputTransformFunction` and `multiInputTransformFunction`. This is useful if one has a variable named `input` (or `inputPath`) and want to take advantage of the frameworks built in support for `standardInputTransformations`, but also has other inputs that need supporting. The behavior will be a combination of the `standardInputTransformFunction` and `multiInputTransformFunction`. In the below example, the transform function will have three inputs available to resolve, namely `factor`, `offset` and `input` (and their `*Path` equivalents), with the `input` not requiring to be expressed in the `inputVariables` declaration of the defaults block. The input variables will be available to the `fluid.transforms.scaleValue` function in the following way: the first argument is the `input` (as for the `standardInputTransformFunction`). The remaining variables will be passed as the second argument in the form of an object keyed by variable names with the values as described in the [multiInputTransformFunction section](ModelTransformationAPI.html#multiinputtransformfunction) above. ```javascript fluid.defaults("fluid.transforms.scaleValue", { "gradeNames": [ "fluid.standardTransformFunction", "fluid.multiInputTransformFunction" ], "inputVariables": { "factor": 1, "offset": 0 } }); ``` ## Building the output document structure The ultimate goal when using the model transformation is to create an output document with some desired structure and values (based on information in some input document). Therefore it is important to know how to build the output structure of the document. There are different ways of achieving this, and these will be explained below. ### Keys in the top-level of your document are output el-paths All keys at the top level of the transformation rules document are interpreted as output paths. Each subsequent key will also be interpreted as so; until a `transform` or `literalValue` keyword is encountered. Anything inside a `transform` directive is interpreted according to the general transformation rules (see below), and the `literalValue` keyword results in its content being output to the current output path. #### Example
sourceruleOutput
{
    "my": {
        "number": 93.56
    }
}
{
    "Magnification": {
        "dataType": {
            "literalValue": "integer"
        },
        "value": {
            "transform": {
                "type": "fluid.transforms.round",
                "inputPath": "my.number"
            }
        }
    }
}
{
    "Magnification": {
        "dataType": "integer"
        "value": 94
    }
}
In the above example, the output structure is defined via the top-level keys: `Magnification`, `dataType` and `value`. Whenever the transformation system encounters a key, that is a non-reserved word, and that is not inside a `transform` block, it will consider that a change in the output path / output structure. So walking through the above rules, the first thing we encounter is `Magnification`. Since this is not a reserved word, the system will ensure that everything within that block is output relative to the path `Magnification`. The same goes for `dataType` - everything inside here will be output relative to the path `dataType` (which in turn is relative to "Magnification", giving us the output path: `magnification.dataType`). `literalValue` inside `dataType` is a reserved word, so instead of interpreting this as a change to the output structure, it will be resolved by the model transformation system . The `literalValue` key, is a way to tell the system to literally print the value to the current output path, which as described above is at this location `Magnification.dataType`. More information about the `literalValue` keyword can be found in the below. As with `dataType`, the `value` key under `Magnification` means that we change the output path for anything in that block. `transform` is a keyword, so this does not affect the output path. As previously mentioned, once inside a `transform` block, keys will no longer be interpreted as output paths. As a result, the `type` and `input` will not affect the output path. This means that whatever the result of the rules inside the `transform` block of the `value` key block is, they will get output to `Magnification.value` path. In this case, the value 94. ### Explicitly outputting to a (relative) path An alternative way to specifying where you want to output values is by using the `outputPath` key. The value of `outputPath` defines the relative location where you want to output the result of the transform. `outputPath` can only be specified in a transform block(i.e. at the same level as the `type` specification for the transform). `outputPath` works for most transformations (namely any transform of the grade `standardOutputTransformFunction` or `standardTransformFunction`). Returning to the example in the previous section, this could be written as:
sourceruleOutput
{
    "my": {
        "number": 93.56
    }
}
{
    "transform": [
        {
            "type": "fluid.transforms.literalValue",
            "input": "integer",
            "outputPath": "Magnification.dataType"
        }, {
            "type": "fluid.transforms.round",
            "inputPath": "my.number",
            "outputPath": "Magnification.value"
        }
    ]
}
{
    "Magnification": {
        "dataType": "integer"
        "value": 94
    }
}
{
    "my": {
        "number": 93.56
    }
}
{
    "Magnification": {
        "transform": [
            {
                "type": "fluid.transforms.literalValue",
                "input": "integer",
                "outputPath": "dataType"
            }, {
                "type": "fluid.transforms.round",
                "input": "my.number",
                "outputPath": "value"
            }
        ]
    }
}
{
    "Magnification": {
        "dataType": "integer"
        "value": 94
    }
}
Looking at the first example, the `literalValue` transformation specifies that the result should be output to the path `Magnification.dataType`. Same for the `fluid.transforms.round` transform, where the output will be sent to the path `Magnification.value`. The second example above, shows how the `outputPath` will be relative to the current output path. Since the entire block is keyed by `Magnification`, this becomes the current output path. When we encounter `outputPath: dataType`, this is interpreted as being relative to the `Magnification` path, and hence result in the full output path `Magnification.dataType`. ## Return values, Arrays of transforms and outputting arrays ### Return values In general, transformations pass their result one level up, unless `outputPath` is explicitly given. This is the reason that something like the below works:
sourceruleOutput
{
    "display": {
        "magnification": 1.5412
    }
}
{
    "Magnification": {
        "transform": {
            "type": "fluid.transforms.round",
            "input": {
                "transform": {
                    "type": "fluid.transforms.linearScale",
                    "inputPath": "display.magnification",
                    "factor": 100
                }
            },
            "outputPath": "Percent"
        }
    }
}
{
    "Magnification": {
        "Percent": 154
    }
}
Here the result of the inner `fluid.transforms.linearScale` transform is passed to the `fluid.transforms.round` transform, which in turn outputs it to the `outputPath`. Whenever an `outputPath` is specified, the result is output to the output document instead, and hence not returned to the parent transform function (if any). Instead what is returned to the parent function is `undefined`. Generally, this behavior should be rather intuitive. ### Arrays of transforms Things get slightly more complicated when we introduce arrays of transforms. Whenever you specify an array of transforms (i.e. `transform: [ (...) ]`), nothing will get returned to the outer transform. The consequence of this is that if you have arrays of transforms, you are required to explicitly output values via `outputPath` if you want anything output. Some examples to clarify this rule:
sourceruleOutput
{
    "display": {
        "magnification": 1.5412
    }
}
{
    "Magnification": {
        "transform": [
            {
                "type": "fluid.transforms.literalValue",
                "input": "percent",
                "outputPath": "dataType"
            }, {
                "type": "fluid.transforms.round",
                "input": {
                    "transform": {
                        "type": "fluid.transforms.linearScale",
                        "inputPath": "display.magnification",
                        "factor": 100
                    }
                },
                "outputPath": "value"
            }
        ]
    }
}
{
    "Magnification": {
        "dataType": "percent"
        "value": 154
    }
}
{
    "display": {
        "magnification": 1.5412
    }
}
{
    "Magnification": {
        "transform": [
            {
                "type": "fluid.transforms.literalValue",
                "input": "percent"
            }, {
                "type": "fluid.transforms.round",
                "input": {
                    "transform": {
                        "type": "fluid.transforms.linearScale",
                        "inputPath": "display.magnification",
                        "factor": 100
                    }
                }
            }
        ]
    }
}
{}
This first example works because we have specified the `outputPath` for both transforms. If we omit these, as in the second example, nothing is output to the document. Note that the `fluid.transforms.linearScale` function does not have an `outputPath`. Instead its output is used as a return value passed to `fluid.transforms.round` - this is because the `linearScale` is not part of an array of transforms, but rather a regular entry in a transform. ### Outputting Arrays Outputting an array in the document is very straightforward. One simply specifies it, as one would normally in JavaScript/JSON - using `[]`. The array will be populated with the results of the content as one would expect as well. If one of the array entries has a result of `undefined` (e.g. due to an `inputPath` referencing something non-existent in the input model -- see below), this would also populate a spot in the array - to ensure that indexes of results are consistent. As an example, take the following transformation:
sourceruleOutput
{
    "petlist": {
        "cat": "Kaspar the Titanic Cat",
        "frog": "Portuguese Steve"
    }
}
{
    "my_pets": [
        {
            "transform": {
                "type": "fluid.transforms.value",
                "inputPath": "petlist.cat"
            }
        }, {
            "transform": {
                "type": "fluid.transforms.value",
                "inputPath": "petlist.goldfish"
            }
        }, {
            "transform": {
                "type": "fluid.transforms.value",
                "inputPath": "petlist.frog"
            }
        }
    ]
}
{
    "my_pets": [
        "Kaspar the Titanic Cat",
        undefined,
        "Portuguese Steve"
    ]
}
Notice the `undefined` value. This is the result of the second transform (since no value was found at the `petlist.goldfish` input path, `undefined` is returned. The difference between this and the use of arrays in the previous sections is that in the previous section the array was used as *the value for a `transform` key*. When this is not the case, an array will be interpreted as a regular array as shown in the above example. ## Static inputs and nested transforms vs. reading from input model For any `standardInputTransformFunction`, `standardTransformFunction`, `multiInputTransformFunction` and most other transformations, there are three ways of providing inputs to be used by the transformation. * **Using values from the input model:** This is done by referencing the path where the value to be used can be found. When doing this, the result of the transformation will vary depending on the input document provided to the transformation. * **Passing a static value, given directly in the rules document:** In other words, one or more of the input values for the transformation is hardcoded into the transformation rule you set up. This means that the result of the transformation in question will be the same every time. * **Nested Transformation:** One can use the output of a transformation as an input value to its parent transformation The three types will be explained in detail in the below: ### Using values from the source model: Obviously, to make model transformations useful, one needs to be able to get a different output document depending on the source model supplied to the transformations. One can read from the source model by use of the `inputPath` key in any `standardTransformFunction` or `standardInputTransformFunction`. In general, any transformation that is not of these types allow to reference the source model, usually denoted by having `Path` as part of the key name - we defer to the documentation on the individual transformations below for more details on these kinds of transforms. In this section, we will only show examples of using `inputPath`.
sourceruleOutput
{
    "petlist": {
        "cat": "Kaspar the Titanic Cat"
    }
}
{
    "my_pet": {
        "transform": {
            "type": "fluid.transforms.value",
            "inputPath": "petlist.cat"
        }
    }
}
{
    "my_pet": "Kaspar the Titanic Cat"
}
{
    "petlist": {
        "cat": "CATTOO"
    }
}
{
    "my_pet": {
        "transform": {
            "type": "fluid.transforms.value",
            "inputPath": "petlist.cat"
        }
    }
}
{
    "my_pet": "CATTOO"
}
{
    "petlist": {
        "dog": "Spot"
    }
}
{
    "my_pet": {
        "transform": {
            "type": "fluid.transforms.value",
            "inputPath": "petlist.cat"
        }
    }
}
{}
In the first two examples, you can see how, using the same transformation rules document, the output will vary based on the source model. The `inputPath` describes the el-path from where to fetch the input to the function. If no value is found in the given path, undefined will be the input to the transformation, and undefined returned. So if we have an input model without the path `petlist.cat`, like in the third example, the result of the `literalValue` transformation would be undefined, and one would receive an empty output document. ### Static Values Any `standardTransformFunction` or `standardInputTransformFunction` supports an `input` key, which can be used for passing either static values or nest transforms ([see next section](ModelTransformationAPI.html#nested-transforms)). Most other transforms support the same types of input - we defer to their individual documentation for more details. As mentioned, static values are written directly into the transformation rule document, resulting in the same input to the transform each time the model transformation is run. Defining static values to transformations is straightforward:
sourceruleOutput
{
    "dog": "Snoopy"
}
{
    "my_pet": {
        "transform": {
            "type": "fluid.transforms.literalValue",
            "input": "Kaspar the Titanic Cat"
        }
    }
}
{
    "my_pet": "Kaspar the Titanic Cat"
}
{}
{
    "my_pet": {
        "transform": {
            "type": "fluid.transforms.literalValue",
            "input": "Kaspar the Titanic Cat"
        }
    }
}
{
    "my_pet": "Kaspar the Titanic Cat"
}
As can be seen, regardless of the input model supplied to the transformation. Every time the transformation is run, the result will be the same. ### Nested Transforms Besides static values, the `input` key for `standardTransformFunction`s and `standardInputTransformFunction`s supports nested transforms (the same is the case for most other transforms, see their individual documentation). Nested transforms are transformations whose output value will be used as `input` value for its parent transformation. An `input` interprets the content as a nested transform if its value is an object containing the `transform` or `literalValue` keywords, else the value is interpreted as a static value as described above.
sourceruleOutput
{
    "display": {
        "magnification": 1.5412
    }
}
{
    "Magnification": {
        "transform": {
            "type": "fluid.transforms.round",
            "input": {
                "transform": {
                    "type": "fluid.transforms.linearScale",
                    "inputPath": "display.magnification",
                    "factor": 100
                }
            },
            "outputPath": "percent"
        }
    }
}
{
    "Magnification": {
        "percent": 154
    }
}
{
    "display": {
        "magnification": 1.5412
    }
}
{
    "Magnification": {
        "transform": {
            "type": "fluid.transforms.round",
            "input": {
                "transform": {
                    "type": "fluid.transforms.linearScale",
                    "inputPath": "display.magnification",
                    "factor": 100,
                    "outputPath": "sneakyPath"
                }
            },
            "outputPath": "percent"
        }
    }
}
{
    "Magnification": {
        "sneakyPath": 154.12
    }
}
The first example above shows the correct usage of nested transforms when one wants to use the return value. The output here from the inner transform (i.e. `fluid.transforms.linearScale`) which is 154.12 is used as `input` for the outer `fluid.transforms.round` function. The second example shows a case where `outputPath` has been specified in the inner transform. As described in a previous section, the consequence of this is that the transform result is output to the given path and `undefined` is returned. In the example, this means that the output of `fluid.transforms.linearScale` is output to the specified path `sneakyPath`. The `fluid.transforms.round` will get `undefined` as `input`, which result in outputting nothing to its outputPath. ### Using static values as default fallback value Generally, `inputPath` takes precedence over `input`. This means that if both `input` and `inputPath` are provided for a transformation, the value found in the model at `inputPath` will be used. This can be used to provide a 'default' or fallback value. As mentioned, it is possible to use both types of inputs in your rule set in the different transforms, but it is also possible to specify both for a given transformation. The effect of this is that the input from the model (via path) will be used if it is found, else the static value (or result of transform) will be used. The usefulness should be made clear by the below examples:
sourceruleOutput
{
    "petlist": {
        "cat": "Kaspar the Titanic Cat"
    }
}
{
    "my_pet": {
        "transform": {
            "type": "fluid.transforms.value",
            "inputPath": "petlist.cat",
            "input": "I have no cat"
        }
    }
}
{
    "my_pet": "Kaspar the Titanic Cat"
}
{
    "petlist": { }
}
{
    "my_pet": {
        "transform": {
            "type": "fluid.transforms.value",
            "inputPath": "petlist.cat",
            "input": "I have no cat"
        }
    }
}
{"my_pet": "I have no cat"
The transformation rule stays the same, but in the second example, there is no `cat` entry in the input model. This means that the model transformation framework will default to the value found at `input` instead. Note that the value at `input` could also be an object containing transforms. ## Inversion The framework can generate the inverse of a document where it does not use nested transforms, and to the extent that individual transforms are invertible (and have their invertible function defined in the framework). One can then use the results of a transform as input to this inverse transformation and should get the original document as output. The function used for inverting rules is: `fluid.model.transform.invertConfiguration(transformDocument)`, which takes a single input: the transformation rules that should be inverted. It outputs the inverted rules (inverted transformation rules). In practice, perfect inversion of a rule is not always possible. Unless extra information is kept, or extra information added about defaulting values, etc., the inversion/lensing will be lossy (see [FLUID-5133](https://issues.fluidproject.org/browse/FLUID-5133) ). If we call our original transformation function `F`, input document `x` and output document `y`, a general transformation is described as follows: `F(x)=y`. If we say the inverse of `F` is called `G`, we use the following vocabulary to describe different levels of invertibility: * **Losslessly Invertible:** `G(y)=G(F(x))=x` * This is in practice not always possible, since the generated `x` may be missing some entries, or have extra entries, depending on how well all the paths in the original `x` match the ones used by the transformation function `F`. * **Partly Invertible:** `F(x)=F(G(F(x)))` * In this case, there is no condition on the output of `G` alone, but it is guaranteed that one can pipe an original input model through an `F->G` sequence an unlimited number of times and always get the same model `y` as output (we say that `F(G)` is *[idempotent](https://en.wikipedia.org/wiki/Idempotence) on the image of `F`*) * **Not Invertible:** `G` **does not exist (or has not been defined in the framework)** * For non-invertible functions, there is either no logical way of deciding what an inverse would mean for that function, or there is no way to reproduce the original input document from an output document. The invertibility of each transform function will be described in connection to the individual transform function below. ## Working with non-serialisable models The framework currently permits the use of model values which cannot be serialised directly to JSON (e.g. objects of type Date, Regexp, DOM nodes or other native types) - however, this is done at the user's own risk, understanding the limitations of this usage, and bearing in mind that in future the framework may forbid such models in some contexts. When the framework encounters any such object (strictly, any object which fails the [`fluid.isPlainObject`](CoreAPI.md#fluidisplainobjecttotest-strict) test), it will treat such an object as immutable, and if it appears in a relay or transform context, its object reference will simply be copied from the source to target document/model. This implies that with respect to these objects, if they are not immutable, the source and target documents will be aliased together, and any changes made to the native object will be reflected in both documents. See the entry on [model objects](FrameworkConcepts.md#model-objects) on the [Framework Concepts](FrameworkConcepts.md) for further discussion on this topic. ## Transformation Functions: ### Output a value given as input (`fluid.transforms.value` and `fluid.transforms.identity`) **Type:** standardTransformFunction #### Description: This transform takes an input value and outputs it. When an `inputPath` is present, the value is taken from that path. Else the value found at `input` will be output (unless it is a `transform`, in which case it will be interpreted). It is primarily used by the framework in its shorthand notation (see examples). It is a synonym to `fluid.transforms.identity`. **Invertibility:** Partly invertible. The `input` default values are always ignored. #### Examples: ##### Example 1: Standard usage
sourceruleOutput
{
    "my": {
        "path": "balloon"
    }
}
{
    "myfavorite": {
        "transform": {
            "type": "fluid.transforms.value",
            "inputPath": "my.path"
        }
    }
}
{
    "myfavorite": "balloon"
}
##### Example 2: Short notation Note that this transform is implicit when using a string as a value to a key, where a `transform` directive would usually be expected.
sourceruleOutput
{
    "my": {
        "path": "balloon"
    }
}
{
    "myfavorite": "my.path"
}
{
    "myfavorite": "balloon"
}
### Literal Value (fluid.transforms.literalValue) **Type:** standardOutputTransformFunction #### Description: Returns the value given as `input`, without attempting to do further transformations or interpretations for that value. It will always produce the same value independent of the source model/document. **Invertibility:** Partly invertible. #### Syntax: ```json5 { "transform": { "type": "fluid.transforms.literalValue", "input": "some constant" } } ``` #### Examples: ##### Example 1: You can use literalValue if you have some constant that you want to output to the document
sourceruleOutput
{}
{
    "transform": {
        "type": "fluid.transforms.literalValue",
        "input": "some constant",
        "outputPath": "foo"
    }
}
{
    "foo": "some constant"
}
##### Example 2: The content of literal value will not be transformed further
sourceruleOutput
{}
{
    "transform": {
        "type": "fluid.transforms.literalValue",
        "input": {
           "transform": {
              "type": "fluid.transforms.helloworld",
              "input": "I'm not interpreted"
           }
        },
        "outputPath": "foo"
    }
}
{
    "foo": {
        "transform": {
            "type": "fluid.transforms.helloworld",
            "input": "I'm not interpreted"
        }
    }
}
### fluid.transforms.stringToNumber **Type:** standardTransformFunction #### Description: Parses a string into a number. The number can be floating point or decimal. If the string is not parseable into a number, `undefined` will be returned. **Invertibility:** Partly invertible. The `input` default values are always ignored. #### Examples: ##### Example 1: Number conversion
sourceruleOutput
{
    "my": {
        "path": "100.91"
    }
}
{
    "transform": {
        "type": "fluid.transforms.stringToNumber",
        "inputPath": "my.path",
        "outputPath": "outie"
    }
}
{
    "outie": 100.91
}
##### Example 2: Non-number string
sourceruleOutput
{
    "my": {
        "path": "i am no number"
    }
}
{
    "transform": {
        "type": "fluid.transforms.stringToNumber",
        "inputPath": "my.path",
        "outputPath": "outie"
    }
}
{}
### fluid.transforms.numberToString **Type:** standardTransformFunction #### Description: Parses a number into a string. If the input is not a number, `undefined` will be returned. Can optionally provide a `scale` which denotes the maximum number of decimal places to round the number to and a rounding `method`. Trailing 0s are omitted and numbers are rounded as follows: * `"round"`: Numbers are rounded away from 0 (i.e 0.5 -> 1, -0.5 -> -1). * `"ceil"`: Numbers are rounded up * `"floor"`: Numbers are rounded down If the `scale` value is not numerical or is `NaN`, it is treated as though it were not specified at all. The `method` is only used when a valid `scale` is provided, and defaults to `"round"`. **Invertibility:** Partly invertible. It is invertible when its domain is restricted to numbers. #### Examples: ##### Example 1: Number conversion
sourceruleOutput
{
    "my": {
        "path": 100.91
    }
}
{
    "transform": {
        "type": "fluid.transforms.numberToString",
        "inputPath": "my.path",
        "outputPath": "outie"
    }
}
{
    "outie": "100.91"
}
##### Example 2: Non-number string
sourceruleOutput
{
    "my": {
        "path": "I'm a string"
    }
}
{
    "transform": {
        "type": "fluid.transforms.stringToNumber",
        "inputPath": "my.path",
        "outputPath": "outie"
    }
}
{}
##### Example 3: Number conversion with scale
sourceruleOutput
{
    "my": {
        "path": 100.91
    }
}
{
    "transform": {
        "type": "fluid.transforms.numberToString",
        "inputPath": "my.path",
        "outputPath": "outie",
        "scale": 1
    }
}
{
    "outie": "100.9"
}
##### Example 4: Number conversion with scale and method
sourceruleOutput
{
    "my": {
        "path": 100.91
    }
}
{
    "transform": {
        "type": "fluid.transforms.numberToString",
        "inputPath": "my.path",
        "outputPath": "outie",
        "scale": 1,
        "method": "ceil"
    }
}
{
    "outie": "101"
}
##### Example 5: Number conversion with invalid scale
sourceruleOutput
{
    "my": {
        "path": 100.91
    }
}
{
    "transform": {
        "type": "fluid.transforms.numberToString",
        "inputPath": "my.path",
        "outputPath": "outie",
        "scale": "one"
    }
}
{
    "outie": "100.91"
}
### Count length of array (fluid.transforms.count) **Type:** standardTransformFunction #### Description: If the input is an array, the length of the array will be the output of this function. If the input is a primitive or object, 1 will be returned. **Invertibility:** Not invertible. #### Examples: ##### Example 1: Counting the length of an array
sourceruleOutput
{}
{
    "transform": {
        "type": "fluid.transforms.count",
        "input": [ "foo", "bar" ],
        "outputPath": "howLong"
    }
}
{
    "howLong": 2
}
##### Example 2: Using path as input
sourceruleOutput
{
    "my": {
        "path": [ "foo", "bar" ]
    }
}
{
    "transform": {
        "type": "fluid.transforms.count",
        "inputPath": "my.path",
        "outputPath": "howLong"
    }
}
{
    "howLong": 2
}
##### Example 3: Primitive value as input
sourceruleOutput
{
    "my": {
        "path": "i am a string"
    }
}
{
    "transform": {
        "type": "fluid.transforms.count",
        "inputPath": "my.path",
        "outputPath": "howLong"
    }
}
{
    "howLong": 1
}
### Round a floating point number (fluid.transforms.round) **Type:** standardTransformFunction #### Description: Rounds the input to the nearest integer, or to a decimal value if a `scale` value is provided. The `scale` option denotes the number of decimal places to round the number to and defaults to `0`. The `method` option denotes the rounding method to use and defaults to `"round"`. Numbers are rounded as follows: * `"round"`: Numbers are rounded away from 0 (i.e 0.5 -> 1, -0.5 -> -1). * `"ceil"`: Numbers are rounded up * `"floor"`: Numbers are rounded down **Invertibility:** Partly invertible. The `input` default values are always ignored. #### Examples: ##### Example 1: Round to the nearest whole number
sourceruleOutput
{
    "myin": 123.41
}
{
    "transform": {
        "type": "fluid.transforms.round",
        "inputPath": "myin",
        "outputPath": "outie"
    }
}
{
    "outie": 123
}
##### Example 2: Round to one decimal place
sourceruleOutput
{
    "myin": 123.41
}
{
    "transform": {
        "type": "fluid.transforms.round",
        "inputPath": "myin",
        "outputPath": "outie",
        "scale": 1
    }
}
{
    "outie": 123.4
}
##### Example 3: Round to one decimal place using a specified rounding method
sourceruleOutput
{
    "myin": 123.41
}
{
    "transform": {
        "type": "fluid.transforms.round",
        "inputPath": "myin",
        "outputPath": "outie",
        "scale": 1,
        "method": "ceil"
    }
}
{
    "outie": 123.5
}
### Get first value of array (fluid.transforms.firstValue) **Type:** fluid.standardOutputTransformFunction #### Description: Returns the first entry of the array that does not evaluate to undefined. The input is required to be of type array. **Invertibility:** Not invertible #### Syntax: ```snippet { "transform": { "type": "fluid.transforms.firstValue", "values": } } ``` #### Examples: ##### Example 1: Takes first entry of an array
sourceruleOutput
{
    "foo": "hello",
    "bar": "world"
}
{
    "transform": {
        "type": "fluid.transforms.firstValue",
        "values": [ "foo", "bar" ],
        "outputPath": "myfirst"
    }
}
{
    "myfirst": hello"
}
##### Example 2: Ignores undefined values
sourceruleOutput
{
    "bar": "world"
}
{
    "transform": {
        "type": "fluid.transforms.firstValue",
        "values": [ "foo", "bar" ],
        "outputPath": "myfirst"
    }
}
{
    "myfirst": world"
}
### Delete path from the output (fluid.transforms.delete) **type:** transformFunction #### Description: Deletes a path from the output document. This is useful when outputting a large structure to the output document, but you require deletion of a certain part of that structure. The only option that `delete` supports is `outputPath`, which points to the outputPath to be deleted. **Invertibility:** Not invertible #### Syntax: ```json { "transform": { "type": "fluid.transforms.delete", "outputPath": "path.to.delete" } } ``` #### Examples: The `"": ""` in the transform would normally mean that the entire input model is copied without any transformations, but the delete transform ensures that the path "foo" is deleted.
sourceruleOutput
{
    "hello": "world",
    "foo": "bar"
}
{
    "": "",
    "transform": {
        "type": "fluid.transforms.delete",
        "outputPath": "foo"
    }
}
{
     "hello": "world"
}
### Mapping based on input value (fluid.transforms.valueMapper) **type:** transformFunction #### Description: This is a very powerful and flexible transformation function, which maps a defined collection of input values to corresponding output values by applying a matching algorithm. The input can be partly or fully matched, the result of the mapping can be customized on a per match basis. For further explanation, see the syntax and examples sections below. **Invertibility:** Partly invertible. #### Syntax: ```snippet { type: "fluid.transforms.valueMapper", defaultInputPath: , defaultOutputPath: , defaultOutputValue: , input: , match: [{ partialMatches: , outputUndefinedValue: , inputValue: , outputValue: , outputPath: },{ // ... }], noMatch: { outputUndefinedValue: , outputValue: , outputPath: } }; ``` ***Top level:*** * `defaultInput` * The direct input data to use. * Will take precedence over any input path directive. * `defaultInputPath` * The input path to use. * Any value provided here will be overwritten by any `inputPath` given in the `match` directives. * This is optional if `inputPath` directives are given in each of the match directives. * `defaultOutputValue` * The value to output by default. * Used if no `outputValue` is provided for a given match. * Optional if `outputValue` is provided for all matches * The meaning of `defaultOutputValue` is NOT "the output in case no case matches" but "the `outputValue` that should be used in a case where it has not been explicitly written". * `defaultOutputPath` * The output path used by default. * Used if no `outputPath` is provided for a given case. * Optional if `outputPath` is provided for all matches. ***Within `match`/`noMatch`:*** * `outputPath` * Path to output to if this case matches. * If not provided, the `defaultOutputPath` will be used. * `outputValue` * Value to output to the `outputPath` if this case matches. * Value is interpreted as literal value unless it contains a `transforms` key. * If not provided, the `defaultOutputValue` will be used. * `inputPath` * The input path to match against. * Overwrites `defaultInputPath` for the given directive. * `outputUndefinedValue` * This will cause the match directive to return `undefined` as an output value, even if `outputValue` and `defaultOutputValue` are specified. ***Only within `match`:*** * `inputValue` * The value to check the input against. * This can also be implicitly provided as a key in the top-level of options block. * Will always be interpreted literally (ie. no transforms allowed here) * `partialMatches` * Boolean flag, signifying whether this directive is allowed to match partly. * If any exact match can be made (even if it contains a partialMatches flag), this beats a partial match. Else the best partial match (ie. deepest matching) will be selected. Else the value from `noMatch` will be used. * If the two best partial matches are equally good, the first one listed will be returned. ##### Priority of keys at parsing: Some of the keys used in the ValueMapper conflict, in that they reference the same part of the transformation mechanisms. Below is a summary of which term takes priority when the valueMapper parses the keys: * if `defaultInput` is provided, `inputPath` and `defaultInputPath` values ae ignored. * `inputPath` before `defaultInputPath` - If an `inputPath` is provided, that value will be used, else `defaultInputPath` will be used. * `outputPath` before `defaultOutputValue` - The `outputPath` will be used used if provided, else `defaultOutputPath` will. * `outputUndefinedValue` over `outputValue` over `defaultOutputValue` - If `outputUndefinedValue` is provided, it will always be used. If it is not provided, but `outputValue` is, this will be used. Finally, if neither are provided, `defaultOutputValue` is used. ##### Shorthand syntax: ValueMapper supports the shorthand syntax shown below. Here, the `` entries are interpreted as `inputValue` by the system. If `` are primitive datatypes (string, number or boolean), they will be used directly as output. Else they should be objects with the same format in the longhand syntax described above - excluding support the `inputValue` option. ```snippet { type: "fluid.transforms.valueMapper", input: , defaultInputPath: , defaultOutputPath: , defaultOutputValue: , match: { "": , "": , // ..., "": }, noMatch: { outputUndefinedValue: , outputValue: , outputPath: } }; ``` #### Examples ##### Example 1: Shorthand with primitive output values
sourceruleOutput
{
    condition: "yes"
}
{
    "transform": {
        type: "fluid.transforms.valueMapper",
        defaultInputPath: "condition",
        defaultOutputPath: "CATTOO",
        match: {
            "yes": "positiveCATT",
            "no": "negativeCATT"
        }
    }
}
{
    "CATTOO": "positiveCATT"
}
##### Example 2: Shorthand with non-primitive output values
sourceruleOutput
{
    condition: "no"
}
{
    "transform": {
        type: "fluid.transforms.valueMapper",
        defaultInputPath: "condition",
        defaultOutputPath: "defPath",
        match: {
            "yes": {
                outputPath: "myPath1",
                outputValue: "positiveCATT"
            },
            "no": {
                outputPath: "myPath1",
                outputValue: "negativeCATT"
        }
    }
}
{
    "myPath1": "negativeCATT"
}
##### Example 3: Longhand syntax with noMatch
sourceruleOutput
{
    whichAnimal: "CATTOO"
}
{
    "transform": {
        type: "fluid.transforms.valueMapper",
        defaultInputPath: "whichAnimal",
        defaultOutputValue: "selected",
        match: [
            {
                inputValue: "eagle",
                outputPath: "eagleCATT"
            }, {
                inputValue: "tiger",
                outputPath: "tigerCATT"
            }
        ],
        noMatch: {
            outputPath: "WhosThat",
            outputValue: "theNoMatchCATT"
        }
}
{
    "WhosThat": "theNoMatchCATT"
}
##### Example 4: Longhand syntax with partial Matches
sourceruleOutput
{
    info: {
        "arms": 2,
        "ears": 2
    }
}
{
    "transform": {
        type: "fluid.transforms.valueMapper",
        defaultInputPath: "info",
        defaultOutputPath: "creature",
        match: [{
            inputValue: {
                "legs": 2,
                "arms": 2,
                "veryhairy": false
            },
            partialMatches: true,
            outputValue: "human"
        }, {
            inputValue: {
                "legs": 2,
                "arms": 2
            },
            partialMatches: true,
            outputValue: "probably monkey"
        }, {
            inputValue: {
                "arms": 2
            },
            partialMatches: true,
            outputValue: "can handstand"
        }]
    }
}
{
    creature: "can handstand"
}
##### Example 5: defaultInput provided
ruleOutput
{
    "transform": {
        type: "fluid.transforms.valueMapper",
        defaultOutputPath: "creature",
        defaultInput: {
            "arms": 2,
            "ears": 2
        },
        match: [{
            inputValue: {
                "arms": 2
            },
            partialMatches: true,
            outputValue: "can handstand"
        }]
    }
}
{
    creature: "can handstand"
}
##### Example 6: Nested transformation
ruleOutput
{
    "transform": {
        type: "fluid.transforms.valueMapper",
        defaultOutputPath: "creature",
        defaultInput: {
            transform: {
                type: "fluid.transforms.identity",
                input: {
                    "arms": 2,
                    "ears": 2
                }
            }
        },
        match: [{
            inputValue: {
                "arms": 2
            },
            partialMatches: true,
            outputValue: "can handstand"
        }]
    }
}
{
    creature: "can handstand"
}
### Binary operation (fluid.transforms.binaryOp) **type:** multiInputTransformFunction, standardOutputTransformFunction #### Description: This will do a binary operation given the two operands (`left` and `right`) and the operator. You can reference to the input model for both left and right by using `leftPath` and `rightPath`. If both `rightPath` and `right` is given, a lookup will be done of `rightPath` first, and if nothing is found the constant from `right` will be used. Same goes for `left` and `leftPath`. Both the `left` and `right` operands are required (either in their path or constant form). The operator is also required. The result of the expansion will be the result of the binary operation, and this will be returned or output to the outputPath as any other `standardOutputTransformFunction`. Valid operands are: Arithmetic Operators (operands are required to be numbers, output will be a number): * **+** (Addition) Adds 2 numbers. * **-** (subtraction) subtracts 2 numbers * ***** (Multiplication) Multiplies 2 numbers. * **/** (Division) Divides 2 numbers. * **%** (Modulus) Computes the integer remainder of dividing 2 numbers. Comparison Operators: (operands are required to be numbers, output will be boolean) * **===** Returns true if the operands are equal. * **!==** Returns true if the operands are not equal. * **>** Returns true if left operand is greater than right operand. * **>=** Returns true if left operand is greater than or equal to right operand. * **<** Returns true if left operand is less than right operand. * **<=** Returns true if left operand is less than or equal to right operand. Logical Operators: (both operands are required to be booleans, output will be boolean) * **&&** (Logical AND) Returns true if both logical operands are true. Otherwise, returns false. * **||** (Logical OR) Returns true if either logical expression is true. If both are false, returns false. **Invertibility:** Not invertible. #### Syntax: ```snippet transform: { "type": "fluid.transforms.binaryOp", "left": , "right": , "operator": } ``` #### Examples ##### Example 1: Standard addition
sourceruleOutput
{
    "some": {
        "path": 200
    }
}
{
    "transform": {
        "type": "fluid.transforms.binaryOp",
        "left": 100,
        "rightPath": "some.path",
        "operator": "+",
        "outputPath": "sum"
    }
}
{
    "sum": 300
}
##### Example 2: Fallback from path
sourceruleOutput
{
    "some": {
        "path": 200
    }
}
{
    "transform": {
        "type": "fluid.transforms.binaryOp",
        "left": 100,
        "leftPath": "some.other.path",
        "right": 0,
        "rightPath": "some.path",
        "operator": "+",
        "outputPath": "sum"
    }
}
{
    "sum": 300
}
### Conditional transform (fluid.transforms.condition) **type:** multiInputTransformFunction , standardOutputTransformFunction #### Syntax: ```snippet transform: { "type": "fluid.transforms.condition", "condition": "true": , "false": } ``` #### Description: Based on the boolean `condition` constant (or the path to the inputModel `conditionPath`) either the value or `true` or `false` (`truePath`/`falsePath`, respectively) will be the result of the transform. The `condition` is required and either `true` or `false` (or both) - or their path equivalents - should be defined. As usual, the `*Path` equivalents of the input variables can be used as fallbacks. **Invertibility:** Not invertible #### Example: ##### Example 1: Standard usage
sourceruleOutput
{
    "some": {
        "path": true
    }
}
{
    "transform": {
        "type": "fluid.transforms.condition",
        "conditionPath": "some.path",
        "true": "It was true",
        "false": "It was false",
        "outputPath": "result"
    }
}
{
    "result": "It was true"
}
##### Example 2: Fallback from path
sourceruleOutput
{}
{
    "transform": {
        "type": "fluid.transforms.condition",
        "conditionPath": "some.path",
        "condition": true,
        "true": "It was true",
        "false": "It was false",
        "outputPath": "result"
    }
}
{
    "result": "It was true"
}
##### Example 3: Nested transform
sourceruleOutput
{
    "some": {
        "path": true
    }
}
{
    "transform": {
        "type": "fluid.transforms.condition",
        "conditionPath": "some.path",
        "true": {
            "transform": {
                "type": "fluid.transforms.binaryOp",
                "left": 100,
                "right": 200,
                "operator": "+"
            }
        },
        "false": "It was false",
        "outputPath": "result"
    }
}
{
    "result": 300
}
##### Example 4: Using an undefined input If either the `left` or `right` (or their path equivalents) evaluate to `undefined` and there is no default in case of using path, the result of the transform will be `undefined`.
sourceruleOutput
{
    "some": {
        "path": true
    }
}
{
    "transform": {
        "type": "fluid.transforms.condition",
        "conditionPath": "some.path",
        "truePath": "nonexistent.path",
        "false": "It was false",
        "outputPath": "result"
    }
}
{}
### Scale value with optional offset (fluid.transforms.linearScale) **type:** multiInputTransformFunction, standardInputTransformFunction and standardOutputTransformFunction. #### Description: This will scale the input value using the equation: `value * factor + offset`. If `factor` is unspecified it will be interpreted as 1 and if `offset` is unspecified it will be interpreted as 0. Both `factor` and `offset` can references to the input model by using: `factorPath` and `offsetPath`, respectively. If both the path and constant for any of these values is defined, first the path is looked up, and if a value is found it will be used. Else the system will fallback to using the constant. **Invertibility:** Partly invertible. #### Syntax: ```snippet transform: { "type": "fluid.transforms.linearScale", "input": , "factor": , "offset": } ``` #### Examples: ##### Example 1: all variables specified
sourceruleOutput
{}
{
    "transform": {
        "type": "fluid.transforms.linearScale",
        "input": 12,
        "factor": 10,
        "offset": 100,
        "outputPath": "mypath"
    }
}
{
    "mypath": 220
}
##### Example 2: Only some variables specified In this example, since no value is found at `some.path`, the factor will default to 1, outputting `12*1+0=12`. If a value was instead found (say, 12) the output would have been `12*12+0=144`
sourceruleOutput
{}
{
    "transform": {
        "type": "fluid.transforms.linearScale",
        "input": 12,
        "factorPath": "some.path",
        "outputPath": "mypath"
    }
}
{
    "mypath": 12
}
##### Example 3: Only some variables specified This example uses the same transformation rule as above, but in this case a value is found at factorPath, so this will be used as factor. Since `offset` is not specified, the value 0 will be used for this.
sourceruleOutput
{
    "some": {
        "path": 12
    }
}
{
    "transform": {
        "type": "fluid.transforms.linearScale",
        "input": 12,
        "factorPath": "some.path",
        "outputPath": "outie"
    }
}
{
    "outie": 144
}
### Mapping a continuous range into discrete values (fluid.transforms.quantize) **type:** standardTransformFunction #### Description: If you have a continuous range of values (e.g. 0...*) and want to change that into discrete values, this is the transform to use. It also works as a non-linear scale of values, as you can define what ranges maps to what outputs. The transform allows you to specify some ranges, defined by an `upperBound`. The first entry, the range will be the `upperBound` value to negative infinite. For the second entry, the range will be the range from the `upperBound` to (but excluding) the previous entry's `upperBound`. For the final entry no upper bound can be given, indicating that this range is from the previous `upperBound` to infinity. #### Syntax: ```snippet "transform": { "type": "fluid.transforms.quantize", "inputPath": , "ranges": [ { "upperBound": , "output": }, { "upperBound": , "output": }, { (...) }, { "output": } ] } ``` **Invertibility:** Not invertible. ### Examples: #### Example 1: Standard usage of quantize transformer
sourceruleOutput
{
    "my": {
        "input": 12
    }
}
{
    "transform": {
        "type": "fluid.transforms.quantize",
        "inputPath": "my.input",
        "outputPath": "mysize",
        "ranges": [
            {
                "upperBound": 11,
                "output": "small"
            }, {
                "upperBound": 13,
                "output": "normal"
            }, {
                "upperBound": 17,
                "output": "big"
            }, {
                "output": "very big"
            }
        ]
    }
}
{
    "mysize": "normal"
}
#### Example 2: Standard usage of quantize transformer - hitting lower bound
sourceruleOutput
{
    "my": {
        "input": 2
    }
}
{
    "transform": {
        "type": "fluid.transforms.quantize",
        "inputPath": "my.input",
        "outputPath": "mysize"
        "ranges": [
            {
                "upperBound": 11,
                "output": "small"
            }, {
                "upperBound": 13,
                "output": "normal"
            }, {
                "upperBound": 17,
                "output": "big"
            }, {
                "output": "very big"
            }
        ]
    }
}
{
    "mysize": "small"
}
#### Example 3: Standard usage of quantize transformer - hitting upper bound
sourceruleOutput
{
    "my": {
        "input": 200
    }
}
{
    "transform": {
        "type": "fluid.transforms.quantize",
        "inputPath": "my.input",
        "outputPath": "mysize"
        "ranges": [
            {
                "upperBound": 11,
                "output": "small"
            }, {
                "upperBound": 13,
                "output": "normal"
            }, {
                "upperBound": 17,
                "output": "big"
            }, {
                "output": "very big"
            }
        ]
    }
}
{
    "mysize": "very big"
}
### Check whether a number is in a given range (fluid.transforms.inRange) **type:** standardTransformFunction #### Description: Checks whether an `input` or `inputPath` is in a given range, outputs `true` or `false` depending on whether it is in that range. The range is indicated by `min` and `max`, both of which are inclusive. The range can be open-ended by not specifying one of these. #### Syntax: ```snippet "transform": { "type": "fluid.transforms.inRange", "inputPath": , "min": , "max": , "outputPath": } ``` **Invertibility:** Not invertible. ### Examples: #### Example 1: Standard usage of inRange transformer
sourceruleOutput
{
    "my": {
        "input": 12
    }
}
{
    "transform": {
        "type": "fluid.transforms.inRange",
        "inputPath": "my.input",
        "outputPath": "isInRange",
        "min": 10,
        "max": 100
    }
}
{
    "isInRange": true
}
#### Example 2: Standard usage of inRange transformer (out of range)
sourceruleOutput
{
    "my": {
        "input": 110
    }
}
{
    "transform": {
        "type": "fluid.transforms.inRange",
        "inputPath": "my.input",
        "outputPath": "isInRange",
        "min": 10,
        "max": 100
    }
}
{
    "isInRange": false
}
#### Example 3: Open ended usage of inRange transformer
sourceruleOutput
{
    "my": {
        "input": 110
    }
}
{
    "transform": {
        "type": "fluid.transforms.inRange",
        "inputPath": "my.input",
        "outputPath": "isInRange",
        "min": 10
    }
}
{
    "isInRange": true
}
#### Example 4: Range endpoints are inclusive
sourceruleOutput
{
    "my": {
        "input": 100
    }
}
{
    "transform": {
        "type": "fluid.transforms.inRange",
        "inputPath": "my.input",
        "outputPath": "isInRange",
        "min": 10,
        "max": 100
    }
}
{
    "isInRange": true
}
### Creates an object indexed with keys from array entries (fluid.transforms.indexArrayByKey) **type:** standardTransformFunction #### Description: This transform is one of the more specialized and complex transforms. It takes an array of objects as `input`, and outputs an object of objects. For each array entry, the value of a given key in that entry (from the `key` transform option) is used as key in the output object. The values in the output object are the remaining content of that arrays' entries. This means that the transform requires a `key` defined - and that this should be present in each of the array-entries of the `input` - and that the values found for this key will be used to key the resulting object. Besides the `key` and standard `input`/`inputPath` options, the indexArrayByKey transform allows optionally for a `innerValue`, which allows one do transforms on the values of the resulting output object. Note that within this `innerValue`, all `inputPath` (and other *Path declarations) are relative to the path defined by the `inputPath` of the indexArrayByKey transform. Note: this transform was developed in relation to the XMLSettingsHandler used by the GPII auto-personalization. This translates data from XML files (which often represents "morally indexed" data in repeating array-like constructs where the indexing key is held, for example, in an attribute) to JSON format. This transform makes it easier (possible) to reference the specific elements within one of these XML arrays that are otherwise only uniquely identifiable via their content (and not their order). **Invertibility:** Partly invertible (into [`fluid.transforms.deindexIntoArrayByKey`](ModelTransformationAPI.html#create-an-object-indexed-with-keys-from-array-entries-fluidtransformsdeindexintoarraybykey)). #### Syntax: ```json5 { "transform": { "type": "fluid.transforms.indexArrayByKey", "inputPath": "some input path pointing to an array", "key": "the variable from array to use as key", "innerValue": [ /* ...inner transforms... */ ] } } ``` #### Examples: ##### Example 1: Simple Example In this example, the `key` provided in our transform function is `product`. This means that for each entry in our input array, we will use the value found by the `product` key (i.e. "salad" and "candy", respectively) and use that as the key in our output object. The output object will contain, for each key, the remaining entries from the array entry's object.
sourceruleOutput
{
    "foo": {
        "bar": [
            { "product": "salad", "price": 10, "healthy": "yes" },
            { "product": "candy", "price": 18, "healthy": "no" }
        ]
    }
}
{
    "transform": {
        "type": "fluid.transforms.indexArrayByKey",
        "inputPath": "foo.bar",
        "key": "product",
        "outputPath": "transformed"
    }
}
{
    "transformed": {
        "salad": { price: 10, healthy: "yes" },
        "candy": { price: 18, healthy: "no" }
    }
}
##### Example 2: Using InnerValue for transforms An optional variable to the transform is `innerValue`. Any variable or transform that needs to refer to the content of the array should be declared here. The input paths within the innerValue block will be relative to the original array entry. For the below example, in the second (innermost) `inputPath`, we refer to `info.healthy`, which is relative to the path defined by our outer `inputPath`. As can be seen the transform defined within `innerValue` is applied to the value of the output object.
sourceruleOutput
{
    "foo": {
        "bar": [
            { "product": "salad", "info": { "price": 10, "healthy": "yes" }},
            { "product": "candy", "info": { "price": 18, "healthy": "no", "tasty": "yes" }}
        ]
    }
}
{
    "transform": {
        "type": "fluid.transforms.indexArrayByKey",
        "outputPath": "transformed",
        "inputPath": "foo.bar",
        "key": "product",
        "innerValue": [{
            "transform": {
                "type": "fluid.transforms.value",
                "inputPath": "info.healthy"
            }
        }]
    }
}
{
    "transformed": {
        "salad": "yes",
        "candy": "no"
    }
}
### Create an object indexed with keys from array entries (fluid.transforms.deindexIntoArrayByKey). **type:** standardTransformFunction #### Description: Transforms an object of objects into an array of objects, by deindexing the object keys. The objects within the output object, will each contain a de-indexed key as an extra item, with the key for that new item being defined by the `key` parameter. A brief illustration of this is given here: ```json { "input": { "cat": { "name": "CATTOO" }, "mouse": { "name": "MAUS" } }, "output": [ { "name": "CATTOO", "animal": "cat" }, { "name": "MAUS", "animal": "mouse" } ] } ``` In the above case, the `key` option would be `animal`. The outer key-names are added to their respective object as an entry with a key `animal`. Besides the `key` and standard `input`/`inputPath` options, the `deindexIntoArrayByKey` transform allows optionally for a `innerValue`, which allows one do transforms on the values of the resulting output object. Note that within this `innerValue`, all `inputPath` (and other `*Path` declarations) are relative to the path defined by the `inputPath` of the `deindexIntoArrayByKey` transform. Note: this transform was developed in relation to the XMLSettingsHandler used by the GPII auto-personalization. This translates data from XML files (which often represents "morally indexed" data in repeating array-like constructs where the indexing key is held, for example, in an attribute) to JSON format. This transform makes it easier (possible) to reference the specific elements within one of these XML arrays that are otherwise only uniquely identifiable via their content (and not their order). **Invertibility:** Partly invertible. (into is [`fluid.transforms.indexArrayByKey`](ModelTransformationAPI.html#creates-an-object-indexed-with-keys-from-array-entries-fluidtransformsindexarraybykey)) #### Syntax: ```json5 { transform: { "type": "fluid.transforms.deindexIntoArrayByKey", "inputPath": "some input path pointing to an object of objects", "key": "the variable to use as key for newly added entries", "innerValue": [ /* inner transforms */ ] } } ``` #### Examples: ##### Example 1: Simple Example In this example, the `key` provided in our transform function is `product`. This means that each of the keys within the object given as input, will be added as a value to the corresponding object with `product` as key.
sourceruleOutput
{
    "foo": {
        "salad": { "price": 10, "healthy": "yes" },
        "candy": { "price": 18, "healthy": "no" }
    }
}
{
    "transform": {
        "type": "fluid.transforms.deindexIntoArrayByKey",
        "inputPath": "foo",
        "outputPath": "bar",
        "key": "product"
    }
}
{bar: [
    { product: "salad", price: 10, healthy: "yes" },
    { product: "candy", price: 18, healthy: "no" }
]
##### Example 2: Using InnerValue for transforms An optional variable to the transform is `innerValue`. Any variable or transform that needs to refer to the content of the array should be declared here. The input paths within the innerValue block will be relative to the original array entry. For the below example, in the second (innermost) `inputPath`, we refer to `info.healthy`, which is relative to the path defined by our outer `inputPath`. As can be seen the transform defined within `innerValue` is applied to the value of the output object.
sourceruleOutput
{
    "foo": {
        "salad": { "price": 10, "healthy": "yes" },
        "candy": { "price": 18, "healthy": "no" }
    }
}
{
    "transform": {
        "type": "fluid.transforms.deindexIntoArrayByKey",
        "inputPath": "foo",
        "outputPath": "bar",
        "key": "product",
        "innerValue": [{
            "transform": {
                "type": "fluid.transforms.value",
                "inputPath": "",
                "outputPath": "info.healthy"
            }
        }]
    }
}
{
    "bar": [
        { product: "salad", info: { price: 10, healthy: "yes" }},
        { product: "candy", info: { price: 18, healthy: "no" }}
    ]
}
### Get the index of an element in an array (fluid.transforms.indexOf) **Type:** standardTransformFunction #### Description: Returns the index of a given element in an array. This transform checks whether the given `input`/`inputPath` is in the array provided via `array`. If it is found, the index of the element is given. If it is not found, `-1` will be returned instead. It furthermore allows for an `offset` to be provided, which will be added to the return value, and a `notFound` which will be returned in case the element is not found in the array. `notFound` is not allowed to be a positive integer, as this threatens invertibility. The `offset` will be added to the output index, even if the element is not found. Returns `undefined` if no array is provided. **Invertibility:** Fully invertible (into *[`fluid.transforms.dereference`](ModelTransformationAPI.html#get-the-value-at-an-index-of-array-fluidtransformsdereference)) *with the domain of values that are present in the array. #### Syntax: ```snippet { "type": "fluid.transforms.indexOf", "array": , "input": , "notFound": , "offset": } ``` #### Examples: ##### Example 1: standard usage
sourceruleOutput
{
    "element": "dog"
}
{
    "value": {
        "transform": {
            "type": "fluid.transforms.indexOf",
            "array": ["sheep", "dog"],
            "inputPath": "element"
        }
    }
}
{
    "value": 1
}
##### Example 2: -1 is returned if element is not found
sourceruleOutput
{
    "element": "goat"
}
{
    "value": {
        "transform": {
            "type": "fluid.transforms.indexOf",
            "array": ["sheep", "dog"],
            "inputPath": "element"
        }
    }
}
{
    "value": -1
}
##### Example 3: Usage with notFound
sourceruleOutput
{
    "element": "goat"
}
{
    "value": {
        "transform": {
            "type": "fluid.transforms.indexOf",
            "array": ["sheep", "dog"],
            "inputPath": "element",
            "notFound": "not there"
        }
    }
}
{
    "value": "not there"
}
##### Example 4: Usage with offset
sourceruleOutput
{
    "element": "dog"
}
{
    "value": {
        "transform": {
            "type": "fluid.transforms.indexOf",
            "array": ["sheep", "dog"],
            "inputPath": "element",
            "offset": 2
        }
    }
}
{
    "value": 3
}
##### Example 5: Offset added even if element is not found
sourceruleOutput
{
    "element": "goat"
}
{
    "value": {
        "transform": {
            "type": "fluid.transforms.indexOf",
            "array": ["sheep", "dog"],
            "inputPath": "element",
            "offset": 2
        }
    }
}
{
    "value": 1
}
### Get the value at an index of array (fluid.transforms.dereference) **Type:** standardTransformFunction #### Description: Returns the value of a given index in an array. This transform looks up the index given by `input`/`inputPath` in the array provided via `array`. It returns the value found at that index. It allows for an `offset` to be provided, which will be added to the index that is being looked up. **Invertibility:** Partly invertible (into [`fluid.transforms.indexOf`](ModelTransformationAPI.html#get-the-index-of-an-element-in-an-array-fluidtransformsindexof)). #### Syntax: ```snippet { "type": "fluid.transforms.dereference", "array": , "input": , "offset": } ``` #### Examples: ##### Example 1: standard usage
sourceruleOutput
{
    "element": 1
}
{
    "value": {
        "transform": {
            "type": "fluid.transforms.dereference",
            "array": ["sheep", "dog"],
            "inputPath": "element"
        }
    }
}
{
    "value": "dog"
}
##### Example 2: Usage with offset
sourceruleOutput
{
    "element": 0
}
{
    "value": {
        "transform": {
            "type": "fluid.transforms.dereference",
            "array": ["sheep", "dog"],
            "inputPath": "element",
            "offset": 1
        }
    }
}
{
    "value": "dog"
}
### Create string from template (fluid.transforms.stringTemplate) **Type:** standardOutputTransformFunction. #### Description: Simple string template system. Takes a template string (via the `template` parameter) containing tokens in the form of "%value". Returns a new string with the tokens replaced by values specified in the `terms` parameter. Keys and values can be of any data type that can be coerced into a string. Arrays will work here as well. Currently it does not support reading any of its values from the input model. Furthermore, both `template` and `terms` are read as literal values, and hence not further interpreted by the model transformation system. **Invertibility:** Not invertible. #### Syntax: ```snippet { "type": "fluid.transforms.stringTemplate", "template": , "terms": } ``` #### Examples: ##### Example 1: Usage with map of terms
sourceruleOutput
{}
{
    "value": {
        "transform": {
            "type": "fluid.transforms.stringTemplate",
            "template": "Paused at: %atFile of %totalFiles files (%atSize of %totalSize)",
            "terms": {
                "atFile": 12,
                "totalFiles": 14,
                "atSize": "100 Kb",
                "totalSize": "12000 Gb"
            },
            "outputPath": "finalstring"
        }
    }
}
{
    "finalstring": "Paused at: 12 of 14 files (100 Kb of 12000Gb)"
}
##### Example 2: Usage with array of terms
sourceruleOutput
{}
{
    "value": {
        "transform": {
            "type": "fluid.transforms.stringTemplate",
            "template": "Paused at: %0 of %1 files (%2 of %3)",
            "terms": [ 12, 14, "100 Kb", "12000 Gb" ],
            "outputPath": "finalstring"
        }
    }
}
{
    "finalstring": "Paused at: 12 of 14 files (100 Kb of 12000Gb)"
}
### Use any globally available function as transform (fluid.transforms.free) **Type:** transformFunction #### Description: Proxy transform to call any globally available function. The function to be called is passed via the `func` key, and the arguments passed are to the function are passed via the `args` key. If `args` is an array, each entry will be passed as individual arguments to the function. If `args` is an object, it will be passed to the function as a single argument which is the object - this is also true for any primitive datatype. Does not support reading any of its values from the input model, and any value passed to this transform via the `func` and `args` keys are passed into the transform as literal values (i.e. further transforms will not be parsed). **Invertibility:** Not invertible. #### Syntax: ```snippet { "type": "fluid.transforms.free", "func": , "args": } ``` #### Examples: ##### Example 1: Usage with array For the below example, imagine that the following function is globally available: ```javascript fluid.myfuncs.addThree = function (a, b, c) { return a + b + c; }; ```
sourceruleOutput
{
{
    "value": {
        "transform": {
            "type": "fluid.transforms.free",
            "func": "fluid.myfuncs.addThree",
            "args": [9, 2, 3]
            "outputPath": "result",
        }
    }
}
{
    "result": 14
}
##### Example 2: Usage with object as args In the following example, imagine you have the following two functions globally available: ```javascript fluid.myfuncs.addThree = function (a, b, c) { return a + b + c; }; fluid.myfuncs.addNumbers = function (options) { return fluid.myfuncs.addThree.apply(null, options.numbers); }; ```
sourceruleOutput
{}
{
    "value": {
        "transform": {
            "type": "fluid.transforms.free",
            "func": "fluid.myfuncs.addNumbers",
            "args": {numbers: [1, 2, 3]},
            "outputPath": "result"
        }
    }
}
{
    "result": 6
}
### fluid.transforms.arrayToSetMembership **Type:** standardTransformFunction #### Description: This transform can be used when one wants to create a set based on values available in an array. This is yet another specialized and complex transformation. Based on which values are present in the input array, an output object will be produced with predefined keys and values indicating whether a specific value was present in the array. As an example, if one has an array listing of capabilities supported by a specific system and need an object listing all possible system capabilities with a true/false value indicating whether the specific system supports it. **Invertibility:** Partly invertible (into [`setMembershipToArray`](ModelTransformationAPI.html#fluidtransformsarraytosetmembership)) #### Syntax: If `presentValue` and `missingValue` are not defined they will default to: `true` and `false`, respectively. ```snippet transform: { "type": "fluid.transforms.arrayToSetMembership", "inputPath": , "presentValue": , "missingValue": , "options": { "expectedArrayEntry1": "Key of output set 1", // ... "expectedArrayEntryN": "Key of output set N", } } ``` #### Examples: ##### Example 1: Standard Usage
sourceruleOutput
{
    "controls": [ "mouse", "keyboard" ]
}
{
    "transform": {
        "type": "fluid.transforms.arrayToSetMembership",
        "outputPath": "detections",
        "inputPath": "controls",
        "presentValue": "supported",
        "missingValue": "not supported",
        "options": {
            "mouse": "hasMouse",
            "keyboard": "hasKeyboard",
            "trackpad": "hasTrackpad",
            "headtracker": "hasHeadtracker"
        }
    }
}
{
    "detections": {
        "hasMouse": "supported",
        "hasKeyboard": "supported",
        "hasTrackpad": "not supported",
        "hasHeadtracker": "not supported"
    }
}
##### Example 2: Not defining presentValue and missingValue
sourceruleOutput
{
    "controls": [ "mouse", "keyboard" ]
}
{
    "transform": {
        "type": "fluid.transforms.arrayToSetMembership",
        "outputPath": "detections",
        "inputPath": "controls",
        "options": {
            "mouse": "hasMouse",
            "keyboard": "hasKeyboard",
            "trackpad": "hasTrackpad",
            "headtracker": "hasHeadtracker"
        }
    }
}
{
    "detections": {
        "hasMouse": true,
        "hasKeyboard": true,
        "hasTrackpad": false,
        "hasHeadtracker": false
    }
}
### fluid.transforms.setMembershipToArray **Type:** standardTransformFunction #### Description: This is inverse of `fluid.transforms.arrayToSetMembership`. This transformation was developed to accommodate a use case where a boolean list of system capabilities needed to be translated to an array containing only capabilities that were true. It takes an object with a set of keys and where the value space only consist of two values (`presentValue`, `missingValue`). This is mapped to an array that only contains entries for which the input object has a `presentValue`. The actual entries output to the array are based on a map passed as `options` to the transformation. **Invertibility:** Partly invertible (into `arrayToSetMembership`) #### Syntax: If `presentValue` and `missingValue` are not defined they will default to: `true` and `false`, respectively. ```snippet { transform: { "type": "fluid.transforms.setMembershipToArray", "inputPath": , "outputPath": "presentValue": , "missingValue": , "options": { "inputObjectKey1": "outputArrayValue1", // ... "inputObjectKeyN": "outputArrayValueN", } } } ``` #### Examples: ##### Example 1: Standard Usage
sourceruleOutput
{
    "detections": {
        "hasMouse": "supported",
        "hasKeyboard": "supported",
        "hasTrackpad": "not supported",
        "hasHeadtracker": "not supported"
    }
}
{
    "transform": {
        "type": "fluid.transforms.setMembershipToArray",
        "inputPath": "detections",
        "outputPath": "controls",
        "presentValue": "supported",
        "missingValue": "not supported",
        "options": {
            "hasMouse": "mouse",
            "hasKeyboard": "keyboard",
            "hasTrackpad": "trackpad",
            "hasHeadtracker": "headtracker"
        }
    }
}
{
    "controls": [ "mouse", "keyboard" ]
}
##### Example 2: Not defining presentValue and missingValue
sourceruleOutput
{
    "detections": {
        "hasMouse": true,
        "hasKeyboard": true,
        "hasTrackpad": false,
        "hasHeadtracker": false
    }
}
{
    "transform": {
        "type": "fluid.transforms.setMembershipToArray",
        "inputPath": "detections",
        "outputPath": "controls",
        "options": {
            "hasMouse": "mouse",
            "hasKeyboard": "keyboard",
            "hasTrackpad": "trackpad",
            "hasHeadtracker": "headtracker"
        }
    }
}
{
    "controls": [ "mouse", "keyboard" ]
}
### fluid.transforms.stringToBoolean **Type:** standardTransformFunction #### Description: Convert a String to a Boolean, for example, when working with HTML checkbox form element values. The following are all false: undefined, null, "", "0", "false", false, 0. Everything else is true. **Invertibility:** Partly invertible via `fluid.transforms.booleanToString`. #### Syntax: ```json { "transform": { "type": "fluid.transforms.stringToBoolean", "inputPath": "checkboxElement", "outputPath": "isChecked" } } ``` #### Examples: ##### Example 1: Convert the string "true" to a boolean.
sourceruleOutput
{
    "checkboxElement": "true"
}
{
    "transform": {
        "type": "fluid.transforms.stringToBoolean",
        "inputPath": "checkboxElement",
        "outputPath": "isChecked"
    }
}
{
    "isChecked": true
}
### fluid.transforms.booleanToString **Type:** standardTransformFunction #### Description: Convert any value into a stringified boolean, i.e. either "true" or "false". Anything that evaluates to true (1, true, "non empty string", {}, etc.) returns "true". Anything else (0, false, null, etc.) returns "false". **Invertibility:** Partly invertible via `fluid.transforms.stringToBoolean`. #### Syntax: ```json { "transform": { "type": "fluid.transforms.booleanToString", "inputPath": "isChecked", "outputPath": "checkboxElement" } } ``` #### Examples: ##### Example 1: Convert a boolean false into the string "false".
sourceruleOutput
{
    "isChecked": false
}
{
    "transform": {
        "type": "fluid.transforms.booleanToString",
        "inputPath": "isChecked",
        "outputPath": "checkboxElement"
    }
}
{
    "checkboxElement": "false"
}
### fluid.transforms.JSONstringToObject **Type:** standardTransformFunction #### Description: Transform stringified JSON to an object using `JSON.parse`. Returns `undefined` if the JSON string is invalid. **Invertibility:** Partly invertible via `fluid.transforms.objectToJSONString`. #### Syntax: ```json { "transform": { "type": "fluid.transforms.JSONstringToObject", "inputPath": "", "outputPath": "string" } } ``` #### Examples: ##### Example 1: Convert a stringified JSON payload into an object.
sourceruleOutput
{
    "string": "{ \"foo\": \"bar\" }"
}
{
    "transform": {
        "type": "fluid.transforms.JSONstringToObject",
        "inputPath": "string",
        "outputPath": "object"
    }
}
{
    "object": {
        "foo": "bar"
    }
}
### fluid.transforms.objectToJSONString **Type:** standardTransformFunction #### Description: Transform an object to a string using `JSON.stringify`. **Invertibility:** Partly invertible via `fluid.transforms.JSONstringToObject`. #### Syntax: ```json { "transform": { "type": "fluid.transforms.objectToJSONString", "inputPath": "", "outputPath": "string" } } ``` #### Examples: ##### Example 1: Convert an object payload into a string payload.
sourceruleOutput
{
    "object": {
        "foo": "bar"
    }
}
{
    "transform": {
        "type": "fluid.transforms.objectToJSONString",
        "inputPath": "object",
        "outputPath": "string"
    }
}
{
    "string": "{\"foo\":\"bar\"}"
}
##### Example 2: Add spaces and carriage returns to the stringified output. You can pass the `space` option to control whether spaces are included between keys and values in the string output. A positive value also results in carriage returns between key/value pairs. The default value for `space` is 0, which disables spacing and line breaks.
sourceruleOutput
{
    "object": {
        "foo": "bar"
    }
}
{
    "transform": {
        "type": "fluid.transforms.objectToJSONString",
        "inputPath": "object",
        "outputPath": "string",
        "space": 2
    }
}
{
    "string": "{\n  \"foo\":  \"bar\"\n}"
}
### fluid.transforms.stringToDate **Type:** standardTransformFunction #### Description: Transform a string to a date using the Date constructor. Accepts (among other things) the date and dateTime values returned by HTML5 date and dateTime inputs. This function allows you to create Date objects from an ISO-8601 string such as `2017-01-23T08:51:25.891Z`. It is intended to provide a consistent mechanism for recreating Date objects stored as strings. Note that framework does not generally recommend that non-serialisable values such as Date objects are stored in models, although this can be done at your own risk. See the section on [working with non-serialisable models](#working-with-non-serialisable-models) for more information. **Invertibility:** Partly invertible using either `fluid.transforms.dateTimeToString` or `fluid.transforms.dateToString`. #### Syntax: ```json { "transform": { "type": "fluid.transforms.stringToDate", "inputPath": "", "outputPath": "string" } } ``` #### Examples: ##### Example 1: Convert an date string into a date.
sourceruleOutput
{
    "string": "2016-11-23T15:28:19.052Z"
}
{
    "transform": {
        "type": "fluid.transforms.stringToDate",
        "inputPath": "string",
        "outputPath": "date"
    }
}
{
    "date": // A javascript Date object
}
### fluid.transforms.dateToString **Type:** standardTransformFunction #### Description: Transform a Date object into a date string using its toISOString method. Strips the "time" portion away to produce date strings that are suitable for use with both HTML5 "date" inputs and JSON Schema "date" format string validation, for example: `2016-11-23`. If you wish to preserve the time, use `fluid.transforms.dateTimeToString` instead. A non-date object will be treated as `undefined`. Note that framework does not generally recommend that non-serialisable values such as Date objects are stored in models, although this can be done at your own risk. See the section on [working with non-serialisable models](#working-with-non-serialisable-models) for more information. **Invertibility:** Partly invertible via `fluid.transforms.stringToDate`. #### Syntax: ```json { "transform": { "type": "fluid.transforms.dateToString", "inputPath": "date", "outputPath": "string" } } ``` #### Examples: ##### Example 1: Convert a Date object into a string.
sourceruleOutput
{
    "date": // A javascript Date object
}
{
    "transform": {
        "type": "fluid.transforms.dateToString",
        "inputPath": "object",
        "outputPath": "string"
    }
}
{
    "string": "2016-11-23"
}
### fluid.transforms.dateTimeToString **Type:** standardTransformFunction #### Description: Transform a Date object into a date/time string using its toISOString method. Results in date strings that are suitable for use with both HTML5 "dateTime" inputs and JSON Schema "date-time" format string validation, for example: `2016-11-23T13:05:24.079Z`. A non-date object will be treated as `undefined`. Note that framework does not generally recommend that non-serialisable values such as Date objects are stored in models, although this can be done at your own risk. See the section on [working with non-serialisable models](#working-with-non-serialisable-models) for more information. **Invertibility:** Partly invertible via `fluid.transforms.stringToDate`. #### Syntax: ```json { "transform": { "type": "fluid.transforms.dateTimeToString", "inputPath": "dateTime", "outputPath": "string" } } ``` #### Examples: ##### Example 1: Convert a Date object into a string.
sourceruleOutput
{
    "date": // A javascript Date object
}
{
    "transform": {
        "type": "fluid.transforms.dateTimeToString",
        "inputPath": "object",
        "outputPath": "string"
    }
}
{
    "string": "2016-11-23T15:28:19.052Z"
}