Saturday 26 April 2014

ColdFusion 11: <cfclient> in the context of the CFML language, not the tooling

G'day:
This article could end up being a complete waste of space, as I am operating under more real-world re-interpretation of the raison d'ĂȘtre of <cfclient>, as described by Ram:
ColdFusion [11] has added support for client side CFML (<cfclient>) and this code is translated to JavaScript
Mike Henke has pointed out that the first part of this "CF11 has added support for client-side CFML" is actually bullshit (my wording, not his):
"ColdFusion Splendor has added support for client side CFML" is probably phrased wrong and gave me flash backs to VBScript or CFML running in the client browser.
Maybe this is better verbiage. "ColdFusion Splendor has added support for CFML to generate JS".
Because... there's not such thing as "client-side CFML". That would imply CFML actually running on the client (ie: the browser, or the mobile device, as is the target of <cfclient> as a concept). This is absolutely not true. I know Ram went on to qualify what he meant there, but the messaging from Adobe on <cfclient> has been inaccurate. Possibly I think to the point of actual misrepresentation (in the legal sense, I mean).

What <cfclient> does is... convert CFML code to JavaScript. So let's look at that idea.

Why I say this article could be a complete waste of space is that I dunno if this is the intent of <cfclient>; because, really, the messaging from Adobe as to what <cfclient> is for is very muddied. In general the narrative about it is bundled-in with various features of ColdFusion Builder 3.0, in regards to mobile application creation and packaging (and debugging, and all sort of stuff that sounds quite nice but I don't care about as I don't use CFB).

However <cfclient> has to stand on its own merit in the CFML language, completely decoupled from CFB features. And as far as I can tell, Mike is right: as far as CFML goes, <cfclient> is a tag which tells ColdFusion to - instead of compiling to byte code - compiles the enclosed CFML code to JavaScript instead.

This of course just sets my alarm klaxons going.  Because Adobe are (I choose my words carefully) shit at converting CFML to client-side code. This has been borne out by <cfform>, <cflayout>, <cfpod> etc. Back in the days of CFMX7 the "CFML-to-client-side" wizards had some merit, because the client-side arena was still immature, and the team at Macromedia actually had some exposure to the idea of "website development", so they knew where the pain points were that they could possible ameliorate. Times have changed, and the client-side of things is probably now more mature an environment than CFML is - Javascript is probably are more coherent language than CFML is, now - and it is very very very clear that the current Adobe ColdFusion Team don't know the first thing about web development (they can't even write decent CFML for their own documentation, before we get onto how they'd be doing web work!), so they are completely out of their depth when it comes to addressing pain points that web devs might have these days. Not only have they not walked a mile in a web dev's shoes, they don't even - as far as I can tell - know what shoes are, or why someone would be wearing them.

And <cfclient> is a far more "meta" solution than <cfform> or <cfpod> were... at least the CFML-to-client-side solution there had a tangible target in mind. <cfclient> has removed the targets, and is just a tag designed for converting general CFML to general JS. Yikes.

600-odd words in, here's some code. This is the first-ever code I have written using <cfclient>:

<cfclient>
<cfset msg = "G'day World">
<cfoutput>#message#</cfoutput>
</cfclient>

So I'm off to a false start: I admit I'm sitting in the pub and I've had a few pints (probably four, I guess; this is "having some drinks before deciding to actually drink this evening" sort of level for me), and I inadvertently ballsed-up my variable names here. And this did not yield a compile error. The error just fell through to JS, where it did error


  1. Uncaught ReferenceError: message is not defined gdayWorld.cfm:4

If, in contrast, I tried this in CoffeeScript, I'd get this:

coffee> msg = "G'day world"
'G\'day world'
coffee> alert msg
undefined
coffee>
alert message
ReferenceError: message is not defined
    at eval (eval at  (http://larryng.github.io/coffeescript-repl/js/main.js:96:26), :1:9)

Important note: I know nothing about CoffeeScript - it seems like a solution to a non-existent problem to me - so I just googled "hello world coffeescript" and REPLed the code on http://larryng.github.io/coffeescript-repl/.

The point is here is that ColdFusion, during the "compile" process should actually notice I am referencing a variable that doesn't exist. I'm OK with this being a runtime error in the normal CFML-compile process, because there could be many moving parts (and other files) involved in the runtime environment, but I don't think this is a valid notion with <cfclient>? Or maybe it is?

Update:

As Ray points out and Aaron agrees with below: I was being unduly harsh here... it's entirely reasonable for variables to be declared in other JS code elsewhere in the response.

Anyway, once I actually write my code properly, I get this:

G'day World

On the screen. What CF actually sends back in the response is this:


<script type="text/javascript" src="/CFIDE/cfclient/cfclient_main.js"></script>
<script type="text/javascript" src="/CFIDE/cfclient/cffunctions.js"></script>
<meta name="viewport" content="width=device-width">
<script type='text/javascript'>globalDivStruct=null;var _$gdayWorld_func=function(){var self=this;var variables={};self.__init=function(){var localdivstruct=globalDivStruct;var __output_var="";var tmpVarArray={};message="G'day World";localdivstruct.outputvar+=message;return""}};
function __startPage__$gdayWorld(){document.write("\x3cdiv id\x3d'__cfclient_0'\x3e\x3c/div\x3e");window.ispgbuild=false;var clientDivStruct={divId:"__cfclient_0",outputvar:""};globalDivStruct=clientDivStruct;try{_$gdayWorld=new _$gdayWorld_func;_$gdayWorld.__init()}catch(__eArg){if(__eArg!=="$$$cfclient_abort$$$")throw __eArg;}__$cf.__flush(clientDivStruct)}__startPage__$gdayWorld();
</script>

Holy fucking christ. To output a string variable? Really? That's what you compile my CFML down to?

Gobsmacked.

And screw emotive things like "gobsmacked"... if one looks at the size of the response, it's 128kB to represent "G'day World" on the screen. 128kB might not be a great deal these days when it comes to a web page, but when it comes to rendering "G'day world", it's lunacy. And on a mobile phone - which intrinsically might be in an area in which one is paying a premium for data - it's just irresponsible. This is before we get to the point that it seriously should not take 128kB of data to render 11 bytes.



Blimey. To be honest I was gonna try some more tricky CFML and see how it compiled, but I've run out of will to live (and I have to concede the beer is getting the better of me, and my laptop battery is almost dead). I'll continue this, but I will let this article stand or fall on its own merit; something <cfclient> certainly seems to be unable to do, thusfar.

I'll be back on the case with this tomorrow.

--
Adam