FLUID-6708: Sourceless relay rule with non-local target causes failure

Metadata

Source
FLUID-6708
Type
Bug
Priority
Major
Status
Open
Resolution
N/A
Assignee
Antranig Basman
Reporter
Antranig Basman
Created
2022-01-06T09:20:48.698-0500
Updated
2022-02-03T10:40:27.817-0500
Versions
  1. 4.0
Fixed Versions
  1. 5.0
Component
  1. Data Binder

Description

A sourceless relay rule whose target is not the current component causes a failure in fluid.materialiseModelPath. Example was

fluid.defaults("hortis.sunburst", {
....
        tabs: {
            type: "hortis.bagatelleTabs",
            options: {
                modelRelay: {
                   sunburstVisible: {
                        target: "{sunburst}.model.visible",
                        func: tab => tab === "sunburst",
                        args: "selectedTab"
                    }           
                }
            }
        },

This is sent into fluid.registerDirectChangeRelay which seems to have a somewhat sloppy test on line 831:

fluid.registerDirectChangeRelay = function (target, targetSegs, source, sourceSegs, linkId, transducer, options, npOptions) {
        var targetApplier = options.targetApplier || target.applier; // first branch implies the target is a relay document
        var sourceApplier = options.sourceApplier || source.applier; // first branch implies the source is a relay document - listener will be transactional
        if (!options.targetApplier) {
            fluid.materialiseModelPath(target, targetSegs);
        }
        if (!options.sourceApplier) {
            fluid.materialiseModelPath(source, sourceSegs);
        }

This then gives

TypeError: Cannot read property 'length' of null

in fluid.matchMaterialiserSpec line 683 since there are no source segments

fluid.matchMaterialiserSpec = function (record, segs) {
        var trundle = record;
        var routedPath = null;
        for (var i = 0; i < segs.length; ++i) {

Comments

  • Antranig Basman commented 2022-01-06T09:41:19.425-0500

    As it turns out the triggering condition is not the non-local source, but the local target. The failure is triggered one level higher up in fluid.parseModelRelay:

    if (transformPackage.refCount === 0) { // There were no implicit relay elements found in the relay document - it can be relayed directly
                    // Case i): Bind changes emitted from the relay ends to each other, synchronously
                    fluid.connectModelRelay(parsedSource.that || that, parsedSource.modelSegs, parsedTarget.that, parsedTarget.modelSegs,
                    // Primarily, here, we want to get rid of "update" which is what signals to connectModelRelay that this is a invalidatable relay
                        fluid.filterKeys(transformPackage, ["forwardAdapter", "backwardAdapter", "namespace", "priority"]));
                } else {
                    if (parsedSource.modelSegs && !shortSingleTransform) {
                        fluid.fail("Error in model relay definition: If a relay transform has a model dependency, you can not specify a \"source\" entry - please instead enter this as \"input\" in the transform specification. Definition was ", mrrec, " for component ", that);
                    }
                    // Case ii): Binds changes emitted from the relay document itself onto the relay ends (using the "half-transactional system")
                    fluid.connectModelRelay(that, null, parsedTarget.that, parsedTarget.modelSegs, transformPackage);
                }
    

    Putting a non-local reference in target causes us to take branch 2 which then doesn't bomb on a sourceless transform. All seems rather dodgy - of course our model of "relay documents are relay sources" is a deep crock.