Archive for the ‘ColdFusion’ Category

Search

Wednesday, January 23rd, 2008

I recently had the need to search a web directory’s application files for a particular string. Not as a customer-facing function, just as a one-off development need.

Well, CFEclipse (as truly awesome a tool as it is) doesn’t have a way to search multiple files for the same string simultaneously, and while Home Site does have this feature (”Extended Find / Extended Replace”), if you use it on anything but the smallest directories, it consistently times out and needs to be restarted again. So below is a little utility I wrote to recursively search a given directory for a given string. Its basically just two Java objects wrapped in a filtered CFDIRECTORY loop.

“But Tyler, why did you use Java when you could have just used CFFILE?” you say. Well, for me there’s now been several projects wherein CFFILE has considerably underperformed compared to its Java counterparts, and with the buffered reader object specifically, I can pass in fairly decent size files without ColdFusion choking on them. And its fast. Blazing fast. My browser almost caught fire (but I guess that’s the price you pay for efficiency). So nowadays, if I need a function that CFFILE would normally handle (at least in regards to reading and writing), I first consider the Java alternatives.

By the way, there is significant mixing of logic and presentation here, so be warned if you need to avert your eyes. Anyway, HTH.

<!--- the string to search for --->
<cfset request.searchstring = "thestring" />
<!--- the path to look in --->
<cfset request.startingpath = "/var/www" />
<!--- should the search be recursive. careful on this one. --->
<cfset request.recurse = false>
<cfset request.filecount = 0 />
<cfset request.linecount = 0 />
<cfset request.fileshown = 0 />    

<cfdirectory name="files"
    action="list"
    directory="#request.startingpath#"
    recurse="#request.recurse#"
    filter="*.cfm|*.cfc|*.cfml|*.js|*.css|*.htm|*.html"
    sort="directory asc, name asc" />    

<html>
<head>
    <style>
        * {
            font-size: 12px;
            font-family: 'courier new';
        }
        table {
            border: 0;
            padding: 0;
            border-collapse: collapse;
        }
        th { text-align: left; }
        th, td { padding: 0 15px 0 5px; }
        .even { background: #dadada; }
        .filename { font-weight: bold; }
    </style>
</head>    

<body>    

    <table>
        <tr>
            <th colspan="3">directory/filename</th>
        </tr>
        <tr>
            <th> </th>
            <th>num</th>
            <th>line of code</th>
        </tr>    

        <cfloop query="files">
            <!--- exclude this file from the search --->
            <cfif files.name NEQ listlast(cgi.SCRIPT_NAME,"/")>    

                <!--- set the source file to be read --->
                <cfset variables.sourcefile = files.directory & "\" & files.name />
                <cfset variables.linenumber = 1 />    

                <!--- instantiate the necessary java objects --->
                <cfset variables.filereader = createObject("java","java.io.FileReader").init(variables.sourcefile) />
                <cfset variables.bufferedreader = createObject("java","java.io.BufferedReader").init(variables.filereader) />    

                <!--- set the current line to the first available line in the file --->
                <cfset variables.currentline = variables.bufferedreader.readLine() />    

                <!--- beginning looping over the file line by line --->
                <cfloop condition="isDefined(' variables.currentline')">    

                    <!--- check the current line for the given search string --->
                    <cfif variables.currentline CONTAINS request.searchstring >    

                        <cfif request.fileshown EQ 0>
                            <!--- stack multiple results under a single file name --->
                            <tr valign="top" <cfif request.filecount MOD 2 EQ 0> class="even" </cfif> >
                                    <td colspan="3" class="filename"><cfoutput>#variables.sourcefile#</cfoutput></td>
                            </tr>   
                            <cfset request.fileshown = 1 />
                        </cfif>    

                        <!--- list the line number and line code containing the search string --->
                        <tr valign="top" <cfif request.filecount MOD 2 EQ 0> class="even" </cfif> >
                                <td> </td>
                                <cfoutput>
                                    <td align="right">#variables.linenumber#</td>
                                    <td>#htmleditformat(variables.currentline)#</td>
                                </cfoutput>
                        </tr>    

                        <cfset request.linecount = request.linecount + 1 />
                    </cfif>    

                    <!--- try to get the next line in the file. --->
                    <cfset variables.currentline = variables.bufferedreader.readLine() />
                    <cfset variables.linenumber = variables.linenumber + 1 />
                </cfloop>    

                <!--- close the buffered reader object --->
                <cfset variables.bufferedreader.close() />
                <cfif request.fileshown EQ 1>
                    <cfset request.fileshown = 0 />
                    <cfset request.filecount = request.filecount + 1>
                </cfif>
            </cfif>
        </cfloop>    

        <!--- list the line number and line code containing the search string --->
        <tr valign="top" <cfif request.filecount MOD 2 EQ 0> class="even" </cfif> >
            <th colspan="3">
                <cfoutput>
                    #request.linecount# matching lines in #request.filecount# files. #files.recordcount# files searched.
                </cfoutput>
            </th>
        </tr>
    </table>    

</body>
</html>

THIS.mappings

Saturday, January 5th, 2008

I’ve always been a bit perturbed at the CFIMPORT tag, both because you have to use the tag on every page in which you’ll call the prefixed custom tag, including pages that are called with CFINCLUDE (will save that rant for another time), and also because the taglib attribute cannot take a variable. To illustrate, you cannot do this:

<!--- this does not work --->
<cfset request.tagpath = "/var/www/tagsdirectory/" />
<cfimport prefix="t" taglib="#request.tagpath#" />

As you probably know, you must declare the custom tag path in the cfadmin, then call the CFIMPORT tag using the name you assigned to the path in the admin. Like so:

<cfimport prefix="t" taglib="/tagpath" />

That is until now. In ColdFusion 8 aka Scorpio aka SomeOtherCoolName, you can now create application-specific paths for your custom tags. You do so by creating a structure called THIS.mappings in your Application.cfc, then adding keys and values to this structure referencing the tag path name and the tag path directory, respectively. Like so:

<cfcomponent displayname="Application">
<!--- application-specific custom tag paths --->
<cfset this.mappings = structnew() />
<cfset this.mappings["/tagpath"] = "/var/www/tagsdirectory/" />

Note the required forward slash preceeding the tag name you assign. Now just call the CFIMPORT tag like normal at the top of your page:

<cfimport prefix="t" taglib="/tagpath" />

Bada bing bada boom, dynamic tag paths. HTH.

–>

Add

Wednesday, November 21st, 2007

As it happens, I had the need today to add a totals row to an existing query object that summed the various column data above it. Like this:

Month Oranges
January 5
February 12
March 3
April 8
May 14
June 11
July 6
August 11
September 10
October 12
November 7
December 4
Total Oranges Sold 103

In this particular case, I preferred to do the totalling in the cfc where i’m building the query, rather than after the fact in the cfm page. I didn’t figure it would be difficult, but as it turns out, there was a bit of a hitch in the way one can reference query object columns from the ArraySum function (specifically, dot notation doesn’t work). But, then I found Ben Nadel’s post on this issue wherein he used array notation, and I’m now good to go. Thanks, Ben. Hope this little tidbit helps someone else.

<cfquery name="orangesales" datasource="#request.ds#">
    SELECT monthname, oranges
    FROM orangesales
</cfquery>

<cfset QueryAddRow(orangesales) />
<cfset QuerySetCell(orangesales,"monthname","Total Oranges Sold") />
<cfset QuerySetCell(orangesales,"oranges",ArraySum(orangesales["oranges"])) />

–>