This last entry in the ColdFusion Newbie Contest is here! It is an application created by Phillip Senn. As with the previous entry, I had difficulty setting up the code locally (due to my not having SQL Server at the time), so I'm going to point people to the online version: http://www.aacr9.com/index/camdenfamily/tamagotchi/
This entry is rather simple. As you can see - you are dropped into the creature maintenance immediately. A database is used for everything and I believe this means that if multiple readers hit the site, they will all be affecting the creature at the same time. So those of you who have easier access to SQL Server and want to try this application should download it instead. Anyway, as you can see, you simply feed and pet the creature, and that is it. In fact, I couldn't even kill off the creature. From a game play perspective, there isn't a lot here honestly, but there is some interesting code here for us to talk about. (Although the trick with making the image bigger as the creature ate was pretty funny.)
First off - Phillip made a huge mistake. Gigantic. He wrote a Word document describing what he did. That just makes me it easier for me to pick on him! (Just teasing Phill!) From the Word document we learn that Phillip only used stored procedures and views to interact with the data. Wow. I have to say I am impressed. It takes a lot of discipline to just use stored procs throughout an application. I will be 100% honest and admit I'm a database wimp and rarely use/write stored procs myself.
In his word document he goes on to explain why he doesn't use the Arguments scope inside of CFFUNCTIONs. His argument is based on the fact that since you can't prefix local var scope variables, and you can't mix the two, then there should be no confusion. Personally I don't agree with that. I think that when working in any slightly complex method, you are going to want to have a good handle on what is an argument and what is a local variable. So I may be being picky here, but I'd always use the argument scope.
Most impressive is the use of Application.cfc. I do not believe many of the entries did this. He caches many CFCs in the Session state making proper use of onSessionStart. I also liked this bit of code:
2 <cfreturn false> <!--- Bots --->
3 </cfif>
I wonder how well this would work. I thought most bots actually had a user agent. It does bring up a good point. I know Michael Dinowitz has done some good work in blocking sessions for robots and has found this to be a big help for server performance. I don't know if Michael has written this up yet into a formal article, but if he hasn't, he should. (In his copious free time of course.)
In the last entry, one of the things I warned about was the use of hard coded numbers in the code. So for example, if "1" means happy, I'd abstract that so that you can do getHappyState instead. I see similar type issues in Phillips code, specifically in index.cfm where he handling updating the state of the creature.
Another issue - and let me be 100% clear this - this is not a fair complaint for Phillip. I am bringing it up as a warning to others but not to say that Phillip did anything bad here. I noticed a file named session.cfm. It simply did this:
While useful, and I've done the same thing myself, it is critical to remember to not push files like this to production (as has been done here). I've made this mistake myself, so I'm just reminding people.
Now let me dig into his CFCs. First off - Phillip points out that his CFCs all extend a common CFC, which is pretty cool to see. His common.cfc contains a base init, read, view, and delete method that his other CFCs can use. He uses an interesting CFC, Database.cfc, to handle abstracting his queries for him. This allows him to select data from other CFCs like so:
2 TableName="Tamagotchi_CreatureStateView",
3 Where="#Where#") />
I'll be hoenst and say I'm not quite sure I like that. Writing the SQL as he does in Database.cfc does mean he loses the ability to do cfqueryparam, and I'm not sure I'd give that up. But it is a very interesting usage. I'm going to give him extra brownie points though for using cfqueryparam everywhere he could though. Along with that I was also happy to see proper var scoping and output restriction.
Phillip pinged me directly a day or so ago to say he removed returnType to keep the code a bit simpler. I guess I can see that - but I tend to be pretty anal about always including my return type. I'd suggest he add that back in. Did you know that in ColdFusion 8 you can turn on duck typing in the ColdFusion Administrator? This means you go crazy with typing and still get the performance benefits on production.
So thats all I have to say about this entry, and that means we are done! My next post will gather up all ten entries and I encourage folks to start thinking about what entry was their favorite. Also please give feedback to Phillips code (and feel free to disagree with my notes as well).
Comment 1 written by Robert Owen on 14 June 2007, at 8:10 AM
Variable HAPPYSTATE is undefined.
The error occurred in C:\Websites\73172bkj\Index\CamdenFamily\Tamagotchi\Index.cfm: line 96
94 : </cfloop>
95 : </ul>
96 : <cfif HappyState GT 0>
97 : <cfset CreatureStateQry = Session.CreatureStateObj.View2(Session.CreatureID,session.HungerStatID)>
98 : <cfif CreatureStateQry.CreatureState LE CreatureStateQry.MaxState>
Comment 2 written by Raymond Camden on 14 June 2007, at 8:13 AM
Comment 3 written by Robert Owen on 14 June 2007, at 8:15 AM
Comment 4 written by Phillip Senn on 14 June 2007, at 9:04 AM
I assumed there would only be one creature, for the sake of not having to write a login script.
So when someone (naturally) added their own creature, it threw an error.
Sorry 'bout that.
I think I should take the time to make it multiplayer and send the zip to Ray.
Comment 5 written by Scott Krebs on 14 June 2007, at 5:50 PM
I bring this up because I had to fix a customer's site on Monday that had this exact problem - a customer of one of our hosting customers tried to do some work shopping from home on Sunday night (it's a persistent, multiple cart system for schools to order and re-order supplies) and was met with:
An error occurred while evaluating the expression:
#HTTP_USER_AGENT#
Error near line 232, column 7.
--------------------------------------------------------------------------------
Error resolving parameter HTTP_USER_AGENT
because the original developer of the code had made that assumption. FWIW I also see this problem with cgi.HTTP_REFERER on occasion too, and I'm sure there are others. Just something to keep in mind when writing code, you don't want to be unintentionally losing customers...
btw - Ray, this must be on the new server now? Your site is orders of magnitude faster for me today. Or is it just that it's not running that slow Kontera stuff right now? Anyway, I like it!
Comment 6 written by Raymond Camden on 14 June 2007, at 5:59 PM
Comment 7 written by Phillip Senn on 15 June 2007, at 3:32 PM
But after seeing how tortured the cf code was in order to determine what action affected what state, I decided to simplify the ColdFusion part and hardcode some of it.
I think I had in the back of my mind an article about how we sometimes make things overly complex by softcoding everything.
Well, anyway....
Thanks Ray, for the opportunity and the review.
Comment 8 written by Scott A. Wimmer on 16 June 2007, at 3:54 PM
Hats off to Phil.
Comment 9 written by Scott Stevens on 18 June 2007, at 4:47 PM
Comment 10 written by Ryan Hartwich on 18 June 2007, at 11:28 PM
Style - I liked the use of capitalizations in the variable names, indentions, CFCs, queryparams, etc. I also liked how you separated the javascript into a separate file for ConfirmDelete.js. I liked your use of cftry/catch.
Database - great use of stored procedures. It allows for extra security and separates your code from the database a bit more than most people tend to do. The down side to this is that your relatively simple application is now bonded to MS SQL. Of course, changing to another database would be a bit harder with stored procedures instead of simple inserts/updates. Having the majority of changes in a few files is nice though.
Select * - it would be preferable if you wrote out the column names in a select instead of using a *.
Good job!
--note, I haven't examined the other 9 entries yet--
[Add Comment] [Subscribe to Comments]