Our first entry comes from Steve Gustafson. Before reading on, please check his application here. Now that you have played with it a bit, let's take a look at selected portions of the code. Let's start with his Application.cfc file:
<cfcomponent output="false"> <cfsetting showdebugoutput="NO"> <cfscript> this.name="gusBlackJack"; this.sessionManagement = true; this.sessionTimeOut = CreateTimeSpan(0,0,20,0); this.scriptProtect = true; </cfscript>
<cffunction name="onApplicationStart"> </cffunction> <cffunction name="OnSessionStart"> <cfset session.playedCardList = ''> </cffunction> <cffunction name="OnRequestStart"> <cfargument name = "request" required="true"/> </cffunction>
I want to point out a few lines in particular. First off - notice how he turns off debugging. Why would you do this? ColdFusion debugging can be turned on at the server level, and if you forget to restrict debugging to a particular IP, then everyone in the world will see your debugging information. That's definitely not something you want. I'm a big fan of "Being Anal" in regards to stuff like this. You will notice I do the same in BlogCFC (although I use an Application.cfm file there instead). So - a minor point - but something to keep in mind.
In keeping with the "Being Anal" thread, he also specifies a session timeout and a scriptProtect. Specifying a sessiontimeout is a good idea since in CFMX 7, there was a bug where onSessionEnd wouldn't fire without it. (This has been fixed in 7.01.) ScriptProtect is a handy way to easily protect against cross-site scripting. It isn't perfect - but it is still a good idea to use if you aren't sure you are protecting against it in code yourself.
Now let's take a quick look at index.cfm. I'm only going to point out one thing here and it is more of a minor nit-pick than anything else. The following is from the top of the file, and is not the entire file itself:
As you can see, he put the code to handle AJAX requests at the top of the file. To me, this doesn't belong here. I'd have a separate file just to handle those requests. Again, this is just my opinion and nothing more. I will, however, harp on something more. Notice how he creates the component for every request. This confused me a bit until I looked at his CFC:
<cfcomponent displayname="BlackJackByGus" hint="This component handles the backend for the Blackjack Application>">
<cffunction name="shuffle" hint="I select a random value" returntype="numeric" access="private" output="No"> <cfargument name="maxNum" type="numeric" required="True"> <cfreturn RandRange(1, arguments.maxNum)> </cffunction> <cffunction name="getSuit" hint="I select the suit to be dealt" returntype="string" output="No" access="private"> <cfset suit = shuffle(4)> <cfif suit EQ 1> <cfset strSuit = 'Spades'> <cfelseif suit EQ 2> <cfset strSuit = 'Hearts'> <cfelseif suit EQ 3> <cfset strSuit = 'Diamonds'> <cfelse> <cfset strSuit = 'Clubs'> </cfif> <cfreturn strSuit> </cffunction> <cffunction name="getCard" hint="I select the card to be dealt" returntype='numeric' output="No" access="private"> <cfset card = shuffle(13)> <cfreturn card> </cffunction> <cffunction name="dealCard" hint="I select the card to be dealt" returntype="string" output="No" access="public"> <cfset cardNum = getCard()> <cfset cardSuit = getSuit()> <cfset thisCard = "#cardSuit#_#cardNum#"> <cfif ListFind(session.playedCardList, thisCard)> <cfreturn dealCard()> <cfelse> <cfset session.playedCardList = listAppend(session.playedCardList,thisCard)> <cfif cardNum LT 10> <cfset cardValue = "#thisCard#|#cardNum#|#session.playedCardList#"> <cfelse> <cfset cardValue = "#thisCard#|10|#session.playedCardList#"> </cfif> <cfreturn cardValue> </cfif> </cffunction> <cffunction name="newGame" hint="I begin a new game of blackjack" returntype="boolean" output="No" access="public"> <cfset session.playedCardList = ''> </cffunction>
What worried me was - how is he keeping track of what cards have been dealt. If you look at CFC, you will see that he references a session variable, playedCardList, to store the used cards. Typically, it is a bad idea to reference any outside scope in a CFC. How would I have done this differently?
First, I would have stored the playedCardList as a variable in the CFC itself. Secondly, I would have then stored the CFC in the session scope. This would kill two birds with one stone. First off - it makes my CFC a bit better in that it is no longer tied to the session scope. If I decide to switch to some other scope, it would be easier since I'm not having to change the CFC itself. Secondly, I would then need to check for the existence of the CFC before creating an instance. This will make the application run a bit quicker since we would only run createObject() once per session.
Another nit - nowhere in his CFC does he use the var scope, and every method (but shuffle) is missing var statements. In getSuit, for example, this line:
<cfset suit = shuffle(4)>
<cfset var suit = shuffle(4)>
Similar changes are needed in his other methods. This is a serious problem. Not using the var scope can lead to some very hard to debug problems. It's times like this where I wish I could tell CF to be 'strict' and not let me create variables like that. (I know, I know, CF wasn't built for it. ;)
So, that's it. All in all, this is a nice submission. Good job, Steve!