So a friend of mine dared to say I had a bit of OCD. Personally I don't think so. I'm just... organized. Why did he say I had OCD? I tend to get really ticked off by CFCs that don't alphabetize their methods. All of my CFCs use alphabetical methods, except for the init method which is always first (if the CFC needs one).
So here is your challenge. Write a UDF/custom tag/snippet that will take as an argument the path to a CFC. Your code will read in the file and:
- Organize all methods in alphabetical order
- Keep a method named "init" as the first method
- And for bonus points - ensure that any "constructor code" (lines outside of any method) are grouped above the init method.
Being that it is not only Friday but Friday afternoon, bonus points if you've been.... "celebrating" the weekend a bit early. (But then again we have to be able to actually read your code!)
Archived Comments
Wow, Ray. I thought *I* was OCD when it came to code organization. Yeah...alphabetical? I wouldn't be able to find ANYTHING in my cfc's if I did that. Here's how one of mine looks, collapsed, mind you (and I trimmed it to shorten the line lengths...)
<cfcomponent name="cmsAdmin" hint="Admin CMS relate
<!--- ------------------------------------------
<!--- GENERAL Related functions --->
<!--- ------------------------------------------
<cffunction name="init" returntype="cmsAdmin" ou
<cffunction name="broadcastCMSChange" access="pu
<!--- ------------------------------------------
<!--- CONTENT Related functions --->
<!--- ------------------------------------------
<cffunction name="cmsContentSearchFormParam" ret
<cffunction name="generateCMSContentSearchWhereC
<cffunction name="getCMSContentPagination" acces
<!--- ------------------------------------------
<!--- PAGE (Layout) Related functions --->
<!--- ------------------------------------------
<cffunction name="cmsPageSearchFormParam" return
<cffunction name="generateCMSPageSearchWhereClau
<cffunction name="getCMSPagePagination" access="
[more functions removed to slim it down]
<!--- ------------------------------------------
<!--- ------------------------------------------
<cfinclude template="/com/model/mixinsSimple.cfm
<cfinclude template="/com/model/mixinsCMS.cfm" /
<!--- ------------------------------------------
</cfcomponent>
You're just plain nuts, dude... :D
If I'm going to participate in something this pointless it better be because there's an iPhone on the line or something. hehe
I concur with Jared... hehe
insane man, but I would like to see the finalized code...
;-)
so you want code, to open code up, snip and tuck that code, reordering the methods by alphabet, then save it, and have a cfc that basically cleans up your mis-ordered methods?
woah.
Should be easy actually with reMatch. :)
I am sure I can make it super complicated somehow... it's that part of the fun :D
Oh - if you want complex - I can make it complex.
The function should take, as an optional second parameter, ANOTHER udf that will work as the sort function. The sort function takes 2 items and returns -1 if the item A should be sorted before, 1 if after, and 0 if the same.
In other words - allow for custom sorts.
If you want to cheat - there is a version of this up on CFLib already.
What is passed to the sorting function? The name of the CFFunction?
That would be telling. ;)
Look to the lib....
Do it "quick"ly...
Well, luckily, that date I had with this super model just fell through, so I should have some time to take a look at this over the weekend :) ... at least that sounds like a better excuse.
Guys... just use CFEclipse and forget about it. ;-)
Heh, I _do_ use CFEclipse. Thing is - I'm anal about keeping my code view big - and I'm too lazy to turn the view back on. ;)
Oh, this is going to be fun ;~)
Like Will B, I prefer to keep semi-related functions close together (or ones that call each other, as all the functions in a class should be related).
I'll just open up the side bar in cfeclipse and sort them alphabetically there if I need to find by name (or hit ctrl-f and search for it that way).
This place ROCKS!!!
like running a 4 d Array of these topics Yea, I would love to see the finish; good luck on this Ray!
Here's my crack at it. No custom sorting, but sorts alphabetically and puts any init functions first.
<cffunction name="sortMethods" output="false" returntype="void">
<cfargument name="filePath" type="string" required="true">
<cfset var funcRegex = "<cffunction [^>]*>.*?<\/cffunction>">
<cfset var fileContent = "">
<cfset var allFuncs = "">
<cfset var nameMatch = "">
<cfset var qFunctions = queryNew("name,theFunction,sortOrder")>
<cfset var i = 0>
<!--- Verify the file exists --->
<cfif fileExists(arguments.filePath)>
<!--- Read file --->
<cffile action="read" file="#arguments.filePath#" variable="fileContent">
<!--- Find all functions --->
<cfset allFuncs = reMatch(funcRegex, fileContent)>
<cfloop from="1" to="#arrayLen(allFuncs)#" index="i">
<cfset queryAddRow(qFunctions)>
<!--- Store function name and contents in query --->
<cfset nameMatch = reMatch("name=[""'][^""']*[""']", allFuncs[i])>
<cfset qFunctions.name[qFunctions.recordCount] = replace(nameMatch[1], "'", '#chr(34)#', "ALL")>
<cfset qFunctions.theFunction[qFunctions.recordCount] = allFuncs[i]>
<!--- If the function name is "init", sort it first --->
<cfif qFunctions.name[qFunctions.recordCount] IS NOT 'name="init"'>
<cfset qFunctions.sortOrder[qFunctions.recordCount] = 2>
<cfelse>
<cfset qFunctions.sortOrder[qFunctions.recordCount] = 1>
</cfif>
</cfloop>
<!--- Sort the functions by name (with init on top) --->
<cfquery name="qFunctions" dbtype="query">
SELECT name, theFunction
FROM qFunctions
ORDER BY sortOrder, name
</cfquery>
<!--- Prepare file to be written --->
<cfset fileContent = reReplace(fileContent, "\n*" & funcRegex & "\n*", "", "ALL")>
<cfset fileContent = reReplace(fileContent, "<\/cfcomponent>( |\n)*", "", "ALL")>
<cfsavecontent variable="fileContent"><cfoutput>#left(fileContent, len(fileContent)-14)#</cfoutput>
<cfoutput query="qFunctions">
#theFunction#
</cfoutput>
<cfoutput>#chr(60)#/cfcomponent#chr(62)#</cfoutput>
</cfsavecontent>
<!--- Write the file back to the source --->
<cffile action="write"
file="#arguments.filePath#"
output="#fileContent#">
</cfif>
</cffunction>
Tyson, thanks for sharing the code. Let me make one style comment. I don't like code where 99% of the functionality is all within one CFIF. Your code does nothing if the cfif condition is false, so I would have put this up top instead:
<cfif not fileExists(arguments.filepath)><cfreturn></cfif>
I didn't really like the idea of having the code in one CFIF, but that was the first idea that came to me, so I just went with it. I'll be interested to see how others implement the custom sort function. Also, this was the first time I'd used reMatch and I see myself using a lot from now on.
Unfortunately this is much more complicated than you think it is.
Tyson's solution works, right until it runs into a cffunction with the char > anywhere in an attribute. It'll also choke if the function contains the text </cffunction> anywhere, for instance <cfset myvar = "<cffunction>">.
If you really want to blow your mind think about:
<
cffunction
name
=
"foo"
hint =
"<cffunction name='bar'></cffunction>"
>
<
/
cffunction
>
That's syntactically valid CFML.
What you want can really only be reliably done with a full CFML parser. Implementing it in CFEclipse would probably be the easiest solution because the thing already has a parser, but I'm curious if CFEclipse even parses like the Macromedia/Adobe ColdFusion parser does.
Regular expressions are nifty, but alone they're not really useful for parsing. There's no finite length regular expression that expresses what a cffunction declaration could look like.
Good point Elliot. Although I'd still say that I'd find this code useful. I think I'd know ahead of time if there was a </cffunction> anywhere in the code (I mean as a string). I'd assume that would be rare (except in the case of the code for this blog entry ;)
Yeah, true. I didn't have time to look at this this weekend (unexpected events). However, I cannot imagine that:
<
cffunction
is considered valid? I am pretty sure that need to have the < and the cf right next to each other.
Also, yeah, its true - people really put some crack-head information into the attributes. Just the other day I saw someone do like a whole documentation schema inside of the Hint attribute.
@Ben
Nope, you don't need the < and the cf next to each other. Try the code snippet I pasted. ;)
CF has a very very lenient syntax. You can add new lines and whitespace between all kinds of things and it won't even blink.
I started working on breaking CF's parsing rules down into tokens for documentation purposes, I should really post that somewhere. I have a feeling that many of the CF engines don't really follow the same rules as the ColdFusion Server, but rather the conventional CFML syntax. I definitely know that Smith's parser is wrong/too strict in a few places.
Oh my god! You are totally right. I just ran the craziest code and it ran just fine. That is just insane. Not sure how I feel about that :) I think it's a little frustrating that they allow syntax like:
<
cfset
x = 4
/>
... and at the same time, they do NOT allow you do this:
<cfset GetFunctionPointer()() />
... or :
<cfset GetArray()[ 4 ] />
Those seem like infinitely more important chainnings and yet the will not parse propertly :( Silly ColdFusion parser.