
Welcome to the fourth entry in the ColdFusion Newbie Contest. Today's entry is Tamagochi by Alexie Protasov. Once again I cannot host an online demo (I'll explain why a bit later), but you can download the code below.
This entry was pretty impressive and rather simple (which is cool - simple can be nice). Tamagochi makes use of AJAX calls to interact with the pet. I also liked how quickly the pet seemed to move from stage to stage. It made the game seem fast paced. One main issue though is that it only works well in IE. I had to fire up Parallels to test. Another issue is that there wasn't much feedback to your clicks. Sometimes it was hard to tell if your clicking actually did anything.
Now lets dig into the code. First and foremost - this is the first application to use Application.cfc! That makes me very happy and earns Alexie extra brownie points. His Application.cfc is rather short so I'll include the whole thing here:
<cfcomponent output="no">
<cffunction name="OnApplicationStart" output="no" returntype="boolean">
<cfset Application.Tamagochi = CreateObject("component", "Tamagochi").init("My Tamagochi")>
<cfreturn true>
</cffunction>
<cffunction name="OnRequestStart" returntype="boolean">
<cfif IsDefined('URL.restart')>
<cfset OnApplicationStart()>
</cfif>
<cfset Application.Tamagochi.check()>
<cfreturn true>
</cffunction>
</cfcomponent>
So while I'm happy he used the feature - I'll point out at least one mistake - no name. He never named his application. Doing so puts his Application in the mysterious unnamed Application area. Try cfdumping the Application scope in an unnamed application and you will see what I mean. This could easily be addressed with this line:
<cfset this.name = "SinceYouDidntNameItICallItRay">
His core CFC, Tamagochi, is pretty interesting. For those who works with beans, you may recognize bean like aspects of his CFC (lots of set/get pairs), but he also has the core functionality of the pet in here as well. Normally I'd split this up - but I believe this is the best use of a CFC yet in this competition so I'm rather proud. He also made good use of output control and var scoping. (Although he forgot to turn off output in his methods.)
As a suggestion to Alexie - I'd move your init method to the top of the function. It doesn't really do anything different up there, but if I may pretend I speak for other developers, it is pretty much the standard.
His AJAX work is pretty simple. It is all done by hand without any framework. Each action for the pet calls a CFM. I was very happy to see some basic error checking in the AJAX called CFMs. Here is the code run when you feed the creature:
<cfsilent>
<cfheader name="expires" value="Expires: Mon, 26 Jul 1997 05:00:00 GMT">
<cfheader name="expires" value="#Now()#">
<cfheader name="pragma" value="no-cache">
<cfheader name="cache-control" value="no-cache, no-store, must-revalidate">
<cfif IsDefined('Application.Tamagochi')>
<cflock scope="application" type="exclusive" timeout="60">
<cfset Application.Tamagochi.feed()>
</cflock>
</cfif>
</cfsilent>
Note the use of headers to prevent caching by the client. Note the basic isDefined check, and the locking. Very nicely done I think.
A few last notes before I wrap up. I didn't put this up as an online application because the creature is stored in the Application scope. This means everyone would share the same pet. He could fix this by simply switching to the session scope. If you download the zip, you will notice a login/register page. These didn't work for me so I assumed Alexie left them behind.
All in all - very nicely done. Simple and well built. Now if he can just get Firefox working...
Archived Comments
So, it still doesn't work in FF :(. Well, I started learning Ajax and Spry only a few weeks ago...
Ray, the name was not given to the Application and not to the Pet. :) The cfc has a property "name" which is passed in the init() method. It is hardcoded now but I added it to give unique names to each pet. Nevertheless I should give some name to my App too.. :)
The login page was created to allow multiple users get their own pets. But unfortunately I didn't have a time to implement it.:(
Anyway it was real fun to participate in this contest and I'm looking forward to the next one.. ;)
Thanks!
Why is the header set to a date in the past?
To indicate that it already expired. The content. It is a way to keep the browser from caching.
one note:
<cfheader name="expires" value="#Now()#">
Doesn't always work as expected. The time portion doesn't get converted to an http request valid date.
A good article on this (3/4 of the way down is the coldfusion portion):
http://www.mnot.net/cache_d...
For the too lazy to read::
Try using GetHttpTimeString(DateAdd('m', 1, Now())) instead of Now()
Cool program. I was going to try some ajax and spry in my code, but time was short and i didn't feel that i would have really added anything to my code. I downloaded yours and it pretty neat. nice job.
why cant you get any of these to run on your server? or at least have the developers have a working copy on their server somewhere??? seems a bit odd... and in my estimation should wholly invalidate an entry :)
hahaha, jk, but seriously... wtf.?????
This entry looks really good! Simple, effective, and room to expand.
Wow! I didn't expect such positive reaction on my entry! :) Thanks guys for all the comments!!
@DK
Thanks for the article on cache. It's really useful.
@Tony Weeg
Unfortunately I don't have my homepage at this moment. But if you have CF on your PC you can copy the folder to your webroot. It's the only thing you should do to make my program work (at least in IE ;)).
Technically this one WOULD have run, but with it being in the app scope, and with my traffic, it would have acted.... oddly. ;)
I've enjoyed this entry the most (so far) as it seems to provide a "fresh" perspective. While Alexei may be a beginner CF programmer, He seems to have a good grasp of OOP. Nice work sir. Your AJAX example is particularly easy to understand.
One (newbie) question:
1. In the Tamagochi.cfc, I can't figure out why this code isn't listed in the init method.
<cfset variables.instance = StructNew()>
<cfset variables.instance.name = "">
<cfset variables.birthDay = "">
...
What is the purpose of listing it outside of a cffunction?
It's an interesting question, Brad.
Frankly speaking I didn't even think about it. :)
I did it mostly because of my way of thinking about 'properties' and 'methods' I suppose. So the properties should be first 'declared' and only then 'defined' in the constructor. Perhaps this is the influence of my former experience in C++. :)
As for me I don't think it will somehow affect the functionality of the app if you include the properties in the init() method.