yulivee
yulivee

Reputation: 347

Using BUILDARGS to substitute a class in a Role

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?

Upvotes: 2

Views: 362

Answers (2)

ikegami
ikegami

Reputation: 385897

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, ... )

Upvotes: 4

JGNI
JGNI

Reputation: 4013

BUILDARGS is used to manipulate the argument list you pass to the objects constructor before the object is created so will have no impact on what new returns. Instead of around BUILDARGS you might want to try around new, then you can replace the object returned by new with your dry run object.

Upvotes: 2

Related Questions