One of the most useful features of CFWheels is 'the Flash'.

Essentially, it's a session scope which is designed to carry messages over page redirects, and only remove itself once it's actually been viewed by the user.
I usually use it when a user has updated, deleted or requested something which requires some sort of feedback - like a password reset, a profile change etc.
Using filters, we can start using these flash scope messages as a basic logging device; did the user see an error message when they tried to update their profile? Was their password change successful? Were they told about it? etc.
In my Users.cfc controller, I've got one important line which makes this possible;

<cfset filters(through="logFlash", type="after")>

This means after any function in my Users.cfc controller is executed, logFlash() is called.

And in my Controller.cfc (where functions which may be required by all controllers live), I've got the two logging functions called logFlash, and addLogLine:

<cffunction name="logFlash" hint="Grabs the message in the flash scope and logs it">
<cfif structkeyexists(session.flash, "error")>
<cfset addLogLine(name=session.flash.error, type="error")>
</cfif>
<cfif structkeyexists(session.flash, "success")>
<cfset addLogLine(name=session.flash.success, type="success")>
</cfif>
</cffunction>

This function looks for struct keys of 'error' or 'success'  in the flash scope and calls addLogLine() - this example creates a db row in a table called logfiles, but you could easily rewrite this to suit your needs (i.e email you, or write to a static file): I'm also logging the current userid, what type of message it was, and the calling IP address - plus I'm using createdAt in the logFiles table to automatically timestamp the entry.

<cffunction name="addLogLine" hint="Manually add a log line">
<cfargument name="name" required="yes" hint="The Actual Log Message">
<cfargument name="type" required="no" default="admin" hint="example options: public | admin | login | file | error | success">
<cfif NOT structkeyexists(session, "currentUser")>
<cfset thisuserid=0>
<cfelse>
<cfset thisuserid=session.currentUser.username>
</cfif>
<cfset logline=model("logfile").create(name=arguments.name, userid=thisuserid, 
type=arguments.type, ip=cgi.REMOTE_ADDR)>
<cfreturn true />
</cffunction>

Overall, a really useful way of working out what the user is seeing, and once you've set up these functions, calling them from other controllers is simply a matter of adding one line!

One of the features of CFWheels which I've been guilty of ignoring in the past is the combination 'filter' and init().

In your controller model, you have two really powerful tools at your disposal. The init() function, which runs before each request to the controller, can allow you to do lots of preprocessing logic, and make sure everything is 'good to go' with displaying your view. The filter function, when combined with init(), means you can call specific functions for a view, and keep any recurring code out of the view function itself.

I often have caught myself doing this multiple times in a controller (in this case, users.cfc) :

<!---View User--->
<cffunction name="view">
<cfset currentuser=model("user").findOneByUsername(session.currentUser.email)>
</cffunction>

<!---Edit User--->
<cffunction name="edit">
<cfset currentuser=model("user").findOneByUsername(session.currentUser.email)>
</cffunction>

etc.

All I want is the current Users data, and often I'd want it for the view, list, edit functions etc. If you find that you're repeating code in wheels, stop and ask your self whether there's a better way.

Using the init function and filter, we can replace each call by simply writing the function once, and popping the call to the function in the filter. So:

<!--- init --->
<cffunction name="init">
<!---Filters--->
<cfset filters(through="getCurrentUser", only="view,edit")>
</cffunction>

<!--- My new private function --->
<cffunction name="getCurrentUser" access="private">
<cfset currentuser=model("user").findOneByUsername(session.currentUser.email)>
</cffunction>

This means our object of currentuser is now available to the view and edit functions. We could also add some checks to see whether the session is valid, the object is returned, etc. Your resulting function might look something like this:

<cffunction name="getCurrentUser" access="private">
<cfif structkeyexists(session.currentuser, 'email')>
<cfset currentuser=model("user").findOneByUsername(session.currentUser.email)>
<cfif NOT isObject(currentuser)>
<cfset userError()>
</cfif>
<cfelse>
<cfset userError()>
</cfif>
</cffunction>

<!---An additional private function to throw a user related error--->
<cffunction name="userError" access="private">
<cfset flashInsert(error="User Not Found")>
<cfset redirectTo(back=true)>
</cffunction>

Once you get your head round this relatively simple principle, it makes creating your controllers very quick indeed..!

 

I've just come back from 'Scotch on the Rocks' 2010, held in London. This has been quite a step up from last year!

I've had the pleasure of hearing Ben Nadel (@bennadel), Ray Camden (@cfjedimaster), Terry Ryan, Gert Franz and Aral Balkan (@aral). All great presentations, with Aral's sheer passion for design (or should I say, 'experience')  coming through in particular.

Ben talked on CF's Application framework, talking about session manipulation is a way I'd really just never thought of before - by opening the subject up, he definitely made it more accessible, and has some great code examples I must get my hands on.

Ray was on CFBuilder duty, and whilst it is something I *really* should get around to trying, the amount of times he said 'oh and this doesn't work, but don't worry I've filed a bug' did leave me slightly worried :) I enjoyed his Ajax Q&A session too - as something which doesn't always come into my projects, it was interesting to hear that my thoughts on Ajax generally are pretty well reflected from those that spoke. 

Railo 4 is looking to be stellar; there's a billion things they're planning which are a bit beyond me (particularly the whole Java integration and command line stuff) but it's very encouraging to see Railo doing so well; I've been using Railo alongside CF8 for a year now, and it really is a fantastic open source project.

Apart from the 6am 'get up grab train and run' (and having to leave early) it's been a great couple of days. Nice to meet so many of the other CF Twitter(er)s too; not to mention seeing people whose blogs I've been reading for 5 years for the first time! Congrats to the whole SOTR team, and all the speakers.

Ok, so I had 5 minutes to myself; inspired by the Model Glue Wallpapers I saw the other day (http://www.model-glue.com/wallpaper.cfm) I thought I'd brush up on my Photoshop skills, and do some CFWheels based ones.

Enjoy the below! All are 1920x1200.

Had a small shock the other day - whilst being all responsible and backing up all my MySQL databases, I noticed this blog's backup taking a rather long time.

There really aren't that many entries here, so the .sql dump should be a few hundred k, certainly no more than a few MB.

Imagine to my horror when I discovered that the total dump size was over 750MB!!

Turns out a plugin I'd tried a while back was failing, and Mango blog was logging the fail silently in the database. Annoyingly, I'd not deactivated the plugin, but as I'd not gotten any indication of it failing, it just kept on being logged, on *every* request.

The real problem is that the entire error struct is stored, so  imagine the raw HTML of  a cfdump error struct x 15000+ rows... it builds up over time!!

Wordle me up

Dec 07, 2009