The problem: Bermuda needs to be relocatable. This means that a lot of the auto-generated code needs to not require that Bermuda be installed. My Bermuda::Writer package needs to supply a base serialization class, but I need to test this class. To do that, I had Bermuda::Serialize::Base both in its own file, but also embedded in my Bermuda writer as a string.
That duplication is bad, but someone needs control over their serialization base class name and it shouldn't force them to use 'Bermuda', so here's what I did:
sub _base_class_code { my ( $class, $package ) = @_; my $writer = $INC{'Bermuda/Writer.pm'}; my @path = splitdir($writer); pop @path; # discard Writer.pm my $base = catfile( @path, 'Serialize', 'Base.pm' ); open my $fh, '<', $base or die "Cannot open ($base) for reading: $!"; my $base_class_code = do { local $/; <$fh> }; close $fh or die "Cannot close ($base): $!"; $base_class_code =~ s/Bermuda::Serialize::Base/$package/gs; return $base_class_code; }
This makes me feel really icky, but I've removed some duplication and now this code can be relocated.
Are you sure you want splitdir
/catfile
and not splitpath
/catpath
?
Re:Unix blinders?
Ovid on 2008-02-14T13:04:11
To be honest, I use those so seldom, I never really paid attention to the difference.
Making both of those changes results in a lot of uninit value warnings from catpath, even though the tests pass
:/ sub catpath {
my ($self,$volume,$directory,$file) = @_;
# this gives me uninit warnings
if ( $directory ne '' &&
$file ne '' &&
substr( $directory, -1 ) ne '/' &&
substr( $file, 0, 1 ) ne '/'
) {
$directory.= "/$file" ;
}
else {
# as does this...
$directory.= $file ;
}
return $directory;
}Re:Unix blinders?
Aristotle on 2008-02-15T01:46:35
Huh, what are you passing to it?
Re:Unix blinders?
Ovid on 2008-02-16T09:20:09
Er, um, really wish I had read the documentation rather than doing a quick %s/one/fortheother/. *blush*.
Re:Unix blinders?
Aristotle on 2008-02-16T15:22:43
Heh. That’s what I meant by “Unix blinders” – and not pejoratively, as I used to wear them for the longest time myself. Unix is so nicely simple: no volumes, no distinct namespaces for file and directory names, just one uniform hierarchy… after a while one forgets the bizarre things that exist elsewhere.
Re:Unix blinders?
jplindstrom on 2008-02-14T16:01:59
I think it's actually spelled:use File::Basename;
dirname($file);
How about a method that returns the serialiser code as a string (with selectable package name), which can then be eval
ed by the test file for testing?
Re:Avoid the extra file?
Ovid on 2008-02-14T13:18:45
I don't want to embed a string of code. I'm doing a huge amount of that already to do code generation and it makes a lot of my code hard to follow, despite aggressive refactoring. If I keep these packages in real packages, I get the benefits of my t/00-load.t test:
#!perl
use Test::More;
use File::Spec::Functions 'splitdir';
use File::Find;
sub path_to_module {
my $path = shift;
$path =~ s/\.pm\z//;
my @components = splitdir($path);
shift @components; # discard lib/
return join '::' => @components;
}
BEGIN {
my @packages;
find({
no_chdir => 1,
wanted => sub {
if (/\.pm\z/ ) {
push @packages => path_to_module $File::Find::name;
}
}
}, 'lib');
plan tests => scalar @packages;
foreach my $package (@packages) {
use_ok $package or BAIL_OUT("Cannot use ($package)");
}
}
diag("Testing Bermuda $Bermuda::VERSION, Perl $], $^X");I always forget to add the modules to my load tests, to this ensures I can never forget them. Plus, I effectively get a compile-time check against the relocatable code instead of the runtime check against embedded strings of code.
It's a trade-off, but if I can get the relocatable code bit working (and it's working on Solaris), then this is a nice win
... I think. Re:Avoid the extra file?
Aristotle on 2008-02-16T01:11:48
Put it in a SelfLoader module and add a method to pull out the source via the package’s
DATA
handle? That way you avoid all the path diddling gymnastics and it automatically works as a standalone module.