3

Could anyone find a way of improving this code a bit? I want to read in an INI file in one felt swoop and create a corresponding data structure.

<cfset INIfile = expandPath(".") & "\jobs.ini">
<cfset profile = GetProfileSections(INIfile)>
<cfloop collection="#profile#" item="section">
    <cfloop list="#profile[section]#" index="entry">
        <cfset app.workflow[section][entry]=GetProfileString(INIfile, section, entry) >
    </cfloop>
</cfloop>
Rick
  • 591
  • 1
  • 9
  • 23
  • So you don't show the functions for parsing the ini files. So no way to know how to improve the code. I guess I could point out that as of CF8 you can loop through a file line by line in cfloop using the file attribute. – Terry Ryan Oct 27 '09 at 21:20
  • 4
    Terry, those are built-in CF functions! – Peter Boughton Oct 28 '09 at 00:15
  • Very late reply for other CF coders: The code is right but it appears that CF loads the file each time you make the GetProfileString call. As a result it slows it down a lot. I've found the fastest alternative is to create a custom function that loads in the file and parses line by line. – Jordan Reiter Apr 07 '15 at 20:08

3 Answers3

4

I don't believe you can improve this using CFML power. Do you need to parse huge ini files? If not, why would you like to improve your code, it looks pretty straightforward for me.

Other possible (though common for CF) solution is to try pure Java. See this SO thread for pure Java examples.

P.S. BTW, in case of special performance needs you should consider using another storage for configuration. Simple SELECT query to the old good MySQL can be much faster for large datasets.

Community
  • 1
  • 1
Sergey Galashyn
  • 6,946
  • 2
  • 19
  • 39
  • I agree that you cant really improve it in CF. I would also question if a ini file is even needed. Does anyone else use the file? (either read or write). Is the app sent to customers or is it internal? If developers are the only people who ever set information in it, then whats wrong with a CF file with a bunch of CF sets? You might be surprised how doing something like that can speed up performance and make everyone's lives easier. Besides, Its the simplest solution that might possibly work! – ryber Oct 28 '09 at 13:32
  • No, it's no a big file. I would also entertain an XML approach. I'm still on CF7 unfortunately. – Rick Oct 28 '09 at 16:15
  • @ryber: A CF file with a bunch of CFSETS is fine in theory, but in my case will require a system change request, a code deployment, test regimen, and all the paperwork that goes with it, simply because it's a code file. An ini file, on the other hand, is configuration, and can be changed with the submission and approval of a trouble ticket. – ale Jan 19 '11 at 19:47
1

To expand on ryber's comment, you might consider using this approach instead. I'm assuming you're using CF8.01 or later, as I make use of nested implicit structure notation. This could easily be converted to CF7/6/etc syntax, but wouldn't be as clean or concise.

Again, this only applies if your ini file isn't used by any other applications or people, and doesn't need to be in ini format.

settings.cfm:

<cfset variables.settings = {
    fooSection = {
        fooKey = 'fooVal',
        fooNumber = 2,
    },
    fooSection2 = {
        //...
    },
    fooSection3 = {
        //...
    }
} />

Application.cfc: (only the onApplicationStart method)

<cffunction name="onApplicationStart">
    <cfinclude template="settings.cfm" />
    <cfset application.workflow = variables.settings />
    <cfreturn true />
</cffunction>

In addition, I've use the CFEncode application to encrypt the contents of settings.cfm. It won't protect you from someone who gets a copy of the file and wants to see what its encrypted contents are (the encryption isn't that strong, and there are ways to see the contents without decrypting it), but if you just want to keep some nosy people out, it adds a little extra barrier-to-entry that might deter some people.


Update: Since you just left a comment that says you are on CF7, here's native CF7 syntax:

settings.cfm:

<cfset variables.settings = StructNew() />
<cfset variables.settings.fooSection = StructNew() />
<cfset variables.settings.fooSection.fooKey = 'fooVal' />
<cfset variables.settings.fooSection.fooNumber = 2 />
<!--- ... --->

Alternatively, you could use JSONUtil and CFSaveContent to continue to use a JSON-looking approach (similar to my original syntax), but on CF7:

<cfsavecontent variable="variables.jsonSettings">
{
    fooSection = {
        fooKey = 'fooVal',
        fooNumber = 2,
    },
    fooSection2 = {
        //...
    },
    fooSection3 = {
        //...
    }
};
</cfsavecontent>
<cfset variables.settings = jsonUtil.deserializeFromJSON(variables.jsonSettings) />
Adam Tuttle
  • 19,505
  • 17
  • 80
  • 113
1

I created a CFC that I use in a bunch of apps. You give it an ini filepath when you init it and it creates a structure based on the ini file. It also optionally keeps the structure flat or creates sub-structures based on the [Sections] in the ini file. You can then either use its getSetting() method to get individual methods or getAllSettings() to return the entire structure. You may find it helpful.

<cfcomponent hint="converts INI file to a structure">
    <cfset variables.settings=structNew() />
    <cffunction name="init" access="public" output="false" returntype="any">
        <cfargument name="configurationFile" type="string" required="yes" />
        <cfargument name="useSections" default="false" type="boolean" />
        <cfset var local=structNew() />
        <cfif fileExists(arguments.configurationFile)>
            <!--- Get the [sections] in the .INI file --->
            <cfset local.sectionStruct=getProfileSections(arguments.configurationFile) />
            <!--- Loop over each of these sections in turn --->
            <cfloop collection="#local.sectionStruct#" item="local.item">
                <cfset local.workingStruct=structNew() />
                <cfloop list="#local.sectionStruct[local.item]#" index="local.key">
                    <!--- Loop over the keys in the current section and add the key/value to a temporary structure --->
                    <cfset local.workingStruct[local.key]=getProfileString(arguments.configurationFile,local.item,local.key) />
                </cfloop>
                <cfif arguments.useSections>
                    <!--- Copy the temporary structure to a key in the setting structure for the current section --->
                    <cfset variables.settings[local.item]=duplicate(local.workingStruct) />
                <cfelse>
                    <!--- Append the temporary structure to the setting structure --->
                    <cfset structAppend(variables.settings,local.workingStruct,"yes") />
                </cfif>
            </cfloop>
        <cfelse>
            <cfthrow
                message="Configuration file not found. Must use fully-qualified path."
                extendedinfo="#arguments.configurationFile#"
            />
        </cfif>
        <cfreturn this>
    </cffunction>

    <cffunction name="getAllSettings" access="public" output="false" returntype="struct">
        <cfreturn variables.settings>
    </cffunction>

    <cffunction name="getSetting" access="public" output="false" returntype="string">
        <cfargument name="settingName" required="yes" type="string" />
        <cfset var returnValue="" />

        <cfif structKeyExists(variables.settings,arguments.settingName)>
            <cfset returnValue=variables.settings[arguments.settingName] />
        <cfelse>
            <cfthrow
                message="No such setting '#arguments.settingName#'."
            />
        </cfif>
        <cfreturn returnValue>
    </cffunction>
</cfcomponent>
ale
  • 6,369
  • 7
  • 55
  • 65