Reviews by david landgren



Module-ThirdParty (0.15) *****

From time to time, I come across a module and I'm surprised to discover that it's actually *not* on CPAN. Sometimes I remember how to find it, but a couple of times I've spent more time that I should have in search engines, trying to ferret out the home base.

If this module can be kept up to date (and other people send links to Sebastien so that he can -- given enough eyes, all modules can be found), this module will be invaluable.

Eventually, CPAN/CPANPLUS might be taught to use it if present, to let people install such modules from non-cpan mirror locations. And it would be Good.

david landgren - 2006-04-20T11:41:35 (permalink)

5 out of 5 found this review helpful. Was this review helpful to you?  Yes No

HTML-CalendarMonth (1.18) *****

I had to whip up a calendar function at work. I narrowed the choice of the workhorse calendar module down to a short-list of two modules, HTML::CalendarMonth and HTML::CalendarMonthSimple. The documentation to the latter modules seemed excessively complex for a module that is purported to be a simpler version of the former, so I went with the former, since its documentation was easier to come to grips with.

I am very pleased with this module. I had a bit of trouble getting it to work, since I was using an aging 5.6 ActiveState distribution on Win32. I would have liked to have used HTML::CalendarMonth::Locale to get a non-English language calendar, but AS doesn't/no longer bundles it for their 5.6 distribution. I did manage to download DateTime::Locale which appeared to have all that I needed by way of non-English language widgets, but I was unable to get the two of them to work. It would be nice to have H::CM try to require DT::L dynamically, and pull in what it needs at run-time. As it was, I was able to work around this defficiency by using the 'alias' attribute.

It took me a bit of time to figure out how to use the 'alias' attribute, as the documentation does not explain clearly how to use it. Fortunately, at the end of the documentation is a link to a web page that shows a number of examples about how to use the module. Go read that page, it's crucial to understanding how this all fits together.

The other hassle I had was the HTML::AsSubs doesn't exist on AS 5.6 either. Again, there was a simple work-around available by using HTML::Element to build the content I needed to insert into each day. The main loop to fill out the calendar looks like this (of course it would look better in a monospace font, trying View Source in your browser):

for my $day (1 .. $cal->lastday) {

$cal->item($day)->attr(width => 80);

$cal->item($day)->attr(bgcolor => '#cccccc')

if Day_of_Week($year, $month, $day) == 6;


if ($open) {






name => 'd',

value => $day,

type => 'checkbox',

(exists $db_day->{$day} ? (checked => 1) : ()),




if (exists $db_day->{$day}) {

$cal->item($day)->attr(bgcolor => '#ccffcc');



(where $open is a boolean that indicates if the month is open for edits, or view only, and $db_day is a reference to a hash that indicates that something is active that day). Day_of_Week comes from Date::Calc.

I'm a big fan of chained methods, but I know that not everybody likes them, nevertheless I missed the ability to do stuff like

$cal->cell($cal->maxrow, 0)

->attr(colspan => $cal->last_col+2, align => 'center')


instead of

$cal->cell($cal->maxrow, 0)->attr(colspan => $cal->last_col+2, align => 'center')

$cal->cell($cal->maxrow, 0)->replace_content($foo);

But I can understand that this would be a lot more effort to support (for instance, cell() would have to broken out into a HTML::CalendarMonth::Cell class to pull that off). But it would be a big win, because sometime the cell specifier gets very complex.

All in all, a very useful module that saved me a considerable amount of time. If you need it, you need it bad.

Well done Matt Sisk, I owe you a beer.

david landgren - 2006-02-27T07:10:43 (permalink)

6 out of 6 found this review helpful. Was this review helpful to you?  Yes No

HTML-CalendarMonthSimple (1.25) ***

I was given the task of creating a simple calendar app on our Intranet to allow people to enter information. The interface is quite simple: a checkbox for each working day, and the user checks as few or as many as needed.

My first step was to search for "calendar" "html" on CPAN. I found two modules that appeared worthy of closer investigation: HTML::CalendarMonth and HTML::CalendarMonthSimple.

Given that my needs were, in my opinion, simple, I first looked at HTML::CalendarMonthSimple. The first thing I saw was a list of methods that continued beyond the bottom of the screen window. I thought "Yeoww! And this is the simple module!" and wondered what the other module must look like.

So I had a look at HTML::CalendarMonth, and straight away I saw a snippet that looked easy enough for me to understand. So I decided to use HTML::CalendarMonth instead. And so far I have had no reason to regret my choice.

I believe that HTML::CalendarMonthSimple is the canonical example of a simple module that has become decidely unsimple. If you're looking for an HTML calendar generator, HTML::CalendarMonthSimple is not, and HTML::CalendarMonth is. That is not to say that this module is not worth using, but first impressions count, life is short, and HTML::CalendarMonth is Good Enough For Me.

david landgren - 2006-02-27T00:48:25 (permalink)

4 out of 4 found this review helpful. Was this review helpful to you?  Yes No

Telephony-CountryDialingCodes (1.02) ****

I was hacking the PABX today, to generate real-time statistics on
international calls being made. Sadly, someone is abusing the
system, and now we have to try and catch them. After a bit of coding,
I was able to pull out international calls that looked like

As I don't have the international dialling codes memorised off by
heart, I figured there would be a module on CPAN to do it for me.
Sure enough, a search for "dialing codes" returned this module as
the first hit.

It installed without a hitch, but I had to write a bit of make-work
code to get it to do what I wanted. First off, it will only isolate
dialing prefixes from numbers if the number starts with + (plus).
My numbers started with 00.

So I had to munge the number a bit:


do { my $n = $number; $n =~ s/^00/+/; $n}

Fair enough, now I had the dialling prefix (41) extracted. I then
had to take this prefix and call another method to get the list of
ISO countries back, so the code wound up looking like this:

my @iso = $dialer->country_codes(


do { my $n = $number; $n =~ s/^00/+/; $n}


Now I have my ISO codes, all I have to do is use Net::Domain::TLD
or Geography::Countries and I'm home.

But it would have been nice to avoid jumping through all those hoops
and go straight from number to ISO list. Possibly specifying in the
new() constructor that international numbers start with '00' (instead
of the default '+'). I might send a patch to do this in my copious
spare time *snort*.

41 is CH, or Switzerland, in case you were wondering.

david landgren - 2006-01-11T07:43:43 (permalink)

2 out of 2 found this review helpful. Was this review helpful to you?  Yes No

Algorithm-Combinatorics (0.13)

I believe it was MJD who said that he couldn't think of a good reason, outside of academic curiousity, why someone would need to generate all the permutations of a set. My need wasn't perhaps exactly real world, but I did need to generate permutations and combinations of lists to prepare data for a test suite.

I found out about this module through fairly arduous process, mainly because searching "combination" or "combinations" on has it lost somewhere beyond the fifth page of results. Perhaps adding "(permutations and combinations)" in the NAME would help raise its visibility. Yucky, but that's life with the search engines.

Nonetheless, if you do manage to find this module, it is a joy to use. The documenation is clear and to the point. You have an immediate idea of what the different functions do. It offers both procedural and object-oriented interfaces. The object-oriented interface feels "Just Right". The heavy lifting is done in XS. The author has chosen efficient non-recursive, non-stack-based algorithms.

My only quibble would be that the distribution lacks an ./eg directory. I like to have example scripts that exercise a distribution in different ways (and ways that perhaps you, as a user, would not think of). But the documentation is good enough that this is not strictly necessary.

david landgren - 2005-12-06T05:45:55 (permalink)

3 out of 3 found this review helpful. Was this review helpful to you?  Yes No

Math-Combinatorics (0.07)

I started writing a module that implements a small subset of this module... until someone pointed out Math::Combinatorics to me.

It shows snippets for some, but not all, of the features. I tried to extend one and received a runtime error.

I tried another snippet (the morse one) and received a runtime error (it recommended calling a different method). When I amended the example to call the other method, I received a runtime error.

There is no eg/ directory of example programs that lets you try out the different aspects of the module.

I had a bad first impression with this module. I thought that the different functions were not documented. It turns out that they are, but at the end of the POD, rather than at the top. When I got to the SEE ALSO section, I stopped scrolling and thus missed the rest of the documentation.

The documentation should be rearranged along a more standard order. At a first glance, it looks as if the only information you get is a hyperlink to a Mathworld article and from there you're on your own. Putting the documentation on how to call the functions at the end is very misleading.

david landgren - 2005-11-18T03:16:57 (permalink)

6 out of 6 found this review helpful. Was this review helpful to you?  Yes No

MIME-tools (5.418) *****

I could not begin to imagine how difficult and time-consuming it would be to deal with MIME-encoded e-mail messages without this set of modules. You can build a CGI that takes a raw SMTP message and webify it, with clickable attachement links, in about 100 lines of code (as I just did).

You do wind up writing a certain amount of make-work code, suggesting that perhaps the distribution's interface is a little too low-level, which in turn probably means you have to a certain amount of time flipping between the documentation of certain modules to get the result you want, but on the other hand, learning how it all fits together gives you a trememndous amount of flexibility.

Specific case in point, I may have been going about it the wrong way, but it took me quite a bit of effort, both in reading the documentation and writing snippets of code to figure out how to decode base64 attachements. As a minimum, I came up with

my $p = $msg->parts(1); # assuming 2nd part is the one we want
my @body;
eval {@body = @{$p->body}};
$@ and die $@;
print decode_base64(join('', @body));

To me, that seems like an awful amount of work. I'd like to be able to say something like

print $msg->parts(1)->decoded;

and it would Just Work.

The Big Five you really should take the time to study are, in order MIME::Entity, MIME::Parser, MIME::Body, MIME::Head and MIME::Decoder. A priceless tool to have in your toolbox.

(edit: typo corrected)

david landgren - 2005-10-18T06:40:04 (permalink)

7 out of 7 found this review helpful. Was this review helpful to you?  Yes No

Text-ASCIITable (0.15) ****

I had to produce a simple report with ASCII characters, you know, like


| foo | bar |


| 734 | agd | <-- imagine this in a nice monospace font

| 13 | pe |

| 82 | fx |


And this module was just what I needed. It was easy enough to use, the documentation was quite straightforwad to understand, although I have a few minor niggles.

It uses CamelCaseMethodNames, which I dislike. It would be nice to have aliased names to uncamel_case_method_names as a forward/backward compatibility shim.

Another hassle is that you can't pass an arrayref of arrayrefs to the addRow() method. This is a pity, because it would admit the following code:

$t->addRow( $db->selectall_arrayref( $sql_command ));

The final hassle that I encountered is that it warns copiously about undefined values. In my particular SQL request some column values are undef, and it is normal. It would be nice to have a property to set to treat_undef_as_no_space or something. I had to write the above snippet as

my $r = $db->selectall_arrayref( $sql_command );

$t->addRow( map { defined($_) ? $_ : '' } @$_ ) for @$r;

But that took me about 3 minutes flat, so it's not much of a problem, more something to be aware of.

I'll write up these reports as bugs, and we'll see how the author responds.

All in all a very useful module, and nice to have in your toolbox.

david landgren - 2005-09-22T03:56:13 (permalink)

6 out of 6 found this review helpful. Was this review helpful to you?  Yes No

HTTP-Size (0.91) ****

I needed to find the size of a web page, with images included. So I searched for HTML::Size on CPAN. A bad choice on my part, but nonetheless I saw what I really needed (this module), about half way down the page.

I installed it, and it works. It works perfectly. The testers page shows a number of failures for the module. It insists on trying to do some tests on pages out on the internet and if you're firewalled or proxied off the net that's not going to work. I suspect that's what the failures represent.

The test suite should test against a locally-bundled set of files, and only if the person installing the module specifically asks to test net resources should it do so. If that were the case I would raise the Ease of Use ranking I gave it.

All in all, a very nice module that does the job it sets out to do very well.

david landgren - 2005-03-15T08:12:21 (permalink)

3 out of 3 found this review helpful. Was this review helpful to you?  Yes No

Compress-Bzip2 (1.03) *


The current maintainer tells me that these issues have been dealt with in the 1.03 release.

This module is next to useless. The only way to compress/decompress is scalar to scalar. This approach is infeasible for large datasets, which is where bzip2 really shines. The only way to get it to work in with "real life" data would be to fudge it with disk-backed tied scalars.

There is no method to read a compressed file "line by line" similar to what one could achieve with an input pipe from bzcat(1).

There is a homemade test supplied that appears to show that the module works correctly, but it does not work from 'make test'.

Several messages to the author have gone without replies.

A minimal useable interface in my opinion would be to target Compress::Zlib.

david landgren - 2005-01-13T06:19:54 (permalink)

1 out of 3 found this review helpful. Was this review helpful to you?  Yes No

Net::Domain::TLD / Net-Domain-TLD (1.5) ****

Locale::Country handles ISO country codes, but when one considers DNS TLDs, it lacks (IIRC) the Big Seven (com/edu/gov/int/mil/org/net) and most certainly does not include the new ICANN additions (aero/biz/info/name...).

I was going to write a module to do just that, but fortunately a quick search on CPAN turned up Net::Domain::TLD (which proves that its name is well chosen).

A minor quibble I have with the module is that the only interface it offers is object-oriented, and a procedural interface would do just fine. For instance, to get my list of TLDs one has to jump through new like so:

my @list = Net::Domain::TLD::new->All;


@list = Net::Domain::TLD::all;

would be adequate. Especially as two objects' All() methods are hardly likely to return different results... we are looking at class invariants here. Still, never mind, it gets the job done.

As long as the author tracks the bowel movements of ICANN regularly, to see what comes out, this module will save me effort. And that's the whole point.

david landgren - 2005-01-04T08:04:39 (permalink)

1 out of 1 found this review helpful. Was this review helpful to you?  Yes No

Mail-Sendmail (0.79) *****

There are other modules for sending SMTP mail (Net::SMTP and the Mimetools spring to mind), but this one is my favourite. It has a very natural interface: a hash. Each key corresponds to a header (and you can add/invent whatever you like) and the "body" corresponds to the body of the message. Call the "sendmail" function with this hash, and off goes your message. Simple as pi.

david landgren - 2003-09-14T07:19:39 (permalink)

Was this review helpful to you?  Yes No

Passwd-Solaris (0.65) *****

It works.

If you need to play around with Solaris' shadow password file, this module does just that. It does its job and does it well.

david landgren - 2003-09-14T05:57:36 (permalink)

Was this review helpful to you?  Yes No