I use a Perl Moose Role (Import::Git::Role
) as an Abstract Base Class to share common behaviour between a Class an actual implementation of functionality (Import::Git
) and a Class that performs some logging instead (Import::Git::dryrun
).
I would like the dryrun class to be transparent. I want to create a object like this:
my $git = Import::Git->new( dryrun => $dryrun );
The variable dryrun could be 0 or 1. If it is 1, I would like to construct an Import::Git::dryrun
object instead, basically replacing the Import::Git
object with it. Thats intended because they share all methods via the role.
I have tried to exchange the Object during the BUILDARGS Method like this:
around BUILDARGS => sub {
my $orig = shift;
my $class = shift;
my %args = ( @_ == 1 ? %{ $_[ 0 ] } : @_ );
if ( !%args || $args{ 'dryrun' } != 1 ) {
return $class->$orig( @_ );
}
else {
return Import::Git::dryrun->$orig( @_ );
}
};
but this doesn't achieve what I am trying to do, it constructs the old class:
DB<1> x Import::Git->new( dryrun => 1 )
0 Import::Git=HASH(0x2fd9210)
'dryrun' => 1
DB<2> x Import::Git->new()
0 Import::Git=HASH(0x301dbb8)
'dryrun' => 0
DB<3>
I thought I would probably have to call the new Method of the dryrun-method so I made the following exchange:
# change this:
return Import::Git::dryrun->$orig( @_ );
# to this
return Import::Git::dryrun->new( @_ );
But it returns BUILDARGS did not return a HASH reference
.
What am I missing?
Having a constructor build a different class than the one requested is icky. I wouldn't take the approach you did even if it did work. I would use
sub factory {
my ($class, %opts) = @_;
return $opt{dryrun} ? $class.'::dryrun' : $class;
}
Import::Git->factory( dryrun => $dryrun )->new( ... )
or
sub instantiate {
my ($class, %opts) = @_;
return ( delete($opt{dryrun}) ? $class.'::dryrun' : $class )->new(%opts);
}
Import::Git->instantiate( dryrun => $dryrun, ... )