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>
</cfcomponent>
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:
<cfsetting showdebugoutput="No">
<!--- This section handles the ajax requests --->
<cfif isDefined('url.action')>
<cfset bjOBJ = createObject("component","blackjack")/>
<cfif url.action EQ 'hitMe'>
<cfset thisRslt = bjObj.dealCard()>
<cfoutput>#thisRslt#</cfoutput><cfabort>
<cfelseif url.action EQ 'newGame'>
<cfset thisRslt = bjObj.newGame()>
<cfoutput>#thisRslt#</cfoutput><cfabort>
</cfif>
</cfif>
<!--- end ajax section --->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Gus's CF BlackJack</title>
<link rel="stylesheet" type="text/css" href="styles.css" />
<script src="xmlhttp.js" type="text/javascript"></script>
<script language = "javascript">
var playerBank = 100;
var wager = 0;
var playerVal = 0;
var dealerVal = 0;
var hasAce = 0;
</script>
</head>
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>
</cfcomponent>
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)>
Should be:
<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!