Welcome!

ColdFusion Authors: Pat Romanski, Liz McMillan, Maureen O'Gara, Greg Ness, Andreas Grabner

Related Topics: ColdFusion

ColdFusion: Article

Making Assertions

Making Assertions

I'm a great believer in modularity of code. Modularity - breaking code into distinct pieces with a well-defined responsibility - is crucial if large-scale development projects are to be on time, on budget, scalable, maintainable and robust.

Modularity alone is no talisman. There's nothing magical about breaking code into pieces; a poorly organized set of a thousand lines of code can be just as disorganized when turned into 10 sets of a hundred lines. But often, long code sections indicate a lack of organization when the code grows topsy-turvy, with new functionality heaped onto old. Keeping modularity in mind when architecting a project can prevent runaway code. In programming, runaway anything isn't desirable.

Modularity means that each part of our code has a small, well-defined task to undertake. Fusebox (www.fusebox.org) is a popular development methodology that's built to take advantage of modularity. In my work with Fusebox I put together a documentation standard called "Fusedoc." You can read more about this in previous issues of CFDJ (Vol. 2, issues 1, 2 and 3). The point I want to make is that having a documentation standard that tells the coder exactly what variables will be passed into/out of the code is a tremendous help in cutting down on runtime errors.

Modules typically don't have control over the variables passed into them. Other coders may call your module, or you may receive information from a credit card authorizer or some other third-party software. Even if you're writing all the code, you're encouraged to think of each module as a separate, stand-alone black box (the computer term is encapsulation). Your module should:

  • Assume as little as possible about the world outside its scope.
  • Make clear what its assumptions are.
  • Protect itself (and its users) when those assumptions prove to be false.

Over time, programmers have developed the notion of "assertions" to protect code against false assumptions. Assertions are statements that the programmer believes to be true at runtime. They're particularly valuable when dealing with variables that will be present when the code executes. For example, if you're expecting to be passed a variable called itemPrice, you might assert that itemPrice should be a numeric variable, and it shouldn't be a negative number or have a value of zero. In case an assertion fails, we want some sort of error-handling routine to engage.

Some languages have built-in support for assertions. ColdFusion doesn't, but I'm going to demonstrate how to create your own assertion facility that you can reuse for all your fuses (or whatever type of modules you use). Begin by declaring your assertions in a custom tag called assert.cfm.

<cf_assert
assertion = "
myAge as a: IsNumeric( |a| ); |a| GT 0; |a| LT 100
myName as b: IsDefined( '|b|' ); Len( '|b|' ) GT 4">

The syntax may look a little odd, but it's really quite simple. Begin with the name of the variable that will be live when assert.cfm is run, aliasing it the way you would within a SQL statement: myAge as a. The alias is used purely as a shortcut (laziness being one of the great virtues of all successful programmers). When you need to refer to the variable in the assertion, just wrap the alias in pipe symbols, as shown above.

In the foregoing example the call to the custom tag makes assertions about two variables. Use as many as you wish, separating each variable/assertion pair with a carriage return. For each pair a colon separates the variable section from the assertion section. For example, the assertion section for the variable myAge states that the variable is numeric and its value should be greater than 0 and less than 100.

Assertions can be any valid ColdFusion statement that's evaluated to True or False. These are all valid assertions as ColdFusion can evaluate them all to a Boolean value:

myVar as a: IsQuery( |a| )
myVar as a: ListFirst( |a|, '.' ) IS 'fnc'
myVar as a: DatePart( 'yyyy', |a| ) GTE '2000'

Note: When the variable name is to be evaluated (rather than its value), place the alias (including its pipes) in single quotes. An example of this is the assertion that myName is, in fact, defined.

What if an assertion fails at runtime? The custom tag assert.cfm then throws an error that you pick up with <CFTRY>/<CFCATCH> blocks that surround the call to assert.cfm.

<cftry>

<cf_assert
assertion = "
myAge as a: IsNumeric( |a| ); |a| GT 0; |a| LT 100
myName as b: IsDefined( '|b|' ); Len( |b| ) GT 4">
<cfcatch type="FailedAssertion">
#cfcatch.message#
</cfcatch>
</cftry>

If you look at the code for assert.cfm (see Listing 1), you'll see that I use <CFTHROW> to generate an error of a custom type: FailedAssertion. My <CFCATCH> tag looks for such an error type and displays the assertion that failed. Of course, in production code I'd have some actual error-handling deal with the error. Finally, assert.cfm looks for the presence and value of a global Boolean variable, request.- testAssertions. If this value is FALSE, the tag will return without doing anything. This lets you turn off assertion checking during development.

If assertions are new to you, I encourage you to try them. They can be key allies in producing code that's robust and scalable. And while assertions should be used for protecting code against anomalies, they shouldn't be used instead of client-side validation. Validation is better done with JavaScript in the client's browser.

More Stories By Hal Helms

Hal Helms is a well-known speaker/writer/strategist on software development issues. He holds training sessions on Java, ColdFusion, and software development processes. He authors a popular monthly newsletter series. For more information, contact him at hal (at) halhelms.com or see his website, www.halhelms.com.

Comments (0)

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.