ColdFusion 10 is Now Available

Adobe has released ColdFusion 10. The engineering team has done a fantastic job putting this release together. Here's a short list of some of what's new:

  • Improved/Updated
    • caching
    • ORM
    • webservices
    • scheduler
    • hotfix management
    • security
    • MS Exchange integration
    • security
    • Solr
    • charting (now html 5)
    • Java integration
    • Flex and AIR lazy loading
    • VFS
    • more!
  • html 5 support
  • websockets
  • RESTful services
  • greater cfscript parity
  • geo-tagging
  • CFC auto constructors
  • method chaining
  • closures
  • Tomcat (no more JRun)
  • more!
    • Wow! A lot of changes and additions. The team has been in overdrive, and it all looks spectacular. Download the free Developer's Edition and start playing.

      As a bonus, they also release an update to ColdFusion Builder as well!

CFQueryReader v2.1: Now with metaData support

The Sencha guys just keep upping the bar, and Ext JS 4.1 is no exception. I've been reading Loiane Groner's Ext JS 4 First Look, to review it, and continually find new, cool stuff. 4.1, however, takes it even further. While upgrading CFQueryReader, I was working with 4.1 RC2. And, while extending the base classes, I came across a new feature that wasn't fully documented yet: adding metaData to a server-side store response for changing configuration on the fly. Sometimes it would be nice to just...change up. Now that 4.1 is fully released, I had to make sure that this worked in CFQueryReader.

So, last night I sat down and hammered out this functionality. It took a lot more than I realised, and I learned a lot more about the Ext JS internal code, but I think CFQueryReader is better for it. Consider the following ColdFusion method:

view plain print about
1/**
2 *    FUNCTION getWithMeta
3 *    This function returns the ColdFusion Query object as part of a struct object.
4 *
5 *    @access remote
6 *    @returnType struct
7 *    @output false
8 */

9function getWithMeta(numeric pageIndex = 1, numeric pageSize = 50, string sort = "", string search = "") {
10    var retVal = {"success" = true, "pageIndex" = ARGUMENTS.pageIndex, "pageCount" = 0, "recordCount" = 0, "message" = "", "getEntries" = "", "metaData" = {"root" = "getEntries", "totalProperty" = "recordCount", "successProperty" = "success", "messageProperty" = "message", "idProperty" = "id", "fields" = []}};
11    StructAppend(LOCAL.retVal, GetEntries(argumentCollection: ARGUMENTS), true);
12    var colArr = ListToArray(LOCAL.retVal.getEntries.columnList);
13    LOCAL.retVal.metaData.fields = [
14        {"name" = "id", "type" = "string", "mapping" = JavaCast("int",0)},
15        {"name" = "title", "type" = "string", "mapping" = JavaCast("int",3)},
16        {"name" = "posted", "type" = "date", "mapping" = JavaCast("int",2)},
17        {"name" = "views", "type" = "int", "mapping" = JavaCast("int",1)}
18    ];
19    return LOCAL.retVal;
20}

I reused my getEntries method, to get my query. Here, I'm creating a metaData object, at the root of the return, to define the dataset. Configuration options that I normally define client side (root, totalProperty, etc) I put in to the metaData key. When the response is received by the client, the reader will pass this metaData in to our app, applying this configuration to our reader, store, model, and so on. In the above method, we let the metaData map our columns to fields, rather than doing it client side (CFQueryReader will automatically skip the column mapping if metaData.fields is present in the response.) Our client side store might now look like this:

view plain print about
1Ext.create('Ext.data.Store', {
2    storeId: 'entryStore',
3    model: 'Entry',
4    remoteSort: true,
5    proxy: {
6        type: 'ajax',
7        url: '/com/cc/Blog/Entries.cfc',
8        extraParams: {
9            returnFormat: 'json',
10            method: 'getWithMeta'
11        },
12        limitParam: 'pageSize',
13        pageParam: 'pageIndex',
14        sortParam: 'sort',
15        reader: {
16            type: 'cfquery'
17        }
18    },
19    autoLoad: true
20});

A full example of this, in action, can be seen on a demo page of the CFQueryReader site. The full source code, of the example, can be found in the CFQueryReader GitHub repository.

Ext JS 4.1 Grid: Part1: Basic Config

Many moons ago, I wrote a series on My First Ext JS Data Grid. It was very popular. In fact, it got me the gig co-authoring two books on Ext JS and spawned an open source project targeted at integrating Ext JS with ColdFusion. But, I did that series back in 2007, using Ext JS 1.1 (maybe?), and an update is long overdue.

[More]

ColdFusion Query Column Order: Did You Know?

Now first let me say that I've only seen this behavior on ColdFusion 9.01 with Cumulative Hotfix 2. My ColdFusion 10 VM blew up this morning, and I haven't had 7 or 8 for a while now, so someone else might have to verify this behavior on those platforms for me.

This morning I was testing out a new feature of Ext JS 4.1, and ran into something interesting. One of the reasons that I wrote CFQueryReader, as well as serializeCFJSON, was because of the way that ColdFusion handles it's JSON conversions of it's native query object.

view plain print about
1{
2    "COLUMNS":["ID","NAME","EMAIL"],
3    "DATA":[
4        [1,"Ed Spencer","ed@sencha.com"],
5        [2,"Abe Elias","abe@sencha.com"],
6        [3,"Cutter","no@address.giv"]
7    ]
8}

I've written before, about how Adobe's dataset serialization is much trimmer, but many client-side libraries and plugins expect a more standardized format.

view plain print about
1{
2 "users": [
3 {
4 "id": 1,
5 "name": "Ed Spencer",
6 "email": "ed@sencha.com"
7 },
8 {
9 "id": 2,
10 "name": "Abe Elias",
11 "email": "abe@sencha.com"
12 }
13 ]
14}

It's not too difficult for us, client-side, to map the column to the proper data position in the record array. In fact, that's basically what CFQueryReader and serializeCFJSON both do. But why is it even necessary? Most of these client-side libraries allow us to write mappings in configurations anyway, so why write something else to do it?

Well, there's two reasons really. First of all, the client-side developer may not have that much insight into the server-side developer's code (they can be different people), and would probably be unaware of API changes. The second reason? Well, if you've used ColdFusion for a while, you might know that there was a time when you could not depend on the column order always being the same. At one point, the columns were returned in alphabetical order. This would make it really hard to figure the position out prior to run time, unless you have intimate knowledge of every change to your API as it happens.

It appears that this isn't as much of a problem today. Kind of. I'll run this query:

view plain print about
1LOCAL.sql = "SELECT    SQL_CALC_FOUND_ROWS id,
2        views,
3        posted,
4        title
5FROM    tblblogentries
6WHERE 0 = 0";
7LOCAL.q = new Query(sql = LOCAL.sql, datasource = VARIABLES.dsn);
8LOCAL.retVal.getEntries = LOCAL.q.execute().getResult();

If you dump the results to the page, you'll see the following:

Now, server-side, ever query comes with a variable we can use to reference the columns used in a query. I'll use ColdFusion's ArrayToList(LOCAL.q.columnList) method to dump the query's columnList property to the page:

Look closely and you'll see that both appear to be in alphabetical order. The query dump and the column list showed them in alphabetical order. That said, we then look at the JSON return:

Wait a minute? Now the columns appear in the same order that they are in the query. That's a change.Let's change the column order in the query, and see if it changes anything.

view plain print about
1LOCAL.sql = "SELECT    SQL_CALC_FOUND_ROWS id,
2        title,
3        posted,
4        views
5FROM    tblblogentries
6WHERE 0 = 0";

Notice that, again, the column order matches that of the order it was used in the query. Running a cfdump of the query, and it's columnList, still show alphabetical order. Why is it different? Why does this matter? Well, I'm going to show you that in a follow-up post (on Ext JS 4.1 and ColdFusion), but in the mean time it's a good thing to know, that the JSON returnFormat will give you query column orders identical to that used in the query. And, as I said above, somebody please verify this for me on other versions of the platform.

JQuery Plugin: serializeCFJSON

Quick note about my serializeCFJSON project out on GitHub. I wrote this quick JQuery plugin to convert ColdFusion's JSON representation of it's native query objects. ColdFusion represents datasets in a trim manner, as an object containing two arrays: One, an array of column names, and the other, an array of arrays, each representing one record of the set. Most representations of recordsets are an array of objects, each record represented as a collection of name/value pairs. ColdFusion's representation is much smaller, removing a lot of unnecessary duplication, but many pre-built frameworks and plugins look for the name/value pair objects.

What the plugin does is convert ColdFusion's representation into the more standard form. Ajax transfer is unaffected, as the trimmer format is still being passed. What we get is some very minor client-side overhead in the creation of a new object. What's more, the plugin recursively searches through a JSON object and converts any ColdFusion query that it finds. So, if you nest your query inside a larger object, the plugin will still convert it for you.

I put up a demo in the Projects menu, at the top of this site. This is a refactor of my Grouped jqGrid example, using the plugin for the data conversion. The full sample code for the demo can be found in the GitHub repository.

Take her out for a spin, and let me know what you think.