Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at NCDevCon 2011 (Raleigh, NC) with:

Randomly Executing Only One ColdFusion Custom Tag Child

By Ben Nadel on
Tags: ColdFusion

Last night, I gave a presentation on Advanced ColdFusion Custom Tags to the New York City ColdFusion User Group. I was hoping to have some time at the end to build a ColdFusion custom tag demonstration together, but unfortunately, we ran short on time. The demonstration, however, does cover a very interesting custom tag work flow, so I thought I would post here.

The problem that I wanted to solve with nested custom tags was randomly executing one of the child tags. Generally, when I want to randomly execute a chunk of ColdFusion code, I put it into a CFSwitch statement:

  • <cfswitch expression="#RandRange( 1, 4 )#">
  •  
  • <cfcase value="1">
  • (1) Hey there, how are you?
  • </cfcase>
  •  
  • <cfcase value="2">
  • (2) It's so nice to see you!
  • </cfcase>
  •  
  • <cfcase value="3">
  • (3) I'm sorry, I can't recall your name?
  • </cfcase>
  •  
  • <cfcase value="4">
  • (4) How's your mother doing?
  • </cfcase>
  •  
  • </cfswitch>

As you can see, this randomly selects a number and then executes the case statement with the given value. If you've never done this before, this might seems like a perfect solution. But, anyone who's ever had to maintain something like over time knows that this seemingly simple tag setup can be extremely frustrating for a number of reasons. First off, every time you add a case, you have to remember to update the RandRange() statement. Second, every time you add a case, you have to make sure that the case value is unique. And third, which is the worst case scenario, if you want to remove a case, you have to shift all of the sibling case values so that there are no holes in the possible value set produced by the RandRange() function.

To better solve this problem, ideally what we want to create is a switch-like statement that doesn't have an expression or any case values; you simply provide the case statements and the switch statement will randomly execute one of them. This way, adding and removing cases becomes painless.

Often times, when building ColdFusion custom tags, I will write out the calling code first so that I can get a sense of how I want custom tags to function. In a way, this is like using Interface Driven Architecture to design your custom tag API:

  • <!--- Import the random switch tags. --->
  • <cfimport prefix="random" taglib="./" />
  •  
  •  
  • <!--- Select one of the following cases randomly. --->
  • <random:switch>
  •  
  • <random:case>
  • (1) Hey there, how are you?
  • </random:case>
  •  
  • <random:case>
  • (2) It's so nice to see you!
  • </random:case>
  •  
  • <random:case>
  • (3) I'm sorry, I can't recall your name?
  • </random:case>
  •  
  • <random:case>
  • (4) How's your mother doing?
  • </random:case>
  •  
  • </random:switch>

As you can see, there is no switch expression and there are no case values; I merely create the switch parent and then define as many child cases as I want.

At first, this seems like a really simple problem - just randomly execute one of the child tags. But, due to the execution work flow of ColdFusion custom tags, this is more complicated than it appears. See, when a parent custom tag starts executing, it doesn't know anything about its child tags. The parent tag can only know about its child tags once they have already executed. But in our case, that's too late - if the child tags have already executed, then we can't randomly execute only one of them.

So, how do we solve this problem? We can't do it in one pass for the reasons mentioned above. What we have to do is pass over the child tags twice - once to get the child tag count and once to execute the randomly selected child (while bypassing all others).

 
 
 
 
 
 
Passing Over A Set Of Nested ColdFusion Custom Tags Twice - Once To Count Them Up, Once To Execute A Randomly Selected Child. 
 
 
 

To get this kind of behavior, the parent tag and the child tags have to really work nicely together. And more than that, the child tag has to listen to and comply with the parent tag. The child tag doesn't have a lot of behavior, so I figured I would write it first (again, using an Interface Driven Architecture for my API). Here's what I came up with:

Case.cfm

  • <!--- Get the parent tag reference. --->
  • <cfset VARIABLES.SwitchTag = GetBaseTagData( "cf_switch" ) />
  •  
  • <!--- Check to see if child should execute. --->
  • <cfif NOT VARIABLES.SwitchTag.ChildShouldExecute()>
  •  
  • <!---
  • The switch tag said not to execute, so exit out of
  • this tag before any code in the body can execute.
  • --->
  • <cfexit method="exittag" />
  •  
  • </cfif>

The child tag (case) is really simple. All it needs to do is get a reference to its parent tag (switch) and ask the parent tag if it should be executing. If not, the child tag simply exits out, preventing any of its body to execute. In using this architecture, the child tag is completely hidden from any work flow concerns. All it ever needs to know is whether or not it should execute and who to ask to get that information.

The parent tag, on the other hand, is still short but significantly more complicated. It has to work over two different passes: Collection, in which it counts the children but doesn't let any of them execute, and Execution, in which it only allows the randomly selected child to execute.

To get from one phase to the other (Collection to Execution), we use the CFExit tag with the method Loop. This allows the end mode of the switch tag to jump back up to the tag body and re-execute all of the child tags. We then use a user defined function (UDF) within the parent tag, ChildShouldExecute(), to count the children on the first pass and then to allow only the targeted child to execute on the second pass.

Switch.cfm

  • <cffunction
  • name="ChildShouldExecute"
  • access="public"
  • returntype="boolean"
  • output="false"
  • hint="I expect to be called by a child tag and I return a boolean as to whether the given tag should execute.">
  •  
  • <!--- Define the local scope. --->
  • <cfset var LOCAL = {} />
  •  
  • <!--- Get the switch tag context. --->
  • <cfset var CONTEXT = GetBaseTagData( "cf_switch" ) />
  •  
  • <!---
  • Check to see which mode we are in. If we are collection,
  • then we only want the count and we don't want any child
  • tags to execute.
  • --->
  • <cfif (CONTEXT.SwitchMode EQ "Collection")>
  •  
  • <!--- Collection mode. --->
  •  
  • <!--- Increment the child tag count. --->
  • <cfset CONTEXT.ChildCount++ />
  •  
  • <!---
  • Return false since we don't want any child tags to
  • execute at this point.
  • --->
  • <cfreturn false />
  •  
  • <cfelse>
  •  
  • <!--- Execution mode. --->
  •  
  • <!---
  • Now that we're in the execution mode, we have to keep
  • track of the number of tags that call this function.
  • We want to return false unless the given tag is at the
  • target index.
  • --->
  •  
  • <!--- Increment child index. --->
  • <cfset CONTEXT.ChildIndex++ />
  •  
  • <!---
  • Check to see if the current index matches the
  • target index.
  • --->
  • <cfif (CONTEXT.ChildIndex EQ CONTEXT.TargetIndex)>
  •  
  • <!--- This is the correct tag. Let it execute. --->
  • <cfreturn true />
  •  
  • <cfelse>
  •  
  • <!--- This is not the target tag. Do not execute. --->
  • <cfreturn false />
  •  
  • </cfif>
  •  
  • </cfif>
  • </cffunction>
  •  
  •  
  • <!--- ----------------------------------------------------- --->
  • <!--- ----------------------------------------------------- --->
  •  
  •  
  • <!--- Check to see which mode we are executing. --->
  • <cfswitch expression="#THISTAG.ExecutionMode#">
  •  
  • <cfcase value="Start">
  •  
  • <!---
  • In the start mode, we don't yet know how many
  • children we have. Therefore, we have to do one pass
  • over the child tags to gether the count before we
  • actually execute any of them.
  • --->
  • <cfset VARIABLES.SwitchMode = "Collection" />
  •  
  • <!--- Keep a counter of the number of tags. --->
  • <cfset VARIABLES.ChildCount = 0 />
  •  
  • <!---
  • Keep an index of the child tag that is running. This
  • will only come into play on the second pass when we
  • know which target index we want to execute.
  • --->
  • <cfset VARIABLES.ChildIndex = 0 />
  •  
  • <!---
  • Keep a variable for the target child tag to execute.
  • This will only be relevant on the second pass once
  • we've counted the child tags.
  • --->
  • <cfset VARIABLES.TargetIndex = 0 />
  •  
  • </cfcase>
  •  
  •  
  • <!--- ------------------------------------------------- --->
  •  
  •  
  • <cfcase value="End">
  •  
  • <!---
  • At this point, we have either finished collecting the
  • child tags OR we have actually executed on. We only
  • need to take action if the current mode is Collection;
  • otherwise, just let the tag exit.
  • --->
  • <cfif (VARIABLES.SwitchMode EQ "Collection")>
  •  
  • <!--- Select a random child based on the count. --->
  • <cfset VARIABLES.TargetIndex = RandRange(
  • 1,
  • VARIABLES.ChildCount
  • ) />
  •  
  • <!---
  • Change the mode to be execution rather than
  • collection. This will signal to the child tags
  • that its time to execute.
  • --->
  • <cfset VARIABLES.SwitchMode = "Execution" />
  •  
  • <!---
  • Loop back to body to allow one of the child tags
  • a chance to execute.
  • --->
  • <cfexit method="loop" />
  •  
  • </cfif>
  •  
  •  
  • <!---
  • ASSERT: If we've gotten here, we are in execution
  • mode and there's nothing left to do. Just let the
  • tag exit naturally.
  • --->
  •  
  • </cfcase>
  •  
  • </cfswitch>

As you can see in the ChildShouldExecute() method, when the parent tag's mode is, "Collection," the method always returns False as none of the child tags should execute. On the second pass (Execution mode), the method return true only when the invoking child's index is equal to that of the randomly selected target index.

Now, when I run the demo code a few times, I get the following output:

(2) It's so nice to see you!
(3) I'm sorry, I can't recall your name?
(1) Hey there, how are you?
(4) How's your mother doing?
(1) Hey there, how are you?
(2) It's so nice to see you!
(2) It's so nice to see you!
(3) I'm sorry, I can't recall your name?

As you can see, each one of the child case tags is randomly executed with each page request. And, it does so without having any expression or case values to evaluate. To me, not only is this the right solution for the problem, it's also a really exciting exploration of the ColdFusion custom tag work flow.




Reader Comments

Interesting! One small comment. Not saying this is better - but - you may want to consider using cfassociate as a way for the child to 'inform' the parent of its existence. The parent can then check the arrayLen. It's probably the same amount of code. I just recommend it since it is the 'standard' (IMO) way for a child to share information with the parent. Of course, in this case you just want a _count_, not shared data.

Reply to this Comment

@Ray,

The problem with CFAssociate in this case is that is merely passes information back to the parent - it doesn't have any impact on execution.

Because I am passing over the child tags twice, I need to make sure that they don't execute on the first pass at all; even on the second pass, I need to make sure that only the selected child tag executes. This requires more communication than the CFAssociate provides.

Reply to this Comment

I think you misread me - or I wasn't clear. I didn't mean to ONLY use cfassociate, but to use that instead of the counter variable.

Reply to this Comment

Here's a challenge. Given a parent tag, and N child tags, make it so that the child tags execute backwards. (Or their output/operations are done backwards.)

Totally useless.... but can you do it? Hmmm? Can you?!?! ;)

Or - and this may actually not be 100% useless. Instead of executing a random child (boy, that sounds bad), execute them all, but in random order.

Reply to this Comment

<cfcomponent output="false">

<cffunction name="execute">

<cfset var local = StructNew() />

<cfset local.options = StructKeyList(this) />

<cfset local.options = ListToArray( ListDeleteAt( local.options, ListFindNoCase(local.options, "execute"))) />

<cfset local.random = RandRange(1, ArrayLen(local.options)) />

<cfinvoke component="#this#" method="#local.options[local.random]#" />

<cfreturn />
</cffunction>

<cffunction name="message1">
Hi 1!
</cffunction>

<cffunction name="message2">
Hi 2!
</cffunction>

<cffunction name="message3">
Hi 3!
</cffunction>

<cffunction name="message4">
Hi 4!
</cffunction>

<cffunction name="message5">
Hi 5!
</cffunction>

</cfcomponent>

And put this in your test file:

<cfset CreateObject("component", "Random").execute() />

Reply to this Comment

Jon, while that works, I think you don't get the point here in regards to _how_ it is written. Considering both examples, I can easily add a new random item by just writing one more child tag. In yours, I have to go into the CFC.

I mean it may give you the same result, but the API is significantly simpler in the custom tag version. (imho)

Reply to this Comment

Custom tags rock :) I think complex nested custom tags are one of the most under used features of CF. The cfassociate tag, GetBaseTagData() and GetBaseTagList() can do some pretty awesome stuff and until I wrote ColdExt I had no idea just how powerful a few custom tags could be.

The cfexit/loop tag is the one thing I haven't used yet because I haven't had a use case for re-executing child tags but I think this is a good reason for it as you've shown - conditional execution of a tag only after the number of child tags or some other meta data about them are known.

Reply to this Comment

@Jon,

I like the concept, but with what Ray said, the custom tags make the interface really easy to update.

@Justin,

Agreed - custom tags are definitely under utilized. I'm hoping to make a video of my custom tags presentation to post online.

Reply to this Comment

@Ray: I was mostly just trying to show that you could do this with a CFC, since Ben didn't think it could be done. I'm not really sure that the tag based version is any more maintainable though: in either case, you are going to have to open a file and add some text wrapped in a CF tag. With the CFC version, the logic and the actual messages can be reused in multiple locations, and the CFC can be extended, so you could remove all of the messages from Random, and create RandomEmail.cfc that extends Random but adds in random messages for Email, and RandomComment.cfc that does the same, but for blog comments. With the base case, I think a CFC is a superior solution, but I'd be hard pressed to figure out how to handle the other case you suggested (executing each of them in reverse order) without passing in some kind of guide as to which comes first.

Reply to this Comment

@Jon,

While I think your CFC concept is really nice, and the idea of extending a CFC is inviting; but, I think the tag-based version has several advantages:

1. You don't have to create a CFC for each use case. This would be like having to create a CFC instance for every time you wanted to use the CFLoop tag - yes, you could if the language required it, but how nice is it that we don't have to do that.

2. You can dynamically generate case statements at run time. Example:

<cfif SESSION.USER.IsAdmin>

. . . . <random:case>
. . . . . . . . -some admin-specific message-
. . . . </random:case>

</cfif>

Kind of a weak example, but I think you get the point.

3. Because the custom tags execute in the context of the calling page, the code within the custom tags has access to the page's variable scope. If you encapsulate this functionality inside of a CFC, you cannot access the page-scoped variables without having to pass them in. Not impossible, but again, just a slight difference that makes the tag-based version a bit more elegant / flexible in my opinion.

Reply to this Comment

@Ben, Yeah, I'd considered the scoping example when I was putting mine together, and that is one of the advantages the tag based implementation does have over CFCs. I keep thinking though that from a maintenance standpoint, if I needed to have the same set of random possibilities in more than one place, the CFC method would prove better in the long run, since you have a single point to edit.

All this is hypothetical to me though... I've never had to execute random code like this. Is this something you do often?

Reply to this Comment

@Jon,

No, certainly not something that I have to do a lot. The only times I really do use this sort of thing is when randomizing Advertisement output or any other "non-crucial" display widgets on a random order.

I don't have CMS for things like that, so it's generally choosing from static code blocks. If you were pulling from a DB, that's a totally different world and different solution :)

Reply to this Comment

Hi,

How would you use custom tags to dynamically generate form controls that may appear in one or many cflayoutarea tabs?

My admin users of the admin site that I am developing need form prototyping capabilities. For each form control they create, a Web page with a form is available to them with which they may assign:

1. an HTML name attribute value for the form control
2. a form control type (radio button, text box, list, dropdown menu, etc.)
3. a label for the form control
4. option values or autosuggest values for the form control
5. an integer that signifies the order in which the option values should appear within a SELECT control
6. an integer that signifies the order in which the form control should appear within the cflayoutarea tab
7. a name that signifies the cflayoutarea tab on which the form control should appear
8. a boolean to flag the control as inactive if the user wishes the control to be ignored when dynamically generating the form

The above values are stored in the site's database for each form control.

I am using CFSELECT and CFINPUT for their Ajax binding and I want a custom tag that automatically generates CF form controls within a four column table, with two columns for the controls' labels and two columns for the controls themselves.

Thanks!
Mike

Reply to this Comment

@Mike,

I am not sure what you are asking? You can certainly use custom tags to build out form fields, but I am not sure what the question is exactly?

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.