Crib Sheet Archives

Template Toolkit’s DEFAULT is not too useful

Quoth the fine manual for Template Toolkit:

The DEFAULT directive is similar to SET but only updates variables that are currently undefined or have no "true" value (in the Perl sense).

Nice. Basically, where SET is like the = operator in Perl, DEFAULT is like the ||= operator. Quite useful! If it were, that is. Because the analogy is only superficially true.

The ordering operators

Perl has two operators, cmp and <=>, which are basically never seen outside of sort blocks.

That doesn’t mean you can’t use them elsewhere, though. Certainly sort and these operators were designed to work seamlessly together but there isn’t anything sort-specific about the operators per se, and in some contexts they can be the most appropriate solution.

An example would be some code that used to go (almost exactly) like this:

my ( $maj, $min, $rel ) = split /[.]/, $version;
my $version_ok = $maj > 3 || (
    $maj == 3 && ($min > 6 || (
        $min == 6 && $rel >= 8
    ))
);

Basically, this is a check that the version number of something with a three-component dotted version is at least 3.6.8. As expressed in the code, for both the major and minor version there are two cases where they might be accepted: if the component is above a given threshold then the whole version is acceptable with no further questions, but if equal to that threshold, the version might still be acceptable depending on the value(s) of the lesser component(s). At the same time, if the value is below that threshold, the version is unacceptable, no further questions necessary. So there are three cases, two of which can succeed, and two of which require no further checks.

This is exactly what the <=> operator offers: if you ask $maj <=> 3 you will get 1 if the major version is above 3 (immediate success), -1 if it is below 3 (immediate failure), and 0 if it is equal to 3 (success to be determined from the lesser components). Because 0 is false, you can chain such comparisons with logical-or, until one of them gives an immediate success/fail – as of course we are used to doing in sort blocks.

And so this is what the code looks like now:

my ( $maj, $min, $rel ) = split /[.]/, $version;
my $version_ok = ( $maj <=> 3 || $min <=> 6 || $rel <=> 8 ) != -1;

The expression in parentheses has each component of the actual version on the left and the respective component of version 3.6.8 on the right, and will stop at the first comparison that gives a definite answer. The final != -1 then checks that the answer was not “the right side is greater” at the final comparison – i.e. “the given version is not below version 3.6.8”.

Making local::lib real easy to use

In bash, at least.

Just paste this (on your command line or into .bashrc or wherever else you want):

perl-lib() { eval "`perl -M'local::lib @ARGV' - "$@" 0<&-`" ; }

Then you can just say things like this:

$ perl-lib ~/locallib/foo
$ perl-lib --deactivate ~/locallib/bar
$ perl-lib --deactivate-all

… instead of having to type stuff like this:

$ eval "`perl -Mlocal::lib - --deactivate ~/locallib/bar`"

… or even, as you would have had to in old versions of local::lib, this awfulness:

$ eval "`perl -Mlocal::lib=--deactivate,$HOME/locallib/bar`"

Note that the shell function will work irrespective of local::lib version.

Update: I originally posted this with a 1<&- redirect in the function, which closes STDOUT. What I actually wanted to do was close STDIN, of course.

Speaker for the dead

I just retired one of my modules. I want to make a note here of the preservational ritual I followed, to invite others to consider doing the same:

Installing Term::ReadLine::Gnu on OS X, the easy way

If you try to install Term::ReadLine::Gnu on Mac OS X, you will ordinarily run into this unpleasantry from the Makefile.PL (which will likely end up in such as ~/.cpanm/build.log):

The libreadline you are using is the libedit library. Use the GNU Readline Library.

Here I will assume that you are using Homebrew and have installed GNU Readline:

brew install readline

Even so, you will get this error. This is because of how Homebrew installs Readline. Since OS X ships libedit as libreadline, Homebrew tries to avoid conflicts with system software by installing Readline as “keg-only” software – that is, it’ll install it within its package-managed filesystem hierarchy beneath /usr/local/Cellar, but it won’t link the libraries into /usr/local/lib, so that they won’t be visible to software that isn’t explicitly linked against it.

There is an easy and obvious way around this:

brew link --force readline
cpanm Term::ReadLine::Gnu
brew unlink readline

This makes Homebrew link the shared libraries into /usr/local/lib – which Homebrew ordinarily refuses to do for packages marked keg-only, like Readline is, hence the scary --force. Of course, all said and done, we unlink the package again straight away, so as to not cause conflicts. By doing this, Makefile.PL run from cpanm picks them up to link. We can confirm this:

otool -L ~/perl5/perlbrew/perls/**/Term/ReadLine/Gnu/Gnu.bundle

Its output should include something like the following line:

/usr/local/opt/readline/lib/libreadline.6.2.dylib (compatibility version 6.0.0, current version 6.2.0)

This /usr/local/opt is another Homebrew-ism:

$ ls -l /usr/local/opt/readline
lrwxr-xr-x  1 user  admin  24 14 Jul 04:53 /usr/local/opt/readline@ -> ../Cellar/readline/6.2.4

Note well that I actually have Readline 6.2.4 installed!

$ brew info readline | head -1
readline: stable 6.2.4

That means doing it this way is not only easier than the hard way found elsewhere on the web (here or there), it even allows me to brew upgrade readline without breaking Term::ReadLine::Gnu.

Making DBI’s type info data usable for its quote method

I’m putting this here mostly as future copy-paste fodder:

my %sql_type = do {
    my $ti = $dbh->type_info_all;
    my $tidx = shift @$ti;
    my ( $n, $t ) = @$tidx{qw( TYPE_NAME SQL_DATATYPE )};
    map {; uc $_->[$n], $_->[$t] } @$ti;
};

Now you can say things like $dbh->quote( $latitude, $sql_type{'DOUBLE'} ). This is useful in situations where you cannot use placeholders, e.g. when generating some kind of fixture.sql file from CSV data.

A little “state” idiom

{
    # ... something that needs to be done twice ...

    if ( not state $first_iteration_done++ ) {
        # ... something that must only happen after the first time ...
        redo;
    }
}

In general, some form of “if state $flag” can be used as a “have I been here?” idiom that avoids the need to mention the flag’s identifier anywhere else. Without state, one must repeat oneself some distance away at least to declare the flag in whichever outer scope has the appropriate lifetime.

A concise forking idiom in pure Perl

One of the first module I took over as a maintainer on CPAN was Proc::Fork.

It is a beautiful interface.

It did get a bit uglier in relatively recent times when I added the run_fork wrapper, an unfortunate necessity in certain cases.

But for small single-file-redistributable programs that can be offered to people who are merely users of a Unix system, who do not have any sort of CPAN setup or installation experience, it always felt like a burden to pull in a dependency for something as… insubstantial as this little bit of syntactic sugar:

run_fork {
    child {
        # ...
    }
    parent {
        my $kid = shift;
        # ...
    }
}

Just the other day, it occurred to me that with given/when, it is easy to construct an idiom for forking that is just as concise, if not self-documenting to the same extent:

Some nifty things you can do with Catalyst on Plack

Catalyst 5.9 is coming up, and the big change is a wholesale switch to PSGI, completely dropping Catalyst’s own engine system (outside of compatibility shims). This is bound to bring in new people wondering what PSGI and Plack are all about and what you can do with them.

I’ve been using Catalyst on top of Plack for a while now (with the aid of Catalyst::Engine::PSGI) and have gotten around to some nifty things with it. So here is my app.psgi for inspiration:

About Aristotle

user-pic Waxing philosophical