Implicit Blocks in Fusebox 5.5.1

The XML syntax used by Fusebox since the 4.0 version allows for conditional expressions like these:

<if condition="x GT 4">
<true>
  <set name="y" value="7" />
</true>
<false>
  <set name="y" value="3" />
</false>
</if>

<if condition="name EQ 'barney'">
<true>
  <set name="session.isSuperUser" value="true" />
</true>
</if>

In the latter case, the <true> tags are basically irrelevant, because they provide the same containership and semantic as the <if> tag itself.  Fusebox 5.0 and older allowed you to omit the <true> tags in this case:

<if condition="name EQ 'barney'">
  <set name="session.isSuperUser" value="true" />
</if>

This only makes sense if you don't have a <false> block, because if you have both blocks, then the <if> tag doesn't provide the correct containership.

Fusebox 5.5, however, made the <true> tag required even for the simple case. This is unfortunate, I think, because it adds needless verbosity, and from digging around in the code, I suspect that it was primarily driven by implementation limitations, rather than user requests.

As part of migrating an app from 5.0 to 5.5.1, I really didn't want to have to go back and add all those extra tags, nor deal with the extra verbosity when maintaining the application down the road.  So I wrote a small patch for the core files that will allow an implicit <true> tag in the 5.0 style when using the 5.5.1 cores:

Index: fuseboxVerb.cfc
===================================================================
--- fuseboxVerb.cfc	(revision 778)
+++ fuseboxVerb.cfc	(working copy)
@@ -33,20 +33,26 @@

 		<cfset variables.action = arguments.action />
 		<cfset variables.attributes = arguments.attributes />
+		<cfset variables.factory = factory />
 		<!--- we will create our children below --->
 		<cfset variables.verb = listLast(arguments.customVerb,".:") />
-		<cfset variables.children = structNew() />
-
-		<cfset variables.factory = factory />
-		<cfset variables.nChildren = arrayLen(arguments.children) />
-
-		<cfloop from="1" to="#variables.nChildren#" index="i">
-			<cfset verb = arguments.children[i].xmlName />
-			<cfset variables.children[i] = factory.create(verb,
-						variables.action,
-							arguments.children[i].xmlAttributes,
-								arguments.children[i].xmlChildren) />
-		</cfloop>
+
+		<cfif isStruct(arguments.children)>
+			<!--- This is an IR manipulation so we already have domain objects for children, rather than XML nodes. --->
+			<cfset variables.children = arguments.children />
+			<cfset variables.nChildren = structCount(variables.children) />
+		<cfelse>
+			<cfset variables.children = structNew() />
+			<cfset variables.nChildren = arrayLen(arguments.children) />
+
+			<cfloop from="1" to="#variables.nChildren#" index="i">
+				<cfset verb = arguments.children[i].xmlName />
+				<cfset variables.children[i] = factory.create(verb,
+							variables.action,
+								arguments.children[i].xmlAttributes,
+									arguments.children[i].xmlChildren) />
+			</cfloop>
+		</cfif>

 		<cfset variables.fb41style = listLen(arguments.customVerb,".") eq 2 />
 		<cfif variables.fb41style>
Index: verbs/if.cfm
===================================================================
--- verbs/if.cfm	(revision 778)
+++ verbs/if.cfm	(working copy)
@@ -35,13 +35,9 @@
 		// at most one <true>, at most one <false>, nothing else:
 		fb_.hasTrue = false;
 		fb_.hasFalse = false;
+		fb_.verbInfo.hasOther = false;
 		for (fb_.i = 1; fb_.i lte fb_.verbInfo.nChildren; fb_.i = fb_.i + 1) {
-			if (fb_.verbInfo.children[fb_.i].getNamespace() is not "") {
-				fb_throw("fusebox.badGrammar.illegalVerb",
-						"Illegal verb",
-						"An 'if' may contain only 'true' and 'false' verbs in fuseaction #fb_.verbInfo.circuit#.#fb_.verbInfo.fuseaction#.");
-			}
-			switch (fb_.verbInfo.children[fb_.i].getVerb()) {
+			switch (fb_.verbInfo.children[fb_.i].getNamespace() & fb_.verbInfo.children[fb_.i].getVerb()) {
 			case "true":
 				if (fb_.hasTrue) {
 					fb_throw("fusebox.badGrammar.illegalVerb",
@@ -61,10 +57,41 @@
 				}
 				break;
 			default:
+				fb_.verbInfo.hasOther = true;
+				break;
+			}
+		}
+		if (fb_.verbInfo.hasOther) {
+			if (fb_.hasTrue OR fb_.hasFalse OR fb_.verbInfo.action.getCircuit().getApplication().strictMode) {
 				fb_throw("fusebox.badGrammar.illegalVerb",
 						"Illegal verb",
 						"An 'if' may contain only 'true' and 'false' verbs in fuseaction #fb_.verbInfo.circuit#.#fb_.verbInfo.fuseaction#.");
-				break;
+			} else {
+				// only non-true/false and not strict mode, so wrap an implicit true around the children
+				// we have to do some acrobatics around the 'children' references
+				fb_.children = structNew();
+				for (fb_.i = 1; fb_.i lte fb_.verbInfo.nChildren; fb_.i = fb_.i + 1) {
+					fb_.children[fb_.i] = fb_.verbInfo.children[fb_.i];
+				}
+				structClear(fb_.verbInfo.children);
+				fb_.factory = fb_.verbInfo.action.getCircuit().getApplication().getFuseactionFactory();
+				fb_.implicitTrue = fb_.factory.create(
+						"true",
+						fb_.verbInfo.action,
+						structNew(),
+						fb_.children
+					);
+				fb_.verbInfo.children[1] = fb_.implicitTrue;
+				// now null out the remaining children
+				for (fb_.i = 2; fb_.i lte fb_.verbInfo.nChildren; fb_.i = fb_.i + 1) {
+					fb_.verbInfo.children[fb_.i] = fb_.factory.create(
+						"noop",
+						fb_.verbInfo.action,
+						structNew(),
+						arrayNew(1)
+					);
+				}
+				fb_.hasTrue = true;
 			}
 		}

Index: verbs/noop.cfm
===================================================================
--- verbs/noop.cfm	(revision 0)
+++ verbs/noop.cfm	(revision 0)
@@ -0,0 +1 @@
+<cfset fb_appendLine('<!--- no op: #fb_.verbInfo.executionMode# --->') />

The implementation is kind of hacky because the XML compiler lacks a IR transformation step, so I had to fake one.  And that was further complicated by the parallelism in the internal datastructures, necessitating the creation of a "noop" verb to use as a placeholder in certain situation.  More specifically, there isn't a way to add or remove verbs from the parse tree, only to change verbs, and implementing the functionality within that constraint required some "creative" programming.  ;)

Comments are closed.