ColdFusion 101: Config Files A-Go-Go

One of the first things a beginning ColdFusion developer realizes, at least after their first few applications, is that configuration is something you want to make as easy as possible. What do we mean by configuration? Well imagine your web site has various forms. Forms for product reviews. Forms for job applications. Forms for budding Jedi Masters. You can build all of these forms so that on submission, they email to some email address, let's say junkbin@microsoft.com. This is all fine and good until the client says, "Hey, can we have all forms go to bjork@scandi.com instead?"

So, if you are using a decent editor, this isn't a big deal. But obviously a multi-file search and replace isn't the answer.

What you need is a config file, son! (Funny, and off-topic story on where that quote came from later on.)

What do we mean by a config file? A config file is a human-readable file that contains configuration information about the application. A perfect example is the scenario we described above. If the "Send All Forms Here" value had been in a config file, it would have been even easier to update the value based on customer demand. It may even have been something the client could do! (Yes, I know, scary, but it's ok. Clients are perfectly ok working with files as long as take deep breaths and type very slowly.)

There are a couple different ways we can create a config file. For this first post, we are going to start with the venerable "ini" file format. (Don't worry, the XML example will be in the next post.) What is an ini file? Ini files have been a part of Windows for many years. Scientists have reported discovering early versions of ini files buried deeply under prehistory middens. Basically they are text files with name/value pairs. Here is a simple example:

name=Jacob Camden
age=5
rank=Padawan

Simple enough, right? Along with name/value pairs, an ini file will be separated into sections, using bracket notation. Every ini file will have at least one section. Here is a simple example:

In the ini file above, there are three sections: live, staging, and dev. (I'll talk more about these sections in my third post.) Each section has an email entry with a different value. My application can select the email key from any section it wants to, or load them all up.

So how can ColdFusion use ini files? ColdFusion comes with a set of built in functions to read and write ini files: GetProfileSections, GetProfileString, and SetProfileString. Let's start by defining a simple ini file. Take the code below and save it as test.ini.

[default]
email=ray@camdenfamily.com

Now let's look at how we can read the file. We will begin with a simple example of getProfileString:

<cfset iniFile = expandPath("./test.ini")>
<cfset email = getProfileString(iniFile, "default", "email")>
<cfoutput>Email=#email#</cfoutput>

The first line simply creates a full path to our ini file. The first attribute that getProfileString expects is the full path to the file. (Am I the only who wishes that more ColdFusion functions would allow for relative paths?) The second argument we pass is the name of the section. In this case, we know it is default. Lastly we tell the function what key to retrieve. If you run this example in your browser, you will see my email address. (Spam away, folks.)

What happens if you pass a section or a key that doesn't exist? No error is thrown. Instead you get a simple empty string. You could example the ini file before hand using getProfileSections:

<cfset iniFile = expandPath("./test.ini")>
<cfdump var="#getProfileSections(iniFile)#">

The function getProfileSections returns both the sections of the ini file and all the keys within that ini file. Since our ini file had one section, our returned structure will have one key, "default". Because that section had one key, "email", the value of the struct's "default" key will be "email":

So we can now read ini file settings. Obviously you would want to store and cache these settings:

<cfif not isDefined("application.settings") or isDefined("url.reinit")>
   <cfset iniFile = expandPath("./test.ini")>
   <cfset sections = getProfileSections(iniFile)>
   <cfset data = structNew()>
   <cfif structKeyExists(sections, "default")>
      <cfloop index="key" list="#sections.default#">
         <cfset data[key] = getProfileString(iniFile, "default", key)>
      </cfloop>
      <cfset application.settings = data>
   <cfelse>
      <cfthrow message="Ini file has a missing default section!">
   </cfif>
</cfif>

<cfdump var="#application#">

What's going on here? First we check to see if an application variable called settings is defined. If it isn't, or if we pass in a special URL variable to refresh the cache, we know we need to load our settings. We use getProfileSections to first load up the sections from the ini file. After checking to see the there is a default section, we loop over the keys from the section and copy over the values. The data structure is basically a copy of all the name/value pairs from the ini file. Once done with the loop, we copy them over to the application scope. Notice we also do a bit of error checking. If for some reason the default section doesn't exist, we immediately throw an error. This makes sense if you think about it - if the application isn't properly configured, we should stop everything.

So, the last function we need to talk about is setProfileString. As you can probably guess, this allows you to update an ini file. It takes four arguments: The name of the ini file, the section, the key, and lastly the value. I'm not really going to bother with an example of this since I don't find myself updating ini files from code very often. In fact, I've never done it. But it is there if you need to.

In my next entry (most likely not till Monday) I will discuss using XML files for application configuration.

So, this was my first 101 posting. Too much? Too little? Be critical!

Comments

I forgot to include the "funny OT story." Back in college I was in film class, watching a movie. I believe it was "The Man Who Shot Liberty Vallance", with John Wayne and Jimmy Stewart. At one point in the film, if I remember right, John Wayne's character was eating steak, drinking liquor, and smoking. He then tells Stewart's character something along the lines of "What you need is a hand gun!"

I bust out laughing. Something about _all_ those vices at once just made me loose my mind.
# Posted By Raymond Camden | 8/26/05 4:21 PM
Ray it is right on target for noobs, but my suggestion would be to take it out of the blog and place it in a new section.

p.s. Congratulations on whatever you got that lit your fire. ;-)
# Posted By Bryan F. Hogan | 8/26/05 4:42 PM
You may want to expand this article to deal a little more with the live, dev, staging sections. For instance, since all of your settings are in the .ini file - how does a beginner identify which section of settings they want.. do they set a variable in the application.[cfm|cfc] file? Do they just hard code it at the line they are reading the settings at? Do they set up some kind of dynamic evaluation thing based on server ip to know which section to grab? I'm sure there are a bunch of different options as well. Which do you use and why?

While there might not be a "best practice" for this adding that little nugget of information would probably be very beneficial considering the target audience.
# Posted By Bill | 8/26/05 4:55 PM
As a CF newbie, i love seeing this type of stuff. Learn something new everyday...!
# Posted By Chris | 8/26/05 5:13 PM
Bill, notice this line in the post:

In the ini file above, there are three sections: live, staging, and dev. (I'll talk more about these sections in my third post.)

My plans are to talk about using an ini/xml file to handle different config values for dev, staging, and live servers. It will be my 3rd post.
# Posted By Raymond Camden | 8/26/05 5:14 PM
Ray,

One thing I notice people often miss with this approach is that, potentially, someone could access the contents of your .ini file via their browser if they guessed the right url. To avoid this security risk, I call the file something like "config.cfm" with the same contents as a typical .ini file; the difference is that the first line of the file is CFABORT ;)

As to whether to use an xml file for config values, I usually find the .ini file approach faster, but I'd love to see a follow-up article that would convince me other wise!

My $0.04

Cheers,
Oliver
# Posted By Oliver Merk | 8/26/05 6:05 PM
Not very much related to the entry, but as a British reader of this blog and watcher of American TV and internet I have to ask what is this 101 thing? It may be naive of me but i never get it!
# Posted By Dave Hill | 8/26/05 6:58 PM
Not very much related to the entry, but as a British reader of this blog and watcher of American TV and internet I have to ask what is this 101 thing? It may be naive of me but i never get it!
# Posted By Dave Hill | 8/26/05 6:58 PM
Not very much related to the entry, but as a British reader of this blog and watcher of American TV and internet I have to ask what is this 101 thing? It may be naive of me but i never get it!
# Posted By Dave Hill | 8/26/05 6:59 PM
Dave: "101" refers to the normal number given to an introductory college class. So your first English call is English 101, first Math is Math 101, etc.
# Posted By Raymond Camden | 8/26/05 7:44 PM
Oliver - good point. I never keep config files in web root. I will bring this up, probably in part 3, or as a 'p.s.' post (part 1.5 ;)

In apps where I _cant_ go out of web root , like some of my DRK projects, I do something close to what you suggested. I use an xml file with a cfm exstension where all the data is wrapped in CFML comments.
# Posted By Raymond Camden | 8/26/05 7:46 PM
Ideally, I'd prefer to put this type of stuff in an "admin" section and allow the users to edit it at their will via a web-based interface. In such a case, I'd probably store it in a DB instead of an actual file.
# Posted By Jeff | 8/26/05 7:47 PM
Jeff,
From a best practices standpoint I think storing it in the file is the better route. Live, Stage and Dev should different databases. Also, not every application makes use of a database. It might be sense in your application, but I think Ray's example was great for CF beginners.

Ray,
Maybe in a future part you should show Fusebox and Mach-II config files for references. It would also be useful to newbies to explain what to store in a config and why.
# Posted By Nathan Hunsaker | 8/26/05 10:45 PM
Nathan - actually, I don't use Fusebox or Mach-II. I am beginning to use Model-Glue.
# Posted By Raymond Camden | 8/26/05 11:21 PM
I use .properties files and load them using the appropriate java classes. You end up with a nice struct to use.

Also, a fellow-coder added the habit of using a classpath resource to load in the .properties file. It allows you to make your app more cross-platform friendly (because the .properties file can live anywhere in cf's classpath)
# Posted By Dave Ross | 8/27/05 12:06 AM
Nathan,

I find it hard to imagine a web application w/o a database behind it. I agree that live, staging, and dev should be different databases. In many cases, the relevant settings we are talking about would also be different on each one. ( Although in an ideal situation, staging and live would be mirrors of each other ).

In Ray's example, he stored, live staging and dev settings all in a single file. If I were storing the settings in a db, I would only store a single set of data for each tier, and change those settings in each database.

Why do you think storing it in a file is best practice over a database?
# Posted By Jeff | 8/27/05 7:46 AM
Jeff, I never said it was -best- per se, just one way. Now, I can think of some good reasons. First off - the actual DSN is normally stored in the config file. Obviously you can't talk to a db until you have the dsn info. I'll also put the mapping info in a config file as well. There is no reason why not to use both either. For example, our Element product, a CMS, uses a mix of both. The config file is normally stuff only we mess with, and the Settings stuff is stuff that the client changes. CF provides many ways of solving the problem. :)
# Posted By Raymond Camden | 8/27/05 8:55 AM
Don't forget that GetProfileString() could be disabled in a shared hosting situation. I found this out trying to implement Ray's fine Soundings app.
# Posted By dick | 8/28/05 1:04 PM
Ray,
I did notice the line; I just thought that including all three now might cause some confusion (since you're targetting beginners) and that they might want just a hint toward the right direction along within the context of this article.

No worries though it was still a well written article with a worthwhile topic that should help some folks.
# Posted By Bill | 8/29/05 8:22 AM
Bill - gotcha. I hope to have part 2 out by Wednesday.
# Posted By Raymond Camden | 8/29/05 9:01 AM
Ray,

One clarification. Nathan said that a file was a best practice, and my question was intended to be directed at him.

And yes, you need the dsn before you can access the database. Just like you need the location of the config file before you can access it. It is the same "catch-22".

I've had problems with the location of config-files before and one application accessing the config of another. ( This was, of course, due to the inconsistency of the environment and should probably not be a reflection on config-files in general).
# Posted By Jeff | 8/29/05 9:08 AM
> Why do you think storing it in a file is best practice over a database?

Jeff, I guess there are a number of reasons but I don't want to push what works for me down your throat. For my applications, I keep what I consider "settings" in the database. Settings are user configurable, easily accessible, and change frequently. When I deploy an application, or setup a new environment, I edit the configuration file. I store "fail safe" parameters in that file. This lets the application know who to email in the event an exception is thrown, if I want tracing, or turn caching on or off, etc. Other developers can easily view, understand, and modify those core settings. Frameworks set a pretty good standard, and both Mach-II and Fusebox use configuration files. Neither require you to setup a database to store the configuration.
# Posted By Nathan Hunsaker | 8/29/05 11:05 AM
Nathan,

There are benefits and cons to each method.

I understood your first post to be advocating one over the other. "From a best practices standpoint I think storing it in the file is the better route."

Your second post makes more sense to me, where you appear to keep some things in files and other things int he db, depending on the type of setting.
# Posted By Jeff | 8/29/05 11:25 AM
Ray:

Just my opinion, but I love the idea of CF101. Some (if not many) coders like me (and I believe you) are completely self taught with no formal programming education. These kind of tutorials are just what I look for to learn. Keep up the good work.

Thanks!

Todd
# Posted By Todd | 8/29/05 6:43 PM
Hi Ray, thanks for that very helpful bit of code with the ini file setup. How do I code in the application.cfm file for multiple sections in the ini file. Second, how do I access specific sections of the ini file? I am okay doing it if I have 1 section not with multiple sections in the ini file.
# Posted By george | 3/1/07 9:59 AM
You would simply loop over the other structs:

<cfloop index="key" list="#sections.default#">

would be

<cfloop index="key" list="#sections.whatever#">
# Posted By Raymond Camden | 3/1/07 4:54 PM