Chrome and Google Spreadsheets

I use Google Spreadsheets for a few things (and other doc types for a few others), but I usually do hard-core editing in Excel.  Export from the web, edit in Excel (or more likely Calc), reimport into Docs.  Hardly elegant, but when I'm going to be cranking a workbook for two or three hours it's worth it compared to editing online.

I was getting set up to do just that this evening, and it occurred to me that the online experience probably doens't suck nearly as much in Chrome.  So I went and grabbed it, set it up with the latest dev build (instructions), and fired 'er up.

Much to my delight, it's actually quite good.  They also added a multi-line editor for cell values that you can resize as needed, which makes editing huge formulas way easier.  No more copying out of the Excel bar into TextPad to edit a formula and then copy it back in.  Still a few issues with keyboard navigation, but they seem to be bound to the fact that "type in the cell" and "edit the cell" are expressed in very similar ways, so you can't tell which mode you're in without thinking pretty hard.  I'm trying to figure out what's different with Excel (where I don't recall having this problem), but I can't put my finger on it.

Spreadsheet's filter function is also pretty slick.  Excel has roughly equivalent functionality in it's DB functions, but filter is way slick.  You don't have to define a database range, it just figures it out, and you don't need explicit criteria cell pairs, you express the filter clauses directly in the formula.  It took me a few minutes of struggling to figure out what the syntax really meant by the examples in the docs and how it applied to my problems, but it quickly became obvious once I willed the nasty Excel way out of my brain.

In any case, I think I'm done with the roundtripping via Excel.  With a kick-ass JS engine behind it, the online version is close enough to a real spreadsheet app.  Recomputing complex formulas is still slower than I could like, but that's about my only remaining complaint.  And no, I didn't do any actual comparative profiling, but again, I don't recall seeing significant lag with Excel when I've done complicated stuff on there.

More FlexChart Stuff

I've made another minor enhancement to FlexChart, this time around grouped legends.  Previously, if you mixed grouped series and ungrouped series (e.g. a stacked column chart with a line series overlaid), the legend would be all kinds of wonky for the line series.  I've fixed that so it'll now render in a reasonable way.

As always, the demo app (with a new preconfigured chart illustrating the functionality) is available at http://barneyb.com/flexchart/, including instructions for downloading both the source and the binaries.

I've also received a couple questions about cross-platform support.  There is absolutely nothing CFML specific about the component.  I happen to package a CF custom tag with the SWF, and the demo app is written in CFML, but the component itself only deals with the Flash Player and XML.  I've personally fed it XML with GroovyServlet and PHP, as well as ColdFusion and Railo.

CFGroovy Demo App Update

A couple users complained that my CF Groovy demo app doesn't work on MS SQL Server because Hibernate doesn't escape the table names it creates, and "user" is a reserved word.  Since I do all my work on MySQL, I never saw the issue.

I've updated the code to use an @Table annotation to specify an alternate table name ("user_t") to use, to alleviate the issue.  No other code changed.  If you update to the new code, your existing data will "disappear".  In reality, it doesn't disappear, Hibernate just created a new blank table with the new name.  If you want the old data back, just do a "insert into user_t select from User;" (or whatever the equivalent is), and then drop the "User" table.

As always, the demo is available at: http://barneyb.com/cfgroovy/, including instructions on how to get the latest code.

Backgrounder FTW

The iPhone has a number of problems, but the inability to background applications is one that just got fixed.  There's a new app on Cydia for backgrounding an arbitrary application, called surprisingly enough, Backgrounder.  It adds a couple behaviours to your home button for backgrounding, as well as supporting the ability configure apps to always background.

Obviously have to have a jailbroken phone to do it, but with QuickPwn it's a breeze.  Now you can listen to Pandora while you surf the web or check your email just like you can with the iPod app.  Or stay signed into chat while you go look up a contact.  Now if only they'd fix copy and paste, attaching images to emails, text selection, etc.

Application.cfc Mappings Gotcha

If you use Application.cfc mappings on Adobe CF, watch out.  If your mapping names contain only alphanumerics, the leading slash is optional.  However, if the name contains other characters (like an underscore), then you HAVE to use the leading slash or it won't resolve.  Ran into this on an app where my "coldspring" mapping worked flawlessly, but my "schema_tool" mapping refused to resolve.  Unfortunately for me, I wasn't aware that "coldspring" worked until about half an hour in to troubleshooting, because the "schema_tool" mapping gets used first.

The livedocs' example does not show leading slashes, which misled me for quite a while after I resorted to looking there when it didn't work.  It's been a long time since I went to livedocs, and I remember why.  ;)  If you scroll down, however, there is a comment about the leading slashes.  It's only five months old, so hopefully it'll get incorporated into the actual document at some point.

In the mean time, make sure you get those leading slashes in there!  I haven't tested this issue on any other CFML engines, but as long as you don't forget the slashes, it shouldn't matter.

FlexChart Update

Been a long while since I've made any updates to FlexChart, and this is only a minor one, but it's potentially important.  Since it's designed to be used in JavaScript applications, data tips and click events are all processed by JavaScript (not ActionScript).  When I'd created the data tip callbacks, I'd neglected to considered the hierarchical nature of sets of series.  So I've added a 'seriesContext' array that contains the context for the series that is having it's data tip generated.  This is strictly an additive change, the existing 'seriesLabel' remains, though it is present in the 'seriesContext' array as well.

What is this good for?  I need to create a funnel chart, and while the built-in stacked-column chart is close, it computes the percentages in the data tips the wrong way.  Reimplementation was a snap with the context, just pull the relevant numbers out of the descriptor XML and do the division.

Even More Groovy

I have a confession to make.  My neat little code counter utility is partially a lie.  I wrote the entire thing as a single Groovy script, and only split it up into individual files for each class to release it.  271 lines of code (419 if you count the blank lines) simply does not justify seven distinct files.  That said, it does justify a runner script and six distinct classes.  So if you want the "real" source, just take the six classes, copy them directly into Runner.groovy and then delete the files.  Everything works exactly the same, except it's all in one concise package.

The project started with a pile of procedural code (i.e. a Groovy script), all inline with no classes at all.  My basic concepts proved sound, and I started refactoring.  Does anyone NOT like refactoring?  If so, raise your hand so I'll know who to slap.  ;)  Classes quickly started falling out of the proof-of-concept code, and soon I had my four core classes (CodeCounter, ProjectInfo, PathInfo, and FileInfo), plus some no-longer-quite-functioning output code.

Once the "backend" was refactored into a stable model, the front-end went just the same.  XML was the format of the original script, and so I reimplemented the same output based on the model, rather than emitting it inline.  To ensure I wasn't being stupid and coupling where I shouldn't, I threw together the text renderer as well.

I'm a huge fan of this style of development, and do much the same thing with CF.  Start writing one big-ass file with no real concern for parameterization, abstraction, or encapsulation.  Once it works "enough", start refactoring for those three characteristics, as well as extending the functionality into something useful to the real world.

Doing Java is a bit harder, since you have the object nature foisted on you from the start.  You can still begin with one class and a pile of static methods, but the refactoring is a bit harder, I think, because of the static/dynamic transition.  With CF and Groovy, you pretty much bypass all of that (thank you compilers!), which is very nice.

What's the downside to this style of development?  Unit testing is hard.  With a rapidly evolving project (structurally), having to evolve unit tests can be a significant burden.  Not to mention that you can't really unit test a single large script; you're stuck waiting until you have an class which is after most of the work is done.  So if you write unit tests at all (I didn't), they have to come late in the cycle.

The main benefit is that you don't have to think about architecture before implementing functionality.  If you know exactly what your desired functionality is, and know how you're going to approach implementing it then architecture is the "hard" part, and unit tests can help ensure your implementation matches what you expect/require.  However, that's not the case a lot of time.

More Groovy Goodness

Yesterday afternoon, I was talking to a friend about one of my apps, and he asked about how big the app was.  I had no idea, and thought I'd write a little app to figure it out for me.  Oh how easy Groovy makes things.  The basic idea was to build a simple parser to return counts of files, directories, lines of code, and bytes, as well as break them down by file types.  Hand that data structure off to a renderer for output, and you're done.

The code is available at https://ssl.barneyb.com/svn/barneyb/code_counter/trunk/, just check it out and execute:

groovy Runner </path/to/project>

Executing the project on itself, you get this output:

                         Count      All Lines     Real Lines          Bytes
---------------------------------------------------------------------------
Paths                        1
Directories                  1
Files                        9            422            291          8,047
   text                      9            422            291          8,047
      groovy                 7            402            271          7,945
      txt                    2             20             20            102
   binary                    0                                            0

<project pathCount='1' fileCount='9' directoryCount='1' byteCount='8047' lineCount='422'>
  <type type='text' fileCount='9' byteCount='8047' lineCount='422' realLineCount='291'>
    <extension extension='groovy' fileCount='7' byteCount='7945' lineCount='402' realLineCount='271' />
    <extension extension='txt' fileCount='2' byteCount='102' lineCount='20' realLineCount='20' />
  </type>
  <type type='binary' fileCount='0' byteCount='0' />
</project>

You can also pass in multiple paths (if your project has multiple components), and you'll get per-path breakdowns as well as the totals.  The two output formats are created by separate renderers (text and xml) operating on the same data structures, and each can be used independently.

Of particular interest was the ease of counting lines (line 2 gets the lines in the file as List of Strings, line 3 counts them, and line 4 counts the ones that have non-whitespace characters):

// 'file' is a java.io.File
def lines = file.readLines()
this.lineCount = lines.size()
this.realLineCount = lines.sum(0, { it.trim().length() == 0 ? 0 : 1 })

Similarly, reading the configuration for which files to ignore, and which extensions are considered text files:

TEXT_EXTENSIONS.addAll(getClass().getResourceAsStream("/text_extensions.txt").readLines())
FILES_TO_IGNORE.addAll(getClass().getResourceAsStream("/ignore.txt").readLines())

This piece, in particular, I'd intended to leave as configured-in-code, because I didn't want to deal with the external resource headache and then the parsing of the files once I got them.  Ha.  As if Groovy would make me do that.

There's a whole bunch of other stuff in there, particularly the mixing of implicit and explicit getters to unify object APIs.  If you want a directed tour, the core of the app resides these methods: CodeCounter's constructor, PathInfo.processDirectory, and FileInfo's constructor.  The output is handled by TextRenderer (using a PrintWriter) and XmlRenderer (using MarkupBuilder).

Total time to build the app was about an hour, required no special editor (I used TextPad), IDE, or compiler.  The classes can be executed in any Groovy environment (including CF Groovy), or run them through groovyc and use them in any Java environment.  Oh how nice it is.

Meme(me)

Per Mark:

The Rules:

  • Take a picture of yourself right now
  • Don't fix your hair, don't change your clothes, just take the picture
  • Post that picture with no editing
  • Post these instructions with the picture

I do this every day, meme or not.  See the stream, or get it as RSS.

CF Groovy Preso

Last night I talked at the PDX RIA group about using Groovy to extend CFML applications and better leverage some of the benefits that Java has to offer (in addition to the benefits Groovy itself brings).  Unfortunately we had some technical difficulties, but the presentation was recorded on Connect, and can be accessed at http://experts.na3.acrobat.com/p71343823/.  I apologize for the delay in posting, I usually try to get things up immediately afterward, but just didn't happen this time.

In addition to the Connect recording, I've posted my slides (in PDF format).  The sample app I demoed is just the CF Groovy demo app available at https://ssl.barneyb.com/svn/barneyb/cfgroovy/trunk/demo.  Within the demo app is the framework itself, including Hibernate (whose JARs are the source of the large download).  The demo app is also available online should you wish to run it without having to download anything.  Of course, there's not much to see without the code.