diff --git a/src/framework/core/js/Fluid.js b/src/framework/core/js/Fluid.js index 4f4d0fc8a6..34657f98ac 100644 --- a/src/framework/core/js/Fluid.js +++ b/src/framework/core/js/Fluid.js @@ -2531,7 +2531,9 @@ var fluid = fluid || fluid_2_0_0; (error.componentSource ? " which was defined in grade " + error.componentSource : "") + " needs to be overridden with a concrete implementation"); })).join("\n"); } - that.lifecycleStatus = "constructed"; + if (that.lifecycleStatus === "constructing") { + that.lifecycleStatus = "constructed"; + } that.events.onCreate.fire(that); fluid.popActivity(); return that; diff --git a/src/framework/core/js/FluidIoC.js b/src/framework/core/js/FluidIoC.js index 41573c4f78..f3dba15c72 100644 --- a/src/framework/core/js/FluidIoC.js +++ b/src/framework/core/js/FluidIoC.js @@ -735,6 +735,8 @@ var fluid_2_0_0 = fluid_2_0_0 || {}; // distributions, collectedClearer: Managing options distributions // subcomponentLocal: Signalling local record from computeDynamicComponents to assembleCreatorArguments // dynamicLocal: Local signalling for dynamic grades + // ownScope: A hash of names to components which are in scope from this component - populated in cacheShadowGrades + // childrenScope: A hash of names to components which are in scope because they are children of this component (BELOW own ownScope in resolution order) fluid.shadowForComponent = function (component) { var instantiator = fluid.getInstantiator(component); @@ -1394,9 +1396,36 @@ var fluid_2_0_0 = fluid_2_0_0 || {}; if (shadow.subcomponentLocal) { fluid.clear(shadow.subcomponentLocal); // still need repo for event-driven dynamic components - abolish these in time } + that.lifecycleStatus = "constructed"; + fluid.assessTreeConstruction(that, shadow); fluid.popActivity(); }; + + fluid.assessTreeConstruction = function (that, shadow) { + var instantiator = fluid.globalInstantiator; + var thatStack = instantiator.getThatStack(that); + var unstableUp = fluid.find_if(thatStack, function (that) { + return that.lifecycleStatus === "constructing"; + }); + if (unstableUp) { + that.lifecycleStatus = "constructed"; + } else { + fluid.markSubtree(instantiator, that, shadow.path, "treeConstructed"); + } + }; + + fluid.markSubtree = function (instantiator, that, path, state) { + that.lifecycleStatus = state; + fluid.visitComponentChildren(that, function (child, name) { + var childPath = instantiator.composePath(path, name); + var childShadow = instantiator.idToShadow[child.id]; + var created = childShadow && childShadow.path === childPath; + if (created) { + fluid.markSubtree(instantiator, child, childPath, state); + } + }, {flat: true}); + }; /** BEGIN NEXUS METHODS **/ @@ -2296,10 +2325,10 @@ var fluid_2_0_0 = fluid_2_0_0 || {}; var localRecord = options.localRecord, context = source.expander.context, segs = source.expander.segs; var inLocal = localRecord[context] !== undefined; // somewhat hack to anticipate "fits" for FLUID-4925 - we assume that if THIS component is in construction, its reference target might be too - var component = inLocal ? localRecord[context] : fluid.resolveContext(context, options.contextThat, options.contextThat.lifecycleStatus === "constructed"); + var component = inLocal ? localRecord[context] : fluid.resolveContext(context, options.contextThat, options.contextThat.lifecycleStatus === "treeConstructed"); if (component) { var root = component; - if (inLocal || component.lifecycleStatus === "constructed") { + if (inLocal || component.lifecycleStatus === "constructed" || component.lifecycleStatus === "treeConstructed") { for (var i = 0; i < segs.length; ++ i) { root = root ? root[segs[i]] : undefined; } diff --git a/src/module/module.js b/src/module/module.js index 2e6506610c..f4b46b9db8 100644 --- a/src/module/module.js +++ b/src/module/module.js @@ -79,26 +79,22 @@ fluid.module.canonPath = function (path) { return path.replace(/\\/g, "/"); }; +fluid.module.getDirs = function () { + return fluid.getMembers(fluid.module.modules, "baseDir"); +}; + +// A suitable set of terms for interpolating module root paths into dataSource file paths +fluid.module.terms = function () { + return fluid.module.getDirs(); +}; + /** Resolve a path expression which may begin with a module reference of the form, - * say, ${module-name}, into an absolute path relative to that module, using the + * say, %moduleName, into an absolute path relative to that module, using the * database of base directories registered previously with fluid.module.register. * If the path does not begin with such a module reference, it is returned unchanged. */ fluid.module.resolvePath = function (path) { - if (path.indexOf("${") === 0) { - var ic = path.indexOf("}"); - if (ic === -1) { - fluid.fail("Malformed context path without }: ", path); - } else { - var context = path.substring(2, ic); - var record = fluid.module.modules[context]; - if (!record) { - fluid.fail("Unrecognised module " + context + ": loaded modules are " + fluid.keys(fluid.module.modules).join(", ")); - } - return record.baseDir + path.substring(ic + 1); - } - } else { - return path; - } + return fluid.stringTemplate(path, fluid.module.getDirs()); }; + diff --git a/tests/framework-tests/core/js/FluidIoCTests.js b/tests/framework-tests/core/js/FluidIoCTests.js index e0366cc89d..c4d02edaaf 100644 --- a/tests/framework-tests/core/js/FluidIoCTests.js +++ b/tests/framework-tests/core/js/FluidIoCTests.js @@ -1323,6 +1323,48 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt jqUnit.assertEquals("Resolved injected component by member name", that.chaundleTileManager, resolved); }); + /** FLUID-5818 - ginger reference from distant construct descendent of incomplete parent **/ + + fluid.defaults("fluid.tests.FLUID5818root", { + gradeNames: "fluid.component", + components: { + child1: { + type: "fluid.component", + options: { + components: { + child3: { + type: "fluid.component", + options: { + listeners: { + onCreate: { + funcName: "fluid.tests.fluid5818fetch", + args: "{child2}" + } + } + } + } + } + } + }, + child2: { + type: "fluid.component" + } + + } + }); + + fluid.tests.fluid5818fetch = function (child2) { + jqUnit.assertValue("Should have caused fetch of child of unconstructed parent", child2); + }; + + jqUnit.test("FLUID-5818 ginger reference to child of unconstructed parent", function () { + jqUnit.expect(5); + var that = fluid.tests.FLUID5818root(); + // White-box testing of lifecycle status + fluid.each([that, that.child1, that.child2, that.child1.child3], function (component) { + jqUnit.assertEquals("All components should have \"treeConstructed\" state", "treeConstructed", component.lifecycleStatus); + }); + }); /** FLUID-4135 - event injection and boiling test **/ diff --git a/tests/node-tests/basic-node-tests.js b/tests/node-tests/basic-node-tests.js index 8b77477715..3375f767c2 100644 --- a/tests/node-tests/basic-node-tests.js +++ b/tests/node-tests/basic-node-tests.js @@ -89,11 +89,11 @@ https://github.com/fluid-project/infusion/raw/master/Infusion-LICENSE.txt }); jqUnit.test("Test module resolvePath", function () { - var resolved = fluid.module.resolvePath("${infusion}/src/components/tableOfContents/html/TableOfContents.html"); + var resolved = fluid.module.resolvePath("%infusion/src/components/tableOfContents/html/TableOfContents.html"); var expected = fluid.module.canonPath(path.resolve(__dirname, "../../src/components/tableOfContents/html/TableOfContents.html")); jqUnit.assertEquals("Resolved path into infusion module", expected, resolved); - var pkg = fluid.require("${test-module}/package.json"); + var pkg = fluid.require("%test-module/package.json"); jqUnit.assertEquals("Loaded package.json via resolved path directly via fluid.require", "test-module", pkg.name); }); diff --git a/tests/test-core/utils/js/IoCTestUtils.js b/tests/test-core/utils/js/IoCTestUtils.js index b1715c7276..fd916d87da 100644 --- a/tests/test-core/utils/js/IoCTestUtils.js +++ b/tests/test-core/utils/js/IoCTestUtils.js @@ -375,7 +375,7 @@ var fluid_2_0_0 = fluid_2_0_0 || {}; namespace: fixture.namespace, priority: fixture.priority }); - id = fluid.pushDistributions(analysed.head, analysed.selector, + id = fluid.pushDistributions(analysed.head, analysed.selector, fixture.event, [{options: options, recordType: "distribution", priority: fluid.mergeRecordTypes.distribution}] ); };