Interesting ColdFusion CFQuery "Feature"

And just to be clear, when I say "feature" I actually mean "completely broken behaviour," though for a change of pace it's not with the compiler.  Probably.  Hopefully.

Consider this innocent-seeming code:

<cfquery datasource="dsn_A" name="">
  insert into test (s) values (
    'dsn_A - record 1'
  )
</cfquery>

Every CF app has this (or equivalent) in a million places.  Now let's change it slightly to make the string we're inserting dynamic (I'll show the 'getMsg' UDF in a moment):

<cfquery datasource="dsn_A" name="">
  insert into test (s) values (
    'dsn_A - record 2 #getMsg()#'
  )
</cfquery>

So far so good.  Now here's the UDF:

<cffunction name="getMsg">
  <cfset var get = "" />
  <cfquery datasource="dsn_A" name="get">
    select count(*) as c
    from test
  </cfquery>
  <cfreturn "(#get.c# records in dsn_A)" />
</cffunction>

What would you expect this to do?  Return a string, of course, containing the recordcount of the 'test' table in "dsn_A", and it does this perfectly.  It also has the glorious side effect of changing the outer CFQUERY's datasource to "dsn_A".  To put that another way, a given query does NOT execute with the datasource passed to the opening CFQUERY tag.  Rather, it executes with the datasource passed to the last CFQUERY tag before the closing CFQUERY tag. Don't believe me?  Here's a test case:

<cffunction name="getMsg">
  <cfset var get = "" />
  <cfquery datasource="dsn_A" name="get">
    select count(*) as c
    from test
  </cfquery>
  <cfreturn "(#get.c# records in dsn_A)" />
</cffunction>

<cfquery datasource="dsn_A" name="">
  insert into test (s) values (
    'dsn_A - record 1'
  )
</cfquery>
<cfquery datasource="dsn_B" name="">
  insert into test (s) values (
    'dsn_B - record 1'
  )
</cfquery>

<cfquery datasource="dsn_A" name="get">
  select * from test
</cfquery>
<!--- one record, as you'd expect --->
<cfdump var="#get#" label="dsn_A" />

<cfquery datasource="dsn_B" name="get">
  select * from test
</cfquery>
<!--- one record, as you'd expect --->
<cfdump var="#get#" label="dsn_B" />

<cfquery datasource="dsn_A" name="">
  insert into test (s) values (
    'dsn_A - record 2 #getMsg()#'
  )
</cfquery>
<cfquery datasource="dsn_B" name="">
  insert into test (s) values (
    'dsn_B - record 2 #getMsg()#'
  )
</cfquery>

<cfquery datasource="dsn_A" name="get">
  select * from test
</cfquery>
<!--- three records!! (the two for dsn_A, plus dsn_B's second record) --->
<cfdump var="#get#" label="dsn_A" />

<cfquery datasource="dsn_B" name="get">
  select * from test
</cfquery>
<!--- one record!! (only dsn_B's first record) --->
<cfdump var="#get#" label="dsn_B" />

In order to run it, you'll need to two DSNs configured ("dsn_A" and "dsn_B"), pointed at two distinct databases, each with a 'test' table containing a single varchar column named 's'.

This was tested on CF8 and CF9, with and without CFQUERYPARAM.  I also tested with implicit and explicit datasources on CF9.  Same behaviour in all cases.  Wheeee…

Fusebox's Noose

From an email Sean Corfield sent to the Fusebox5 mailing list (http://tech.groups.yahoo.com/group/fusebox5/message/4566):

I just wanted to provide a brief update on [Fusebox and 4CFF]. 4CFF discussed Fusebox with TeraTech (specifically John Zhu of 4CFF and Michael Smith of TeraTech) and were unable to reach an agreement on Fusebox joining 4CFF. One particular sticking point was that Michael wanted TeraTech to be paid for the Fusebox copyright and name.

This is exceptionally disappointing news.  Fusebox is an ancient web framework, regardless of what language you use, and has been under TeraTech's control for only a brief portion of that lifespan.  The vast majority of development and building of community happened before TeraTech took the reins, and even under TeraTech's "management", most contributions have come from outside their company.

Hal Helms (who helped shepherd Fusebox for many years) and John Quarto-von Tivadar (who provided both the architecture and initial implementation of Fusebox's current compiled model) held the rights to the Fusebox name and IP (via The Fusebox Corporation) for many years, and turned it over to TeraTech in 2006.  Since that time, Sean has developed and released Fusebox 5.5 and 5.5.1, and little else has happened.  This is not an exemplary track record for TeraTech.

In the past month I have made some significant optimizations inside the 5.5.1 cores to reduce memory consumption and speed the compiler.  These efforts were partially sponsored by a third party, have been donated to TeraTech on behalf of myself and the sponsor, and are slated to be released as Fusebox 5.6 sometime this year.  I also helped John with the 4.0 architecture, wrote a significant portion of the 4.5 release, and contributed much of the 'cf' lexicon, along with various smaller bits and pieces along the way.  In addition to the core files themselves, I have been a member of Team Fusebox for a number of years, and an active member in the community for much longer.  To my knowledge, I have not received official credit for these contributions aside from source comments within the 'cf' lexicon verbs and being listed on the Team Fusebox web page.

As someone who has invested significantly in Fusebox over the years, this refusal of TeraTech to contribute the name and IP to 4CFF over the issue of money is a slap in the face.  I know Fusebox has helped innumerable developers over the year (myself included), and that is more than compensation enough for the contributions I've made, but to see TeraTech have the gall to demand payment for what I (and many others) have freely donated is very disappointing.

I sincerely hope that Michael and TeraTech will reconsider their position on the issue.

WordPress 3.0

I just pulled down the latest and greatest from WordPress's SVN repository to give the new 3.0 code a whirl.  Specifically, I wanted to see what was happening around the WP.org and WPMU merge that is one of the big features of 3.0, since I run a bunch of WP.org blogs that I'd love to have multihosted, and at work we run an WPMU installation and are in the works of three more.  Having a single way to do everything is quite compelling, especially if there's a way to transparently "upgrade" from a standalone site to a multihosted one.

I was initially disappointed when I looked at the source, because the MU bits seemed to be missing, but a little more digging showed telltale signs that it was at least partially integrated.  After setting up the installation, I found a mysterious "Network" option in my "Tools" menu, clicked it, and was presented with basically the MU installer UI.  Sweet!

Submitting that resulted in a page with three simple instructions: create the blogs.dir directory, install this new wp-config.php script (in a TEXTAREA), and update .htaccess with this new config (also in a TEXTAREA).  My suspicion is that if PHP had write access it would've done all three automatically.

After following instructions and clearing my cookies (not explicitly mentioned), I had a fully functional multihosted WordPress install with the normal MU site administrator menu and everything.  This is awesome.  And just in case that was unclear, it's awesome.  The database, of course, was refactored behind the scenes.  Unfortunately it still uses the tableset-per-blog paradigm (just minus the "_1″ infix for the root blog) rather than a single tableset with foreign keys, but there's no way they could change that without having all kinds of upgrade issues for existing WPMU installations.

There are still a pile of things to update (like the per-blog permalink config shouldn't attempt to write .htaccess) to get the WP.org functionality to morph into the WPMU functionality, but for the "normal" authoring tasks it appears to be as solid as ever.  I suspect a careful administrator could, with a bit of patience, produce a security setup that would let authors have the run of their site without breaking anything even before the actual restrictions are put in place by Automattic.

In any case, very exciting to see this integration project already so far along.  WordPress.org says 3.0 is slated for March, which is crazy soon, but I'ma ll for it.  I've been waiting to upgrade a pile of WP.org blogs to a WPMU instance for a while now, and this is going to make it much easier.

Sudoku GameRulesStrategy

A couple days ago I posted about implementing a Sudoku solver in Groovy as a sort of cross-training exercise, and promised to delve into the strategies a bit more.  So here's GameRulesStrategy:

class GameRulesStrategy implements Strategy {

  static getTests() {
    [
      new Test(
        '900637100030001000107520300004810500019000820006059400002048705000200080001793006'
      )
    ]
  }

  boolean play(Board board) {
    def madePlay = false
    board.cells.findAll {
      it.isKnown()
    }.each { c ->
      c.houses.flatten().findAll {
        it != c && ! it.isKnown()
      }.each {
        madePlay = it.removeMark(c.value, this) || madePlay
      }
    }
    madePlay
  }

}

As with all strategies, it starts with test cases that exercise the strategy: in this case a single board that can be solved by application of the rules alone.  That board spec, just for reference, describes this board:

+-------+-------+-------+
| 9     | 6 3 7 | 1     |
|   3   |     1 |       |
| 1   7 | 5 2   | 3     |
+-------+-------+-------+
|     4 | 8 1   | 5     |
|   1 9 |       | 8 2   |
|     6 |   5 9 | 4     |
+-------+-------+-------+
|     2 |   4 8 | 7   5 |
|       | 2     |   8   |
|     1 | 7 9 3 |     6 |
+-------+-------+-------+

There are three things a strategy can do when asked to play: remove a mark from a cell, set a cell's value, or nothing.  It's up to the strategy to determine if doing several of them at once is appropriate or not.  And keep in mind that a cell with only one mark remaining is implicitly set to that value, so removing a mark can actually set a cell's value behind the scenes.

For GameRulesStrategy, we're only concerned with removing marks from cells, and we can do many of them in a single pass without stepping on our own toes.  We start by asking the board for all the cells, and then grabbing only the known ones.  Then we iterate over those cells, and for each one get the cell's houses (the row, column, and block the cell is in), flatten them into a single collection, and find all the other cells that aren't know.  Finally we iterate over those cells and remove the mark from each of them, keeping track of whether it actually did anything in the 'madePlay' variable.

If you're interested, this same algorithm can be implemented with for..in loops, instead of .each { } constructs, which is a technique I've used in later strategies where the closures get "in the way" of returning from the method early.  Here's the loop based implementation (minus the tests):

class GameRulesStrategy implements Strategy {

  boolean play(Board board) {
    def madePlay = false
    for (c in board.cells.findAll {
      it.isKnown()
    }) {
      for (it in c.houses.flatten().findAll {
        it != c && ! it.isKnown()
      }) {
        madePlay = it.removeMark(c.value, this) || madePlay
      }
    }
    madePlay
  }

}

If you're wondering, the loop performs marginally faster (<1%) than the iterator, but I find it to be a bit less readable, so I generally prefer the iterator approach.  If performance is paramount, Groovy is probably the wrong choice - it's forte is concise, readable code (and therefore development throughput) not CPU throughput.  People are more expensive than machines in most cases, and certainly this one.

This is a really simple strategy, but there are plenty more complicated ones to come, so stay tuned.

Subclipse 1.6's AWESOME New Commit Dialog

I just upgrade to Subclipse 1.6 and it has an awesome new feature: diffs right in the commit dialog.  It seemed a common use case for myself that when it came time to commit, I'd open my commit dialog and cycle through the files writing my commit message in Kate because I couldn't type while looking at the diffs.  Then I'd paste the message into the commit dialog and hit OK.  With the 1.6 release you can now see your commits right next to your message, and switch back and forth directly.  Kick ASS!

Sudoku is Groovy

Last week I spent a bunch of time implementing Sudoku solving strategies in Groovy.  Actually a pretty interesting exercise, I thought.  Even the simple solving techniques/strategies require a bit of thought to generalize into code.  This might seems like a pointless exercise, but think of it a cross training.  Football players can't hope to compete without lifting weights, and developers can't hope to be at their peak without doing some heavy lifting as well.

I picked Groovy because it let me avoid so much of the boilderplate code you might have with Java, and gives you some really powerful syntactic constructs that are missing from CFML.  Data structure searching/manipulation is the task at hand, and the code reflects that without much extra cruft.  I did run into some issues with aboring .each { } "loops", so had to convert a number of those to for..in loops instead.  I prefer the 'each' syntax, but it's problematic with aborting iteration several levels deep since you don't have the equivalent of a method-return or a labeled-break construct.

I'm going to walk through some of these strategies, but first I'm going to present the general Sudoku framework I built.  It's founded on two main types (Board and Cell) with a number of anciliary types (BoardSpec, House, Row, Column, Block, Chute, Stack and Band), and the actual solving types (Strategy, Solver, TestRunner and Test).  Starting at the top, the "runner" script looks like this:

new TestRunner(new Solver([new GameRulesStrategy()])).test()

That'll create a new Solver with just the GameRulesStrategy, and then supply that to a TestRunner to run the test (using the Strategy's internal test case(s)).  Here's GameRulesStrategy:

class GameRulesStrategy implements Strategy {
  static getTests() {
    [new Test('900637100030001000107520300004810500019000820006059400002048705000200080001793006')]
  }

  boolean play(Board board) {
    def madePlay = false
    board.cells.each { c ->
      if (c.isKnown()) {
        c.houses.each { h ->
          h.findAll {
            ! it.isKnown()
          }.each {
            if (it != c) {
              madePlay = it.removeMark(c.value, this) || madePlay
            }
          }
        }
      }
    }
    madePlay
  }
}

The getTests() method returns a List of Test objects, which contain a BoardSpec and an optional 'check' Closure for when the test has passed (the default is when the board is solved).  The BoardSpec uses a board literal - an 81 character string representing the board's cells starting in the upper left, and moving to the lower right, exactly like reading an English-language page.  1-9 represent themselves, any other character (zeros in this case) represent blank cells.

The solving loop simply iterates over each strategy invoking play(Board) on each one in turn, where a play is either removing a mark from a cell, or setting a cell's value.  Cells with a single mark are automatically promoted to a "known" cell.  Any time a strategy makes a play, the loop starts back at the first strategy.  Itereration continues until every strategy fails to make a play (the test fails), or the board passes the check (usually being solved).

Raw code is available at https://ssl.barneyb.com/svn/barneyb/sudoku/groovy/trunk/ and requires Groovy 1.7 to run.  You should be able to export the code and run 'groovy driver.groovy' and have it.

Amazon CloudFront CFC

Amazon CloudFront is a CDN that sits atop their S3 file hosting service to provide caching and geographically dispersed delivery.  It's all very simple, except security.  Much like my Amazon S3 CFC's original goal, my new Amazon CloudFront CFC's primary purpose is to ease the creation of signed URLs for CloudFront.  You can grab a copy from amazoncloudfrontcfc.txt.  The API for the CFC is about what you'd expect:

<cfset cloudfront = createObject("component", "amazoncloudfront").init(keyPairId, privateKeyFile) />
<cfset signedUrl = cloudfront.signUrlWithTimeout(resourceUrl, 600) />

This will generate a signed URL for the CloudFront resourceUrl (direct domain or CNAMEd) which expires in 600 seconds (10 minutes).  Very much like the S3 CFC.  Here we're dealing with resourceURLs directly (which correspond to a bucket and object key) rather than separate buckets and object keys.  CloudFront doesn't distinguish between the two parts, so the CFC doesn't either.

The biggest gotcha, however, is with the signing mechanism.  S3 uses a simple pre-shared key, but CloudFront uses an RSA private key which is significantly more complicated to deal with.  Unfortunately, Amazon provides it's keys in PEM format, but core Java can only read DER format, so you must either convert your private key to DER format with OpenSSL, or use a third party library.  Fortunately, both are pretty simple.

Here's the command to convert your key with OpenSSL:

openssl pkcs8 -topk8 -in pk-KEYPAIRID.pem -nocrypt -outform DER -out pk-KEYPAIRID.der

Alternatively, you can use the not-yet-commons-ssl package, which provides support for reading keys in PEM format (among a pile of other things).  If you can add the JAR to your classpath, this is definitely a superior solution to manual conversion, since you can transparently use pretty much any RSA private key.  And there's nothing to enable in the CFC; if not-yet-commons-ssl is available on the classpath, it'll automatically use it for reading in the private key.  As an aside, the name 'not-yet-commons-ssl' reflects the fact that the package has applied fro Apache Commons incubation, but it hasn't been accepted yet.  The code is orgniazed in the 'org.apache.commons.ssl' package assuming it's acceptance, but it is still unofficial.

As always, updates and such are available on the project page.

I Am No Longer Married

I learned today, via a letter from the court, that I am no longer married (and haven't been since the 12th). Seven years, five months, and nine days.

Improving the JSON/P Twitter Widget

For quite some time now I've had a sidebar widget that displays an excerpt of my Twitter feed on my blog.  It uses the standard Twitter JSON/P interface for loading the tweets and then a Twitter-provided script (http://twitter.com/javascripts/blogger.js) for rendering them on the page.  Unfortunately the default installation instructions would have to set up something like this:

<ul id="twitter_update_list"></ul>
<script type="text/javascript" src="http://twitter.com/javascripts/blogger.js"></script>
<script type="text/javascript" src="http://twitter.com/statuses/user_timeline/barneyb.json?callback=twitterCallback2&count=5"></script>

That works, but it puts the JSON/P call (the second SCRIPT tag) into the main page flow, so anything after the call won't render until the script load is complete.  When Twitter is slow/down it can cause problems, but even when it's fast you still get a brief hang with a partial page before the rest snaps into view.  Fortunately, the solution is simple: do the call on DOMLoaded instead of inline.  I'm using jQuery, but the principle is the same regardless of what framework you use (or none at all):

<ul id="twitter_update_list"><li class="load-ind"><img src="http://www.barneyb.com/s/loading_indicator.gif" /></li></ul>
<script type="text/javascript">
jQuery(function() {
  jQuery("body").append(
    '<sc'+'ript type="text/javascript" src="http://twitter.com/javascripts/blogger.js"></sc'+'ript>' +
    '<sc'+'ript type="text/javascript" src="http://twitter.com/statuses/user_timeline/barneyb.json?callback=twitterCallback2&amp;count=5"></sc'+'ript>'
  );
});
</script>

Now the SCRIPT tags will be written on DOMReady, allowing the full page to render before the calls to Twitter are dispatched.  This gives a faster user experience, though you do get a reflow of the sidebar when the statuses load.  This has become so prevalent on modern websites, however, that I don't think it's worth caring about, but if you're concerned, you could specify a height to the loading indicator LI that is approximately what the rendered tweet list will be to mitigate the effect.  It won't avoid the reflow, but it'll make things move less because the space will be reserved up front.

I've also added a simple "loading" image to the UL that the JS will place the tweets in.  The way blogger.js is written, any content within the UL will be removed (i.e. the tweets overwrite the content; they're not appended to the content), so no need to explicitly remove the image or it's container LI.

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.  ;)