Yay Akismet

Just looked at my stats and Akismet has killed off almost 200,000 spam comments for me. Not sure when I switched to WordPress, but in any case, that's a lot of spam I've been blissfully unaware of, and no CAPTCHA required. And for those of you who aren't regular readers, my opinion of CAPTCHA is somewhere just below serial child molesters.

Crossing the Digital Line

I went and saw a movie (Yes Man – it was good) tonight for the first time in a long time.  The Dark Knight (also good) was the last, and before that Spiderman 3 (decent) and then Return of the King (good again), but I can't say for sure.  Lots of reasons to not go: kids, long legs, attention deficit – let's go ride bikes! – disorder, general dislike of staring at a non-interactive screen, etc.

And actually, now that I've written that, I realize I just went and saw Role Models (decent) last week.  I am an idiot.  Though in my defense, it was at the Bagdad and the food stuck in my mind more than the movie.  But whatever, the topic at hand has relatively little to do with movies.

Rather, it's about crossing the digital line, the distinction between real life and digital life.  The Internet (which, as we all know, is a series of tubes) is great for a number of reasons: virtually limitless information sharing, easy communication without regard for physical location, ability to easily adapt a persona that isn't the real you (IRC, WoW, blog/twitter, …), etc.  The Internet is also a bane of existence for various reasons, and not coincidentally, they're exactly the same reasons it's great.  Everyone with an Internet presence has to cross back and forth between their real self and their online persona (or personas) all the time.

I've gone to great lengths to ensure the Internet projection of myself is consistent with my real self as much as possible.  Not a complete projection, of course, but containing as little as possible that the real me wouldn't do.  As you would expect, the technical aspects of my life are better suited to my Internet persona and so they get the lion's share of the "airtime" online.  Notable exceptions include a relative absence of profanity and dirty humor online and a relative absence of personal/life details.  I don't know if that's a "normal" scenario, and I'm not going to speculate, but it's certainly not an all-encompassing scenario.

What strikes me as remarkably interesting is that every significant digital relationship I participate in either started as a meatspace relationship or has had a non-trivial meatspace component.  This isn't surprising, necessarily, as people aren't very interesting online because the ability to communicate is so constrained, but it never the less seems interesting that meatspace remains important.  Since you're still reading, let me ask favor: if you know me or have met me in real life, what surprised you when you met the other version of me?

One particular aspect of this dichotomy is going to a development conference or two each year and swilling beer with all kinds of fellow developers that I usually interact with only via email, blogs and Twitter.  While technical topics are always on the table with a bunch of geeks, discussion is usually of a non-technical nature (e.g., pie fights).  This is always one of the highlights of the year.  Again, not really that surprising, but it's interesting that it's so vital to the day-to-day technical interaction with these people.  Having experienced the real person lends shadowing and depth to the digital projection that simply can't be created any other way.

One specific example, sorry Sean.  Sean was on the ANSI C++ Standards Committee (or something like) that some conference he told the story of one of their dinners where they ordered by committee.  Seriously.  They actually discussed until they had, as a group, decided what the combined order would be for the group.  14 people or something like that.  Obviously this has nothing to do with anything technical, but everything to do with the interaction amongst this group of people.  And the fact that Sean was in this situation validated so many perceptions I had of him (that were also validated other ways, of course), about the way he does things.  He once accused me of accusing him of being tactful.  But I stand by my "accusation", because while he takes no shit, I think he's quite tactful about it, and that perfectly meshes with how you'd have to be to function on a committee like that. http://slots33.com/web/mobile  Not that there is a causal relationship there, but certainly interplay, and it's fascinating to see it materialized both ways.

I always feel … weird … crossing over that line for myself, but find it perfectly natural for others to do it.  Perhaps some sort of paranoia that the Internet-mandated schizophrenia will migrate from the digital border into my head and become real.  Perhaps just a simple issue with vastly different media (the world, the internet) and trying to communicate consistently between them.  Who knows.

And the actual impetus of this post was that I went to the movie tonight with a friend whom I only knew online.  We "met" on Twitter (thanks places search!) and learned more through blogs.  I was planning on going to the movie by myself, but on a whim shot off an invite via DM.  This all seemed perfectly natural, and then as I was heading out from the office it struck me as an absolutely insane course of events.  Not that any specific aspect of it seems all that odd, but as a whole…  I dunno.

One of these days I'll be one person in both realms.  Maybe.  At least when I choose to be.  In the meantime, I'll continue to be fascinated by the dichotomy, both in myself and in others.  Here's to me! And me!

I'm Speaking at CFUNITED

Just got official word from Liz that I'll be speaking at CFUNITED this year on Groovy and CFML integration.  Joe Rinehart was originally slated to speak on this topic, but we're splitting the topic into two sessions.  He'll be speaking on backing Flex with Groovy and I'll be speaking about the CFML side.

My Found Art Finder

I was bored tonight, so I found some art.  And being the good programmer, generalized it to create a tool for others to find their own art.  Here are a couple samples (dynamically generated):

You can also grab the source from Subversion.  It's quite simple, it's not very scalable, and dear god please let CF9 make the image functions not suck.  Why can't I stream an image to the browser?  Why don't boolean imageSetXXX() functions accept booleans?  Why do I have to pass an empty string to imageNew?  CFML is null-less, don't fake it with the empty string.

More CFGroovy Goodness, Now With RC2

Four things of import, in no particular order:

First, another bug fix to CFGroovy today, this one more serious.  I made a silly blunder in the way I was executing scriptlets that lead to a slow memory leak from extraneous java.lang.Class instances being created.  It also reduced performance of scriptlets by a tangible amount.  If you're using CFGroovy under any sort of load, this is a highly recommended update.  It's a very slow leak, but leak none the less, and you'll appreciate the performance improvement even though it was already quite fast.

Second, I've deprecated the 'attributes' binding in favor of a new 'params' binding.  I initially picked 'attributes' because of Fusebox's use of it, but the semantics are different and I've changed my mind.  You can still use 'attributes', but it'll go away at some point so start using 'params'.  If you're using CFGroovy with Fusebox, you can access the Fusebox attributes scope at 'variables.attributes' as you have always been able to.

Third, it turns out that Mentor.com account was not the first production deploy of CFGroovy at Mentor.  Our backend interaction services have been running on CFGroovy for a while now and I didn't know about it.  Guess how I discovered this?  Yep, the now-fixed memory leak.  ; )

Finally, I've made a formal RC2 release available for download.  This includes the three patches since the RC1 release.  There will probably be another RC with a couple small tweaks to the Hibernate loading, but I wanted to make a formal release available with the scriptlet issue resolved.

Mentor.com Accounts are Live!

And why am I writing about this?  Because it's also the first CFGroovy / Hibernate app we've deployed into production.  So how did this first real-world adventure go for my youngest project?  Just swimmingly.

The adoption was actually championed by Joshua since I was working on another project at the time.  We went skunk-works, helped Koen set up the infrastructure to get started, and she went to town.  In came Rob and Mike to help out on this and it's sister project (also using CFGroovy / Hibernate) and away we went.

Joshua had done some Groovy and some Hibernate (including both CFGroovy and Grails) before, but the rest were new to the language and frameworks.  Not that this was a problem.  The Groovy syntax proved very straightforward (as expected), Hibernate was ridiculously simple to use since we just let it manage the mapping/schema with defaults (also as expected), and CFGroovy's development experience proved exactly what I'd hoped: virtually no change from normal CFML development aside from basically skipping the database and persistence operations.

So we (as a team) are excited, and I'm doubly excited, even though I barely worked on the app at all.  I've used CFGroovy on several of my own projects, but having a team of other people use it successfully (and enjoy it!) is really gratifying.

CFGroovy HibernateTransactionAdvice Update

If you've used the HibernateTransactionAdvice CFC from the CFGroovy framework you may have witnessed weird behaviour after a failed transaction.  I had neglected to close the active session after an error is encountered and the transaction rolled back.  If you continued to use the Hibernate context after this happened for write operations you could encounter errors.  The database was not affected, only Hibernate's in-memory caching.

I've added the line of code required to close out the active session when a transaction fails and is rolled back.

2008 in Numbers

Here are some numbers for 2008, in ascending order:

  • 1 – birthdays I had
  • 106 – blogs posts on barneyb.com
  • 246 – tweets (I started halfway through the year)
  • 366 – total days
  • 411 – "real" email conversations (excluding all mailing lists)
  • 458 – real comments on barneyb.com
  • 1,609 – Google Talk conversations
  • 1,627 – commits to my personal Subversion repository
  • 2,084 – hours I spent working for Mentor
  • 3,228 – ranks received by PotD
  • 7,910 – emails sent by PotD
  • 86,584 – distinct visitors to barneyb.com (Google Analytics)
  • 118,309 – images spidered by PotD

getSubversionRevision UDF

First, a confession.   I cheat hard-core on the deployment for most of my personal apps.  In almost all cases I check out a working directory in the production webroot and just use `svn update` to "deploy".  I've a couple apps that I use an Ant/Rsync-based mechanism to deploy where I actually have stuff to build, but in general I skip it.  It also allows easy tweaking in production which I don't generally recommend, but for apps that I'm the only user it's quite handy.

Anyway, I needed to check the Subversion revision of files and directories within my working copy so that I can append a query string to my static assets and then set their Expires header to "forever".  That way browsers will cache them forever, but when the app gets updated the query string will change so browsers will download a new copy.  Here's the function:

<cffunction name="getSubversionRevision" output="false" returntype="numeric">
  <cfargument name="path" type="string" required="true" />
  <cfset var output = "" />
  <cfif NOT directoryExists(path) AND NOT fileExists(path)>
    <cfthrow type="IllegalArgumentException"
      message="You can't get the revision of a non-existent path" />
  </cfif>
  <cfset path = REReplace(path, "[\\/]", createObject("java", "java.io.File").separator, "all") />
  <cfexecute name="svn"
    arguments="info #path#"
    timeout="10"
    variable="output" />
  <cfif NOT output CONTAINS "Last Changed Rev">
    <cfthrow type="SubversionException"
      message="Unknown output returned from 'svn info #path#'"
      detail="#output#" />
  </cfif>
  <cfreturn REReplaceNoCase(output, "^.*Last Changed Rev:\s*([0-9]+).*$", "\1") />
</cffunction>

To use it, pass it the absolute path of a directory or file that is SVN managed, and you'll get back the revision number for that path.  Obviously it requires the `svn` command line binary to exist on the system path for the user your CFML engine is running as.  The output parsing also assumes a Subversion 1.5 client, though it may work with older releases.  I've also only tested on Linux, but it shouldn't matter.

On application startup I run the UDF to set an application variable:

<cfset application.subversionRevision = getSubversionRevision(
  getDirectoryFromPath(getCurrentTemplatePath())
) />

And then in my JS/CSS URLs, I append that revision:

<script src="static/util.js?m=#application.subversionRevision#"></script>

The second step is clearly divorced from the subversion stuff, you can use any indicator thing you want, but revision is about the right level of stable.  With a "real" build process you would just write the tagged revision to the CFSET as a static string, but in the absence of that, this seems to be a nice approach.

NB: I've simplified the examples – they use the revision of Application.cfc (or wherever the first line lives) for the caching of util.js.  In real life, you'll probably want to be a little more specific in your revisions, or just use a real deployment mechanism.  ;)

User Stylesheets

Just used a user stylesheet for the first time yesterday for tweaking WordPress 2.7's new QuickPress widget.  The TEXTAREA for entering the body is way too short, but a simple line in my userContent.css file and it's magically fixed.  Not even remotely the use case user stylesheets were designed to handle, but it's simple and effective.  Far better than Greasemonkey, which is what I had considered first.