Entries Tagged as 'ColdFusion'

Does This Method Exist?

ColdFusion 6 Comments »

In one of our applications, we have a couple of "core views", i.e. view stuff that is used in a couple different places, so we have it in one tidy CFM for easier maintenance. For most of them, the content coming in is the same or similar in nature, however we have one where they are not quite the same. With our registration view, the populating object (a "bean") could be a "real" registration object, with a full set of methods, or it could be a "pending" registration object, which has a slightly smaller set of methods.

When we first built the view, it wasn't really an issue because the methods that only a real registration would have were also only called on when certain flags were in place, which were never set for a pending registration. Then we added a new bit to the view, which isn't limited by the flags, whether or not it was a "transferred" registration. This caused us to start getting system errors when trying to view a pending object using this view. We could have just made yet another flag, but I figured there had to be a way to just say "hey, if you don't have the method getPreviousRegistration, skip this bit".

Well, we have IsDefined, but that just got us an error that the method wasn't found (well duh, that's why I asked you!). I could just wrap it in a try/catch, but then I'd have to deal with trying to filter out this fake error from real ones, and that really doesn't match how I like to code. So I Google'd around for "ColdFusion function exists". To my surprise, I found almost nothing, beyond a very brief thread on Adobe (2 posts) with an "answer" that lacked any details.

The brief answer did give me a starting point, though, so I decided to expand on it here in case anyone else finds themselves wondering hwo to do it. Basically, you can do this using a combination of StructFindValue and getMetaData. StructKeyExists doesn't work, at least the way I solved this, because the function names in getMetaData aren't set as keys, but rather values. So the resulting bit that went into my CFIF?

ArrayLen(StructFindValue(getMetaData(bRegistration), "getPreviousRegistration")) GT 0

If the function is there, you'll have a populated there. If it isn't, you'll have an empty array. It may not be the most "elegant" of solutions with the multiple nested functions, but it works and it is working well. So if anyone else wants to test for a function's existence, here is one way to do it. If you can think of some others, by all means share so that next time someone Google's the question, they get some hits! ;-)

Update 4/17/13 @ 7:45 am: Thanks to Aaron who showed me how StructKeyExists can work in this case, if you skip all the MetaData stuff.  So now we have a cleaner bit of code:

StructKeyExists(bRegistration, "getPreviousRegistration")

I didn't even think to try it cause I was stuck on the idea that "it's an object", forgetting that often for ColdFusion, they can be treated similarly :)

WTF with CF Structs

ColdFusion 18 Comments »

I like ColdFusion's structs, they are simple and easy to use/reference, work great for transporting bits of related data, and names are so much easier to reference than numbers. ;-) We use them in all of our applications for returning result messages and the like. Never really had any issues with them, until something came up this week that literally had my partner and I going WTF??

For the application we're working on, we have "products", which can be events, online trainings, etc. Each product has a core set of properties that are identical, but there are some properties unique to each type. For example, events have locations and dates for when they occur and when people can register, while online trainings are always available and may have an access URL attached to it. Rather than have a bunch of maybe properties on the products, we have a struct called ExtraProductDetails that we push those bits into, all nicely encapsulated in a function as we call those from several places:

<cffunction name="getProductExtraDetails" acces="public" returntype="struct" output="false">
    <cfargument name="ProductID" type="numeric" required="true" />
    <cfargument name="ProductTypeID" type="numeric" required="false" />

    <cfset var qProductExtra = "" />
    <cfset var sExtraProductDetails = StructNew() />

    <!--- if we have a location, throw it into the struct--->
    <cfquery name="qProductExtra" datasource="#variables.dsn#">
        SELECT address, buildingname, city, base_states_stateid, postalcode, base_countries_countryid
        FROM products_locations
        WHERE products_productid = #Arguments.ProductID#
    </cfquery>

    <cfif qProductExtra.RecordCount GT 0 OR (IsDefined("Arguments.ProductTypeID") AND Arguments.ProductTypeID EQ 1)>
        <cfset StructAppend(sExtraProductDetails, variables.oUtilities.queryRowToStruct(qProductExtra)) />
    </cfif>

    <!--- what about dates?  --->
    <cfquery name="qProductExtra" datasource="#variables.dsn#">
        SELECT startdate, enddate, registrationstart, registrationend, earlyregistrationend, lateregistrationstart
        FROM products_productdates
        WHERE products_productid = #Arguments.ProductID#
    </cfquery>

    <cfif qProductExtra.RecordCount GT 0 OR (IsDefined("Arguments.ProductTypeID") AND Arguments.ProductTypeID EQ 1)>
        <cfset StructAppend(sExtraProductDetails, variables.oUtilities.queryRowToStruct(qProductExtra)) />
    </cfif>

    <cfquery name="qProductExtra" datasource="#variables.dsn#">
        SELECT onlinetrainingurl
        FROM products
        WHERE productid = #Arguments.ProductID#
    </cfquery>

    <!--- if we have an online training URL, add it to the struct --->
    <cfif qProductExtra.onlinetrainingurl NEQ "" OR (IsDefined("Arguments.ProductTypeID") AND Arguments.ProductTypeID EQ 2)>
        <cfset sExtraProductDetails.onlinetrainingurl = qProductExtra.onlinetrainingurl />
    </cfif>

    <cfreturn sExtraProductDetails />
</cffunction>

Everything seemed great until we were testing a bug fix somewhere else and made an event that had no location...which then borked up one or our table displays royally.


Pretty dataTable


Not so pretty, no longer sorted, broken dataTable

When we explored why the table broke, we discovered that the product with no location was also missing the empty fields that should have been there. You may be thinking, so what? Your function just didn't include it because it didn't have one. Of course, that was our first thought too...and that is where the weirdness starts. We did the usual debugging type things, i.e. dump the struct for the event that has no location within getProductExtraDetails right before the return line:

That's what we expect - looks good. Dump the same structure in the original function that calls getProductExtraDetails:

<cfset sAdditionalDetails = getProductExtraDetails(ProductID = productid) />
<cfdump var="#sAdditionalDetails#" abort="true" />

WTF?? Somewhere in there, it suddenly drops all of the location fields. At first I wondered if it was some oddity with the struct set, but we got the same result if we dumped getProductExtraDetails(ProductID = productid) directly. Then I wondered if it could be an issue with the queryRowToStruct function we use and it being CFSCRIPT, so I rewrote it to tags, but same results. Tried explicitly resetting the sAdditionalDetails struct to new within the loop before populating, in case of some weird bleed over - still no change.

For an extra twist, this code worked fine in earlier testing when we did tests with stuff with no locations. It only started borking recently. Think think think think...not too long ago I cleaned up code to split out the date and location stuff, as an product could have dates without a location, so we wanted it more flexible. So maybe the issue is with the StructAppend? What if we do regular sets instead (while still having a bit of flexibility)?

<cfif qProductExtra.RecordCount GT 0 OR (IsDefined("Arguments.ProductTypeID") AND Arguments.ProductTypeID EQ 1)>
    <cfloop list="#qProductExtra.ColumnList#" index="thisColumn">
        <cfset sExtraProductDetails['#thisColumn#'] = qProductExtra[thisColumn][1] />
    </cfloop>
</cfif>

Same results. Okay, fine, what if we just get clunky with it and do this?

<cfset sExtraProductDetails.address = qProductExtra.address />
<cfset sExtraProductDetails.buildingname = qProductExtra.buildingname />
<cfset sExtraProductDetails.city = qProductExtra.city />
<cfset sExtraProductDetails.base_states_stateid = qProductExtra.base_states_stateid />
<cfset sExtraProductDetails.postalcode = qProductExtra.postalcode />
<cfset sExtraProductDetails.base_countries_countryid = qProductExtra.base_countries_countryid />

They are still getting dropped! It can't just be an issue of it dropping "blanks" because LateRegistrationStart is coming over with a blank as well. So seriously...WTF?

I have absolutely no explanation of what might be causing this bug (and yes, at this point, I'm calling it a serious bug in CF 9). Googling found me no similar issues reported anywhere and nothing in the documents explains why a struct would selectively drop some elements, but not others. I have no idea if you get the same on CF 10 as all of our boxes are CF 9.

At this point, the only fix we found was a ugly work around of basically creating the structure before populating it as well as switching out the StructAppend

<cffunction name="getProductExtraDetails" acces="public" returntype="struct" output="false">
    <cfargument name="ProductID" type="numeric" required="true" />
    <cfargument name="ProductTypeID" type="numeric" required="false" />

    <cfset var qProductExtra = "" />
    <cfset var sExtraProductDetails = StructNew() />
    
    <!--- Prebuild our struct cause otherwise some of the values sometimes get lost when passed back to the callers on whims --->
    <cfif IsDefined(Arguments.ProductTypeID) AND Arguments.ProductTypeID EQ 1>
        <cfset sExtraProductDetails.address = "" />
        <cfset sExtraProductDetails.buildingname = "" />
        <cfset sExtraProductDetails.city = "" />
        <cfset sExtraProductDetails.base_states_stateid = "" />
        <cfset sExtraProductDetails.postalcode = "" />
        <cfset sExtraProductDetails.base_countries_countryid = "" />
        <cfset sExtraProductDetails.startdate = "" />
        <cfset sExtraProductDetails.enddate = "" />
        <cfset sExtraProductDetails.registrationstart = "" />
        <cfset sExtraProductDetails.registrationend = "" />
        <cfset sExtraProductDetails.earlyregistrationend = "" />
        <cfset sExtraProductDetails.lateregistrationstart = "" />
        
    <cfelseif IsDefined(Arguments.ProductTypeID) AND Arguments.ProductTypeID EQ 2>
        <cfset sExtraProductDetails.onlinetrainingurl = "" />
    </cfif>
    
    <!--- if we have a location, throw it into the struct--->
    <cfquery name="qProductExtra" datasource="#variables.dsn#">
        SELECT address, buildingname, city, base_states_stateid, postalcode, base_countries_countryid
        FROM products_locations
        WHERE products_productid = #Arguments.ProductID#
    </cfquery>

    <cfif qProductExtra.RecordCount GT 0 OR (IsDefined("Arguments.ProductTypeID") AND Arguments.ProductTypeID EQ 1)>
        <cfloop list="#qProductExtra.ColumnList#" index="thisColumn">
            <cfset sExtraProductDetails['#thisColumn#'] = qProductExtra[thisColumn][1] />
        </cfloop>
    </cfif>

    <!--- what about dates?  --->
    <cfquery name="qProductExtra" datasource="#variables.dsn#">
        SELECT startdate, enddate, registrationstart, registrationend, earlyregistrationend, lateregistrationstart
        FROM products_productdates
        WHERE products_productid = #Arguments.ProductID#
    </cfquery>

    <cfif qProductExtra.RecordCount GT 0 OR (IsDefined("Arguments.ProductTypeID") AND Arguments.ProductTypeID EQ 1)>
        <cfloop list="#qProductExtra.ColumnList#" index="thisColumn">
            <cfset sExtraProductDetails['#thisColumn#'] = qProductExtra[thisColumn][1] />
        </cfloop>
    </cfif>

    <cfquery name="qProductExtra" datasource="#variables.dsn#">
        SELECT onlinetrainingurl
        FROM products
        WHERE productid = #Arguments.ProductID#
    </cfquery>

    <!--- if we have an online training URL, add it to the struct --->
    <cfif qProductExtra.onlinetrainingurl NEQ "" OR (IsDefined("Arguments.ProductTypeID") AND Arguments.ProductTypeID EQ 2)>
        <cfloop list="#qProductExtra.ColumnList#" index="thisColumn">
            <cfset sExtraProductDetails['#thisColumn#'] = qProductExtra[thisColumn][1] />
        </cfloop>
    </cfif>

    <cfreturn sExtraProductDetails />
</cffunction>

If anyone else has experienced this or has any clues on it, I'd love to hear from you. Also would love to know if it happens on CF 10 or on other CF servers (i.e. Railo? Blue Dragon?)

ColdFusion SpreadSheet Creation and DataFormat Fustrations

ColdFusion No Comments »

The addition of the CFSPREADSHEET and all the SpreadSheet functions to ColdFusion 9 was an awesome thing. We have so many apps where clients want an Excel sheet and for whom a CVS is a foreign language. Before CF 9, we used POI to build our spreadsheets, and it worked well though it was slower and coding was clunkier. CF9 seemed to make it a breeze - building out a 3 -4 sheet download could be done in a relatively small amount of code. And there was a great number of functions to really let you "get happy" with building whatever insane spreadsheets you might need to.

On the whole, we've welcomed the change and it has been awesome. The XLSX files are smaller, easier to transfer, and it runs fast, except with large data sets and even then it isn't hideous. However, we have stumbled on what appears to be a really annoying bug. Namely, while the various functions allowed you manipulate individual sheets, format cells and columns, etc. the formatting doesn't always work well. What triggers this "breakage" though, we have yet to figure out.

We discovered it while building a multiple sheet spreadsheet for a client, which had some dollar figures on several sheets that needed formatting. So we coded it all up and it ran perfectly, except the formatting only "took" on the first sheet - kinda. The formatting of our header row worked perfectly, as did any other changes to the column formats - everything BUT the dataformat (which let's you tap Excel's "format cell") option. So time to run some test scenarios...

Here is a two sheet spreadsheet. On the first sheet, I told it format the last column to a number with appropriate comma separation. On the second sheet, Fake Value should be formatted as a dollar figure.

For the purposes of this post, I am only including the parts related to the spreadsheet, not the queries themselves.  And of course the file starts by turning off debugging.

<cfset writeDirectory = left(cgi.cf_template_path,(len(cgi.cf_template_path) - len(cgi.script_name))) />
<cfset myExcelFile = writeDirectory & "/testing_spreadsheets.xlsx" />
<cfset oSpreadsheet = SpreadsheetNew("Book Series", true) />
<cfset SpreadsheetAddRow(oSpreadsheet, "Series ID,Series Name,Number of Titles,Is On Going,Number Held,Total Num Pages") />


<cfset formatCell = StructNew() />
<cfset formatCell.bold = "true" />
<cfset formatCell.bottomborder = "thin" />
<cfset formatCell.bottombordercolor = "black" />
<cfset formatCell.fgcolor = "light_yellow" />
<cfset SpreadsheetFormatCellRange(oSpreadsheet, formatCell, 1, 1, 1, 6) />


<cfset SpreadsheetAddRows(oSpreadsheet, qSeries) />


<cfset formatCell = StructNew() />
<cfset formatCell.dataformat = "######,####0" />
<cfset SpreadsheetFormatCellRange(oSpreadsheet, formatCell, 2, 6, (qSeries.RecordCount+1), 6) />


<cfspreadsheet action="write" filename="#myExcelFile#" name="oSpreadsheet" sheetname="Series" overwrite="true" />


<!--- snip snip --->
<cfset SheetColumns = "Title ID, Title, ISBN, Date Acquired, Num Pages, Format, Category, Series, Fake Value" />
<cfset oSpreadsheet2 = SpreadsheetNew("Registrations", true) />
<cfset SpreadsheetAddRow(oSpreadsheet2, SheetColumns) />


<!--- FORMAT FOR COLUMN HEADERS --->
<cfset formatCell2 = StructNew() />
<cfset formatCell2.bold = "true" />
<cfset formatCell2.bottomborder = "thin" />
<cfset formatCell2.bottombordercolor = "black" />
<cfset formatCell2.fgcolor = "light_yellow" />
<cfset SpreadsheetFormatCellRange(oSpreadsheet2, formatCell2, 1, 1, 1, 9) />


<cfset SpreadsheetAddRows(oSpreadsheet2, qBooks) />


<!--- format dollar values --->
<cfset formatCell2 = StructNew() />
<cfset formatCell2.dataformat = "$######,####0.00" />
<cfset formatCell2.bold = "true" />
<cfset formatCell2.fgcolor = "pale_blue" />
<cfset SpreadsheetFormatCellRange(oSpreadsheet2, formatCell2, 2, 9, (qBooks.RecordCount+1), 9) />
<cfspreadsheet action="update" filename="#myExcelFile#" name="oSpreadsheet2" sheetname="Books" />

Nothing too extreme or anything. But notice the second spread has all other formatting except the dollar format. Now to prove that it was doing something to the column, I added an additional formatting of bold and threw on a blue background.

Yep, that worked. Only the dollar formatting is ignored. Now, for extra fun, if I remove the first sheet....

WTH? And if I switch the order of the sheets....

The Books sheet still has it's dollar formatting, yay. However, now it is also incorrectly dollar formatting the 6th column of the Series sheet, instead of doing the number format it actually has in the code!

Now if we say okay, fine, do "old" Excel instead of XLSX...then it loses the number formatting all together on both sheets, so it isn't an issue with that. We also tried doing FormatColumn instead of FormatCellRange, still the same. Then I wondered if it just didn't like my "custom" format, which is supported by Excel but what the heck. So I grabbed ones from the Adobe Docs. Of course, it failed because they didn't bother to escape the pounds in their code (yeah, I know - if you got here and are having that issue, double up each pound sign), so fixed that and....nope, no change. So I tried a number format without any pounds signs - still no go. Then I tried just formatting a single, individual cell, and again it doesn't work.

So what to do? At this point, it seems like this is a bug in ColdFusion 9 - and one I can't find any information about beyond a single ticket on the old ColdFusion bug tracker that is no where to be found at the newer one. If any of you have encountered this issue and know how to fix it, by all means let me know. For now, it seems our only options are formatting it using MySQL in the query, or looping the query results, formatting with ColdFusion and then reupdating the query. At least, for Excel files with more than one sheet.

IsValid Cannot Answer the Question of Is It Really An Integer

ColdFusion No Comments »

In the course of developing a good, secure, well functioning web application, you will inevitable spend quite a few lines of codes validating input so that you know it's meeting whatever business rules/logic may apply. ColdFusion has quite a few functions to help with validations, including the IsValid function, which let's you pass in a value and a type that it should be and get a true/false result. You can also do checks as to whether a value is within a certain range or run it against a regular expression pattern.

On the whole, IsValid is a darn useful function, with its 25 available type matches, including email, creditcard, telephone, zipcodes, dates, and of course basic variable types like integer, numeric, string, and boolean. Unfortunately, it has one flaw that may be a "gotcha" for users who don't dig through the validation. Namely, when it comes to some of the variable types, ColdFusion is really checking it against the SQL standard, not the general meaning.

For example, the one that brought this to my attention was my trying to do a quick check to make sure a list of values is a list of numeric values. I thought I was clever by just checking to see if it was an integer once the delimiter was removed. Now, mathematically and in common terms, an integer is a number with no decimal or fractional component, i.e. a "whole number." So, using IsValid, you would have a good reason expect all of these to return true:

<cfoutput>
#IsValid("Integer", 1)#
#IsValid("Integer", 1234)#
#IsValid("Integer", 12345678)#
#IsValid("Integer", 1234567890)#
#IsValid("Integer", 11234567890987654321)#
</cfoutput>

And these to all return false:

<cfoutput>
#IsValid("Integer", '1.0')#
#IsValid("Integer", '12.34')#
#IsValid("Integer", '$2345.78')#
#IsValid("Integer", '$1,23,45,67')#
#IsValid("Integer", '$1,234,567')#
#IsValid("Integer", 'A123456')#
</cfoutput>

In reality what you get from the two sets is:

Should all be integers:

IsValid("Integer", 1) = YES
IsValid("Integer", 1234) = YES
IsValid("Integer", 12345678) = YES
IsValid("Integer", 1234567890) = YES
IsValid("Integer", 11234567890987654321) = NO


Should NOT be integers:

IsValid("Integer", '1.0') = NO
IsValid("Integer", '12.34') = NO
IsValid("Integer", '$2345.78') = NO
IsValid("Integer", '$1,23,45,67') = YES
IsValid("Integer", '$1,234,567') = YES
IsValid("Integer", 'A123456') = NO

WTF? Well, the reason for it is ColdFusion's IsValid for the integer type does NOT validate that it is an integer in normal terms, rather it is validating that it meets the SQL and some programming standards of being an "long integer". So any number that isn't in the range of ?2,147,483,648 to +2,147,483,647 will fail. Interesting enough, it isn't even using the Java range, which is ?9,223,372,036,854,775,808 to +9,223,372,036,854,775,807; instead the range it uses harkens back to its C++ based days (presumably for backwards compatibility). Further, when doing it's IsValid check for Integers, it ignores commas - it doesn't even check if they are in a "proper" place!

So what does this mean for you? Well, if you want to know if something is an integer in the mathematical sense, and do not care if it is within an SQL integer range, you need to use a different validation method. The question is, what alternatives are there. I've seen a few suggestions floating around the net on getting around this, which I'm compiling here and showing the results of each:

Use IsNumeric (or IsValid with the Numeric type)

Will fail anything that isn't numerals and a decimal point, which will work for most needs. You could combine this in conjunction with looking for decimal separately if you want to be sure it is an actual integer versus a decimal,

#IsNumeric(11234567890987654321)# YES
#IsNumeric('$1,23,45,67')# NO
#IsNumeric('12.34')# YES
#IsNumeric('$2345.78')# NO
#IsNumeric('234,578')# NO
#IsNumeric('A123456')# NO
#IsValid("Numeric", 11234567890987654321)# YES
#IsValid("Numeric", '$1,23,45,67')# NO
#IsValid("Numeric", '12.34')# YES
#IsValid("Numeric", '$2345.78')# NO
#IsValid("Numeric", '234,578')# NO
#IsValid("Numeric", 'A123456')# NO

Using Round to do a comparison

Using this method, you have to wrap your check in a CFTRY/CFCATCH as Round will not accept non-numbers at all. You can see below the two that failed through the catch versus the equivalent failing.

#ROUND(11234567890987654321) EQ 11234567890987654321# NO
#ROUND('A123456') EQ 'A123456'# Not Valid (cfcatch)
#ROUND('$1,234,567') EQ '$1,234,567'# Not Valid (cfcatch)

But you probably also noticed that the first one failed too. If you output the results of #ROUND(11234567890987654321)#, you'll see why:

9.22337203685E+018

Yep, with larger numbers, Round returns them in scientific notation.

Using some Regular Expression Checks (courtesy of the incomparable Ray Camden :) )

On the whole, these seem to work pretty decently. However, if your number has a leading zero, it will also come back as invalid (which technically it is). If you did need to have long numbers with leading zeros, if you change the [1-9] to [0-9], it should then work.

#IsValid("RegEx", 11234567890987654321, "^-{0,1}[1-9]+[\d]*")# YES
#IsValid("RegEx", 11234567890987654321, "^-{0,1}[1-9]+[\d]*")# YES
#IsValid("RegEx", 01345, "^-{0,1}[1-9]+[\d]*")# NO
#IsValid("RegEx", '1.0', "^-{0,1}[1-9]+[\d]*")# NO
#IsValid("RegEx", '12.34', "^-{0,1}[1-9]+[\d]*")# NO
#IsValid("RegEx", $2345.78', "^-{0,1}[1-9]+[\d]*")# NO
#IsValid("RegEx", A123456', "^-{0,1}[1-9]+[\d]*")# NO

Going down to the Java Level

This method requires wrapping the checks in a try/catch as it outright fails if it isn't a valid integer; and Java's parseInt doesn't actually let you go up to it's own integer limit, rather it enforces the more standard range.

#createObject("java","java.lang.Integer").parseInt("987654321987)# Not Valid (cfcatch)
#createObject("java","java.lang.Integer").parseInt("1.0")# Not Valid (cfcatch)
#createObject("java","java.lang.Integer").parseInt("A123456")# Not Valid (cfcatch)
#createObject("java","java.lang.Integer").parseInt("$2345.78")# Not Valid (cfcatch)
#createObject("java","java.lang.Integer").parseInt("1234567890")# 1234567890

So for my needs, it looks like the regular expression is the best option, in conjunction with stripping out the delimiter, to ensure that my list of stuff is really a list of integers before I shove it to the database or try to do other stuff to it.

Is ColdFusion Dead? Individual Perspective

ColdFusion , Developer's Life 9 Comments »

Recently we had a scare at work, and were told that we will lose our jobs very soon.  Fortunately it was a false alarm but still we lived 24 hours without fear.  The same week I had a younger person I knew ask me what I thought about web development and if I would suggest going to ColdFusion.  These two incidents caused me to rethink the oft asked question of "if ColdFusion is dead" and how it applies to me personally.  I have to say for me the answer may end up being yes.

In my area ColdFusion is not very well used, in fact, it's rarely used.  Not many places other than our own unit within our state agency that uses it.  There may be a few departments on the college campus that use it and I think one local development shop, maybe two.  That's about it.  Now elsewhere in the country, ColdFusion is quite alive and well.  There are quite a few places that use it, but here when I had the fear that I was going to lose my job, I found that there is no ColdFusion work available at all.

So what I answered my younger friend, I found myself having to answer in a more ambiguous fashion than I normally would have.  Namely I told him that it would depend on what you want to do with your life in general.  So if you wanted to stay here in my area, Bryan-College Station, I would say no, do not get into ColdFusion, instead, learn another language like PHP or .Net.  If however you are open to travel and you didn't mind relocating then I would say yes, consider giving ColdFusion a shot.  Particularly if you want live on the East Coast up near Washington DC, Maryland, etc. which is a heavy ColdFusion area, or even if you're interested in living in Austin or Houston, which have higher ColdFusion bases.

There are still quite a few companies using ColdFusion, including some very big companies, so it's not as if there aren't jobs out there.  But if you're like me and you are not willing to leave where you live because you happen to really love it here and you have a house and a wonderful sweetie and stuff.  Then you don't have whole lot of choices.

So for me I have a strong suspicion that ColdFusion will be dying, and not be my primary language anymore. I've already made plans to start learning PHP 5 next year, it being the least this tasteful of the other languages available and one that I have some passing familiarity with.  It also has high job marketability and my boss doesn't hate it.  I still love ColdFusion, it is still my favorite language, and if I had my choice I keep doing it much longer, but I don't have a choice.  And so I must be a good adult and do what is required to continue doing the main thing I love which is building web applications.

So is ColdFusion dead?  For me, maybe it is.  For other people, I hope not.  I also hope there comes a time when Adobe gets ColdFusion out there enough that it comes back to my area and it's something I can return to.  But for now my mortgage won't allow me to hold out hope, neither will my tummy or my car for my other life expenses.

Powered by Mango Blog. Based on the Glossy Blue mango template design and utilizing the Icons of N.Design Studio
RSS Feeds