Slash Boxes
NOTE: use Perl; is on undef hiatus. You can read content, but you can't post it. More info will be forthcoming forthcomingly.

All the Perl that's Practical to Extract and Report

use Perl Log In

Log In

[ Create a new account ]

nkuitse (193)

  (email not shown publicly)

Journal of nkuitse (193)

Saturday November 22, 2003
09:54 AM

HyperCard and shell scripting

So I have this HyperCard stack that serves as a CGI backend...

It runs on the 1GHz G4 server that sits in my office, about 18 inches from my computer.

Both machines run Mac OS X. HyperCard runs in Classic (of course).

Since my HyperCard stack requires hands-on intervention any time something goes wrong or I need to make any changes, I find myself scuttling back and forth from time to time.

Needless to say, cutting and pasting from one to the other is out of the question (well, I suppose I could use pbpaste and scp). Writing custom scripts for ad hoc reports, etc. is a problem because the stack runs as a standalone; hence you can't just pull up a card script and edit it.

So I wrote this little shell script...

if [ -z "$1" ]; then
    args=$(cat <&0)
htscript=$( { echo 'lock error dialogs'; echo "$args"; } \
    | perl -pe 's/\n/\\r/g; s/"/\\"/g' )
echo "tell app \"tls.cgi\" to do script \"$htscript\"" \
    | osascript | perl -pe 's/\r/\n/g'

(tls.cgi is the name of the standalone stack.)

And now I can ssh into the server and do things like this:

% tls return number of cds in bg 1

% tls >recipes.txt
put "" into reports
repeat with i = 1 to number of cds in bg "Recipes"
  put (bg fld "Title" of cd i) & return after reports
end repeat
return reports

% tls 'return somethingOrOther()' | grep ...

HyperCard is dead! Long live HyperCard!

(Next up: remote script editing using BBEdit!?)

Friday August 15, 2003
08:22 AM


Ann Arbor lost power at about 16:12 EDT yesterday, along with everybody else. (Well, not everyone of course...) 50 million people lost power in the U.S. and Canada, apparently.

I popped in at work this morning to see what was what and was amazed to see we've got power again. (I'm currently being treated very nicely by a Lasko "Wind Machine" fan, sitting about 2 feet away.)

The University has its own power-generating capabilities (duh!) and the medical library (my workplace) is currently hooked up to the hospital's power. To conserve energy all non-essential staff are supposed to stay home, and I'm afraid I fall in that category (flash to that Twilight Zone episode where Burgess Meredith is obsolete) so I'll have to leave the fan behind and mosey on home and sweat it out. The air outside is thick and torpid, and when I awoke this morning I felt as though I had lain in a pool of sweat all night. On the bright side, the toilet there is flushing OK. At least for now.

Well, I've put the computers to bed and they should be all right when I come back on Monday. Hopefully we'll get power back soon. More importantly, I hope everybody else out there is OK.

Signing off for now...

Thursday August 14, 2003
12:01 PM

When tests fail: a human comedy in one act.

Curtain rises. The sun sets slowly over a snowy countryside...

CHORUS, chanting offstage:

Running package tests,
Many eyes gaze on failure;
Few understand why.


What happens when a test fails? How do we respond?

Enter chorus in confusion:

  • Panic. The deadline loometh and woe is me!
  • Indifference. Ça m'est égal.
  • Resignation. Oh well, so much for that package.


What happens when a test fails and the test's documentation explains what it was testing, why, and how?

CHORUS, regaining their confidence:

  • Enlightenment. Oh. Well, I don't really need that functionality anyhow.
  • Righteous anger. Silly package author doesn't know how to write %@#$*!? tests! Here, give me that keyboard!
  • Forgiveness. Yea, we have all written sinful code that fails in unexpected ways. Let me just whip up a patch to fix this...


Who writes documentation for their tests?

CHORUS, muttering angrily and casting eyes accusingly from side to side:

Do you? Do you? And you? Do you? Hey you over there! How about you?

Enter THE AUTHOR, blushing:

Ah, the shame! But I'll change, you'll see!

CHORUS, softening their rebuke:

Tests should be documented using POD, just like any other Perl code.



I think we've all learned an important lesson today.

Un ange passe.

CHORUS exits, embarrassed:


Thursday August 07, 2003
10:46 AM

SQL templates

I'm not crazy about positional parameters in DBI, but I don't want to use a heavy-duty module like Tangram or Alzabo* to get away from them.

What I decided was to use simple SQL templates with placeholders like $foo instead of ?, then write a simple module to expand the template given a hash of placeholder names and values.

my $str = 'SELECT id FROM People'
    . 'WHERE age >= $min AND age <= $max';
my $template = SQL::Template->new($str);
my ($sql, @args) = $template->process(
    'min' => 20,
    'max' => 29
# $sql = 'SELECT id FROM People'
# . 'WHERE age >= ? AND age <= ?'
# @args = (20, 29)
$dbh->prepare_cached($sql, @args);

That was simple enough. But then I also wanted multiple substring searching like this:

my $str = 'SELECT id FROM People'
    . 'WHERE { name ILIKE $name }:AND';
my $template = SQL::Template->new($str);
my ($sql, @args) = $template->process(
    'name' => 'tolk% %chris%'
# $sql = 'SELECT id FROM People'
# . 'WHERE name ILIKE ? AND name ILIKE ?'
# @args = ('tolk%', '%chris%')
$dbh->prepare_cached($sql, @args);

That was easier than I had thought it would be.

The hardest part is what to call my module. SQL::Template is the most obvious choice, but to me that sounds like a big, fancy module whereas mine is very simple (and likely to remain that way).

Meanwhile, the module isn't quite ready for release, since I think there are some Postgresisms to root out.

* Not that there's anything with Tangram or Alzabo!

Update 18 Aug: Fixed the quotes in the sample code (should have been single quotes, not double. D'oh!)

Friday July 04, 2003
11:30 AM

Idle dreaming on a hazy, lazy day...


Command-Line Easy Wiki. Console-based wiki and blog.

% clew list --last-first
Today 11:43:49 - Pie in the sky #11 /project/pie rev:37
% clew new /foo/bar
---+ Bar
I like [bar], but [BAZ] is better!
% clew path -a /foo/bar | sed 's/like/luv/' | clew store -v -m 'luv!' bar
Storing new revision of node bar...
Multiple matching nodes:
  [1] /iron/bar #90
  [2] /bar/bar/bar #226
  [3] /foo/bar #870
Pick one or enter 'c' to cancel: [3]
Node /foo/bar revision 2 checked in.
% EDITOR='bbedit -w -c' clew edit baz


Shell to manage FRELI.

% freli add blogroll (n) "list of related weblogs"
Added 1 entry.
% freli find /gry/
angry (adj) #1776
hungry (adj) #15622
% freli find blogroll -q && echo "It's in there."
It's in there.
% freli stem --show-confidence insidiousness
100% insidious -ness


A command-line utility to manage a versioned, files-built-from-many-sources Web site. Think lots of small makefiles and plenty of YAML.

% nkc discover -i '/tt2$/'
Found 3 unknown files:
foo/bar/baz.html.tt2 - add? [y]
% nkc shell
nkc> build
Making web site...
nkc> test --all-links
localtest/foo/bar/index.html Bad link <URL:> 404


Project manager, where a project might be a Perl module, a script, a web site, a book of bad poetry, or any of the things listed above.

Dream, dream, dream...

Thursday July 03, 2003
12:44 PM


Arg. People who squash together their Perl code excessively just to keep the line count down (only n lines long!) should...

Well, they should please stop it.

Thank you.

Friday June 27, 2003
01:53 PM

Brain. Hurts. Must. Stop.

My current project is a database for work. To save myself future headaches, I've been sticking to the code it once principle. Specifically, I've been coding table and class information in YAML, then defining templates (using the Template Toolkit, of course) to produce SQL code, Perl modules, and (ta da!) a Makefile.

With lots of Perl glue.

And now I've got the biggest headache of them all.

Moral: trying to save myself headaches later is pointless if my brain explodes in the process!

Update: Happily, the headache is quickly fading. I added a target in my makefile spec to produce a (longish) SQL command file that populates the database in one transaction, and the resulting file is looking good. It's not running to completion, because my data has a few hiccups in it that my data preparation scripts didn't catch, but that's easy enough to fix.

Moral #2: When trying to save yourself headaches later, don't try to save them all at once. One aspirin at a time, my friend!

Wednesday February 26, 2003
09:43 PM

Amazon sucks

(That's, not The Amazon.)

Well, they've done it again. So much for Jeff Bezos's claim that he "shares my concern for both customers and innovation" (according to Tim O'Reilly).

Here's what I'm doing about it:

From: (me)
Subject: Please help

I want to cancel my account with,
because of its abuse of the US patent system.
(Sorry, the stupidity of the USPTO is no excuse.)

Instead of shopping with, I'll shop with its competitors.

Please tell me how to cancel my account.



It isn't much (heck, it's hardly anything at all) but their latest abuse was the last straw for me.

Monday February 24, 2003
05:48 PM

Stars in my eyes

I'm just back from a trip to East Lansing, Michigan, where my girlfriend was kind enough to accompany me to the star show at Abrams Planetarium. After the show, she bought me some glow-in-the-dark stars. What more could a geek who never really grew up ask for?
Wednesday February 19, 2003
09:18 PM

Runtime rigmarole

I've just finished testing my initial implementation of a simple module you can use to get and set variables, define and call functions, and evaluate arbitrary Perl code. It simply formalizes calls to eval($src) and ${"${pkg}::$varname"} and whatnot: fairly common things that can be tricky to get right. It works by creating a unique, randomly named package and then accessing variables and functions in that package. (Duh.)

Right now it's called JOM::Shell::RunTime::UsingPackages (I'm using it in a simple shell); it inherits from the abstract class JOM::Shell::RunTime at some point I'll rename these for general consumption and post them to CPAN.

The following example is a nearly complete picture of what the module provides:

use JOM::Shell::RunTime::UsingPackages;
my $runtime = JOM::Shell::RunTime::UsingPackages->new();

$runtime->setvar('@fib' => (0, 1, 1));
$runtime->defsub('fib' => <<'--EOS--');
   my ($n) = @_;
   push @fib, $fib[-2] + $fib[-1]
      while $#fib < $n;
   return $fib[-1];
$runtime->callsub('fib', (9)) == 34
   || die "Not 34";
   $fib9 = fib(7) + fib(8);
   die unless $fib9 == fib(9);
$foo = $runtime->getvar('$fib9');

There are some wrinkles to work out (notably, die and warn and their ilk won't produce very helpful error messages) but all in all it's pretty useable as is. Now to get my PAUSE ID and roll it up for release. But what name to use??