Entries Tagged as Best Practices

Best Practice: Separate App config from Framework Config

Best Practices is something that I don't think anyone, anywhere, ever gets 100% right. There's no "right" way to do everything. But we're striving to do things better all of the time, right?

For example, in the ColdFusion world we have Unit Testing frameworks like MxUnit, CFUnit, CFCUnit, and Selenium. I know Selenium isn't just for CF, but it does integrate nicely! Test-driven Development is all the rage these days, and while that's a good thing it's not for everyone and certainly something very difficult to "get right."

Consider that in a large project -- one with 1,000 test cases -- if you refactor some code that affects 10% of your tests, you have to re-write 100 test cases. This isn't to say that TDD is a bad thing... far from it! Just that there is no silver bullet, and that you always have to be thinking ahead to try and do things right the first time, and not repeat yourself.

This brings me to a change I recently made in Grub.

Grub uses the Model-Glue framework to, as I like to say, "code less and do more." In Model-Glue's XML config, we have an <include> tag that allows us to separate our config into logical sections to keep maintainability high. But did you know you can do the same thing with your ColdSpring XML config?

Well, not with out-of-the-box 1.0... You have to download the Bleeding Edge Release, here. The feature was first mentioned in November of 2006, when Jared announced that he had written it. It was then blogged by a bunch of other ColdSpring users, but I don't think any of them mentioned that it wasn't yet included in the official release except Mark Drew, and even then I don't think I picked up on that fact until well after my second or third reading of his post. I ended up reading this bug report that clued me in, after about an hour of trying to figure out why the necessary method wasn't where it should be.

After you download the BER and update your local copy of ColdSpring, you can add lines like the following to your beans XML:

I used this code to separate out my Model Glue config -- which will be constant in every environment, but different between them (debug, reload, allow event generation in dev; no debug, no reloading, and no generation in production or staging) -- from my bean definitions, which change frequently as I work on the applicaiton. Now, with my ANT build script, I can build and deploy my project without ever thinking about config, and I know it will always be right because it will never change without me doing so manually.

I'm somewhat surprised that over a year has gone by and it's still not included in the official release. This means that I'm currently using two unreleased frameworks in Grub (the other being the Model-Glue 3 RC), and that I am going to have to include them both in the download to make it as user-friendly as possible.

in Best Practices | ColdFusion | Frameworks | 5 Responses Posted 2008-07-29 01:12

Excellent article on customizing Application.cfc per-environment

About a week ago (who has time to keep up with this stuff?) Ben Nadel wrote an excellent article about Setting custom Application.cfc settings based on the server environment, which I think is great supplemental reading to my article about Setting environment-specific Application.cfm settings.

As Ben points out, the important distinction is that his internal function is only setting up the Application.cfc settings, not Application variables; while the goal of my post was to aid in determining which values to set for your Application variables.

We differ on the fact that he thinks you should avoid having configuration files outside of the web root, while I think they are just fine as long as you code appropriately. In my example, if the file is not found, or the contents don't match any of the expected contents, then we assume we are in production. This may cause some frustration while configuring new development, test, QA, or staging environments; if you forget that the file has to exist and have appropriate contents, but ensures that unless someone deliberately sets a non-production value in the config file, production will always be right.

in Best Practices | ColdFusion | No Responses Yet Posted 2007-08-17 06:52

Scope Nazi

Some people are anal retentive about not using pound signs where they aren't necessary (my boss), some people are anal retentive about var-scoping local variables in components (Ray Camden); and then there's Me. I'm anal retentive about scoping all of your variables. Consider this. When you execute this line of code:

<cfset foo="bar">

There is only one thing that could happen: A variable in the "variables" scope is set with the value "bar". (Unless you're in a CFC, in which case you should be using var scope, like Ray advocates. ;) On the other hand, when you execute this line of code:

<cfif foo is "bar">

The value could potentially be read from one of NINE scopes, in MX7. To clarify, I'm fine with implying your variable scope, however, it irks me to no end when you infer your variable scope. (What's the difference between implying and inferring?) To wit, a colleague pointed out that she was having issues with this code:

<CFQUERY name="orders" datasource="#application.DS#"> 
    SELECT distinct ORDERS.ord_id, ORD_placedOn, ORD_billFirstName, ORD_billLastName, 
        ORD_Total, Ord_Total + isnull(Adjust_total,0) as net_sales, ORDERS.OSTATUS_id, 
        ord_assignedTo, OSTATUS_name, orders.ORD_shipAmount, p.prod_assembly_reqd 
    FROM ORDERS, OrderStatus, payflowpro,
        (Select ord_id, sum(adjust_total) as adjust_total from Order_Adjust where result=0 group by ord_id) as Order_Adjust, 
        Order_Item OI, Item_View I, Product P, ordershipaddr osa 
    WHERE ord_completed = 1 
    AND ...
</CFQUERY>

<cfoutput query="orders"> 
  <cfquery name="getManu" dataSource="#application.DS#"> 
    select distinct man_name, manufacturer.man_id, item_name, album_id 
    from Order_Item, item_view as item, product, manufacturer 
    where order_item.ord_id=#orders.ord_id# 
    and item.item_id=order_item.item_id 
    and item.prod_id=product.prod_id 
    and product.man_id = manufacturer.man_id 
  </cfquery> 
  <cfloop query="getmanu"> 
    <cfif (listfind(application.vendor_inhouse_list,man_id) gt 0 or 
      listfind(application.inhouse_album_list,album_id) gt 0) and 
      prod_assembly_reqd eq 'Y' and 
      oitem_sample neq 1>A<cfelse> </cfif> 
  </cfloop> 
</cfoutput>

The issue was that sometimes the variable prod_assembly_reqd was undefined. The resolution was simply to use proper scoping. This is the code I sent back:

<cfif (
        listfind(application.vendor_inhouse_list, variables.getManu.man_id) gt 0
        or 
        listfind(application.inhouse_album_list, variables.getManu.album_id) gt 0
    ) 
    and 
    variables.orders.prod_assembly_reqd eq 'Y'
    and 
    ?????.oitem_sample neq 1
>A<cfelse>&nbsp;</cfif>

Using explicit scoping like this leaves zero room for confusion where the data is coming from. The last line, where I used the scope "?????", I noted that this is probably inferring the Variables scope, but that she should check on it and make sure she indicates which it is.

in Best Practices | ColdFusion | No Responses Yet Posted 2007-07-24 06:48

Please use label tags!

Please, please, PLEASE use label tags! Accessibility has been on a lot of people's minds for a while now, and I'm surprised to see the lack of zeal for the HTML label tag.

Recently, people have wised up and made entire blocks clickable, not just the text within them, sometimes doubling, tripling, (or more!) the size of the rectangle representing the link. Generally you recognize one of these menus when you hover over the menu item's row and it or the link changes color, and your cursor changes to a hand. This is a great step forward in usability, and it pains me to see the forms equivalent being neglected like the red-headed step child.

Take for example, your average navigation menu. Mine will do: over to the right there. --->

When you hover your mouse over a menu item's row (list item), the row changes background color, the link changes color, and the mouse cursor changes to the "hand" to indicate that you are over a link.

Think about it: A check box is only about 15 pixels tall by 15 pixels wide. That's a very small rectangle and not only requires extra precision on the part of your average user, but may prove to be difficult for someone with poor eyesight or arthritis. A radio button is even smaller. In most cases, your checkbox is going to have some text next to it that describes what checking it accomplishes. Wrapping that text in a label tag will only take you a few more seconds, and exponentially grows the clickable rectangle for that form control.

Try clicking on the text labels:


Try clicking on the text labels:


Add some CSS to change the cursor, and you're in good shape:

label { cursor: pointer; }

Try clicking on the text labels:


Your users and I thank you!

in Best Practices | HTML/CSS | No Responses Yet Posted 2007-06-08 05:41

Application.cfm: Determining environment

As I mentioned two weeks ago, application.cfm tweaking is important, but reaching the best compromise between efficiency, readability, and functionality is highly subjective and there are exceptions to every rule.

The question of how to manage application.cfm in multiple environments or between servers in a cluster is one that every developer you asked would probably answer differently. You could have separate versions of the file for every server, you could have a flag set at the top that indicates what set of code to run, you could check CGI variables... there are a hundred ways to skin a cat.

My personal preference is to use CFFile. But before you start to criticize me based on the overhead of reading a file, hear me out. If you still disagree with me, feel free to leave a comment to that effect!

Before diving in to the code, let's consider some rules of thumb.

  1. You want your production environment to be the fastest path through the code, for what I hope are obvious reasons.
  2. You want the default case to be production, so that if all else fails, at least production will work.
  3. Your solution should be extensible and scalable for the addition of future environments.

I will admit up front that more overhead is required to use a CFFile tag than to check the contents of a CGI variable or request scope variable, or quite possibly any other method. But with that said, this method is set it and forget it. In order to implement this method, we need to add a small text file to every server.

C:\env.txt on my development server: DEV

I'm sure you can extrapolate this to be applicable for your other servers, right? Test should say "TEST", Staging should say "STAGE", and so forth. With one exception: Don't create a file for Production. Or at least, you don't have to. And your code will run precisely 4.3 iotas of a second faster if you don't. Because once you've created these files, you should add this code to your application.cfm:

<cfif not isDefined("application.env") or isDefined("url.resetAppVars")>     <cfif not FileExists("C:\ENV.TXT")>         <cfset application.env = "production">     <cfelse>         <cffile action="read" file="C:\ENV.TXT" variable="application.env">         <cfset application.env = trim(lcase(application.env))>     </cfif> </cfif>

Now, going back to my three rules above:

  1. Production is the fastest path through this code, assuming you don't create the ENV.TXT file in that environment.
  2. The default case is production: If the file doesn't exist, assume production!
  3. Scalable? Check! Adding a new testing server called Test2? Make it's file contents "TEST2" and you're off and running.

Now that you've defined your environment, it's time to set some environment specific variables. Hopefully you remember that you should only set your application variables when they don't exist in memory! I've already demonstrated how to accomplish this above: Check whether or not one of them is defined first. And while you're making that check, you should always give yourself the ability to refresh them at your will, which is why I included the check for the variable url.resetAppVars. Instead of restarting ColdFusion services to reset your application variables, you can simply append ?resetAppVars=true to your URL.

When setting your environment specific variables, the same rules apply: Production should be the fastest path through your code, the default case, and your solution should be easily scalable. I like this approach.

<cfif not isDefined("application.myVariable") or isDefined("url.resetAppVars")>     <cflock scope="application" type="exclusive" timeout="30">         <cfif application.env is "production">             <cfset application.myVariable = "my value for production">         <cfelseif application.env is "dev">             <cfset application.myVariable = "my value for development">         </cfif>     </cflock> </cfif>

in Best Practices | ColdFusion | No Responses Yet Posted 2007-05-09 06:17