Tuesday, February 26, 2008

I still miss CFOUTPUT

It's been about a year now since I last coded CF in anger, and what coding I've done since then has been mainly in Java and Ruby On Rails. These days, most of my coding is done in my spare time, which is a resource in increasingly short supply - so coding actually tends to be done during my random bouts of insomnia. Like many similar turncoats, I've found RoR development to be settling into a fairly steady cycle :



  1. Do some really complicated stuff really quickly.

  2. Nod appreciatively and make coo-ing noises.

  3. Stop yourself just before standing up and announcing that your name is Boris and you are invincible

  4. Try to do something ostensibly simple, get stuck

  5. Spend ages scouring the web for the simple solution that surely must be out there somewhere

  6. Get pissed off because you found a blog post telling you that you shouldn't want to do that because it's "not the rails way"

  7. Figure out a really ugly cumbersome way of doing it step-by-step

  8. Fail to get to sleep because it's just bugging you that something so simple should be so hard in a framework that makes so many other more complicated things so simple

  9. Two days later, discover entirely by accident that the fifty lines of hackery could have been done in one long line of twenty method calls ( things.each do{ |foo| foo.do.some.other.thing }.and.then.do.some.thing.else( bar ) ) if only you'd known that the method you needed was called (insert counter-intuitive method name here) and that there was a plugin called (insert name of obscure plugin here)

  10. Go back and redo your hack with the one long line of twenty method calls. Feel like a l33t h4x0r d00d because you just replaced fifty lines with one.

  11. Discover that somewhere in your one long line of twenty method calls, one of them is returning nil.

  12. Think "hey, it's ok, I can debug this easily with the console!". Feel smug.

  13. Spend what seems like an aeon starting the console, reproducing the conditions in which you get your nil, changing something, then restarting the console so you can test your change.

  14. Resolve to write better unit tests in future.

  15. Pine a little for the good old days of "make your change then hit f5" to see if something works.

  16. Start a trawl through the source code looking for the cause of the problem

  17. Reflect that while duck typing is indeed an orgy of sheer loveliness, in this particular case it would be nice if just this once you could know for sure that this particular object is a Foo and therefore all you need to know would be found in foo.rb

  18. Discover that the cause of your woe is that at least one of your magical plugins doesn't work on Oracle, or SQL Server, or indeed anything other than MySQL.

  19. Go back to the blog on which you found the plugin to see if it's a known problem with a new version

  20. Discover that the blog is down.

  21. Write a plugin to patch the plugin to work on Oracle

  22. Feel vaguely uneasy that you now have a chain of umpteen plugins patching plugins patching plugins patching plugins patching ActiveRecord to do something that you apparently shouldn't want to do, but dammit, you needed to get it done by 5pm.

  23. Go out and pull scary faces at small children to make them cry for a while until you feel better.

  24. Come back feeling much better now that you've spread a little frustration around. Reflect that you're probably just not thinking about things in the "right" way.

  25. Adjust your thought-angle, and come at it again

  26. Repeat from step 1


To be clear, I do think that Ruby has some wonderful features. Blocks, open classes, method_missing - all of these little niceties make some fantastically cool things possible. The Enumerable#sort_by method in particular was one discovery that just gave me a wonderful warm fuzzy feeling - e.g.

some_collection.sort_by { |element| [ element.method1, element.method2, element.method3 ] }

Rails, also, has some really great features, and makes some of the donkey work so easy it's almost laughable. But sometimes it feels like all the thinking went into the elegance of the back-end design, and not enough thought went into the templating. RHTML feels like a tacked-on afterthought. HAML is better in some respects, but it still feels awkward. I haven't yet found any templating language that even comes close to the sheer simplicity and ease-of-use of CFOUTPUT.



The example that triggered this rant was grouped output. Let's keep this simple for the purpose of example - say I have a recordset with three fields:

























































Type Sub-type Title
Type 1 Sub-type 1 Foo 1
Type 1 Sub-type 1 Foo 2
Type 1 Sub-type 1 Foo 3
Type 1 Sub-type 2 Bar 1
Type 1 Sub-type 2 Bar 2
Type 1 Sub-type 2 Bar 3
Type 2 Sub-type 3 Foobar 1
Type 2 Sub-type 3 Foobar 2
Type 2 Sub-type 4 Foobar 3


- etc etc.



If you wanted to output these with headers and sub-headers whenever the type or sub-type changed, it would be almost trivially easy in CF:



<cfoutput query="myRecordset" group="type">
<h2>#myRecordset.type#</h2>
<cfoutput group="subtype">
<h3>#myRecordset.subtype#</h3>
<ul>
<cfoutput>
<li>#myRecordset.title#</li>
</cfoutput>
</ul>
</cfoutput>
</cfoutput>


- but in Rails? I'm still at step 5. I know I'm not the first person to need to do this, not by a long shot, but I just don't yet know what the method that surely must exist would be called. I know you can use Enumerable#group_by to group an array of
objects into something approximating the raw recordset above, but that's kind of working backwards to me.

I also know that someone will probably post the answer in a comment, probably with some kind of dismissive one-word instruction like "Read." or "Learn." linking to the relevant part of the docs. And that's all well and good. I'm just in a temporary bout of misty-eyed nostalgia for the things that CF made easy - particularly CFOUTPUT.