February 2010 Archives

Custom Hacks and Comfort Levels

As I'm working on my Veure project, I've used DBIx::Migration to handle database migrations. If you're familiar with this module, this might seem like a surprising choice since it is, well, awful. However, I know exactly what the limitations are and since I'm so familiar with it, I now have a custom Veure::DBIx::Migration. There are several things I find useful about this.

  • It's not tied into a specific ORM.
  • It's very simple.
  • It's easy to fix and make PostgreSQL-specific hacks I need.

Actually, that's almost a lie.

The Last Lines of a Particularly Painful Class I Wrote

no warnings 'void';
"if you stare into the void, the void stares back at you";

One Thing I Love About Git

If you've been using Subversion or (shudder) CVS, you only have the briefest glimmerings of what source control is about. I don't really like having to dig too deeply into tools that I use. I want them to be easy, but I dig when something's hard.

On thing which frustrated me about Subversion is that fact that, as mentioned, I don't think about some things. More than once I've quickly hacked up a change to a module, switched other modules to use that module and do a quick svn rm and svn add.

Oops. I just lost my version history. Damn it.

Not with git. It figures it out for me. My Veure project uses DBIx::Class::Schema::Loader because I don't want to think about building my schema classes. The 0.5003 version is fantastic. It does a better job of naming relationships and the DBIx::Class::Schema::Loader::DBI::Pg support is fantastic.

But what happens if you rename a table? I've done this more than once on my new project, continually working to ensure that I keep things clean and consistent. Unfortunately, that means that a renaming the foo_bar table to foo means that a different schema class is created, separate from the old one. Naturally, I don't think about this and I do a quick git rm and git add.

And git sees what I did and it realizes that the file has been renamed and I don't lose my history.

From what I read (I could be wrong), git actually uses heuristics to determine whether or not a file has been renamed, but so far it's worked flawlessly for me. I am still using Subversion at work (I've not felt comfortable checking out the git-svn work, but that's me being silly), and it's just painful. I doubt I'll ever use Subversion for a personal project again.

Update: came into work this morning and was asked to merge one branch into another. It was conflict hell and I was having trouble figuring out all of the changes. A colleague, using git-svn, merged the two branches cleanly, no problem. I think it's time for me to learn git-svn now.

The All New Yahoo! eFail Client

So let's say you're looking around for a Web-based email client. Even though the Application Service Provider industry has struggled, many people want Web-based email. So when you evaluate an email client, what are you looking for?

You probably want to be able to send email. You probably want to be able to receive email. In fact, I'll go out on a limb and suggest you might want to read the email you receive. So when you are looking at email clients, what's the one feature you really, really want?

You might think, I dunno, that email is an important feature of email clients. Yahoo! thinks you want to send e-cards.

Exceptions as Flow Control

So, you're deep inside of a series of functions and you find that you really, really want what is effectively a goto: that is to say, you want to jump right out of all of those functions to the top level. Many people think "aha!, I'll just throw an exception."

Years ago, many Java programmers used to do this (of course, Exceptions are first class in Java). To paraphrase Mencken, it was simple, easy, and wrong. As understanding grew of how exceptions should be used, it was learned, surprisingly to some, that exceptions should be used for exceptional conditions. One rule of thumb was that exceptions (like aspects later) should be reserved for code where, in theory, if nothing went wrong, they could be completely removed without affecting the program's correctness. So you might throw an exception if you fail to open a file, but if you're iterating over the lines in that file (and if it's OK for that file to have no lines), then you don't throw an exception just because you've reached the end of the file. EOF is expected. Failure to connect is not. Unfortunately, failure to understand failure caused some horrible knock-on effects.

Why Doesn't the BBC Upgrade Their Software Often Enough?

There are three things that I really love about Perl:

  • Moose
  • Catalyst
  • DBIx-Class

That's where the problem starts. It's not really a BBC problem, though. It's a problem for all "enterprise" customers.

Spot the Test::Class Bug!

If you run prove on this code, it will fail. Why? Click "Continue Reading" for the answer.

package Check;

use Test::Class::Most parent => 'Test::Class';

use Readonly;
Readonly my $CONSTANT => 4;
INIT { Test::Class->runtests }

sub checkit : Tests(1) {
    is $CONSTANT, 4, 'Constant should be set';
}

1;

Easier Test Debugging

The vast majority of the time when I run the debugger on a test, I get frustrated because I have to wait for the test to load, then type '{l' (always list next window of lines before the debugger prompt) and then 'c' (continue to breakpoint -- because I've almost always set one at this point).

So I dropped this into my $HOME/.perldb file:

sub afterinit { 
    push @DB::typeahead, "{l", "c"
        if $ENV{DEBUGGING_TESTS};
}

And in my vim config (where you put it will vary depending on your setup), I have this:

    noremap <buffer> <leader>D :!DEBUGGING_TESTS=1 perl -d -Ilib %<cr>

With that, I hit ',D' (comma D) and my debugger starts up and runs to my breakpoint for me, rather than me getting annoyed at typing those instructions at the start of every debugger sections. I really need to read perldoc perldebug more carefully. There are some lovely tidbits of information there.

Long term, I'm thinking about combining some tricks with Devel::CoverX::Covered such that I can use that module with a custom $HOME/.perldb file to simply insert a breakpoint in my code (not the test) and automatically have the appropriate test run to that point without me doing anything more than simply adding a breakpoint. No more hunting for tests when narrowing down a bug.

Sanity Checking My PostgreSQL Tests

I had written about Testing With PostgreSQL and today discovered some failing tests. It turns out this is because I had migrated a database down two levels, altered a table, and migrated it back up (I can do this because no one is relying on a production copy). Unfortunately, when I migrated it back up, the new tables didn’t have their correct triggers assigned because the _test_changed_table check was in place. As a result, my test module assumed the testing system was in place.

Unhappy with @INC

Ever since I upgraded to Snow Leopard, my Perl has been unstable. I've fixed most of it, but I sometimes have strange behavior. Today I discovered why (and why didn't I notice this sooner?)

@INC:
/System/Library/Perl/5.10.1/darwin-2level
/System/Library/Perl/5.10.1
/Library/Perl/5.10.1/darwin-2level
/Library/Perl/5.10.1
/Library/Perl/5.10.0
/Library/Perl/5.8.9
/Library/Perl
/Network/Library/Perl/5.10.1/darwin-2level
/Network/Library/Perl/5.10.1
/Network/Library/Perl

Great. A bunch older Perl's have somehow made their way into my @INC. Time to recompile.

Tracking Down Bug Reports

I often find that test reports I get from smokers are not terribly useful for failures. Sometimes it's obvious. Other times it's not. One thing which helps a bit is this bit in your t/load.t test:

diag(
    "Testing My::Module $My::Module::VERSION, Perl $], $^X"
);

In the CPAN testers reports, you can click on a test report and instantly see the module version, Perl version and path to the current Perl (arguably not as useful).

I like to go a step further and include information about all modules I claim to require. For my t/00-load.t for Test::Most, I have the following:

use Test::More tests => 9;

BEGIN {
    local $^W;
    use_ok('Test::Most')
      or BAIL_OUT("Cannot load Test::Most");
    use_ok('Test::Most::Exception')
      or BAIL_OUT("Cannot load Test::Most::Exception");

    diag("Testing Test::Most $Test::Most::VERSION, Perl $], $^X");
    my @dependencies = qw(
      Exception::Class
      Test::Deep
      Test::Differences
      Test::Exception
      Test::Harness
      Test::More
      Test::Warn
    );
    foreach my $module (@dependencies) {
        use_ok $module or BAIL_OUT("Cannot load $module");
        my $version = $module->VERSION;
        diag("    $module version is $version");
    }
}

Now on my test reports, I see output like this:

# Testing Test::Most 0.21, Perl 5.008009, /home/chris/pit/rel/perl-5.8.9/bin/perl
#     Exception::Class version is 1.29
#     Test::Deep version is 0.106
#     Test::Differences version is 0.5
#     Test::Exception version is 0.29
#     Test::Harness version is 3.21
#     Test::Simple version is 0.94
#     Test::Warn version is 0.21

If I want to replicate a bug, this is a huge benefit

Miscellaneous Thoughts

Though 1: Test::Class::Most fails on Perl 5.010001 -- but only on Linux. Not on FreeBSD, OS X or Solaris. I've already emailed the testers and hopefully someone can track down what's going on. I don't want to say "bug in Perl", but this is not platform-specific code, so it looks suspicious.

Thought 2: Can anyone please explain to me why anyone would want to buy a whyPad?

Thought 3: What does the following print and why? Results are the same for 5.10.1 and 5.8.9, in case you're wondering. Try and figure out the output before you run the code. I now know why it does this, but I was confused by the behavior.

#!/usr/bin/env perl -l

use strict;
use warnings;

my $foo = 3;
sub Foo::showit { print $foo }
sub Foo::incit { $foo++ }
my $foo = 10;
sub Bar::showit { print $foo }
sub Bar::incit { $foo++ }

Foo::showit;
Bar::showit;
Foo::incit;
Foo::showit;
Bar::incit;
Bar::showit;

Changing Test::Most (and a note about my OSCON proposals)

After uploading Test::Class::Most, I kept thinking and thinking about the fact that you automatically get strict and warnings with it. I started to think about my annoyance with test suites in general and am now thinking about doing this with Test::Most. I proposed this to Perl-QA and received three positive responses (two offlist) and no negative. It won't import "features" of 5.10, but instead of this:

use strict;
use warnings;
use Test::Most tests => 34;

You can just write:

use Test::Most tests => 34;

... and it's the same thing. I can't recall the last time I've seen a test suite using modern tools and not using strict and warnings, so I think this is a win. chromatic's Modern::Perl is a tiny bit of code but the underlying idea is very important and it's a pattern I'd like to see more of.

In other news, the OSCON Call for Proposals is now closed. I have two proposals in there:

  • Refactoring Enterprise Class Test Suites -- Too Slow, Too Complex, Too Fragile
  • Scratching the 40 Itch of Inheritance with Smalltalk-style Traits

I feel that both of these are very important topics that aren't covered enough. With luck, I'll be in Portland in July (and will be a happily married man at that time).

About Ovid

user-pic Have Perl; Will Travel. Freelance Perl/Testing/Agile consultant. Photo by http://www.circle23.com/. Warning: that site is not safe for work. The photographer is a good friend of mine, though, and it's appropriate to credit his work.