CFQueryReader Update: Support for ExtJS 5 and ColdFusion 11

Due to a donation from Bruce Lomasky (thanks Bruce), I've written a new CFQueryReader for ExtJS 5. CFQueryReader is an Open Source project, available on GitHub, that I began years ago to create a proper reader for parsing Adobe's native ColdFusion query JSON return format into something usable by ExtJS data stores. As ExtJS has changed over the years, I have updated the reader to accommodate new versions. But, I don't do much ExtJS development anymore. It's a great front-end app development framework, but I've changed jobs a few times since co-authoring the first books on the library, and just don't work with the technology on a day to day basis anymore. Bruce had a need, and offered to pay for the development of an updated reader. So I dove back in to the ExtJS pool, to help him fulfill his requirements and put a new reader out there.

ExtJS went through a major rewrite between versions 3 and 4, requiring an entirely new direction. While much is backward compatible, in the move from version 4 to version 5, there were some big changes to ExtJS' data packages. Sencha has always done a good job with creating a rich, dynamic framework for web application development. I have always been impressed with their commitment to improving the product, continuously modernizing the framework, and creating more of a "software craftsmanship" approach to web application development. That said they haven't always done such a great job with ensuring an easy upgrade path for developers. Changes to their data package completely broke some of the use cases of CFQueryReader, requiring some refactoring to accomadate.

And that's OK. Sencha's changes to their data packages are some welcome changes, especially for our little plugin. In all of the past revisions of CFQueryReader, we've extended a base Array Reader class, and written an override method of the core readRecords() method. While this worked, it was really kinda kludgy. What we really needed was a way to transform the incoming data packet prior to the reader really processing the object. With ExtJS, we now have that ability to do just that.

ExtJS 5 introduced a new configuration option for data readers: transform. This new configuration attribute takes a function within which you can manipulate the return data of a store's ajax request prior to the reader actually processing that data. This gives some underlying flexibility that wasn't really there before, especially when using Ext.Direct, but for now you really just need to know the basics.

ColdFusion upper cases object keys when it serializes to JSON. If you are manually creating the structure, there are ways to fix that:

view plain print about
1var myStruct = {
2 "key1": "some value,
3 "Key2": "this one is cased differently",
4 "differentKey3": "this one is more different"
5 };

This is fine, and helps a little bit, but ColdFusion creates an object when it serializes a ColdFusion query object, and you can't control it's casing:

view plain print about
1ColdFusionJson = {
2 COLUMNS: ["KEY1","KEY2","DIFFERENTKEY3"],
3 DATA: [
4 ["some value","this one is cased differently","this one is more different"]
5 ]
6 };

When you build your reader configurations, we no longer worry about this casing, as we will do a case insensitive matching of keys during the pre-process:

view plain print about
1reader: {
2 type: "cfquery",
3 rootProperty: "myQuery",
4 totalProperty: "totalCount"
5 }

NOTE: We will lowercase column names during preprocess, and you should remember that when creating your "dataIndex" attributes of your Model's Field configurations.

CFQueryReader will read ColdFusion query JSON serializations that are:

  1. The root of your return
  2. Nested within a larger return object (struct)
  3. Have been converted with ColdFusion QueryForGrid (not suggested, but supported)

And, because ColdFusion 11 includes the new "struct" type for the queryFormat parameter (even as an argument of your ajax request), CFQueryReader will properly parse that as well.

And, since we can now process the return prior to ExtJS calling it's internal readRecords() method, CFQueryReader no longer extends Ext.data.reader.ArrayReader as it's base class, but the more appropriate Ext.data.reader.JsonReader instead, allowing for a more native access approach under the hood.

I've made the new plugin available, for now, within the Ext5 branch of the repository. If you have the need, take it for a spin, and let me know if there's anything that might require tweaking.

Task Queues with IronWorkers

As most of my readers know, I still love ColdFusion. In 15 years of development there are still very few things that I've found that I can't do quickly and easily with CF. That said, ColdFusion isn't always the right tool for the job. I don't mean that ColdFusion can't do the job, only that it's maybe not the best tool. The UI stuff is one great example. The ColdFusion UI stuff was written so that the most novice of developers can spin something up fast. But, anyone who's had to do something a bit more complex quickly finds the limitations behind it's generated UI code. Another is extremely resource intensive processes. Doing image manipulation on one image occasionally isn't such a big deal, but what happens when you have to process several thousand? As powerful as the language constructs are for manipulating these images, the constant conversion of binaries into Java Image Buffer objects, the increasing back and forth within RAM, it begins to bog your server down to the point of a dead crawl.

This image example is just one of many, and when you're writing enterprise level applications you're going to hit these hurdles, and using ColdFusion in these instances is like using a hammer when you need an Xacto blade. This is when you start looking for better options to these processes, that can interoperate with your current ColdFusion services. In looking for just such an option, I was pointed to Iron.io.

Iron.io is a cloud-based set of services that can be run on many of the major clouds. They began with a distributed Message Queueing service (IronMQ) built for handling critical messaging needs for distributed cloud applications. Building upon their queueing abilities, they also created IronWorkers. IronWorkers are async processing task queues. They allow you to define what your process environment needs to look like, your task script to process, and then you can queue up tasks which can asynchronously run in their own independent container environments. Once queued, IronWorker with run X number of tasks asynchronously (X being dependent on the plan level you choose). Each task runs within it's own sandbox, with it's own independent RAM and processor allocation, so that one running process does not affect another. As tasks complete their sandbox is torn down, the queue continues to spin up the remaining tasks on demand, until the task queue is done.

The ease with which they've developed this service is amazing. Your ".worker" file defines your environment. Each instance is a headles Ubuntu system. You can select from a number of "runtime" setups, allowing you to work in the language that you're most comfortable with (Node, Ruby, PHP, Java, etc), as well as picking a specific "stack" if you want to mix and match the setup a bit. Each instance also already comes preloaded with several common Linux Packages (ImageMagick, cURL, SoX, etc). Within your ".worker" file, any additional files, folders, etc that you require can also be defined, including .deb packages.

Once your worker is defined, assets gathered, scripts written, etc., you then create your worker from a simple command line call. Once the "build" is complete, your new task service is ready to be called. This can be done via command line or (perhaps more commonly) via an http call. You can even define a webhook for your worker that can kick off your tasks from Git or elsewhere. You can pass variables to your task as part of it's "payload". This is just a simple HTTP Post, passing in name/value pairs that can be used within your process script. The "payload" is limited to 64k in size, so any files you may need on the fly (such as images to process) should be retrieved from within your process, most likely from somewhere like S3. Your process does it's thing, your script sends a command to exit the process (process.exit()) and it's done, spinning up the next task in the queue.

There really is a lot more to it. You can get as simple or as complex as you are capable of writing. IronWorkers are extremely powerful, and scale beautifully. After several weeks working on multiple processes, I can also say that their support is exceptional. They have HipChat channels setup to assist people, and they've been extremely responsive and helpful. They also maintain sample repositories of many common tasks, in a variety of languages, to help you get started while working in whichever environment you're most comfortable with.

What's Wrong With ColdFusion - 2014 Edition

Well, it's that time again. Later this week is the second annual ColdFusion Summit, put on by Adobe. Last year, just prior to the summit, I posed the question What's Wrong With ColdFusion. The feedback was immediate, and intense, with many people weighing in with their thoughts on what could be done to progress ColdFusion, both as a platform and a language.

Unfortunately, last year I wasn't able to attend the summit, and development of the ColdFusion 11 server was nearly complete, so most of that feedback had zero impact on the new release. While their were many advancements in security and JSON processing, some of the other introductions were seemingly underwhelming, or even unwanted, to many long time CF developers.

Now, to be fair to Adobe, and the ColdFusion product development team, they do have a responsibility to cater to their paying customers. Developers are rarely the ones buying the server itself, and the money handlers buying the server licenses rarely have enough real understanding of the development process to truly provide reasonable feedback when asked "What do we do next?"

Past issues aside, the ColdFusion product team is beginning to explore what they will do with the next version of the ColdFusion server, so now is a good time to begin the discussion again as to what Adobe might do the next time around. It is a delicate balance, so our feedback to the team needs to be positive, constructive, and backed by hard arguments to support our requests.

ColdFusion has been known, since the very beginning, for making the hard things easy, with it's first inroads being in the are of data connectivity. ColdFusion makes it extremely easy to connect with a wide range of datasources. This was probably the first true task of CFML (originally called DBML). It's meeting this type of root level need that ColdFusion might need to focus on going forward.

There were a number of core concepts that came out of last year's discussion:

  • Script Language Overhaul
  • Command Line Interface
  • Package Manager
  • Modular Architecture
  • Support for NoSQL Databases
  • Application Profiling

There are some great suggestions here, and Rupesh actually had included these in his final slide of the cf.Objective keynote presentations as possible items of inclusion in the next version of the ColdFusion server. I, personally, say to Adobe that it is important to review the past year to help define the focus for the future. It is important for the product team to not waste time on solutions that are already available, or that developers can already do themselves with a little study. In that vein, the CLI and Package Manager may not be areas for Adobe to spend time on, since we now have CommandBox available to us. Focusing on solutions that are already freely available to us seems to be a waste of time and resources, unless you intend to go above and beyond any available solution. A key question to ask, in this type of evaluation, is not only "is this already available?" but also "does this truly fit the purpose, enhancement, and growth of our product?"

Modular Architecture

So, what sort of things can Adobe do? Well, the Modular Architecture concept has multiple levels of benefit, not only for developers but for Adobe as well. By creating a "core" build of ColdFusion (db interactivity, includes, custom tags and the like), with "packages" of additional functionality (ORM, CFFORM, CFCLIENT, etc) that could be installed only when required, Abode creates an architecture where developers can customize their environment to the needs of their application. This decreases the overall footprint of the server, removes the "cruft" that might be unnecessary, and expands the licensing options for Adobe as well. I also believe this could significantly improve the cloud options that could be offered for the server as well, which could greatly improve adoption of ColdFusion as a platform.

Image Processing

Image manipulation was a long time coming to ColdFusion, and was a great addition once it finally arrived natively. That said, the current implementation is very process intensive and slow, and support isn't quite what you would expect from a brand like Adobe. It may be time to revisit image processing in ColdFusion, possibly even exploring avenues other than Java for handling these processes, to improve quality and performance.

Server Performance

There is strong evidence pointing to other CFML engines (Railo) as having much better overall server performance. It is difficult to allocate time and resources to something that currently works. But, just because it functions doesn't mean it shouldn't be improved upon. Response time is a critical piece to any application, and Adobe ColdFusion (as a paid and licensed product) should be the best of breed in this category.

Language Overhaul

Again, it's difficult to put time and effort into something that already works. But, the current syntax of script and functions within ColdFusion is a barrier to adoption of the server. This may be one of the biggest, and most asked for, enhancements that Adobe could provide to ColdFusion. Developers, who have traditionally worked in other languages, spend little time evaluating ColdFusion as an option because of the belief that ColdFusion is a second class language. Why would you use ArrayAppend() when every other language uses array.push()? Many objects do provide Java level object access, but many of these are undocumented, or aren't displayed in the documentation as the first line of how something should be done. While ColdFusion must support the current language constructs for some time to come, it really is time to make a more ECMAScript compliant form of script for ColdFusion. Or, at minimum, something much more uniform and familiar.

What Next?

So, with the summit starting later this week, I pose the question again "How can ColdFusion be better?" What would you like to see Adobe focus on for the next version of the ColdFusion server? And why? How? To what purpose? What do you think are areas where Adobe should absolutely steer clear of? It is important that we provide our feedback now, early in this process, and that we do so in a positive and constructive manor.

I will bring any comments given here back to the ColdFusion product team at the summit later this week (yes, I'm presenting this year). I encourage everyone who is going to the summit to voice your opinions directly to the product team as well. I look forward to hearing everyone else's input on this.

Mailgun and ColdFusion

A while back I was playing around with Ghost, a blogging platform written for NodeJS. Crazy simple to setup, one of the nuggets that I found during the 5 min install was a reference to Mailgun.

Mailgun is advertised as "The Email Service For Developers", allowing you to send 10,000 emails for free every month. It has a nice API, and is very simple to use. A common problem that many ColdFusion developers have, when writing an app, is having access to an SMTP server solely for development practices. You can setup a local server, use some Windows SMTP service, or traffic mail through your personal accounts, but it really is better to have a sandbox that's a bit more separated, for security, sanity, and other reasons. ColdFusion 10+ have a built in SpoolMail utility (based loosely on Ray's project), but sometimes you just need to see the real output in a mail client instead of the browser.

Setting up an account was easy on Mailgun. Right from the homepage was a link to the free sign up page. Once the account was created I was given a Mailgun Subdomain. This is my sandbox to play in, so I clicked on the domain name, for the domain admin page, then clicked on the "Manage SMTP Credentials" link on the "Default SMTP Login" line item. From this page I was able to create a new sender (you can use the default if you wish, but sometimes I'll setup separate addresses for specific tracking purposes).

The final stage is to put the settings in the ColdFusion Administrator. From the mail settings area I entered my new "sender" address as my SMTP server username, the password that I created with the account, set the SMTP server address to "smtp.mailgun.org", and changed the SMTP port to 587. (The default port 25 will NOT work, but 587 with no other settings changes will work fine.)

Now I can send mail with ColdFusion from my dev environment without having to load my own SMTP server or subbing out my personal addresses.

Math, PhantomJS and Command Line Arguments

So, recently I've been playing with PhantomJS. For those who are unfamiliar, PhantomJS is a headless webkit engine, that you can use to perform automated testing, take screenshots, and dozens of other things.

Phantom is still fairly new, and this has been a learning experience. My first trials were using the phantomjs-node module for NodeJS. I soon discovered (with some assist) that this bridge has some performance issues of it's own, and had to move to the command line.

When working with PhantomJS in this fashion, you write a script (in JavaScript), and pass it to PhantomJS along with any necessary arguments. I call this command line from NodeJS directly, but you can do so in Terminal or the cmd shell if you want.

Here was the catch. I had some math in my PhantomJS script. Not even complex math, really, just some basic equations converting milliseconds to seconds and rounding it off:

view plain print about
1var system = require('system'),
2 args = system.args,
3 pageArgs = {
4 elapsedTime: args[6] // this value is in the command line as 13762
5 };
6
7 var totalTime = Math.round((pageArgs.elapsedTime)/1000);
8 console.log("The answer is not " + totalTime);

Now, you and I know the answer is 14, but PhantomJS didn't seem to like that:

The answer is not 0

If I hard code the numeric value of elapsedTime (13762) into that statement then the math parses correctly. It appears that performing math on the values passed in causes the issue. I can change up the equation by adding 250 milliseconds:

view plain print about
1var totalTime = Math.round((pageArgs.elapsedTime+250)/1000);

But PhantomJS still doesn't like it:

The answer is not 413

Crazy, right? What I found was, if I needed to do any math to set some variables for use in the script, then I needed to do those in my node process, and pass the evaluated values to my PhantomJS script as arguments in the command line.

REVISION NOTE: Further testing shows that the command line arguments will all come across as a "string" type, which may be why these equations were miss firing. Remember to explicitly convert your command line arguments to the necessary data type, to avoid this type of issue.

Angular UI Sortable for Managing Sort Order

In my newest position I've been learning AngularJS. After diving in for a little over two months now, I'm seriously loving it, and wondering why I didn't seriously look at it much sooner.

One of the things it has taken a bit to get used to is that Angular does some things differently. I love Bootstrap but, like all other plugins, Angular's MVC approach has you do things "the Angular way", which can take a bit to break old habits. Luckily for us there are projects like Angular UI to help take the pain out of these sort of things.

One of the very few things that I use jQueryUI for is it's Sortable, Draggable and Droppable plugins. Angular UI has modules for these too. In this example, we're going to use the Sortable plugin for easy Drag n Drop display order management.

Using Drag n Drop for display order management is a fairly standard task. In the old days, we used up and down arrows that would typically be links, firing calls back to the server for database interaction and the refreshing the page for the new order. This was long, tedious, and a horrible user experience. Drag n Drop takes care of that. It allows the user to quickly define the order of things prior to ever sending data back to the server.

This demo isn't going to get into the server side mechanics of any of this, but rather the client-side stuff. First we'll start with the Bootstrap sample template (we'll use the Bootstrap CSS for basic layout stuff in our example). Don't forget to take out the Bootstrap JS file, and we're going to load the jQuery file up at the top ("why" will become clearer later).

view plain print about
1<!DOCTYPE html>
2<html lang="en">
3 <head>
4 <meta charset="utf-8">
5 <meta http-equiv="X-UA-Compatible" content="IE=edge">
6 <meta name="viewport" content="width=device-width, initial-scale=1">
7 <title>Bootstrap 101 Template</title>
8
9 <!-- Bootstrap -->
10 <link href="css/bootstrap.min.css" rel="stylesheet">
11
12 <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
13 <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
14 <!--[if lt IE 9]>
15 <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
16 <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
17 <![endif]-->

18
19 <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
20 </head>
21 <body>
22 <h1>Hello, world!</h1>
23
24 </body>
25</html>

A requirement of the Angular UI Sortable plugin is the actual jQueryUI sortable plugin, and it's dependencies. Luckily we can download a really trim file, giving us only what we need. We will insert the JS file in our document head, directly after our jQuery file.

view plain print about
1<script src="/path/to/file/jquery-ui.min.js"></script>

After that, we drop in the CDN link for AngularJS:

view plain print about
1<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.14/angular.min.js"></script>

And then we need the Angular UI sortable. I haven't found a CDN for this one, so you'll have to install it locally:

view plain print about
1<script src="/path/to/file/ui-sortable.js"></script>

And finally, let's create a file to put all of our application logic:

view plain print about
1<script src="/path/to/file/sortable-app.js"></script>

In review, and inventory of our code show us that:

  • We're loading the Bootstrap CSS
  • We're loading some shivs, for handling HTML 5 stuff in IE 9 or earlier (good luck with that)
  • We're loading jQuery, and our chopped down version of jQueryUI
  • We're loading Angular, the Angular UI Sortable plugin, and a file for our application

Our app file is empty right now. Let's get it started by initializing our Angular application. We do this by implementing our app as an Angular module:

view plain print about
1angular.module( "sortable", [ "ui.sortable" ] )

Basically this says our app is called "sortable", and it has a dependency on "ui.sortable". That part was easy enough, but now we have to associate the app with our display. We do this by modifying our opening html tag:

view plain print about
1<html lang="en" ng-app="sortable">

The next thing I'll do is create a controller for the next part of our app.

view plain print about
1.controller( "ListController", function ( $scope ) {
2
3})

view plain print about
1<body class="container" ng-controller="ListController">

We created a controller, and associated it with the body of our document. You can assign a controller to any element within your document, but this is a pretty simple example, so we're going simple here.

We need some data. Ordinarily you'd probably pull some data from the server, but for our demo we'll just create an array in our controller. The variable will be within the controller's scope, making it available to the block of our view within the ngController's elements.

view plain print about
1$scope.bands = [
2 {
3     name: "The Beatles",
4     rank: 1,
5     mode: "view"
6 },
7 {
8     name: "The Who",
9     rank: 2,
10     mode: "view"
11 },
12 {
13     name: "The Jimi Hendrix Experience",
14     rank: 3,
15     mode: "view"
16 },
17 {
18     name: "Led Zepplin",
19     rank: 4,
20     mode: "view"
21 },
22 {
23     name: "The Doors",
24     rank: 5,
25     mode: "view"
26 }
27];

view plain print about
1<body class="container" ng-controller="ListController">
2
3<!--- The "bands" are available inside this block -->
4
5</body>

Now that we can access our controller's variables, let's go ahead and output them to our screen:

view plain print about
1<div class="container-fluid">
2     <div ng-repeat="band in bands">
3         <div ng-if="band.mode == 'view'" class="row well">
4             <div class="col-xs-2">
5                 {{band.rank}}
6             </div>
7             <div class="col-xs-10">
8                 {{band.name}}
9             </div>
10         </div>
11         <div ng-if="band.mode == 'edit'" class="row">
12             <div class="col-xs-10">
13                 <input type="text" class="form-control" ng-model="band.name" placeholder="Band Name...">
14             </div>
15             <div class="col-xs-2 text-right">
16                 <button type="button" class="btn btn-primary" ng-click="addBand( band )">Add</button>
17             </div>
18         </div>
19     </div>
20 </div>
21 <hr>
22 <div class="text-right">
23     <button ng-click="createBand()" class="btn btn-primary"><span class="glyphicon glyphicon-plus"></span></button>
24 </div>

OK, I need to slow down a minute. Let's explain some code here. First, I loop over the bands for output. If it's in "view" mode, it shows you the rank, and the name. If it's in "edit" mode, then you see a text box and a button. Lastly, there's a button at the bottom for adding new bands.

There are two methods tied in here. Let's add them both to our controller:

view plain print about
1$scope.createBand = function () {
2    
3    var band = {
4        name: "",
5        rank: $scope.bands.length,
6        mode: "edit"
7    };
8    $scope.bands.push( band );
9    
10};
11
12$scope.addBand = function ( band ) {
13    
14    band.mode = "view";
15    
16};

Nothing crazy here. We aren't doing any server-side stuff in this demo, so all we're trying to say here is "create" will put a stub object last in our bands array, while "add" will change a step's mode from "edit" to "view".

You can try that out now, if you want. Clicking on the "Plus" button at the bottom will add a new line for you. Type in a Band Name and then click that line's "Add" button. Each time you hit "Add", the mode changes to "view", and you see the display order followed by the Band name. You can add as many bands as you like, but so far we still can't re-order them, and anyone who knows me knows that as much as I love The Beatles, Jimi is number one in my book.

You'll remember that we already have the Angular UI Sortable plugin available to us. We included the code, and the dependency, now we just need to implement it. On the div that contains our ngRepeat loop directive, just add the directive call for the Angular UI Sortable:

view plain print about
1<div ui-sortable ng-model="bands" class="container-fluid">

This adds the directive to the container, as well as defining the model involved. ngModel is critical here, or it won't work. If you reload your page right now, you'll find that you can Drag n Drop rows backwards and forwards with ease. But you're not done. Just for grins, hit your "Plus" button for a form row, then try to drag it. It's not easy to grab it, but it can be done. Suddenly you're trying to re-order stuff that isn't ready for re-order!

This is easily bypassed, by adding some configuration to your sortable. The jQueryUI Sortable has it's own API and configuration options, and you can easily apply them to your Angular UI Sortable as well. First, create a configuration object in your controller. We don't need much, just something that identifies what our drag handles are (if there's no handle, it can't be dragged):

view plain print about
1$scope.sortConfig = {
2        
3    handle: ".well"
4        
5};

We aren't applying the well class to edit rows, so this will exclude them. All we have to do is tell our directive to use this config:

view plain print about
1<div ui-sortable="sortConfig" ng-model="bands" class="container-fluid">

Now, if you reload and try it again, you'll see that you can no longer Drag n Drop the edit rows.

But, as you continue to drag items around, you'll notice that your display order column of data is way out of wack after a bit. The order should still be "1, 2, 3, etc", but now it's just a jumble. You know that the output is tied to your model, by accessing the band.rank, so as items get dragged around the rank needs to get updated. This can easily be done by watching the bands variable in the controller. We can set a method that says "when this changes, update it":

view plain print about
1$scope.$watch(
2    "bands",
3    function ( newValue, oldValue ) {
4        
5        for ( var i = 0; i < newValue.length; i++ ) {
6            
7            newValue[i].rank = i+1;
8            
9        }
10        
11    },
12    true
13);

The $watch function allows you to set model listeners. Here we are looking for any change to the bands array in the $scope. Since bands is a complex data type we include the "true" to match objectEquality, meaning it will watch it "deeply", for any change, and fire when it changes. We're looping every item in the array, and resetting the rank according to it's new position in the array. The rank may never have changed (each time a new row is added this will also fire), but the overhead is pretty minimal, and it's a simple way to ensure the display order is always right.

That's it. Sample Code can be pulled from the download link. If you have questions, comments, or war stories, just hit me with a comment below.

Re-Imagining BlogCFC

Team CF Advance is dedicated to providing and maintaining Open Source software built in CFML. We welcome anyone to get involved, whether it's writing code, creating tests, or assisting with technical documentation, any contribution is a good one. There's already a decent list of ongoing projects that we are working on or maintaining.

On that track, how about giving us a little feedback. Let us look at a re-imagining of BlogCFC. BlogCFC was the first Open Source CFML based blogging platform, and still shares a wide install base. In that knowledge, it is important to consider not only maintaining full feature support, but also maintaining support for existing linking structures, data integrity in data migration, as well as support for multiple platforms.

There are several other ColdFusion based platforms for blogging. Mura CMS provides a way of creating blog pages. Mura is not truly a blogging platform, per se, but does make it possible. Mango Blog is also a fairly pervasive blogging platform, providing some additional level of extensibility as well. ContentBox is another platform, based largely on the popular ColdBox framework. Like Mura, ContentBox is really a CMS, so not really designed to be a blog.

Knowing that these options exist, one question then becomes "Why do ColdFusion developers adopt WordPress or Ghost as their blogging platform?" I believe this comes down to three core reasons. 1) ColdFusion hosting can be expensive, 2) the complexity of these other ColdFusion based applications, or the learning curve involved in their customization and maintenance, is more than some of these developers want to undertake, or 3) the developer was just trying to learn something different.

Issue #3 doesn't factor into this discussion. The decision of any developer to expand their knowledge base and learn new languages is a good thing. Doesn't necessarily look good to have a largely ColdFusion topic specific blog hosted on PHP, but we aren't writing this to solely serve developers. We want to build an application that anyone can deploy for their use, whether it's a development blog, a political blog, or just something to journal your trip to Istanbul. 

So, you then have two main issues you possibly need to address: 1) Hosting, and 2) simple install, usage, customization, and maintenance.

Issue #1 will still be hard to address (Team CF Advance is already in talks with a hosting provider who may offer low cost Blog hosting with everything preinstalled), but it's worth noting for future discussions. Issue #2 is more easily addressed.

Proposals For Discussion

  1. The current BlogCFC is actually two applications, one desktop and one mobile. I propose that we look at a responsive site design for a single maintainable codebase that is then viewable from multiple screen resolutions and devices. I propose adopting Bootstrap as the client-side CSS framework, to assist in that task. (One contributor has already done this with the current version of BlogCFC, so we may get a branch for this one soon...)
  2. There is currently no set style guide for skinning sites in BlogCFC. First, I think it would be extremely beneficial to build a template converter system, that could take existing themes for WordPress, and possibly Ghost, and convert them to BlogCFC themes. There is already a huge number of available WordPress themes out there, and the Ghost theme base is expanding daily as well. If we can leverage that, and make it dead simple for a user to implement, then we've covered a major hurdle to adoption. Ghost themes are the easier side of the coin, as their themes are already in Bootstrap, and already responsive designs. Second, a base template with well thought out HTML architecture and a solid CSS naming convention, that is then thoroughly documented, should make it easy for a user to upload a stylesheet that would completely customize their blog. At some point, we could even build a module for theme customization directly in the admin. And, with all of it, we heavily review page architecture to completely maximize SEO visibility.
  3. I think BlogCFC would thoroughly benefit from a server-side framework. I propose using FW/1 with DI/1. FW/1 (and DI/1) is extremely powerful and light weight, operates as a very natural extension of ColdFusion's built in application framework, and it's subsystem architecture lends well to extension/addition of new modules. I'm not positive, but we may also be able to leverage it's routing capabilities when considering legacy link support.
  4. I think we definitely need an "override" service architecture, where all instantiated objects are (at inception) blank objects that extend our base objects. This is used for individual developer customization, in a directory that never gets updated (other than the addition of new objects). Our tests should always pass, in development. Any individual overrides are the responsibility of that developer, and them running our tests should allow them to identify holes in their changes with any upgrade.
  5. I think we should employ REST services for Ajax interaction, and SOLR for site indexing and search. I do not propose the use of ORM. I think it is very important that our SQL is handwritten, well optimized, and that our indexes are carefully chosen for high availability/performance.
  6. I propose using AngularJS as a client-side JS interaction framework for the "admin" side of the blog, for data-binding and interaction. I think it best to avoid it on the front-end, in favor of generated code bits and more favorable SEO. Bootstrap should be sufficient for any "site" front-end functionality.
  7. I think we put considerable effort into the user stories centered around UI/UX, especially in terms of the admin area.
  8. I think that we generate output, where possible. Blog posts are, ultimately, static bits of page content.
  9. I think that we build in a simple plugin architecture, allowing others to contribute to furthering the platform by creating new add-ons that just be "dropped in".
    1. As with all projects overseen by Team CF Advance, we will be including tests for BlogCFC, written in TestBox. This will allow us to build out the project iteratively, testing each step along the way, to ensure that one patch doesn't break other parts of the system.

      Again, none of this is in stone. Just some random thoughts to get the ball rolling. This is just to really start the discussion, so please share your feedback. We already have a working group within Team CF Advance, who is contributing to this discussion. Ultimately these projects are community driven to fulfill a need, so knowing what you want is critical to the process. This also doesn't deter us from any ongoing patching efforts. If anyone has begun writing any patches, please let the team know so we aren't duplicating effort anywhere (or just join The Team).

Firefox, IE, and Why Browser Testing is Vital

OK, I made a mistake. I know, my wife will be amazed that I admit that, but it does happen. Testing another developer's code, I missed a critical error. Consider the following bit of code:

view plain print about
1cValue = $("#StartDate").val();
2cMessage += $.trim(StartDate).length == 0 ? "Start date is required.\n" : "";

Do you see it? The conditional, in the ternary operator, should be looking at cValue, but what the developer had intended to rename that variable. There is no StartDate variable in the script, so it should error out as undefined.

And, in Internet Explorer, it does error. StartDate doesn't exist. But, I screwed up. I tested this code in Firefox, and it didn't throw an error, even though that value was empty. You know why? Well, if you put StartDate in the Firebug console, instead of undefined it gives you this:

view plain print about
1<input id="StartDate" type="text" name="StartDate" />

Yes, that's right, because StartDate was an exact case match to the id of an element in the DOM, it returned the actual DOM element (which, by the way, is a valid string with a length).

Now, I'm not going to complain about who's right and who's wrong in this scenario (the value of StartDate), but it does underline the importance of testing in all browsers.

Once Bitten...

ColdFusion UI: cfwindow

Not sure why I forgot to blog this, but a few weeks back Ray Camden told everyone that he wasn't going to tell you to stop using ColdFusion UI tags anymore. Instead, he and Adam Cameron started the ColdFusion UI the Right Way project. This project is a collection of articles and demo assets to show people that you can use other methods for displaying these dynamic client-side "widgets" in your applications, without using the CFUI tags.

Why? Well, it was a nice concept, but honestly (and in my continuing opinion) ColdFusion doesn't need to do UI. Yes, cfoutput, and looping over queries is fine, but tags like <cfform>, <cfpod>, and <cfwindow> are the devil's work. There have been long running issues with letting ColdFusion write your HTML, CSS and JavaScript for you, the chief among them being that they're bloated, buggy, limited, and (ultimately) unnecessary. You have a much finer degree of control over your application, and how it functions, when you write it yourself.

The CFUI tags were provided to allow the lowest common denominator (i.e. someone who can't really write code) to crank out something that works. They are not intended for skilled developers who write web sites and applications for a living.

And so, Rey kicked off the project with an article on replacing <cftootip> with the jQueryUI tooltip plugin, and I wrote the next article on replacing <cfwindow> with Bootstrap's modal plugin. I just checked the repository, and now there are also articles for <cfajaxproxy> and <cfdiv>.

The idea of the project is to show you a) that it's possible to do these things without the CFUI tags, and b) show you examples using a variety of libraries, to reiterate that you aren't limited to a single option.

Build A Better ColdFusion App: Simple Conditionals

In really large apps, every line of processing can become critical. Every small process can add up, in terms of overall overhead. Especially in high traffic apps. Sometimes, the little things can really make a difference. Even your simple code might be...simpler. Take the following example:

view plain print about
1<cfset mySelections = "1">
2<cfif #mine# eq "0">
3    <cfset mySelections = "0">
4</cfif>

I saw something like this in code this morning. Right off, the first thing you see is that all of the variables are unscoped, forcing the server to do a scopeCheck() to find out which scope each variable is a part of. Then there's the unnecessary bits to go along with it, like setting a variable, then checking another to reset the variable based on a condition. There's also the bit where the one variable is unnecessarily hashed. On top of all of that, variable assignments are one of those things it just makes more sense to write in CFScript. Let's take a look at how this might be improved:

view plain print about
1REQUEST.mySelections = (URL.mine) ? 1 : 0;

One line of code. Less keystrokes. Clearer references, and logic.

The "mySelections" variable, in this instance, is a part of the REQUEST scope (I upper case all variable scopes, to make them easier to spot when editing code).

In this situation, "mine" was a URL variable, param'd further up the page. We've used short-circuit boolean logic in our conditional statement. For those that don't understand that, any numeric value other than "0" is evaluated to true. If the value is "0", then it is false.

And speaking of numeric values, you don't need to quote them. Even though ColdFusion would figure it out, a quoted value is a string. Don't quote numeric, and don't quote Booleans.

We use the URL conditional inside of a ternary operator. Ternary operators were introduced in ColdFusion 9. The basics of this are, if the conditional is true, then use the value after the question mark. If the conditional is false, then use the value after the colon.

Finally, there's no need to hash the URL variable. You only need to hash variable references as part of output, or when they are used inside of quotation marks in code.

Now comes the really fun part. There really isn't any need for REQUEST.mySelections at all. Since the value or URL.mine was param'd, further up in the code, then the variable is always available here. Rather than copying that value to another variable (which takes up more memory), we can just reference URL.mine anywhere that REQUEST.mySelections was being used.

As you maintain your applications, it's always good to take some time and read through what's been written before. Refactoring little nuggets like this one, and a little testing, can eventually go a long way in preserving the life of your app and your server.

More Entries