Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

This new work on transformations together with model relay is described by JIRAS http://issues.fluidproject.org/browse/FLUID-3674http://issues.fluidproject.org/browse/FLUID-5045 and http://issues.fluidproject.org/browse/FLUID-5024. The current discussion (mostly arising on a call with Colin on Thursday 28th) relates to particular strategies to be used for writing Model Transformations "transform" elements that are used in linking together models - with the particular example taken from Infusion's Pager component which has motivated ChangeApplier work for a number of years. The 2010 ChangeApplier system of "guards" was largely designed with this use case in mind, but when the rewrite updating the Pager in Spring 2013 finally arrived, it was discovered that the system wasn't adequate. A brief sketch of the Pager's model:

...

Unordered List

a) Expand the transformer fully in terms of elementary transforms - with one JSON node per expression node. This is extremely verbose and not suited to the needs of those using textual representations. It may be reasonable for those using visual or non-visual tools. The transform in the listing immediately above lines 29-31 (target: "pageIndex") is in this form.

b) Adjust the definition of the base transformer with "decorators" or further options which try to customise its behaviour for more situations. This may result in a compact definition but creates a "findability" burden - the additional options need to be document and users must be able to find them - and somehow enough value added to this process so that users are recompensed for the cost of searching for the transform and its options.

c) Make as much use as possible of "ad hoc" short functions written in conventional JavaScript operating the transform. This might be the most compact option and eliminates any "findability" burden - however, it inhibits reuse and code sharing by users of the system. It is also not transparent to tools, cutting non-textual users out of the system. The transform in lines 10-17 (target: "pageCount") is in this form.

Given the general ridiculousness of the "pageIndex" transform in form a) above, we might perhaps convert it into form c) as follows:

Code Block
languagejs
titleForm c) for pageIndex guard
{
target: "pageIndex",
singleTransform: {
    type: "fluid.transform.free",
    func: "fluid.tests.limitPageIndex",
    args: [
        "{that}.model.pageIndex",
        0,
        "{that}.model.pageCount"]
    }
}
 
fluid.tests.limitPageIndex = function (input, min, max) {
    if (input < min) {
        input = min;
    } else if (input > max - 1) {
        input = max - 1;
    }
    return input;
    } 

This would be effective, and still an improvement on the "pageIndexGuard" from the old implementation (top listing) - however this may still not be best for this case - which is indeed so ubiquitous that it might better be handled by approach b) - see below.

Harmonising the extremes - trajectory for authoring tools for transformations

The stark difference between approaches a) and c) and the differing interests of the communities which might author them cut to the heart of the issues we hope to address when beginning our authoring infrastructure next year (2014). During Thursday's (30/10/13) conversation with Colin (sadly this can't be abscribed a location such as "pupusa conversation") ideas for a tooling approach emerged which might help to get alternatives a) and c) in better contact. For those using approaches c) we might draw up a limited subset of simple JS functions (perhaps, those containing no control structures, references to other functions or use of higher-order functions) for which we supply special interconversions support. This is reminiscent of other "subsetting" approaches such as Mozilla's asm.js or ECMAScript 5's "strict mode" etc. This support could be operated by the well-known Esprima parser or otherwise. For a suitably simple vocabulary of short and simple functions, we could guarantee bidirectional conversion between form a) as produced by authors using visual or non-visual tools, and form c) as produced by coders - a form c) equivalent would also improve performance considerably.

Another important reminder from this conversation was of the visual form of the authoring process - which should always "carry the test cases along with the code". That is, that the initial authoring experience would consist of supplying some paradigmatic test cases, which would then permanently accompany the implementation, acting as both documentation and test cases. The fact that this procedure is really what is followed by real developers in any case can be seen by the cryptic comment // 10 -> 1, 11 -> 2 preceding the implementation of the function fluid.pager.computePageCount. The form of its body is already relatively obscure, although the expression return Math.max(1, Math.floor((model.totalRange - 1) / model.pageSize) + 1) is relatively readable compared to some "expressions of the art" which can be seen in the more complex regular expressions.

Form b) after all for range limitation

Discussion on authoring tools notwithstanding, it was considered desirable to provide a "form b)" for the range limiter after all. After all, this is the paradigmatic "validation function" (insisting that a value lies within a particular range) and if it isn't a candidate for being part of a "standard library", then nothing is. The oddity with the handling of the upper range could be handled by supplying three extra properties to the transformation function which operates it:

(documentation for standard function to be commissioned, fluid.transform.limitRange)

argumenttypedefaultpurpose
inputNumbernone: requiredThe value to be transformed
minNumber-InfinityThe minimum of the range to which the value is to be constrained
maxNumber+InfinityThe maximum of the range to which the value is to be constrained
minExclusiveBooleanfalseIf min is specified, minExclusive may be specified to indicate that the actual minimum value itself is not a valid part of the range. A value which is within granularity (defaults to 1) of the minimum value will be mapped to min + granularity
maxExclusiveBooleanfalseIf max is specified, maxExclusive may be specified to indicate that the actual minimum value itself is not a valid part of the range. A value which is within granularity (defaults to 1) of the maximum value will be mapped to max - granularity
granularityNumber1The "buffer range" which is operated by the properties minExclusive and maxExclusive. They will in addition to excluding values beyond min and max themselves, if enabled, also exclude values which lie within granularity of the limit.