Thursday, April 28, 2011

Advanced INI Parsing in ColdFusion

At work, I've been using the Zend Framework for PHP a lot. One of the best features in my opinion is how you're able to manage your configuration settings between different environments within a project.

I've always been a fan of using INI files for storing configuration settings, but the Zend_Config_Ini class that Zend Framework comes with takes INI files to a whole new level by adding section inheritance and nested properties.

After a couple hours, I was able to take the same powerful functionality that Zend Framework provides and port it to ColdFusion, mainly for the purpose of adding it to my ColdMVC framework.

Why do I think this is cool? Let's look at a sample INI file that you might see on a project:


; Settings for the production environment
[production]
autoReload = false
development = false
reminderService.sendReminders = true
facebook.api.username = prod@mycompany.com
facebook.api.password = 90ujlc890$f

; Settings for the staging environment
[staging : production]
reminderService.sendReminders = false
facebook.api.username = staging@mycompany.com
facebook.api.password = 879kjasdf!

; Basic settings for the development environment.
; Each developer should create their own environment that extends this one.
[development : staging]
development = true
autoReload = true

; Settings for Tony's development environment
[development-tony : development]
emailService.options.forceTo = [ "tony@mycompany.com", "tony@gmail.com" ]
facebook.api.username = tony@mycompany.com
facebook.api.password = w1nn1ng

; Settings for Ryan's development environment
[development-ryan : development]
emailService.options.forceTo = ryan@mycompany.com
facebook.api.username = ryan@mycompany.com
facebook.api.password = govikes

; Settings for Joe's development environment
[development-joe : development]
emailService.options.forceTo = joe@mycompany.com
facebook.api.username = joe@mycompany.com
facebook.api.password = welcome


Pretty straightforward settings file. Each environment has its own section, but now it comes with a twist.

See the colons in the section names? That's inheritance in action. If you look at the development-tony environment, you'll see that it extends the development environment, which extends the staging environment, which extends the production environment.

Also, notice the periods in the property names. Those are nested properties, which will be automatically converted to ColdFusion structs at runtime.

So how does this work? Pretty simple:


var ini = new Ini("/path/to/config.ini");
var config = ini.getSection("development-tnelson");


If I were to now dump the config variable that was returned, I'd see the following output:



Pretty sweet if you ask me.

You can find all of the code on GitHub at https://github.com/tonynelson19/ini/, which also includes 26 green unit tests.

Also, I've included the new INI parser in the latest version of ColdMVC, which you should definitely check out if you haven't yet.

4 comments:

  1. This is an awesome piece of code and something I've been looking for.

    I am curious as to why you don't remove the structure keys that contain periods (.) in them such as "facebook.api.username" shown in the example.

    Only reason I can see why not to remove them, is it to allow for both:
    * config["facebook.api.username"]
    * config["facebook"]["api"]["username"]

    ReplyDelete
  2. While Zend removes them, I left them in because I think they can still serve a purpose.

    For example, if your project uses ColdSpring and you want to use dynamic variable replacement within your XML mapping file, you can still reference ${facebook.api.username} inside of a property node.

    ReplyDelete
  3. Nice approach, I have an integration question though. How would you go about retrieving the config settings for a particular environment? Your example has:

    var config = ini.getSection("development-tnelson");

    But...how does your code (presumably in OnApplicationStart() or similar) know to retrieve values for "development-tnelson"?

    Typically we've used the InetAddress class to return a servername:

    servername = createObject("java", "java.net.InetAddress").localhost.getHostName();

    Based on that can set/retrieve relevant config values. However this has also proved to have contraints on if/when the sysadmins decide to rename things (i.e. this is out of our control).

    Do you have a different way of setting this up?

    ReplyDelete
  4. You could put a small file (environment.txt) in the root of your project that contains the environment name. That's how I do it in my ColdMVC framework.

    You could also use the domain (cgi.http_host), but that gets a little trickier if you have multiple developers all using localhost.

    ReplyDelete