Comments
Richard Davies wrote: The UK has a good crop of technology pioneers in cloud computing - for example ElasticHosts, FlexiScale, Flexiant, OnApp - and also some strong government initiatives such as G-Cloud. We will have to see whether this kind of technical leadership converts into swift mass-market adoption or not.
Cloud Computing
Conference & Expo
November 2-4, 2009 NYC
Register Today and SAVE !..
SYS-CON.TV
Today's Top SOA Links


User Defined Functions - Round 2
User Defined Functions - Round 2

The introduction of user-defined functions (UDFs for short) was the most requested and anticipated event in the ColdFusion 5 era - developers desperately wanted to be able to extend CFML, using not just tags, but functions as well. I first introduced UDFs a year or so ago (CFDJ, Vol. 3, issue 5), but now it's time to revisit the subject. Why? Because ColdFusion MX provides us with a whole new way to write UDFs, this time using tags.

Why UDFs?
The ColdFusion Markup Language is made up of two types of instructions:

  • Tags: Like <CFQUERY> and <CFMAIL>
  • Functions: Like Now() and StructNew()
Ever since ColdFusion version 2, developers have had ways to write their own tags, and many have done so. After all, writing your own tags allows you to write maintainable and reusable code.

Prior to CF5 we had no way to write our own functions. For example, ColdFusion provides a whole range of list-manipulation functions - List-First(), ListGetAt(), ListSort(), and so on - but there was no function to get the smallest or greatest value in a list - ListMin() and ListMax(). To get these values you'd have to loop through the list, doing the comparisons yourself. There were two ways to do this: (1) inline, right in the middle of your code; or (2) by creating a custom tag.

Neither option was ideal. The former wasn't reusable; each time we wanted to obtain these values we had to copy and paste the block of code. The latter was inefficient (custom tags execute more slowly than functions do) and clumsy (because custom tags have no mechanism by which to return data). The right solution is a user-defined function, which became possible in CF5.

CF5 UDFs
UDFs in CF5 were written in <CFSCRIPT>, ColdFusion's scripting interface. In fact, in CF5 UDF support was the only feature that actually required <CFSCRIPT>. Anything else <CFSCRIPT> could do could also be done in straight CFML.

Here's a simple example of a <CFSCRIPT>-based UDF (this is the example I used a year ago). Suppose you need to use yesterday's or today's date repeatedly within your code. The CFML DateAdd() function can be used to add or subtract a day. To refer to yesterday you could use DateAdd("d", -1, Now()); to refer to tomorrow, DateAdd("d", 1, Now()). These function calls have to be repeated every time they're needed.

Or you could create a couple of UDFs. CFML already has a function, Now(), that returns today's date (and time). Why not Yesterday() and Tomorrow() functions to complement it?

Here's the CF5 UDF code that creates these two new functions:

<CFSCRIPT>
// Get yesterday's date
function Yesterday()
{
return DateAdd("d", -1, Now());
}

// Get tomorrow's date
function Tomorrow()
{
return DateAdd("d", 1, Now());
}
</CFSCRIPT>
As you can see, the UDFs are created using CFML scripting between <CFSCRIPT> and </CFSCRIPT> tags. This <CFSCRIPT> block defines two functions, Yesterday and Tomorrow. Each function is preceded by the keyword function so <CFSCRIPT> knows we're about to define a function.

These functions are very simple. All they do is return the result of a DateAdd() function: one adds 1 to Now(), the other subtracts 1 from Now(). The value returned must be preceded by the keyword return, and whatever follows return is returned by the function to the caller. You can return functions (as we did here), variables, or any other valid CFML expression.

Once defined, these user-defined functions can be used just like any other CFML functions. As long as the UDFs appear on the same CFM page, or are included in your code via <CFINCLUDE>, they can be used like this:

<CFOUTPUT>
Today is #DateFormat(Now())#<BR>
Yesterday was #DateFormat(Yesterday())#<BR>
Tomorrow will be #DateFormat(Tomorrow())#<BR>
</CFOUTPUT>
As you can see, the new Yesterday() and Tomorrow() functions are used just like the built-in Now() function, and all three functions are passed to DateFormat(). Once defined, your own UDFs can be used like any other ColdFusion functions - there's no difference whatsoever.

UDFs Revisited
The UDFs and syntax reviewed thus far are CF5 based (although they'll work in CFMX as well, obviously). But CFMX now provides a whole new (additional) way to create UDFs addressing some important limitations of <CFSCRIPT>-based UDFs.

  • For many ColdFusion developers <CFSCRIPT> was foreign. CFML is far more familiar and comfortable.

  • <CFSCRIPT> supports a subset of CFML - functions but not tags. Thus <CFSCRIPT>-based UDFs can't access tags (for example, you can't execute a <CFQUERY> in a <CFSCRIPT>-based UDF).

  • <CFSCRIPT> UDF syntax provides limited parameter validation (no support for data types), and doesn't provide a mechanism to simply work with optional parameters.

    So CFMX introduced tag-based UDFs - the ability to create user-defined functions using CFML tags. The tags used are:

    • <CFFUNCTION>: To define the function
    • <CFARGUMENT>: To define any arguments (parameters)
    • <CFRETURN>: To define return codes
    Do these tags look at all familiar? They should - they're the same ones used to create ColdFusion Components (as explained in my two previous columns). Here are the same two functions, Yesterday() and Tomorrow(), but this time created using tags:

    <!--- Get yesterday's date --->
    <CFFUNCTION NAME="Yesterday"
    RETURNTYPE="date">
    <CFRETURN DateAdd("d", -1, Now()>
    </CFFUNCTION>

    <!--- Get tomorrow's date --->
    <CFFUNCTION NAME="Tomorrow"
    RETURNTYPE="date">
    <CFRETURN DateAdd("d", 1, Now()>
    </CFFUNCTION>
    They're pretty much self-explanatory. Each function is created using a <CFFUNCTION> tag and ends with a matching </CFFUNCTION> tag. The code between the tags makes up the function itself. These particular functions have no processing, just a return value specified using <CFRETURN> (as in the <CFSCRIPT> versions).

    So how would you use these functions? Just as you would the <CFSCRIPT> versions:

    <CFOUTPUT>
    Today is #DateFormat(Now())#<BR>
    Yesterday was #DateFormat(Yesterday())#<BR>
    Tomorrow will be #DateFormat(Tomorrow())#<BR>
    </CFOUTPUT>
    It's worth noting that there's one difference between the <CFSCRIPT>- and tag-based versions of these functions: the latter define a RETURNTYPE. This optional attribute is used to define the type of data returned by a UDF. This value is used in validating return values as well as where functions can and can't be used. If RETURNTYPE isn't specified, any type will be allowed, just as in <CFSCRIPT>-based functions. As a rule, you should always define return types.

    Accepting Parameters
    The functions created above are atypical in that they accept no parameters, something that most functions do. For example, UCase() takes the string to be converted, Min() takes the two values to be checked, and DateFormat() takes a date and an optional formatting specification.

    Tag-based UDFs (like <CFSCRIPT>-based UDFs) can accept parameters (technically called arguments). All you have to do is define them in the order that they're expected by using the <CFARGUMENT> tag in one of two ways:

    <CFARGUMENT NAME="n"
    REQUIRED="yes">
    That line of code defines a required parameter. If it isn't provided, an error is thrown. Additional validation can be specified to check the type of passed data: -

    <CFARGUMENT NAME="n"
    REQUIRED="yes"
    TYPE="numeric">
    In this example the passed value must be numeric. If it isn't specified at all, or if a nonnumeric value is specified, an error will be thrown. <CFARGUMENT> can also be used to define optional parameters, like this:

    <CFARGUMENT NAME="date"
    REQUIRED="no">
    In this example a date may be specified, but it isn't required. In practice a default value should be used in this situation, like this:

    <CFARGUMENT NAME="date"
    REQUIRED="no"
    TYPE="date"
    DEFAULT="#Now()#">
    Here the date is optional. If it's specified, it had better be a valid date; if it isn't specified, then today's date will be used as the default.

    Now that you know how to use <CFARGUMENT>, here's a complete (albeit simple) example. Cube(), as its name suggests, returns the cube of a specified value (pass it 3 and it will return 27). Here's the tag-based Cube() UDF:

    <!--- Get the cube of a number --->
    <CFFUNCTION NAME="Cube"
    RETURNTYPE="numeric">
    <!--- Number must be passed --->
    <CFARGUMENT NAME="n"
    TYPE="numeric"
    REQUIRED="yes">
    <!--- Return cube --->
    <CFRETURN n*n*n>
    </CFFUNCTION>
    Cube() accepts a single parameter. To accept additional parameters all you do is insert additional <CFARGUMENT> tags. Simple as that.

    Using Local Variables
    We're not done yet. The next thing to learn is how to use variables within your UDFs. Any and all variables and scopes are visible within your UDF, but to prevent possible conflicts or overwrites, variables created within a UDF should be visible only within the UDF itself.

    Let's look at an example. The function in Listing 1 verifies a datasource name to make sure it's working (in the past this was doable only from within the ColdFusion Administrator). This particular UDF uses some advanced CFMX features that I'll explain in detail next month.

    VerifyDSN() does just that: pass it the name of a datasource and it'll return TRUE if the datasource can be verified (as usable) or FALSE if not. VerifyDSN() accepts one required parameter, the name of the datasource to be verified. The actual work is done by a Java call to a class named coldfusion.server.ServiceFactory and objects within it (that's what I'll cover next month).

    But there's a big potential problem here. Within VerifyDSN() two variables are used, dsService and result. As already stated, all scopes are visible within UDFs. So what if a variable named result already exists outside the UDF? The <CFSET> calls will overwrite it (a real likelihood with a variable named result). Any variables created within a UDF will be of type VARIABLES if no explicit scope is specified. So how can you create local variables (local only to the UDF)? Look at this code snippet (taken from the VerifyDSN() function):

    <!--- Init local variables --->
    <CFSET VAR dsService="">
    <CFSET VAR result="true">
    These two <CFSET> tags define local variables; what makes them local is the VAR keyword. As local variables named dsService and result have been defined (because VAR was specified), they'll be used within the UDF (and not any variable of the same name in other scopes).

    As a rule, every variable used within a UDF should be defined this way so as to prevent accidental overwrites.

    Things to Keep in Mind
    Now that we've covered the basics of tag-based ColdFusion UDFs, here are some important points to remember:

  • UDFs are fast, much faster than custom tags (yes, it may pay to rewrite some of your custom tags as UDFs).
  • To execute a UDF, it must be in the same CFM file, or included via <CFINCLUDE>, before it's used.
  • UDF code can call any function - both CFML functions and UDFs.
  • Only tag-based UDF code may call tags (and not <CFSCRIPT>-based tags).
  • Within a UDF all variables and scopes are visible.
  • Variables created within a UDF aren't visible outside the UDF if they're defined as VAR.
  • Recursion is fully supported within UDFs.
  • You can't create a UDF with the same name as a built-in CFML function.
  • All UDFs, whether written in <CFSCRIPT> or CFMX tags, can be used within CFML tags, functions, and expressions.

    UDFs can be written when needed (right within your CFM page), or in libraries (files containing nothing but lists of UDFs), where they can be included as needed. There's no limit to the number of UDFs that can be written and used, and there's no limit to how many UDF libraries you can include in your page.

    Note: Visit www.cflib.org to access the best public collection of ColdFusion UDFs.

    *  *  *

    There you have it. UDFs are useful, they're fast, and they're fun. And now they're easier to write and more powerful than ever before.

    About Ben Forta
    Ben Forta is Adobe's Senior Technical Evangelist. In that capacity he spends a considerable amount of time talking and writing about Adobe products (with an emphasis on ColdFusion and Flex), and providing feedback to help shape the future direction of the products. By the way, if you are not yet a ColdFusion user, you should be. It is an incredible product, and is truly deserving of all the praise it has been receiving. In a prior life he was a ColdFusion customer (he wrote one of the first large high visibility web sites using the product) and was so impressed he ended up working for the company that created it (Allaire). Ben is also the author of books on ColdFusion, SQL, Windows 2000, JSP, WAP, Regular Expressions, and more. Before joining Adobe (well, Allaire actually, and then Macromedia and Allaire merged, and then Adobe bought Macromedia) he helped found a company called Car.com which provides automotive services (buy a car, sell a car, etc) over the Web. Car.com (including Stoneage) is one of the largest automotive web sites out there, was written entirely in ColdFusion, and is now owned by Auto-By-Tel.

  • In order to post a comment you need to be registered and logged in.

    Register | Sign-in

    Reader Feedback: Page 1 of 1

    Subscribe to the World's Most Powerful Newsletters
    Subscribe to Our Rss Feeds & Get Your SYS-CON News Live!
    Click to Add our RSS Feeds to the Service of Your Choice:
    Google Reader or Homepage Add to My Yahoo! Subscribe with Bloglines Subscribe in NewsGator Online
    myFeedster Add to My AOL Subscribe in Rojo Add 'Hugg' to Newsburst from CNET News.com Kinja Digest View Additional SYS-CON Feeds
    Publish Your Article! Please send it to editorial(at)sys-con.com!

    Advertise on this site! Contact advertising(at)sys-con.com! 201 802-3021

    SYS-CON Featured Whitepapers
    ADS BY GOOGLE