I've been doing a bunch of XML stuff over the past few days, and ran
into an interesting issue. If you have a default namespace
defined in a document that you're going to run xmlSearch on, you have
to respect that namespace. What does that mean? You need to
prefix your element names with a colon. Take these three examples
(for searching an RSS feed):
XMLSearch(rss,"/rdf:RDF/item")
XMLSearch(rss,"/rdf:RDF/:item")
XMLSearch(rss,"//:item")
The first one doesn't work, because the 'item' element belongs to
the default namespace, not to the null namespace, so it needs to be
explicitly qualified as in the second and third lines.
Much thanks to Pete Freitag and Sean Corfield (the 3rd comment) for making this solution known.
A long while ago, I wrote a utility called Fusedocer that parses a Fusebox 3 application and generated per-fuse documentation similar to the way JavaDoc works. It was quite useful at the time, but since I moved to Fusebox 4, I'd almost forgotten about it. Turns out people are still using it though, since I got an email this morning from a guy in the Netherlands saying it didn't work on CFMX. More specifically, that it wasn't detecting any of the fuses in the circuits, so it wasn't actually generating anything. He'd also isolated the problem: I was using a filter on a cfdirectory call that CFMX wasn't handling correctly.
The filter was [a-zA-Z]{3}_*.cf*, which apparently worked on CF4.5 (what I was running when I wrote the app), but no longer works on CFMX. The fix was simple, just change it to *_*.cf*, and everything started working. I've uploaded a new version of the utility to the Fusedocer page, so you can get the CFMX-compatible version if you want it.
Update: Since my server crashed last year, the /go/ URLs have been broken, and the links above haven't worked. The ZIP file can be downloaded here.
I've been working on a project that makes heavy use of metadata over
the past few weeks and just got bit hard by a little quirk with
metadata. Metadata is created on the first call to getMetaData()
for a given CFC type, and then cached for quicker subsequent
recall. That's a good thing. However, you can run into
issues with inheritance, because the metadata is only updated for the
specific CFC it was generated for. In other words, if you update
a superclass of a CFC, the CFC's metadata won't change, even though
it's behaviour might. If you're on *nix, running touch `find .
-name "*.cfc"` will take care of the problem by forcing CF to recompile
all your CFCs (and consequently regenerate their meta data).
However,
that's not the really insidious quirk. You would think that since
a superclass's metadata doesn't refresh when you change the superclass,
that the metadata originally from the superclass would be separate from
the superclass's meta data. However, that's not the case, and you
can actually modify the superclass's metadata by updating a copy of the
subclass's metadata.
Couple
caveats. First, this was demonstrated (repeatedly) on CFMX 6.1; I
have
no idea if the same gotcha exists on CF7. Second, I can't make a
simple repeatable test case out of it. It happens every time in
my app, but when I make a standalone example things works
consistently. I suspect it has something to do with the specific
order that the getMetaData calls, the copying of the metadata
structures, and the updating of the fields, but I haven't been able to
pin-point it.
Solution? Don't ever maintain a reference to any part of a
CFC's metadata. If you need to keep a registry of metadata
snippets, always use duplicate(). Adding that fixes the problem,
because duplication ensures no lingering cross-object references
persist.
Ever wanted to do a list loop inside a CFSCRIPT block? I mean,
you can already loop over an array or a structure just as easily as you
can with tags, why not a list? Well, for some reason Macromedia
decided that capability wasn't particularly important, but you can work
around it using function variables.
runningTotal = 0;
listLoop("1,2,3", loopBody);
function loopBody(item) {
runningTotal = runningTotal + item;
writeOutput("Running Total: #runningTotal#
");
}
This code will loop over a the list "1,2,3", calculating and
outputting a running total as it goes. It's not amazingly
elegant, because there isn't actually a for or while keyword, and the
loop "body" is in a separate function, but it is a simple loop over a
list in CFSCRIPT. The magic 'listLoop' function is here:
function listLoop(myList, bodyFunction) {
var a = listToArray(myList);
var i = "";
for (i = 1; i LTE arrayLen(a); i = i + 1)
bodyFunction(a[i]);
}
It's pretty simple, just a quick dynamic function call. This
type of programming is usually found in JavaScript (and related
languages) where functions are nothing more than a data type. CF
doesn't quite meet that spec, because it forces you to declare
functions explicity (unlike other variable types), and doesn't provide
a function literal syntax (along with the omitted array and structure
literal syntaxes). However, it's still an effective code trick
for certain scenarios.