Frequently Under-Implemented Functions
The first overlooked function I'd add is setVariable(). This function does just what it sounds like - it creates a variable and assigns it a value. Yeah, there are loads of other ways to do this... why is setVariable() different? The name of the variable is dynamic - VERY dynamic. I say "VERY dynamic" because setting variables with dynamic names is a very common task, and more often than not simple array notation is all you need (ex: variables[myVarName] = 'foo';). In the case of setVariable() the first argument is the name of the variable - this string can be built as dynamically as need-be (the second argument is the new variable's value). This flexibility is not needed too often but is extremely useful when you begin dynamically setting values that are nested very deep inside complex variables. Specifically, I've found it to be the only elegant solution for dynamically changing values of values nested deep within an XML DOM when you can't easily determine how to create the alternative array notation needed to access the value (you don't always know the child position of every node in the path from the root node to the value you're setting).
The other often times overlooked function I wanted to mention is structAppend(). If I had a dollar for every time I saw code that loops over structure A and create a key in structure B with the same name and value, I'd have a fat wad o' cash. One of the cardinal rules of performance tuning/troubleshooting that I teach in my custom architecture classes is to suspect loops - i.e. "never loop when you don't need to, and always be extra-careful about the code you place inside the body of your loops because it's going to be repeated. In the case of developers not using structAppend(), we're talking about looping when you don't need to. Here's what the 'bad' code and the 'good' code look like side by side:
// BAD CODE for looping over the URL scope and 'copying' it's members into the form scope
for (i in url){
form[i] = url[i];
}
// GOOD CODE for looping over the URL scope and 'copying' it's members into the form scope
structAppend(form, url, true);
</cfscript>

~Brad
Tks
<cfset "CALLER.#ATTRIBUTES.ReturnVariable#" = some_value />
This is really awesome, especially in custom tag variable returns as demonstrated above. I am not sure if this can do everything that SetVariable() can do - maybe you can speak more on that.
Also, can you explain the XML dom thing a bit more? I didn't quite follow what you meant?
So the question, of course, is "what's the alternative?" - sometimes it is setVariable, but like I said in the entry - in most scenarios you can use array notation. Your code example would look like the following:
<cfset caller[attributes.returnVariable] = some_value >
Note - no more hash marks or quotes on the left side of the equals.
I'm a stickler for "pretty" code (it's easier to read and thus easier to maintain) ;)
I have used the array notation before, however, it makes me a bit uncomfortable because it seems to be a bug and violates the general rule about struct notation. Please see this blog post for a more in-depth exploration of its non-standard functionality:
http://www.bennadel.com/index.cfm?dax=blog:224.vie...
I have not been able to find more information on it, but I just don't like using array notation that has clearly been overloaded in an undocumented way (as far as I have been able to research).
As for "I would also, probably 99% of the time, not hire a candidate who did it on a code test":
Really???? Not hire someone for using a documented feature of the language? I'm gonna pretend I didn't read that part, because that just sounds very silly :)
"This function is no longer required in well-formed ColdFusion pages."
It's totally superfluous; you can use struct notation in every single case excepting local variables within UDFs, but you have to explicitly declare those for thread safety, so you wouldn't be in a position to need to do dynamic assignment anyway.
Dan and Barney:
I'm sure the CF Documentation says that setVariable() is obsolete/superfluous/etc... but you have to remember that the primary reason setVarible() was introduced was to set variables with dynamic names.... back when loads of scopes didn't expose themselves as scopes. Like I've said - you don't need it 99.99% of the time that you set variables.... but, yes - there are times when it's an EXTRAORDINARILY VALUABLE tool. Like I said, when you deal with deeply nested data it's almost essential. If you want to see a code example, let me know and I'll dig it up... like I said, it's not a frequently required thing. In the interim, here's a challenge for you that will FORCE you to use setVariable (if it doesn't -- let me know what you did!):
First: Create a fairly complex XML file - an XML file with different types of nodes nested within each other that each use attributes and/or XMLText - and make sure it has at least 3 or 4 levels of nesting.
Secaond: The challenge: create an html/CF interface (no AJAX or Flex) that let's you pick a top level node, then submit, then pick a node of the parent node that was submitted, and submit... eetc., etc. - in the real world it should let the end user drill as deep as they need/wany to - at any given level you should be able to type into a text-area and set the XML Text for the current tag. What's tricky about this is that the only way to deal with it is to pass a string from form to form that contains the string-path to where you're working. You might be thinking that you can just set a session scope variable on each form submission... but the problem is that if the XML is complex enough that won't work, as only XML Nodes and XML Node XML Attributes will create pointers. Go ahead... give it a try if you have the time :)
This is executed as part of a POST to 'simon.cfm?path=web-app/servlet/init-param/param-name' with an 'xmlText' form variable containing the new value for the node.
[cffile action="read"
file="#xmlFile#"
variable="xml" /]
[cfset xml = xmlParse(xml) /]
[cfset node = xmlSearch(xml, "/" & url.path)[1] /]
[cfset node.xmlText = form.xmlText /]
[cffile action="write"
file="#xmlFile#"
output="#toString(xml)#" /]
This was tested on Railo 3.0.0.002.
I think I'll have tot take the time over several days and post a thorough example of what I'm talkin about, as what you've posted doesn;t, as far as I can see, address the issue. What I'm challenging is to see the code that reads in the top-level nodes in an XML Object and lets the user drill down within that XML, as far as they choose but at least 3 or 4 levels deep, and then changing the XML Text for that node. It's not so easy to do.... the only "easy solution" is to track the path to the variable to edit from page to page... but in the end that will mean resolving that "path" string as the path to the variable you want to set.
The documentation is correct in stating that it's "somewhat" obsolete - for a typical direct set it is... but as things get more dynamic in deeper nestings, good luck tracking it any way other than by building a string that points to the path in question.
That string is what's passed on the URL. You just take your path string, find the node it corresponds to (shown in my example), and loop over it's children. For each child write a link that has the target node's path plus the specifier for the child in question. Click the link to drill in. Wasn't that the easy part?
I've posed my complete code at http://www.barneyb.com/simons_xml_challenge/, for you to peruse and test. Like I said before, I was using it to modify a web.xml document. That seemed to meet the complexity requirement, but if not, let me know. That's running on CF8. I had to made one slight change from the Railo version (because CF's expression parser is weak), but it works on both now.
Specifically, on ColdFusion, "/" is not a valid xmlSearch expression for some reason. When I tried the app on CF8 after deploying, I'd copied a URL that had a path specified, so never got the default case.
I think we were heading down the same path:
http://www.bennadel.com/index.cfm?dax=blog:1280.vi...
I also used XmlSearch() to find the node in question. The difference is that rather than passing the actual XPath string, I am just passing back the node indexes. I found this a bit easier to maintain and to change (esp. when jumping to the parent).
I gathered a lot from your conversation with Ben too.
On the SetVariable front; let me get your take on the best alternative for trimming form data.
I see this in the legacy code I work with all the time:
<cfloop index="f" list="#form.fieldnames#">
<cfset dummy = SetVariable("form.#f#", Trim(Evaluate("form.#f#")))>
</cfloop>
So clearly that's the old outdated method, but we've been using CF5 until recently so I can live with that. With CF8 it's now time to move forward.
From Ben's comment I see that I could do this:
<cfset "form.#f#" = Trim(Evaluate("form.#f#"))>
However it wouldn't fit your definition of pretty code.
So which of these is the best alternative in the loop:
#StructUpdate(form.fieldnames, "form.#f#", Trim(Evaluate("form.#f#")))#
Or
<cfset form[fieldnames][f] = Trim(Evaluate("form.#f#"))>
Thanks.
I got a little ahead of myself; neither of my example solutions did what I thought they were doing...
This did: <cfset form[f] = Trim(Evaluate("form[f]"))>
So my revised question:
Is that ideal or is there a better way?
Thanks
I'd like to recommend my rapidshare SE http://rapid4me.com . Will be glad, if it becomes useful to you.