Next week, we’ll be releasing v2.1.2 of SuperDuper with various improvements, including Growl support. I think you’re all going to like it.
We’d been investigating various methods of notifying users of successful and failed copies for some time, especially for users who are backing up headless or remote systems, and decided to leverage Growl’s well-tested and mature functionality rather than roll our own. It’s a nice package, and I’m glad it’s out there.
There’s a ton of flexibility built into Growl: you can show status visually with various types of floating panels and pop-ups, mail to any account, and even forward notifications to other machines on your network.
Putting this into SuperDuper! initially seemed pretty easy, but we ran into some unusual problems with our AppleScript-based “schedule driver” that I thought might prove interesting.
Obviously, we couldn’t require Growl—a 3rd party application—to be present on a user’s system. The idea here is to use Growl if present, not mandate it. But, our schedule driver—which can be extended by the user—is compiled “on-the-fly” when a schedule is created or modified.
But, while AppleScript has various techniques for dealing with a missing application or dictionary when running a compiled script, it really, really, really wants it to be present during compilation. So, a statement like
tell application “GrowlHelperApp”
notify with name “Scheduled Copy Succeeded” title “SuperDuper! Copy Succeeded” description “Copy was successful.” application name “SuperDuper!”
end tell
won’t work if GrowlHelperApp isn’t present on the User’s System. Drat.
AppleScript does, however, have something called “raw event syntax”. Basically, you pre-compile your statement, hand-compiling it into Apple Events. Thus:
tell application “GrowlHelperApp”
«event notifygr» given «class name»:"Scheduled Copy Succeeded”, «class titl»:"SuperDuper! Copy Succeeded”, «class desc»:"Copy was successful.”, «class appl»:"SuperDuper!"
end tell
But, this won’t work either: even though there’s nothing in the tell block that needs the dictionary, the tell itself will try to reference GrowlHelperApp, and fail. And if GrowlHelperApp is there, your AppleEvents will be magically transformed into AppleScript when you compile!
So, there’s no choice here—you have to have a tell block, or the events don’t go to the right application. But you can’t “tell” an application and have the compilation succeed if the user doesn’t have Growl installed! Or can you…
Fortunately, a little evil goes a long way in AppleScript. By using a string variable, rather than a string literal, we can prevent the compiler from loading the dictionary at compile time—and since we’re using raw event syntax, there’s no error. Thus:
set growlAppName to “GrowlHelperApp”
tell application growlAppName
«event notifygr» given «class name»:"Scheduled Copy Succeeded”, «class titl»:"SuperDuper! Copy Succeeded”, «class desc»:"Copy was successful.”, «class appl»:"SuperDuper!”
end tell
Bingo! This actually allowed compilation… and worked fine when tested in a simple script. But, in the real one, it didn’t work. Instead, I got the following runtime error:
Finder got an error: application “GrowlHelperApp” doesn’t understand the «event notifygr» message.
Now, of course, it does understand the message, because it worked in the simple case!
It took a while for me to figure it out, but the difference, was that in the real script, the Growl notification was in a nested tell block. And, even though the “nearest tell” is for GrowlHelperApp, for some reason this was seen as Finder’s GrowlHelperApp, rather than mine. (You’d think they’d be the same, but not so much.)
The solution, after much gnashing of teeth, was to explicitly reference the script’s main execution context, with:
set growlAppName to “GrowlHelperApp”
tell my application growlAppName
«event notifygr» given «class name»:"Scheduled Copy Succeeded”, «class titl»:"SuperDuper! Copy Succeeded”, «class desc»:"Copy was successful.”, «class appl»:"SuperDuper!”
end tell
I’m telling you, AppleScript has the unique ability to make me feel like the stupidest developer on the face of the earth. Kudos to you, AppleScript!
Anyway, expect this early this week…