Monday 30 June 2014

A quick primer: porting CFCs from tag-based to script-based

G'day:
I'm assisting someone from porting their (ColdFusion 9) CFC code from tag-based to CFScript based, and part of this is to knock together some examples of analogous CFCs in both formats. I know some people struggle to get their brains around CFScript, so thought perhaps it might be slightly worthwhile to post the notes here too. This is just a first pass, and if other requirements come up or examples needed, I'll post 'em too.

Here's a CFC written in tags:

<!--- InTags.cfc --->
<cfcomponent extends="Base" output="false" hint="This CFC is implemented entirely in tags">

    <cfprocessingdirective pageEncoding="utf-8">

    <cffunction name="someMethod" returntype="void" access="public" output="false" meta:someAnnotation="Its value" hint="This is a function which tags three arguments and does nothing with them">
        <cfargument name="reqdArg"                type="string" required="true"    hint="We need to pass this one">
        <cfargument name="defaultedOptionalArg"    type="string" required="false"    default="a default value" hint="This one has a default value if it's not passed">
        <cfargument name="optionalArg"            type="string" required="false"    hint="This one is entirely optional">
    </cffunction>
    
</cfcomponent>

And here is a direct analogue in CFScript:

// InScript.cfc

/**
* @hint This CFC is implemented entirely in script
*/ 
component extends="Base" {

     pageEncoding "utf-8";    

    /**
    * @hint                 This is a function which tags three arguments and does nothing with them 
    * @meta:someAnnotation     Its value
    * @reqdArg                 We need to pass this one
    * @defaultedOptionalArg This one has a default value if it's not passed
    * @optionalArg             This one is entirely optional
    */ 
    public void function someMethod(required string reqdArg, string defaultedOptionalArg="a default value", string optionalArg){

    }

}

That is a huge mess of highlighting, I know: it's perhaps best to look @ the actual code on Github (the file names are linked to their github version). But here's the key:
  • extends (and other attributes of the <cfcomponent> tag) are expressed in exactly the same way as before.
  • output does not need to be specified, in the context of whitespace suppression. Note that this does not imply output="false", it merely doesn't specify it (output is a tri-state setting, not a boolean one). However CFScript does not emit whitespace, so it amounts to the same thing.
  • Function hints are expressed as annotations, @hint.
  • Argument hints are expressed as annotations, but differently from functions: the annotation is the name of the argument.
  • The separator between an annotation and its value must be a space. Tabs do not work. However after the initial space, tabs are fine if one is prone to want to line-up one's code.
  • Annotations are listed in a block similar to a comment, but note that the block starts with "/**" not "/*". These are not comments, although they ignore anything that's not an annotation within them.
  • Page encoding is done as a statement taking a value.
  • Custom metadata annotations are done the same as any other attribute.
  • The [access] [return type] function [function name] are done similar to other languages.
  • Arguments are assumed optional unless required is specified.
  • One prefixes an argument with its type (this is optional).
  • Optional arguments specify a default value simply by assigning it.

I think that's about it.

For good measure, here are some CFScript equivalents of common tag-based constructs:

<!--- tagComparison.cfm --->
<cfsavecontent variable="someVar">
    <cfset myContent = "this is the content">
    <cfoutput>#myContent#</cfoutput>
</cfsavecontent>

<cflock name="myLock" type="exclusive" timeout="1" throwontimeout="true">
    <!--- stuff to lock --->
</cflock>

<cfthread action="run" name="t1">
    <!--- code in thread --->
</cfthread>


<cfscript>
    savecontent variable="someVar" {
        myContent = "this is the content";
        writeOutput(myContent);
    }

    lock name="myLock" type="exclusive" timeout=1 throwontimeout=true {
        // stuff to lock
    }

    thread action="run" name="t1"{
        // code in thread
    }
</cfscript>

As a rule, the syntax is just to lop off the angle brackets and the "cf", but otherwise the syntax is the same. Note: this guidance is for ColdFusion 9 (the target audience for this article). CF11 brings in different general syntax for CFScript, for reasons best known to some muppet at Adobe.

I'm out of time, and this covers what I needed to cover. If anyone wants any elaboration, let me know and I'll add / update as needs must.

Righto.

--
Adam