Archive for February, 2010

Phone Number Compressor Method

Friday, February 26th, 2010

This is part 1 of a 2 part post.  In this part (part 1) we will compress a phone number for database storage.  But what good is a compressed phone number stored away the remote darks of some database?  This is where part 2 comes in.  In that part we will break our phone number back up into its three parts to be formatted for display.  More on that in a couple hours.  First let’s compress it.

I built this UDF because I got tired of always manually having to add in special characters to remove from the original user entered phone number.  Every time the client decided to type their phone number into the form field with new special characters I had to make an update I couldn’t charge for.  No fun!  After a few of these updates I decided it would be worth just writing a UDF that I could implement and forget forever.  The client could write their phone numbers in any format they wished and no matter how off-the-wall this UDF would remove everything except the number itself.

A phone number is really just a sting of ten numbers unless you include the ’1′ at the beginning, and I don’t – so just ten numbers. (On this note, I should state that this UDF works only with U.S. phone numbers.)  These ten numbers, and only these ten numbers is what we want to store as raw data.  If we want the maximum front-end formatting flexibility we need to start with raw data, not pre-formatted phone number of someone you don’t even know.

As such, let’s look at the UDF.  It’s posted here in its entirety so feel free to copy and paste it into your own CFC.

<cffunction name="compressPhoneNumber" displayname="Compress PhoneNumber" description="Compresses a phone number to remove formatting." access="public" output="false" returntype="struct">

	<!--- PASS IN NUMBER TO BE COMPRESSED --->
	<cfargument name="phonenumber" displayName="Phone Number" type="string" hint="Phone Number to be compressed." required="true" />

	<!--- BUILD STRUCT --->
	<cfset var number = structnew()>
	<cfset number.compressednumber = ARGUMENTS.phonenumber>
	<cfset number.originalnumber = ARGUMENTS.phonenumber>
	<cfset number.charary = arrayNew(1)>
	<cfset number.specialchars = arrayNew(1)>
	<cfset number.success = true>
	<cfset number.message = "">

	<cftry>
		<!--- CREATEA BYTE ARRAY OF PASSED IN STRING --->
		<cfset number.charary = ARGUMENTS.phonenumber.GetBytes()>
		<cfset number.aryLength = arrayLen(number.charary)>

		<!--- LOOP OVER ARRAY --->
		<cfloop from="1" to="#number.aryLength#" index="VARIABLES.i">
			<!--- IF NOT A NUMBER SET CHARACTER INTO A LIST --->
			<cfif number.charary[VARIABLES.i] lt 48 OR number.charary[VARIABLES.i] gt 57>
				<cfset arrayAppend(number.specialchars, chr(number.charary[VARIABLES.i]))>
			</cfif>
		</cfloop>

		<!--- LOOP OVER SPECIAL CHARACTERS ARRAY AND REPLACE IT IN THE ORIGINAL STRING --->
		<cfloop array="#number.specialchars#" index="VARIABLES.i">
			<cfset number.compressednumber = replace(number.compressednumber, VARIABLES.i, "", "all")>
		</cfloop>

		<!--- CATCH ANY ERRORS --->
		<cfcatch type="any">
			<cfset number.success = false>
			<cfset number.message = "There was a problem compressing the number.  Please refresh the page and try again.">
		</cfcatch>
	</cftry>

	<!--- RETURN STRUCT --->
	<cfreturn number>

</cffunction>

The method needs one thing and one thing only.  The phone number to be compressed or have the extra formatting removed.  Nothing too complicated happens with the number we pass in.  The gist of it is that we need to determine what is not a number and then replace it with nothing.  Although there are a couple ways to break a string down into its parts and pieces I prefer to break them down into their corresponding bytes.  This way we can determine characters according to their byte code.  Also another reason for using this method is the handy Java GetBytes() method.  Although undocumented in Coldfusion you can dig down into the underlying Java and use its methods to do your bidding.  In our case the GetBytes() method returns an array characters in byte format of the string that was passed to it.  Just remember that Java is a heavily typed language and as such make sure that you use the toString() method to ensure that your phone number is string and not a number as it was intended to be.  If for some reason you later needed it to be typed back as a number you can always use Java’s ParseInt() method to return it to a number, although this is probably not advisable for formatting reasons.

Moving on, we now have an array of byte code.  Perfect.  If we look at our ascii table of character codes we’ll see that the number ’0′ is represented as ’48′ in byte code and the number ’9′ is represented as ’57′ in byte code, and the other digits fall in-between.  All we need to do is loop over the bytes, check if they fall between ’48′ and ’57′.  If not we ditch them leaving us with just the number itself as a string.

I return the data in a struct that shows the original number, special characters that were removed from the string, and the compressed string.  You could of course remove this struct and just return the string for a little performance gain, but thought you might like to see what’s happening behind the scenes a little.

Access denied for user ‘someone’@’localhost’ (using password: YES)

Thursday, February 25th, 2010

If I had a nickel for every time I’ve gotten this error I’d be somewhere around 35 cents richer.  Point being this error really isn’t that big of a deal, but can certainly be somewhat annoying.  If you’re using shared web hosting and are receiving this error on your production site just call your host because there is probably nothing you can do on your end.

However, if you’re working on a VPS, dedicated server, the cloud, or any environment in which you have access to both the server and the Coldfusion Administrator then you’re in luck.  This error is usually thrown as a result of trying to access the database with a datasource set up for a user that doesn’t exist.  So if you’re using MySql on your server and you try to access it with an account that maybe exists on the local drive, but not on the server you’ll receive this error.  To test if this is the case go to the Coldfusion Administrator and verify the data source connection in question under the datasources menu.  It probably won’t verify.  At this point you will want to connect the datasource using a legitimate account or just use the ‘root’ username and no password.  This should verify the datasource and eliminate the error.

Phone Number Divider Method

Thursday, February 25th, 2010

This is part 2 of a previous post on using a phone number compression UDF.  Here we will look at a UDF I built that will take that compressed phone number from the database or wherever and break it up into its area code, prefix, and line number.  We need to break up our phone number for formatting purposes.  For example our compressed number looks like ’8005559876′ and we need it to look like ‘(800) 555-9876′. This is actually really simple, so let’s just look at the UDF.  Feel free to use this in your own CFC.

<!--- *****************************  DIVID PHONE NUMBER  ***************************** --->
<cffunction name="divide" displayname="Divide Phone Number" description="Divides a string of 10 digits into three parts." access="public" output="false" returntype="struct">

	<!--- PASS IN NUMBER TO BE DIVIDED --->
	<cfargument name="phonenumber" displayName="Phone Number" type="string" hint="Phone number to be divided." required="true" />

	<!--- BUILD STRUCT --->
	<cfset var number = structnew()>
	<cfset number.areacode = "">
	<cfset number.prefix = "">
	<cfset number.linenumber = "">
	<cfset number.success = true>
	<cfset number.message = "">

	<!--- MAKE SURE NUMBER PASSED IN 10 DIGITS --->
	<cfif len(trim(ARGUMENTS.phonenumber)) eq 10>
		<cftry>
			<!--- AREACODE --->
			<cfset number.areacode = left(ARGUMENTS.phonenumber, 3)>

			<!--- PREFIX --->
			<cfset number.prefix = left(ARGUMENTS.phonenumber, 6)>
			<cfset number.prefix = right(number.prefix, 3)>

			<!--- LINE NUMBER --->
			<cfset number.linenumber = right(ARGUMENTS.phonenumber, 4)>

			<!--- CATCH ANY ERRORS --->
			<cfcatch type="any">
				<cfset number.success = false>
				<cfset number.message = "There was a problem dividing the number.  Please refresh the page and try again.">
			</cfcatch>
		</cftry>
	<cfelse>
		<cfset number.success = false>
		<cfset number.message = "The number passed in did not contain 10 digits.  Please refresh the page and try again.">
	</cfif>

	<!--- RETURN STRUCT --->
	<cfreturn number>

</cffunction>

We’re just using the right() and left() functions to get the characters we want.  The left three characters will always be our area code.  The right four characters will always be our line number.  And the fourth, fifth, and sixth numbers will always be our prefix.  Stick them in a struct, return it, and format it to your hearts content on the front-end.

Firebug and Safari Web Inspector do not return errors when using onError method.

Thursday, February 25th, 2010

Just a quick development note.  This is obvious, but had me fooled for a little while.  Simply put coldfusion does not return an error via AJAX if the onError method is set in the Application.cfc file.  This is obvious because the error is being handled elsewhere.  Nonetheless, if you are in development mode using AJAX and have the onError method turned off then you can view your the errors that are thrown in the cfc being called remotely.  Very nice and very helpful.  Then you go into production, and, like any good developer you set up all of your error handling.  But.. But there is a problem with one of the ajax calls to a cfc.  No big thing, just inspect it in Firebug or Safari’s Web Inspector (or even IE8′s new developer tool if you can make it through all the security alerts).  But you don’t see an error, just an empty call.  Not only was an error not returned, nothing was returned.  Well, it’s just because your error handler template has done something else with the error.

So choose to tackle the error in whatever way seems fit.  But the console will not return Coldfusion/AJAX errors while the onError method is running.

String Truncation Method

Sunday, February 21st, 2010

I’ve worked up a UDF that will truncate a string according to a set of rules defined by the user.  My reasoning for building this is that it seems like every time I need to truncate data it’s being truncated differently.  One client wants it truncated to a certain letter, others a certain word, or a sentence.  God knows that next it’ll be the nearest asterisk or dash or some other off-the-wall character.  So I came up with a set of rules I thought would let me define my truncation methods for the future without having to change any of the foundation code.  So first I’ll just post the method.

<!--- *****************************  TRUNCATE TO WORD  ***************************** --->
<cffunction name="truncateToChar" displayname="Truncate To Word" description="Truncates a string to a certain character length at the next or previous word." access="public" output="false" returntype="struct">

	<!--- PASS IN ARGUMENTS --->
	<cfargument name="stringtobetruncated" displayName="String to be Truncated" type="string" hint="This is the string that will be truncated." required="true" />
	<cfargument name="numofcharacters" displayName="Number of Characters" type="numeric" hint="Number of characters to truncate string at." required="false" default="300" />
	<cfargument name="beforeafter" displayName="Before or After" type="string" hint="To truncate to the nearest word before or after the character cutoff limit." required="false"  default="before" />
	<cfargument name="showellipses" displayname="Show Ellipses" type="boolean" hint="Whether or not to show ellipses at the end of the truncated data." required="false" default="true" />
	<cfargument name="chartotruncateto" displayname="Character to Truncate To" type="string" hint="This is the character that the program will truncate to." required="false" default="">

	<!--- SET SOME LOCAL VARS --->
	<cfset var counter = 1>
	<cfset var dataarray = arrayNew(1)>
	<cfset var truncatechar = "">
	<cfset var charindex = ARGUMENTS.numofcharacters>

	<!--- CREATE A STRUCT --->
	<cfset var data = structnew()>
	<cfset data.success = true>
	<cfset data.message = "">
	<cfset data.originalstring = ARGUMENTS.stringtobetruncated>
	<cfset data.truncatedstring = "">
	<cfset data.numofchars = 0>
	<cfset data.maxchars = 0>
	<cfset data.ischar = "">

	<!--- TRY TRUNCATING THE DATA --->
	<cftry>
		<!--- CONVERT STRING TO AN ARRAY OF CHARACTERS --->
		<cfset dataarray = ARGUMENTS.stringtobetruncated.GetBytes()>
		<cfset data.numofchars = arrayLen(dataarray)>
		<cfset data.maxchars = data.numofchars - 1>

		<!--- MAKE SURE NUMBER OF CHARACTERS IS GREATER THAN 0 AND LESS THAN THE NUMBER OF CHARACTERS IN THE STRING --->
		<cfif ARGUMENTS.numofcharacters lt 1 OR ARGUMENTS.numofcharacters gt data.numofchars>
			<cfset data.success = false>
			<cfset data.message = "Please enter a number for characters that falls between 0 and #data.numofchars#.">
		<cfelse>
			<!--- FIND THE CHARACTER AT THE SPECIFIED CHARACTER NUMBER --->
			<cfset truncatechar = chr(dataarray[ARGUMENTS.numofcharacters])>

			<!--- CHECK IF NO CHARACTER TO TRUNCATE TO IS SPECIFIED --->
			<cfif ARGUMENTS.chartotruncateto eq "" OR truncatechar eq ARGUMENTS.chartotruncateto>
				<!--- IF SO SET THE TRUNCATED STRING INTO STRUCT --->
				<cfset data.truncatedstring = left(ARGUMENTS.stringtobetruncated, ARGUMENTS.numofcharacters)>

				<!--- SHOW ELLIPSES IF NEEDED --->
				<cfif ARGUMENTS.showellipses eq true>
					<cfset data.truncatedstring = data.truncatedstring & " ...">
				</cfif>
			</cfif>

			<!--- MAKE SURE THE CHARACTER DIDN'T FALL AT END OF WORD BEFORE CONTINUING --->
			<cfif data.truncatedstring eq "">
				<!--- CHECK IF DATA SHOULD BE TRUNCATED TO PREVIOUS OR NEXT WORD --->
				<cfswitch expression="#ARGUMENTS.beforeafter#">
					<!--- WORD BEFORE --->
					<cfcase value="before">
						<!--- BEGIN MOVING BACKWARDS CHARACTER BY CHARACTER UNTIL WE REACH A SPACE --->
						<cfloop from="1" to="#ARGUMENTS.numofcharacters#" index="VARIABLES.i">
							<!--- LOWER CHARACTER INDEX BY 1 --->
							<cfset charindex = charindex - 1>

							<!--- MAKE SURE THAT THE CHARACTER TO TRUNCATE TO EXISTS --->
							<cfif charindex eq 0>
								<!--- JUST TRUNCATE TO CHARACTER INDEX --->
								<cfset data.truncatedstring = left(ARGUMENTS.stringtobetruncated, ARGUMENTS.numofcharacters)>
							<cfelse>
								<!--- GET THE CHARACTER AT NEW INDEX --->
								<cfset data.ischar = chr(dataarray[charindex])>

								<!--- IF THE CHARACTER STOP LOOPING --->
								<cfif data.ischar eq ARGUMENTS.chartotruncateto>
									<!--- TRUNCATE AT SELECTED CHARACTER INDEX --->
									<cfset data.truncatedstring = left(ARGUMENTS.stringtobetruncated, charindex)>

									<cfbreak>
								</cfif>
							</cfif>
						</cfloop>
					</cfcase>

					<!--- WORD AFTER --->
					<cfcase value="after">
						<!--- BEGIN MOVING BACKWARDS CHARACTER BY CHARACTER UNTIL WE REACH A SPACE --->
						<cfloop from="1" to="#ARGUMENTS.numofcharacters#" index="VARIABLES.i">
							<!--- LOWER CHARACTER INDEX BY 1 --->
							<cfset charindex = charindex + 1>

							<!--- MAKE SURE THAT THE CHARACTER TO TRUNCATE TO EXISTS --->
							<cfif charindex gte data.maxchars>
								<!--- JUST TRUNCATE TO CHARACTER INDEX --->
								<cfset data.truncatedstring = left(ARGUMENTS.stringtobetruncated, ARGUMENTS.numofcharacters)>
							<cfelse>
								<!--- GET THE CHARACTER AT NEW INDEX --->
								<cfset data.ischar = chr(dataarray[charindex])>

								<!--- IF THE CHARACTER STOP LOOPING --->
								<cfif data.ischar eq ARGUMENTS.chartotruncateto>
									<!--- TRUNCATE AT SELECTED CHARACTER INDEX --->
									<cfset data.truncatedstring = left(ARGUMENTS.stringtobetruncated, charindex)>

									<cfbreak>
								</cfif>
							</cfif>
						</cfloop>

						<!--- TRUNCATE AT SELECTED CHARACTER INDEX --->
						<cfset data.truncatedstring = left(ARGUMENTS.stringtobetruncated, charindex)>
					</cfcase>

					<!--- IF EITHER "BEFORE" OR "AFTER" WAS NOT PASSED IN --->
					<cfdefaultcase>
						<cfset data.success = false>
						<cfset data.message = "An inappropriate befor/eafter argument was passed in: #ARGUMENTS.beforeafter#.">

						<!--- THROW ERROR --->
						<cfthrow message="#data.message#">
					</cfdefaultcase>
				</cfswitch>

				<!--- SHOW ELLIPSES IF NEEDED --->
				<cfif ARGUMENTS.showellipses eq true>
					<cfset data.truncatedstring = data.truncatedstring & " ...">
				</cfif>
			</cfif>
		</cfif>

		<!--- CATCH ANY ERRORS --->
		<cfcatch type="any">
			<cfset data.success = false>
			<cfset data.message = "There was a problem truncating the data.">
		</cfcatch>
	</cftry>

	<!--- RETURN STRUCT --->
	<cfreturn data>

</cffunction>

Also feel free to copy and use this.  That’s really the point of posting it.  We have five arguments or parameters here.  First we need to make sure we tell the method which string is being truncated.  Second, at which character should the data be truncated.  Third, if truncating to a certain type of character such as a space for a word or a period for a sentence should it be truncated to the nearest character before or after the cutoff character.  Two acceptable strings are accepted here: “before” and “after”.  Fourth, should ellipses be appended to the end of the truncated string.  This is ‘true’ if yes and ‘false’ if no.  And lastly, the type of character that the truncation should shoot for.  If this is left as an empty string it will truncate to the cutoff letter.

For example we have this string: “The brown fox. The grey dog.”.  We truncate to the character 5 with no type of character to truncate to.  The truncation would look like “The b …”.  Remember that spaces are considered characters here.  Now we do the same truncation, but tell it to truncate to the nearest space (” “) “before” the cutoff character.  Our cutoff character is still the “b” in ‘brown’, but this time our truncation will return “The …”.  If we specified the same thing but “after” the cutoff character our truncation string would look like “The brown …”.  We can also do sentences by passing in the type of character as a period (“.”).  If we passed this in for “after” the cutoff character our truncation would return “The brown fox. …”.  You could cut off to any character just by passing in that character to the ‘chartotruncateto’ argument.

Lastly, just keep in mind that if you pass in an asterisk as the cutoff character and there is no asterisk in the character it will truncate the string to the specified character count.  So again, if we told the method to truncate to the 5th character with a character type of asterisk (“*”) after the cutoff character the truncation would look like “The b …”.  This is because there is no asterisk.  And one more thing.  Obviously if you pass in a cutoff character number greater than the number of characters in the string or less than 1 it will just not truncate the string.

Finally I’ve set it up to return a struct called data that contains some of the data that is being passed around and manipulated in the method.  You could of course choose to just return the string, but thought it might be nice if you could see some of the inner workings of the method.  Enjoy.

Filtering on keypress without one character delay

Sunday, February 21st, 2010

If you’re using jQuery to filter SQL data in something like a grid or live search results you may find that the search filters according to the key pressed two times back.  For example if I’m searching for the word blue in an SQL table containing a bunch of colors it may work so that when I type ‘blu’ into the search tool it returns both ‘black’ and ‘blue’.  This is because it has not filtered according to the ‘u’ that was just typed.  This can lead to pretty confusing result listings for the user.  We’re not used to being on a one character delay.

If you’re having this problem luckily it’s a very minor thing.  If you’re jQuery is performing the filter on ‘keypress’ then this is your problem.  ‘keypress’ is pretty much the same as ‘keydown’.  As a result it performs the filter query before it knows what the key press is returning.  If you have a search field with an id of ‘search’ your jQuery may look something like

$("#search").keypress(function()
{
     //do some type of filtering
});

So the search is being performed just before the new character is registered. To fix this just change ‘keypress’ to ‘keyup’. This will make sure that the search is not performed until after the new character has registered.  This way your filter will run with all of the characters in the search field.

The new jQuery would look something like the following:

$("#search").keyup(function()
{
     //do some type of filtering
});

Removing extra white space before returning JSON data

Monday, February 15th, 2010

Although generating HTML in a CFC is not always good practice, when you have the choice between generating the HTML in a CFC or with jQuery it might be best to go with the CFC.  The only reason I say this is for readability and updatability.  But remember that it’s always best to create the HTML structure on the front-end page and fill in the data with Coldfusion or on the spot with jQuery and AJAX.  So enough with the disclaimers.

Sometimes it is best to return a chunk of HTML to the browser built in a CFC.  For example an application I’m working on at the moment consists of a nutritional label for a line of food products.  I of course want the same label for each product, but with different data in each label.  On top of this the label is used in several places in the application.  To add one more layer of complexity, the application is very, very AJAX dependent.  So the label is not only used in multiple places, but updates via an AJAX proxy to a CFC when the product changes.  So in this case I opted to build my label with each call inside a <cfsavecontent> tag in a remote CFC.

When I began writing the application I was getting the data for the label via AJAX and returning it to some jQuery that used the html() method to write the html with the data returned from the CFC.  It worked just fine like this.  However, it didn’t take long for the client to want to make some very minor changes.  Maybe it’s just me, but my html label in jQuery was not very readable or easy to update.  At this point I moved the entire set of HTML to the CFC and just returned a save content variable to jQuery which output the label as a single prewritten chunk of code.

Once again this was just fine until I implemented the ability for the administrator to update the data in the label.  Double-clicking a piece of the data in the label opened that data in a text field where the data could be manipulated and saved.  The problem I found was that when the data became editable in the text area it was filled with a ton of white space around the data.  So much so that the actual data could not even be seen in the text area without deleting a slew of white space.  I went back and looked at the JSON that was returned and realized that all of my indentions and line breaks I created in the CFC for readability had carried over into the JSON.  Of course, the browser ignores this white space so the label was showing correctly, but text fields do not ignore it.

So how do we remove this spacing and breaks so that it doesn’t show up in input fields and certainly not when saved to the database? As usual someone else out there has built a UDF to handle removing extra formatting. The UDF can be found at cflib.org under ‘HtmlCompressFormat(sInput)‘.  It’s a fairly simple method, but from my use seems to work very well at removing extra formatting in HTML.

The method is written in script form, and since my CFC was working in tag form I quickly rewrote it to reflect a tag based structure.  They work exactly the same and the tag based version below is simply a copy of the original script form.  To use the UDF you just need to pass in the string to remove white space from.  The method defaults to level 2 compression which I’ve found perfectly fine.  But if you did want to more or less compression you can pass in a 1, 2, or 3 after the string.

Let’s look at an example.  We have a save content variable in a cfc in which we write some nice readable indented HTML.  Everything works fine when we output it in the browser.  But if we load that HTML into a text area or rich text area we notice a whole lot of white space around the content.  When we look at the JSON that is returned from the proxy call we notice a whole lot of ‘/t/t/t/t/t/t/t/n my content /t/t/t/t/t/t/t/t/n’ going on in the code.  These are the formattings we need to get rid of.  If this string is contained in a variable called ‘mystring’ we need to run this variable through the HtmlCompressFormat() method before inserting into a database or returning to the browser.  So our method call would look something like

<!--- RUN STRING THROUGH COMPRESS HTML METHOD --->
<cfset compressedstring = HtmlCompressFormat(mystring, 2)>

So when writing dynamic HTML to be returned dynamically by jQuery I keep this method handy. Lastly, I’ll post the tag based copy of the method below. And if you want to see the original at cflib.org click here.

<cfcomponent displayname="Compress HTML" hint="Compresses any HTML passed in." output="false">

	<!--- *****************************  COMPRESS HTML  ***************************** --->
	<cffunction name="compress" displayname="Compress HTML" description="Removes any extra whitespacing in HTML." access="public" output="false" returntype="String">

		<!--- PASS IN HTML AND LEVEL OF COMPRESSION --->
		<cfargument name="htmlString" displayName="HTML String" type="string" hint="String to be compressed." required="true" />
		<cfargument name="level" displayName="Compression Level" type="numeric" hint="Level of compression" default="2" required="false" />

		<cfset var stringToCompress = ARGUMENTS.htmlString>
		<cfset var compressionLevel = ARGUMENTS.level>

		<!--- TRIM OFF ANY EXTRA SPACES FROM STRING TO BE FILTERED --->
		<cfset stringToCompress = trim(stringToCompress)>

		<!--- RUN FILTER BASED ON SPECIFIED COMPRESSION LEVEL --->
		<cfswitch expression="#compressionLevel#">
			<cfcase value="3">
				<cfset stringToCompress = reReplace(stringToCompress, "[[:space:]]{2,}", " ", "all")>
				<cfset stringToCompress = replace(stringToCompress, "> <", "><", "all")>
				<cfset stringToCompress = reReplace(stringToCompress, "<!--[^>]+>", "", "all")>
			</cfcase>

			<cfcase value="2">
				<cfset stringToCompress = reReplace(stringToCompress, "[[:space:]]{2,}", chr( 13 ), "all")>
			</cfcase>

			<cfdefaultcase>
				<cfset stringToCompress = reReplace(stringToCompress, "(" & chr( 10 ) & "|" & chr( 13 ) & ")+[[:space:]]{2,}", chr( 13 ), "all")>
			</cfdefaultcase>
		</cfswitch>

		<!--- RETURN COMPRESSED HTML --->
		<cfreturn stringToCompress>

	</cffunction>

</cfcomponent>

Refreshing the Services Browser in Coldfusion Builder

Sunday, February 14th, 2010

Quick post on refreshing the Services Browser in the new Coldfusion Builder. I found it to be a little frustrating that the Services Browser does not contain a refresh button. The problem only occurs when you have Coldfusion Builder open, create a cfc method, and then try to access it through the Services Browser. If you would like more info on using the services browser I’ve posted on it here with a video.

Anyways, if you create a cfc method you’ll notice that it obviously doesn’t immediately show up in the Services Browser pane.  To refresh the browser you need to scroll all the way to the top and find the top level called whatever the name of the server you’re working off of is named.  Whatever it’s named, rest assured that it is the top level of the Services Browser.  To refresh, find this top level, right-click and choose to refresh.

So, it’s not the most intuitive thing I’ve encountered, but once you know it it’s pretty simple to refresh.

Converting a complex list to a struct

Wednesday, February 10th, 2010

I created an image upload for some projects.  Having dealt with image uploads in the past I decided that I would just do some after hours work in order build one that I could give to the designers and let them go to town with it without hopefully needing my help.  I did this like many plugins with some settings.  So for each upload the designer has a list of settings where they can specify MIME types, destinations, sources, names, database table names, and so forth.  One setting that is available is the ability to specify a set of sizes to resize the original image to.

While I naturally wanted to pass these values in as an array of values that my method could use to do the actual resizing I knew that each time the upload was implemented I would have to stop what I was doing to create the array of images and their resizing data.  Not interested.

My solution was what I’m calling here a complex list.  By complex I simply mean that it has two sets of delimiters in it.  In my case commas and semi-colons.  I chose this route over the array because it’s something more along the designers line of work.  No fancy methods and square brackets.  Just some good ole’ quotes, commas, and semi-colons.  So let’s look at an example of a complex list, and then we’ll take a look at a method that will convert that list into a struct that the image upload method can work with.

The Calling Page

<!--- SET A STRING THAT CONTAINS IMAGE DATA IN THE FORM
OF WIDTH, HEIGHT, TYPE; WIDTH, HEIGHT, TYPE; AND SO ON.
SEMI-COLONS SEPARATE IMAGES AND COMMAS SEPARATE IMAGE
SPECIFIC DATA. --->
<cfset VARIABLES.images = "200,200,large;150,150,medium;100,100,small">

<!--- RUN METHOD TO CONVERT DATA LIST TO STRUCT --->
<cfinvoke 	component="path.to.cfc.complex_lists"
			method="convertComplexList"
			returnVariable="complex_lists" >

	<!--- PASS IN LIST ABOVE --->
	<cfinvokeargument name="imagesizes" value="#VARIABLES.images#" />

</cfinvoke>

<!--- DUMP THE STRUCT --->
<cfdump var="#complex_lists#">

(more…)

Local variable in CFC function cannot be named same as an argument

Wednesday, February 10th, 2010

I just ran into this issue today. It seems that while a variable with the same name can be declared in the arguments, variable, session, and application scopes without conflict, the name of an argument cannot be named the same as a local var set in the function. Judging by the error Coldfusion throws (“Cannot declare local variable [variable name] twice. Local variables cannot have the same names as parameters or other local variables.“) it’s not so clear that arguments and local variables cannot have the same name.

The error is slightly misleading in that it states explicitly that the local variable cannot be declared twice, when in reality it is not being declared twice.  Instead we need to look at the fine print of the error where it states that local variables cannot have the same name as parameters, or the arguments.  Something to watch out for.