Project moreADS - Setting Up The Application

Work continues on Project moreADS! To see where we're at, you can click here. You might say that not much has changed since Monday. I'd have to agree with you, at least visually. I did change the menu list a bit, made it a bit more graphic, and added some valid and powered by buttons at the bottom of the page. However, the big part of the work was done under the hood. As always, at every step of development the source code is available as a download at the bottom of the post.

Getting Some Resources

A common issue when starting up a new application is getting and extracting basic configuration values. Logically, we want to abstract this config data out of our application cfc/cfm. The main reason is to make sure moving the application elsewhere won't be affected by different environment values. Updating these values when they are all in the same place also makes things easier. Now, consider this basic configuration file:

relativePath=/projects/moreADS<br />
appName=Project moreADS<br />
appVersion=0.2

As you can see, all we have here is some simple environment variables. We'll want to call this config file only when our application initializes, so OnApplicationStart is our best bet. We'll place this code in a file named config.cfm in the root of our application. Now the here's the fun part: we want our application.cfc to be able to find this file, regardless of which template was requested. To read the file, we can't assume that it's in the same folder as GetBaseTemplatePath(). However, we can assume that it's somewhere within a parent folder of wherever we are. Since we need to search for the file, and parse it afterwards, I went and cooked up a little component for this purpose.

org.critical-web.resources

<CFcomponent name="Resources Component" output="false">

    <CFset variables.findFile_tries=[] />
    <CFset variables.vars={} />

    <CFfunction name="getFile" access="public" returntype="resources" output="false">
        <CFargument name="fileName" type="string" required="true" />

        <CFset variables.resourceLocation=variables.findFile(arguments.fileName, GetBaseTemplatePath()) />

        <CFset variables.parseFile(variables.resourceLocation) />

        <CFreturn this />
    </CFfunction>

    <CFfunction name="getVars" access="public" returntype="struct" output="false">
        <CFreturn variables.vars />
    </CFfunction>

    <CFfunction name="findFile" access="private" returntype="string" output="false">
        <CFargument name="fileName" type="string" required="true" />
        <CFargument name="path" type="string" required="true" />

        <CFset var locals={} />

        <CFset locals.path=REReplace(arguments.path, "[^\\]+$", "") />

        <CFset ArrayAppend(variables.findFile_tries, locals.path & arguments.fileName) />

        <CFif FileExists(locals.path & arguments.fileName)>
            <CFreturn locals.path & arguments.fileName />
        <CFelseif ListLen(locals.path, "\") gte 2>
            <CFreturn variables.findFile(arguments.fileName, ListDeleteAt(locals.path, ListLen(locals.path, "\"), "\")) />
        <CFelse>
            <CFthrow message="Resource file not found" />
        </CFif>

    </CFfunction>

    <CFfunction name="parseFile" access="private" returntype="void" output="false">
        <CFargument name="resourceLocation" type="string" required="true" />

        <CFset var locals={} />

        <CFloop file="#arguments.resourceLocation#" index="line">
            <CFset locals.parsedLine=REFind("^([^/]{2}[^=]+)=([^=]+)$", line, 0, true) />

            <CFif ArrayLen(locals.parsedLine.pos) gt 1>
                <CFset variables.vars[Mid(line, locals.parsedLine.pos[2], locals.parsedLine.len[2])]=Mid(line, locals.parsedLine.pos[3], locals.parsedLine.len[3]) />
            </CFif>

        </CFloop>

    </CFfunction>

</CFcomponent>

The object has two public methods: getFile and getVars. Logically, getFile retrieves the actual file and then parses it, while getVars is just a basic getter and returns a structure of all retrieved variables.

getFile

The getFile method accepts one argument: the name of the file to search for. It then calls the private method findFile to search and find the full path to the resource file. It passes the requested file name and the current GetBaseTemplatePath() as arguments.

findFile

This method accepts two arguments: the file name and the path to search in. The way it works is that if it doesn't find the file in the current folder, it calls itself with the same file name and a truncated path, always removing a folder from the top. If the method runs out of parent folders, it throws an error. When the method finally finds the file, it returns the full path pointing to it.

Back to getFile

Now that we have location for the resource file, we can load and parse it. This is where we call the parseFile method, including the path to the resource file.

parseFile

This simple method simply loops over the resource file and uses a regular expression to parse each line in detail. Here is the logic behind the regex (notice the leading ^ and trailing $, meaning the expression must encompass the entire string):

  • 1st group: Any characters, except for an equal sign (=), and does not start with two forward slashes (for comments).
  • Both groups are separated by an equal sign.
  • 2nd group: Any characters, except for an equal sign.

For every successfully parsed line, the method stores it's name and value within a private variable, accessible through the getVars method.

Putting The Object To Good Use

Now that our object is good and ready, we can instantiate it and call it with two simple lines of code:

<CFset application.resources=CreateObject("component", "org/critical-web/resources") />
<CFset application.config=application.resources.getFile("config.cfm").getVars() />

And that's pretty much it for tonight, and all we'll be able to see before my vacations. You can browse through the code if you like, I started on a layout component for managing the look and feel of the application, although it isn't finished yet. The good news is, however, that we'll be able to get to the good stuff during our next session. So stay tuned!

Finally got those code tags to work...

Related Blog Entries

TweetBacks
There are no TweetBacks for this entry.
Comments
BlogCFC was created by Raymond Camden. This blog is running version 5.9.3.000. Contact Blog Owner