chromatic wrote:
On Monday 25 December 2006 21:44, James Keenan via RT wrote:

A few style comments here.


Utils.pm
   unshift @{$allargsref->{include}}, (
       ".",
       "$FindBin::Bin/../..",
       "$FindBin::Bin/../../src/pmc/"
   );


Why no File::Spec here?

There are certain ways in which maintenance programming is like open heart surgery. "First, do no harm" applies with a vengeance. You have to make sure you don't kill the patient (translation: cause 'make' to fail), then accomplish your stated objective -- and only then can you consider fixing what's not fatally broken.

I agree that all that $FindBin::Bin stuff is ugly, but it appears to have been needed because 'make' changes its working directory a couple of times and calls pmc2c.pl from at least two different directories. It was pure hell trying to use it when constructing tests, since my tests live at a level (t/tools/pmc2cutils) one level deeper than pmc2c.pl. This meant that I would be forced to decipher error messages with *three* sets of '../' in them.

In the test suite I deal with this in two ways. In 00-qualify_.t (a file which is intended to ensure that all the other files can plausibly work), I have this code:

use Test::More tests =>  8;
use FindBin;
use lib (
    "$FindBin::Bin/../..",
    "$FindBin::Bin/../../lib",
    "$FindBin::Bin/../../../lib",
);
use_ok( 'Parrot::Pmc2c::Utils' );

ok(-f "$FindBin::Bin/../../../Makefile", "Makefile located");
ok(-f "$FindBin::Bin/../../../myconfig", "myconfig located");
ok(-f "$FindBin::Bin/../../../lib/Parrot/PMC.pm", "lib/Parrot/PMC.pm located");

And in all the other test files I have this BEGIN block:

BEGIN {
    use FindBin qw($Bin);
    use Cwd qw(cwd realpath);
    realpath($Bin) =~ m{^(.*\/parrot)\/[^/]*\/[^/]*\/[^/]*$};
    $topdir = $1;
    if (defined $topdir) {
        print "\nOK:  Parrot top directory located\n";
    } else {
        die "Unable to locate top-level Parrot directory";
    }
    unshift @INC, qq{$topdir/lib};
}

Once I've applied Cwd::realpath() to $Bin, I get a value for $topdir in which all those ugly '../' segments are cleaned up, which meant my error messages began to approach intelligibility. The price was that in the balance of the test files I had to call $main::topdir.



[snip]

   if (File::Spec->file_name_is_absolute($file) && -e $file) {
       return $file;
   }


There's File::Spec here.



=head3 C<dump_vtable()>

   $self->dump_vtable("$FindBin::Bin/../../vtable.tbl");


... but not here (this is documentation).


sub dump_vtable {
   my $self    = shift;
   my $file    = shift;


Why two shifts here, when @_ goes unused through the rest of the method?


sub print_tree {
   my $self  = shift;
   my $argsref = shift;


Ditto here.


sub find_and_parse_pmc {
   my ($self, $file) = @_;


... but not here.



I'll look into the above.

sub gen_parent_list {
   my $self = shift;
   my ($name, $all) = @_;


This one just confuses me.


Confuses me too. Most of the real action in pmc2c.pl takes place in subroutines called several levels down inside dump_pmc(). The farther down you go (a) the less I understand what's going on; (b) the more difficult is it for me to write tests for what's going on (especially tests that *only* call an internal subroutine rather than implicitly testing it via testing its caller); and hence (c) the greater the likelihood that Devel::Cover is going to report uncovered code. (You can view coverage reports at: http://thenceforward.net/parrot/cover_db/coverage.html).

In the case of gen_parent_list(), I never encountered a problem until 9:45 PM last night when -- with all my tests passing -- I went to 'make' (which had succeeded several times with my new code) and it failed when it tried to build 'null.dump'. Took me three hours to debug.

Some of the confusion you see arises from the fact that in the current pmc2c.pl, all of the above are subroutines inside a script. In my version, pmc2c.pl builds a Parrot::Pmc2c::Utils object and then calls methods on the object as directed by processing of command-line options. So much of my early refactoring consisted of eliminating variables passed to subroutines in favor of extracting that information from the data structure inside the object. Some methods rely solely on data from the object itself, while others (including a post-make diagnostic method like print_tree()) require additional arguments. Other subroutines are still pure subroutines.

Fertile ground for future refactoring, eh?

kid51

Reply via email to