I searched and found CFSearching

I've been working on The Health Challenge over the last month and ran into a usability issue using my CFChart implementation. Apparently with enough data points, the labels would all jam into each other and become an unreadable mess. Have a look...

CFChart is actually a wrapped up implementation of WebCharts 3D, a full featured charting engine. ColdFusion ships with a Chart Stylizer that adds significant functionality to the generated charts. I've been using this Chart Stylizer for years now. However, I couldn't find an option to help me out with this X Axis Traffic Jam. Through a bit of google magic, I ended up on a blog I'd not seen before called CFSearching which addressed this issue for the Y Axis and after digging around the stylizer, I found a similar option for the X Axis.

view plain print about
2 <groupStyle skipLabels="{SkipLabels}"/>
Problem solved! See:

CFSearching has a number of very nicely written articles about CFChart, iText, Coldfusion and other important topics. For example, have you ever wanted to Create a Gantt Chart with CFChart or what about Calling ColdFusion functions from PDFPageEvents in iText? There are some very good articles up at CFSearching, go take a look!

Must Read: Matt Woodward on Refactoring Yourself In A Corner

Matt Woodward has a keen mind for technical topics. I've enjoyed his work for a variety of years on a variety of topics such as Mach-II, The CF Weekly, Conference Presentations, CHUG and others. Today, Matt wrote a thought provoking piece about a situation that happened on his team. The piece is entitled Avoid Refactoring Yourself Into A Corner

Apart from being well written, this article explains how a change in the stakeholders precipitated a supposed change in requirements. Implementing this requirements change would have been lengthy and time-prohibitive. Through the benefit of a sharp team and open dialogue, Matt's crew was able to forgo a long and drawn out refactoring.

Refactoring is a great way to make incremental improvements to a code base, or to deal with shifting requirements. Often refactoring requires a deep understanding of many ongoing processes and the dependancies between each. All of this complication can obscure possible solutions. As Matt points out:

It's easy when you're neck deep in an application to have your thinking get a bit rigid and not be able to see the numerous other ways in which the problem you're facing might be solved. So you start going down straight-line path you see in front of you and figuring out ways to tweak what's already there to handle a new requirement that may well be the antithesis of what your domain model was originally designed to address.

I challenge you to give Avoid Refactoring Yourself Into A Corner a read and then have an honest think about similar situations you've been in before.

Give Model-Glue A Piece Of Your Mind

We on the Model-Glue team have put together a simple survey and want to hear from you. Even if you aren't using Model-Glue or Frameworks or OO at all, your views are important to us.

How long will it take?

We've all taken long and boring surveys with endless questions requiring 1-10 rankings on sundry topics like "How much do you prefer crunchy peanut butter over creamy peanut butter?". Model-Glue is all about making things easy for developers and this survey is no exception. This survey was tested with several monkeys from the North Carolina Zoo. Each monkey was able to complete the survey and give quality answers, all within a 20 minute period. The way we see it, a reasonably intelligent ColdFusion user like yourself should be able to complete the survey in 10 minutes.

Would you kindly give us 10 minutes of your time for the Model-Glue Survey? We promise to read all answers and use the answers for the good of the framework.

No monkey's were harmed in the making of this Blog Post.

Practical Refactoring In ColdFusion - Live Example

Practical Refactoring

I've done a few presentations on refactoring in ColdFusion and in those presentations, I show a lot of code samples towards the end. No matter how eloquent I am in the first part of the presentation, the light always goes on for the audience when I show code. If you think about it, it makes sense, why try to explain concrete principles in abstract terms?

I do my refactoring in sweeps. A sweep is a cycle through the code in which I make incremental improvements. At the end of a sweep, the code should (it better) still function as well as it did pre-sweep. By breaking up the refactoring up into small sweeps, we can stay focused and keep out of the weeds.

In this article, we are going to look at some code that could use a refactor. Then we'll make a few sweeps through the code to tidy it up a bit. Along the way, I'll explain why I am refactoring out a certain piece. Keep in mind, we are all still learning. None of this is meant as Final Law of Software Design. I'm always refining my viewpoints and learning new things so I'll probably disagree with something I've done here at some point in the future. In the end, as long as this article is thought provoking for you, we've both won. Can Ya Dig?

First, here is the code sample. Scan the code and fix in your mind the general idea of what is going on. We'll pick back up on the other side.

The Original Code

view plain print about
1<cffunction name="onError" output="true">
2    <cfargument name="exception" required="true"/>
3    <cfargument name="eventName" required="true"/>
4        <cfif compareNoCase(trim(arguments.eventName),"onSessionEnd") And compareNoCase(trim(arguments.eventName),"onApplicationEnd")>
5            <cfif compareNoCase( arguments.Exception.RootCause.Type, "coldfusion.runtime.AbortException") >
6                <cfoutput>
7                <CFIF isDebugMode() is false>
8                <cfinclude template="/errortemplate.htm"/>
9                <CFELSE>
10                <h2>An unexpected error has occurred.</h2>
11                <p>Error event: #arguments.eventName#</p>
12                <p>Error details:</p>
13                <cfdump var="#arguments.exception#"/>
14                </CFIF>
15                <cflog file="#this.name#" type="error" text="Event name: #arguments.eventName#"/>
16                <cflog file="#this.name#" type="error" text="Message: #arguments.exception.message#"/>
17                </cfoutput>
18            </cfif>
19        <cfelseif not compareNoCase(arguments.eventName,"onApplicationEnd")>
20            <cflog file="#this.name#" type="Information" text="Application #this.name# ended."/>
21            <cflog file="#this.name#" type="error" text="Event name: #arguments.eventName#"/>
22            <cflog file="#this.name#" type="error" text="Message: #arguments.exception.message#"/>
23        </cfif>


Ok, answer to yourself the following questions:

  • Is the code right?
  • Do you know the purpose of the code?
  • Is it easy to understand?

The first question is a bit of a slippery slope. In my book 'Right' and 'Wrong' are determined by whether or not the code works. The code above actually works just fine. Compiled down to Java Bytecode, it happily processes errors with no defects, so it is 'Right'. When dealing with fuzzy subjects like code-cleanliness and proper organization, you'll find many more opinions than hard/fast rules. Clean Code is like Good Art, you know what you like when you see it. So stay away from calling other developers' code 'Wrong'. It only makes people defensive and defensive minds are not in learning mode.

Now that we've gotten past that, did you understand the purpose of the code? If you said "this code handles errors", give yourself 2 points. (The function name helped, didn't it?)

Was the code easy to understand? Can you verbalize what the code is doing? Try... no seriously, read this code from top to bottom. Can you do it? Did you have to reread parts of it?

It is a known fact that code we write personally instintively makes more sense to us than code other people write. This holds true even months after the code has been written. Good programmers write code that can be easily be understood by others. We are going to refactor this code to be more easily read.

First Analysis

A few easy things to point out... compare() and compareNoCase() are two functions that work opposite of how we read. If you use compare() to compare two identical strings, you get a return of 0, which ColdFusion treats as false, meaning we have to reverse our boolean logic in conditionals to accomodate this. Some people will tell you that compare() is faster than using an operator like IS or IS NOT. They might be right, I'm going to refactor the use of compare() and compareNoCase() out of the code anyways. If it turns out we needed the extra 'performance' of these functions, we should have implemented this part of the site in Hardware.

Next, the case is mixed inappropriately. Some operators are lower case, some are mixed case. Some of the conditionals are upper case, some are lower case. We'll normalize this and get it all looking consistant.

Also, this code has nested conditionals. Some times the only way to represent logical flow is to nest conditionals though most often it can be simplified for better readability. In this case, it looks like we truly care about a few conditions so I'll look at untangling the nest as well.

I'll put a few comments in as well, since that will help us remember what we were thinking in the middle of the refactor. Without further preamble...

First Refactor

view plain print about
1<cffunction name="onError" output="true">
2    <cfargument name="exception" required="true"/>
3    <cfargument name="eventName" required="true"/>
5        <!--- Exit conditions --->
6        <cfif arguments.Exception.RootCause.Type IS "coldfusion.runtime.AbortException">
7            <cfreturn />
8        </cfif>
10        <cfif trim(arguments.eventName) IS "onSessionEnd">
11            <cfreturn />
12        </cfif>
13        <!--- Log and Exit --->
14        <cfif trim(arguments.eventName) IS "onApplicationEnd">
15            <cflog file="#this.name#" type="Information" text="Application #this.name# ended."/>
16            <cflog file="#this.name#" type="error" text="Event name: #arguments.eventName#"/>
17            <cflog file="#this.name#" type="error" text="Message: #arguments.exception.message#"/>
18            <cfreturn />
19        </cfif>
20        <!--- use error template file or print to screen --->
21        <cfoutput>
22            <cfif isDebugMode() is false>
23                <cfinclude template="/errortemplate.htm"/>
24            <cfelse>
25                <h2>An unexpected error has occurred.</h2>
26                <p>Error event: #arguments.eventName#</p>
27                <p>Error details:</p>
28                <cfdump var="#arguments.exception#"/>
29            </cfif>
30            <cflog file="#this.name#" type="error" text="Event name: #arguments.eventName#"/>
31            <cflog file="#this.name#" type="error" text="Message: #arguments.exception.message#"/>
32        </cfoutput>

Second Analysis

Ok, That is a pretty good start. We got rid of compare() and compareNoCase() so the code reads a little nicer. We got rid of the nested conditionals so we don't have to keep track of several layers of boolean logic and we cleaned up the inconsistent use of case.

Another thing that I try to stay away from is using negative boolean logic. See where we check for the isDebugMode() function? I'd much rather have the TRUE case up top and have the negative case be the ELSE portion.

Also, some of the logging is duplicated. Looks like someone was Copy/Pasting :). Let's clean that up as well.

Lastly, we are going to put in good comments all the way down the line and use appropriate whitespace for readability.

Second Refactor

view plain print about
1<cffunction name="onError" output="true">
2    <cfargument name="exception" required="true"/>
3    <cfargument name="eventName" required="true"/>
5    <!--- We don't want to deal with the cflocation as an error, so bail --->
6        <cfif arguments.Exception.RootCause.Type IS "coldfusion.runtime.AbortException">
7            <cfreturn />
8        </cfif>
9    <!--- Lets deal with events next. --->
10        <!--- We don't want to deal with the onSessionEnd as an error, so bail --->
11        <cfif trim(arguments.eventName) IS "onSessionEnd">
12            <cfreturn />
13        </cfif>
15        <!--- From this point onward, we want to log the errors --->
16        <cflog file="#this.name#" type="error" text="Event name: #arguments.eventName#"/>
17        <cflog file="#this.name#" type="error" text="Message: #arguments.exception.message#"/>
19        <!--- Log as Information and Bail --->
20        <cfif trim(arguments.eventName) IS "onApplicationEnd">
21            <cflog file="#this.name#" type="Information" text="Application #this.name# ended."/>
22            <cfreturn />
23        </cfif>
24    <!--- Now, let's deal with explicit types we want to handle... --->
25        <!--- If we get here, we have something to handle --->
26        <cfif isDebugMode() is true>
27            <!--- print the error to the screen --->
28            <cfoutput>
29                <h2>An unexpected error has occurred.</h2>
30                <p>Error event: #arguments.eventName#</p>
31                <p>Error details:</p>
32                <cfdump var="#arguments.exception#"/>
33            </cfoutput>
34        <cfelse>
35            <!--- use the nice error handler --->
36            <cfinclude template="/errortemplate.htm"/>
37        </cfif>

Final Analysis

That looks even better. Now that our conditionals use positive boolean logic they are much easier to read. We also were able to remove duplicate logging calls. Not all code duplication is bad, of course, but when we can get rid of it and improve the clarity and readability, we all win.

The comments are also very descriptive and tell us the intent and reasons for the code. This way, other developers can quickly understand what we were trying to do, even if it isn't working properly.

Just for fun, I'm going to paste the finished code below without comments and whitespace. Read through the code and notice how it reads more like English language.

Final Code - No Comments/No Whitespace

view plain print about
1<cffunction name="onError" output="true">
2    <cfargument name="exception" required="true"/>
3    <cfargument name="eventName" required="true"/>
4        <cfif arguments.Exception.RootCause.Type IS "coldfusion.runtime.AbortException">
5            <cfreturn />
6        </cfif>
7        <cfif trim(arguments.eventName) IS "onSessionEnd">
8            <cfreturn />
9        </cfif>
10        <cflog file="#this.name#" type="error" text="Event name: #arguments.eventName#"/>
11        <cflog file="#this.name#" type="error" text="Message: #arguments.exception.message#"/>
12        <cfif trim(arguments.eventName) IS "onApplicationEnd">
13            <cflog file="#this.name#" type="Information" text="Application #this.name# ended."/>
14            <cfreturn />
15        </cfif>
16        <cfif isDebugMode() is true>
17            <cfoutput>
18                <h2>An unexpected error has occurred.</h2>
19                <p>Error event: #arguments.eventName#</p>
20                <p>Error details:</p>
21                <cfdump var="#arguments.exception#"/>
22            </cfoutput>
23        <cfelse>
24            <cfinclude template="/errortemplate.htm"/>
25        </cfif>

Not to shabby, yeah? The code works just like the original sample, but it is much easier to understand the flow of the function. Also, by doing our refactoring in sweeps, we simplified the code in stages, reducing the chance we'll miss something. Now go to the last sample and read it aloud. Notice how it feels natural when you read it? This code will definitely be easier to maintain in the future, won't it? There are lots of other principles to keep in mind when refactoring. We'll look at a few more in the next article.

Agree? Disagree? Tell me all about it in the comments.

P.S. Do you have code you'd like to see refactored?

If you have a code sample you would like me to refactor, send it to me using the Contact Form on this blog. Please try to keep the code chunk below 100 lines as that seems to be the limit of clarity for a blog article.

Transfer 1.1 has compelling features

Last night, Mark Mandel released Transfer 1.1. While only a .1 release on paper, this is a compelling upgrade for Transfer users. Let's dig in a little at some of the more fun features, shall we?

Performance Enhancements

Performance gains in any software is always appreciated. In Transfer, Mark notes about a 25% speed improvement. Upgrade for this alone!

TQL Custom Tags

TQL (Transfer Query Language) is designed to work like SQL and operates on your configured Transfer Objects. Thanks to Elliott, you get to use a common CFQuery like wrapper, making the syntax even more intuitive.

Cache Enhancements

There were several enhancements to the cache mechanism. Important stuff like Introspection and statistics for caching layer (Now you can see what is going on inside that caching layer), new time based caching config and enhancements to the object discard routine.

Bug Fixes

A number of bugs have been squashed in this release.

Download TransferORM v1.1

CFMeeup Presentation on Intermediate ColdSpring - Remote CFCs

Tomorrow Oct 16th, Tom Chiverton will present Intermediate ColdSpring - Remote CFCs to the online ColdFusion Meetup at 2 PM Est. Many people enjoyed my ColdSpring presentation and expressed interest in learning more about ColdSpring. Your wishes have been granted.

As the trend in development grows to include more SOA and/or Rich Client applications, we all seek to leverage our current codebases efficiently to deliver maximum value for our business units and customers. Using the ColdSpring framework to expose services is one key tool in the tool chest and should be a part of all advanced developers skillset. Tom's presentation tomorrow will hit several key areas and also provide a good base for those who want a more in-depth knowledge of ColdSpring and who also want to add Remote Access to their applications. Here is the published description from the man himself:

When exposing ColdSpring beans as remote (web, AJAX, AMF etc.) services there are several common problems that need to be solved. In this presentation, we'll discuss some of the problems and the ways ColdSpring can address these.

We will use a RemoteFactoryBean to generate a remote façade automatically, and then see how Pointcuts can automatically apply Before and Around Advice to this façade. We will see how these two powerful techniques simplify development and maintenance, by showing practical examples of both types of Advice.

If you are using ColdSpring already for Dependancy Injection, these techniques are a next step.

Tom Chiverton is an active blogger and has been a vocal contributor on several mailing lists. (He is also known for his rotating email signatures :) ) It is clear he knows his stuff. Be there tomorrow at 2PM EST, or Be Square!

Comment Spam Reduced to 0

Whaddya mean? Spam on nodans.com?

Blame popularity, notoriety or just plain old dumb luck. The Nodans.com blog has been recently attacked by a particularly motivated spammer crew operating from IP addresses in China and Russia. This meant endlessly going into my blogCFC administration and removing any spam posts from the night before. Annoying, redundant and frustrating.

Sounds Annoying, Redundant and Frustrating, what did you do?

In a moment of intense frustration, I pulled every IP address in my block list and wrote a script to block the entire Class A address for that specific spammer. Even though this solution would block whole countries and over 2 billion people from adding their comments to the conversation on nodans.com, it was a decent tradeoff to not deal with these persistent spammers.

Hmm... Seems drastic, like killing a fly with a shotgun

As the Rage subsided, I realized that even that Nuclear option was really only a temporary solution. I'd be forced to chase an endless stream of IP addresses and would still subject my readers to assaults of Viagra, Batteries and WoW gold. There had to be another way...After a litte effort, I reached the 0 Comment Spam solution

0 Comment Spam? Do Tell!

Jake Munson wrote a library called CFFormProtect. This library protects any form from robotic spam and well as human entered spam. It has all sorts of tests that it uses to grade comments as spamorific or legit and blocks the spamorific ones from posting. Imagine if ColdFusion had an IsSpam() function.

How long did it take you to implement?

From start to finish, about 20 minutes. That includes downloading the project, reading the docs, and altering the appropriate parts of BlogCFC.

That's it? Why didn't you do it sooner?

A good question. I've asked myself that. A few times.

And you have gotten 0 Comment Spam?

Yeah, 0. Not a single one. The weeks leading up to this I had been assaulted from numerous sources, sometimes as many as 30 comment spam a night. Now I get 0.

What are you waiting for? Go Download CFFormProtect today!

8441 Views Print Print Comments (15) Wow

Interested In Helping Promote Adobe User Groups? We Need Your Ideas!

Update: Download Meeting Notes

Today on Twitter, a number of us expressed interest in a meeting about how to promote and expand our User Groups. This is an important issue because we put a lot of time and effort into providing a quality User Group experience for our members. I imagine most groups are like ours (TACFUG) and have considerably more registered members than consistent attendees. We want to get some ideas out in the open about what is working/not-working and collaborate on ways to extend our reach. Your input is valuable and much appreciated.

I'll be scheduling and hosting a Connect meeting and want to open up to all User Group Managers/Co-Managers that are interested in this topic. The date for the meeting is Monday Sept 29th at 7:00 PM EST. If you are interested in attending, please register by leaving a comment below. If you can't make it and wish you could, leave a comment as well, maybe we'll switch the day/time just for you :).

The URL for tonight's meeting is http://experts.na3.acrobat.com/danwilson/ and it will be open before 7:00 EST.

Dan's Latest Home Improment Project

I like doing Home Improvement projects in my spare time. I find the manual labor to be satisfying, especially during stressful coding stretches. I'm on a project that just launched a revamped eCommerce project and there was some stress, and of course, an accompanying Home Improvement project.

The area on the side of my house that holds my garbage cans and recycling bins is very shady, so shady that nothing grows over there. I kept it covered in mulch in the past, since that has a nicer look than just pure North Carolina clay, you know what I mean?

Last winter, I cooked up a plan to make a fieldstone pathway/patio for that side of the house and I just completed it.

Here is a before shot, taken after I dug out most of the pathway. :

Here is an after shot, taken after finished setting the fieldstones and completing the edging:

Like most Home Improvement projects I do, I got a nice excuse to buy some tools (Rubber Mallet, Mason's Chisel). I bet I could do the next similar project a faster and better but the important thing is the side yard area is much improved and I had a good time getting this project done.

Best Tweet Of The Day

Today on Twitter...

PeterBell Garrhh - my helicopter is broke - got to take a van to Newark

And I thought I had problems in my life...