HTML in Application.cfm

The CF_Talk list recently had a discussion about whether it is acceptable to put HTML in Application.cfm. I thought the topic was a very worthy one, but ended up shedding as much heat as light.

I will do my best to shed some light on the topic.

In the short-run, putting HTML in Application.cfm (or including HTML from Application.cfm) can speed up development. If your project ends up changing over time (as most do), however, this can lead to code that is difficult to maintain.

This approach causes problems when different pages have different layouts. For example, a pop-up page would use a different layout than other pages on a site as might the home page.

A common solution is to use a different directory for pages with a different layout so that you could use a different Application.cfm. This makes it slightly more difficult to make common, site-wide logic available to the pages with alternate layouts. The difficulties are more pronounced for a home page with a different layout. Putting every other page in a different directory is impractical.

You could have Application.cfm decide which layout to use based on the file name instead. For example, index.cfm might have a different layout than other files. This is problematic when you or your client want to make a quick copy to test a change.

For example, you might copy index.cfm to index2.cfm at which point the correct layout is no longer being used. Or, perhaps the client might want an alternate home page. Yes, you can always edit Application.cfm with the new file name, but it is counter-intuitive. People expect a copy of a page to look the same as the original.

In fairness, one advantage to including HTML in Application.cfm is that it makes including one page from another very easy (whether or not that is a good idea is another discussion).

On the flip side, it is possible that a client might want part of the layout to change based on which page is being displayed. If you were including the layout code from the page, you could set a variable above the call to the layout to indicate this and perhaps to make some data available for that purpose. If you were including your layout from Application.cfm, that is one more file name check that you have to add to Application.cfm.

These extra bits of code in Application.cfm may not seem like a big deal, but over time they add up to confusing code that is difficult to maintain.

Good luck!

tag.cfc 0.1: Write Your Own Code Generator

Back when I had some free time, I started working on my own code generator. Partly because other code generators create code slightly different from my preferences and partly for the challenge. I have since gotten too busy to finish it, but I went ahead and finished the kernel component of the system - the one that actually generates the code.

One advantage of ColdFusion being a tag-based language is that generating code for ColdFusion largely means writing tags.

The best way that I can think to show how tag.cfc works is by example. cf_sebForm.cfc and cf_sebField.cfc are both components that are inherited from tag.cfc. They are each used to generate the code for my cf_sebForm and cf_sebField custom tags.

These components are actually very simple. They extend tag.cfc and each have to methods: vtml() and schema() which return the VTML for the tag and the XML Schema for the tag, respectively.

Here is some example code using these components to output the code needed for these custom tags.

<cfset sebForm = CreateObject("component","cf_sebForm").init()>
<cfset fields = ArrayNew(1)>

<cfset sebForm.setAttribute("formname","myform")>
<cfset sebForm.setAttribute("librarypath","/lib/")>

<cfset fields[1] = CreateObject("component","cf_sebField").init()>
<cfset fields[1].setAttribute("fieldname","blah")>
<cfset fields[1].setAttribute("label","Hello")>
<cfset fields[1].setAttribute("type","text")>
<cfset fields[2] = CreateObject("component","cf_sebField").init()>
<cfset fields[2].setAttribute("label","Submit")>
<cfset fields[2].setAttribute("type","submit")>

<cfset sebForm.addTag(fields[1])>
<cfset sebForm.addTag(fields[2])>

<cfoutput>
<pre>#HTMLEditFormat(sebForm.write())#</pre>
</cfoutput>
The code that this code would output follows:
<cf_sebForm formname="myform" librarypath="/lib/">
<cf_sebField fieldname="blah" type="text" label="Hello" />
<cf_sebField type="submit" label="Submit" />
</cf_sebForm>
In order for the previous code to be useful, of course, you wouldn't want the values to be hard-coded. You would need to get those values from a form or from data in a database, for example. While tag.cfc doesn't handle that part for you, it will handle much of the last-step of code generation once you have gathered that information.

Incidentally, when I was working on my own code generator (a task I hope to return to someday), I used my DataMgr component to get the structure of the database. Since it works the same across multiple databases, it makes for a nice crosss-database solution.

You can download tag.cfc from my site. I also have to sets of tag CFCs that inherit from tag.cfc. The first, "CFCs" includes components to generate cfcomponent,cffunction and cfargument. The second, "sebtags" is a set of CFCs used to generate my custom tags.

This code illustrates my best understanding of good OO code, so it should be a good example of such. That being said, if someone can show why it isn't a good example of such, let me know.

Good luck!

Switch between Client and Session Variables

Many developers often ask whether client variables or session variables are better. The answer to this question (as with many others) is "It depends". A common suggestion is to create some kind of wrapper for these scopes to allow easy switching.

The SessionMgr component provides this functionality. It will allow you to specify Client or Session scope when you intialize it. After that, you can have SessionMgr handle all of your Client/Session data. If you ever need to change which scope you use, just change the scope passed in and re-initialize SessionMgr - no need to change any other code.

SessionMgr will ensure proper locking and handle complex variables in Client scope, so you don't have to worry about those issues in your code.

[More]

Starfish Rocks!

Ray Camden recently released an initial version of his free Starfish debugger. This is the first product that I have tried that really provides the information that you need to get detailed performance information on your ColdFusion applications.

I tried it because we were having performance problems on an application that I have been working on. The information provided by the ColdFusion debugging provides a great deal of detailed performance information. Unfortunately, it provides that information only on a per-page basis - which is difficult to aggregate and use to find performance problems that crop up over time.

The logging provided in the ColdFusion administrator will report long-running pages, but it won't provide more detailed information about which routines and queries.

Starfish provides all of the information available in the debugging aggregated over multiple requests and does so from the convenience of the ColdFusion administrator.

The information is easy to sort and provides information about how long templates take to run as well as CFC methods (distinguished by values passed in as arguments) as well as queries (also distinguished by the contents of the SQL).

As a version 0 product, you might expect that it would have some bugs. So far, I haven't found any that are major. The units of measurement aren't consistent (seconds used for Min Time as milliseconds used for the other two). For the record, I prefer seconds. Either the "Template Time Data" doesn't work or I haven't figured it out. These are very small nits, however, and it runs much better than one should expect from version 0 software.

In just two days of use, I have found several small (and one fairly large) performance problems on the site and fixed them. I highly recommend you check it out.

Good Luck!

PS - Raymond, I hope you enjoy your new copy of Civilation IV.

Ben Forta on Enterprise Gateways

I just returned from hearing Ben Forta speak in Dallas. My friend, Will Spurgeon, and I took the short drive from Tulsa to Dallas to hear him speak (about 4 1/2 hours).

It was well worth the drive.

I have been intending to look into enterprise gateways, but I have always felt like it was too much and I didn't have the time. After Ben Forta's presentation, enterprise gateways seem much more approachable. Still plenty to learn, of course, but I feel that I have a good place to start.

Enterpise gateways allow ColdFusion to respond to a variety of non-HTTP requests. Including file actions, sockets, SMS, IM, and asynchronous ColdFusion requests. While all of these are interesting, it is IM interaction that has the potential to meet a somewhat immediate client need (the ability for users of a site to interact directly with him without his needing to be on the site himself).

Ben has also written some blog entries about interacting with IM via ColdFusion.

Building an IM Bot

He also has a CF Live Docs Bot, which allows you to send the name of a ColdFusion tag or function to the bot and get back a link docs on that tag or function. Pretty handy!

I didn't see a version that worked with MSN IM, so I downloaded Google Talk - which I was planning to download to use with an IM Bot (as above) anyway. I'm not sure it is actually any easier than using cfdocs.org, but still very nifty.

Hopefully I will get to play with an IM gateway soon and blog my findings (assuming I have anything interesting to say on the topic).

Ben Forta was a great presenter and very patient with questions both during and after the presentation.

Thanks Ben for such a great presentation! Thanks also to the DFW CFUG for hosting such a great presentation.

Now I just have to get a production install of ColdFusion MX 7 Enterprise...

SQL Server Comments and ColdFusion MX 7

This problem (and solution) comes from my good friend Will Spurgeon of Tech-It-Easy. He has long been using SQL comments when developing for SQL Server. The advantage of this approach is that you can see these comments when reviewing transactions in SQL Server Profiler. It can really help in a lot of trouble-shooting situations.

For example:

[More]

ColdFusion Doesn't Need NULLs

I recently read Joe Rinehart's great blog on OO features in ColdFusion. His blog is, largely, a response to some discussions on the CFCDev list. In short, he feels that many of the OO features that some people are requesting be added to ColdFusion aren't a good idea.

The argument that I found the most interesting (on Joe's blog and the CFCDev list) was regarding NULLs.

The Problem

He defines the problem better than I can (reading his post first if you have time). Essentially, the problem is that having a returntype (say "numeric") for a method is helpful. With that help, however, comes a challenge. It isn't possible to return no value. With a returntype of "string", you could return an empty string. With a returntype such as "numeric", however, returning zero isn't the same. In fact, an empty string might not be the same as no value either.

Many (typed) languages (including SQL) handle this with a NULL. Basically, a NULL is an indication of no value at all.

The argument made by many is that NULL is needed so that we can take advantage of returntype and still return no value from a method.

My Proposal

I have run into this issue myself. Of course, adding a NULL to ColdFusion (or any typeless language) seems like a pretty major change.

As it happens, ColdFusion already has its own conception of this. A variable can be undefined (that is non-existent). A component with a returntype of "any" or "void" need not execute a cfreturn (and thereby return a value).

I would like an optional attribute (maybe "allowVoid"), which would allow a method with any other returntype to not execute a cfreturn. The attribute would have to default to false in order to maintain backward compatibility.

It is already possible to have the following code leave "foo" undefined (if myMethod doesn't execute a cfreturn). This change would only mean that myMethod could have a returntype of something other than "any" or "void" (if allowVoid is set to "true").

<cfset foo = comp.myMethod()>

This nice thing about this approach is that it plays nicely with how things work in ColdFusion already. It doesn't add a concept of NULL, which is foreign to ColdFusion. It does, however, add the ability to return nothing from a component without giving up the type-checking benefits of returntype.

I might also add that the problem already doesn't exist in cfargument as you can already set required="false" in cfargument.

Well, there you have it. If you have any thoughts on my proposal, I would love to hear them. If you have any thoughts on other OO features or anything else discussed in Joe's blog, please post them there to keep that discussion coherent.

Hide Your Errors (Application Events: onError)

I have noticed that a great many ColdFusion sites show the default ColdFusion error when something goes wrong. This is a bad idea for many reasons.

In the "Research-Based Web Design & Usability Guidelines" (pdf) put out by Usability.gov, "Detect Error Automatically" was given an importance of 5 out of 5. In his popular "Top 10 Web Security Tips" article, Michael Smith listed "Have an error-handler" as his number-one security tip.

In his article "Toward Better Error Handling" (part 1, part 2, part 3), Charlie Arehart covers some techniques for error-handling in ColdFusion. As of the release of ColdFusion MX 7, a new method exists for handling errors in ColdFusion; the onError event of Application.cfc.

[More]

Ditch ColdFusion Evaluate()

The topic of evaluate() recently came up on the CFCDev list. Sean Corfield asserts that evaluate() is only needed for backward compatibility since all variables scopes can be treated as structures as of ColdFusion MX. He argues against its use in any new code siting performance reasons.

Here are some situations in which you might be using evaluate(), but don't need to do so.

1) Getting the value of a Form variable where the field name is (or contains) a variable.

Using evaluate:
<cfset foo = evaluate("Form.bar#i#")>

Without evaluate:
<cfset foo = Form["bar#i#"]>

Since Form is now a structure, you can use structure syntax to get the variable. This will work in other scopes as well (request or URL, for example).

2) Get the value of a column in a query where the column name is (or contains) a variable.

Using evaluate:
<cfset foo = evaluate("qBar.text#lang#")>

Without evaluate:
<cfset foo = qBar["text#lang#"][CurrentRow]>

The important thing to keep in mind with queries, is that while you can use structure syntax, you must indicate the row. The "CurrentRow" variable is handy if you are within a query loop (using the query attribute for the given query in either <cfloop> or <cfoutput>). Otherwise, you will have to pass in the number another way. If you know you want the first row, for example, you could simply pass in 1.

3) Checking the existence of a variable.

This isn't actually an evaluate() issue, but I know that Mr. Corfield also recommends avoiding isDefined() in favor of StructKeyExists(), so I thought I may as well cover that here as well.

Using isDefined()
<cfif isDefined("Form.bar#i#")>

Using StructKeyExists()
<cfif StructKeyExists(Form,"bar#i#")>

The same principle applies here as from the first example. Since "Form" is a structure, it can be treated as such (as can other variable scopes).

If you have any other situations in which you currently use evaluate(), let me know.

Good luck!

UPDATE

Scott Stroz noted that you can use the same approach with queries as well. Since the format doesn't show up correctly from comments, here it is:

The format for that would actually be:

<cfset foo = qBar["column name"][CurrentRow]>

or, with a variable,

<cfset foo = qBar[colvar][CurrentRow]>

Scott's example for queries and columns:

<cfset foo = qBar["A column with spaces"][currentRow] />

<cfset foo["a structKey with spaces"] = 'bar' />


Thanks Scott!

Session is Invalid Solution

Paul Kenney has found a solution to the "Session is invalid problem" in ColdFusion MX 7. I had a similar approach, but I think that his is more graceful and I will switch to that approach soon.

More Entries

BlogCFC was created by Raymond Camden. This blog is running version 5.8.001.