November 2010 Archives

Blocked on debugger syntax highlighting

In my last post I mentioned my attempt to provide syntax highlighting for the Perl debugger. It works and I think it's useful, but I did that by hacking directly on the debugger. Every version of Perl gets a slightly newer version of perl5db.pl (the debugger) shipped with it and hacking on a particular version doesn't help¹. Thus, I need to write a separate module which encapsulates my hack and (relatively) cleanly alters how the debugger operates.

I could do something stupid like try to override print or something like that, but it's fragile. Instead, I only want to highlight Perl code and I can do that by only highlighting the code in @DB::dbline. That's because if you know the debugger, you know this little tidbit:

The array @{$main::{'_<'.$filename}} (aliased locally to @dbline via glob assignment) contains the text from $filename, with each element corresponding to a single line of $filename.

At this point, you may be wondering "what the hell is $main::{'_<'.$filename}?" Basically, the *::main{"_<$filename"} typeglob contains a lot of special information useful for the debugger. The array, scalar and hash slots are where the debugger has a lot of magic. Regrettably, this magic typeglob comes straight from the Perl internals and because of this, I have a problem.

Syntax highlighting for the Perl debugger

It's a nasty local hack and, sadly, I haven't found a way to generalize it. You can click on the picture for a larger image.

Perl Debugger with Syntax Highlighting

If you find this interesting/useful, let me know and I'll see if I can make this releasable.

Method Extraction in Vim

I'm hacking on code with some methods which are fairly long (inlined code for performance), but sometimes I have to extract some code out into its own method. Padre uses Devel::Refactor for this, but I didn't want to go down that road as it doesn't use PPI. Thus, I hacked my own using PPIx::EditorTools. It's not great, but long-term, I think it's a more robust solution.

Vim's quickfix mode and Perl

Very happy that f00li5h tweeted to me about the Vi::QuickFix module. In vim, my leader is the comma and when I type ',c', I now execute perl -MVi::QuickFix -c % and that creates an error.err file which vi's quickfix mode can read. :cf will take me to the first error in the fix and :cn will take me to the next error. It will even jump to the correct file, as needed. See vim's :help quickfix for more information.

However, I've done more than that. See a bunch of problems in your files? Try running this:

find lib/ -type f -name '*.pm' -exec perl -c {} 2>&1 \; | grep -v 'syntax OK' \
    | tee errors.err

On our code base, that takes around 25 minutes to run. Then I have this in my .vimrc:

" quickfix for Perl error formats
set errorformat+=%m\ at\ %f\ line\ %l\.
set errorformat+=%m\ at\ %f\ line\ %l

Those two formats match the Perl errors I'm getting (note how one ends with a dot). With those, I can now jump to all errors and warnings that compiling our code generates.

Update: Fixed the find invocation.

Perl101: Simplified Abstract Methods

In my previous post (you should read it if you haven't), I eventually arrived at this:

package Employee;

use strict;
use warnings;
use Carp 'croak';
use Scalar::Util 'looks_like_number';

sub new { bless {} => shift }

sub salary {
    my ( $self, $month ) = @_;

    # validate arguments
    unless ( $month->isa('Fiscal::Month') ) {
        croak("... with an appropriate error message ...");
    }

    my $salary = $self->_salary($month);

    # validate returned values
    unless ( looks_like_number($salary) && $salary > 0 ) {
        croak("... with an appropriate error message ...");
    }
    return $salary;
}

sub _salary { croak 'You must override _salary() in a subclass' }

1;

That's going to look intimidating to a new programmer, but I wanted to point out how you can use this technique now without all of that extra code:

package Employee;

use strict;
use warnings;
use Carp 'croak';

sub new { bless {} => shift }

sub salary {
    my ( $self, $month ) = @_;
    return scalar $self->_salary($month);
}

sub _salary { croak 'You must override _salary() in a subclass' }

1;

I should have used this example up front, but failed to do so. Note that there's no extra validation because you really don't need it yet. Your class provides public methods and you tell subclass authors to override the protected ones. Also, note the use of scalar in the public method. This guarantees that subclasses overriding the protected method won't be relying on side-effects of context. As your system grows, you can go back and add validation as needed. You don't have to pile on tons of it up front until you really know what your validation should look like.

Perl101: Encapsulation via Protected Abstract Methods

Imagine you have an employee base class, but you know that the salary calculation will be different per employee type. You might decide to do this:

package Employee;

use strict;
use warnings;
use Carp 'croak';

sub new { bless {} => shift }

sub salary { croak 'You must override salary() in a subclass' }

1;

The idea is that you're providing this class to a bunch of other programmers and they're going to subclass Employee for their specific needs. There are a variety of things you would probably want to do differently for this class but for this post we'll just talk about the risks of making a public abstract method and how to minimize them. But first, some terms need to be defined.

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.