Say that I have two roles: Simple::Tax and Real::Tax. In testing situations, I want to use Simple::Tax, and in production, I want to use Real::Tax. What is the best way to do this? My first thought was to use different versions of the new
method to create objects with different roles:
#!/usr/bin/perl
use warnings;
{
package Simple::Tax;
use Moose::Role;
requires 'price';
sub calculate_tax {
my $self = shift;
return int($self->price * 0.05);
}
}
{
package A;
use Moose;
use Moose::Util qw( apply_all_roles );
has price => ( is => "rw", isa => 'Int' ); #price in pennies
sub new_with_simple_tax {
my $class = shift;
my $obj = $class->new(@_);
apply_all_roles( $obj, "Simple::Tax" );
}
}
my $o = A->new_with_simple_tax(price => 100);
print $o->calculate_tax, " cents\n";
My second thought was to use an if statement in the body of package to use different with
statements:
#!/usr/bin/perl
use warnings;
{
package Complex::Tax;
use Moose::Role;
requires 'price';
sub calculate_tax {
my $self = shift;
#pretend this is more complex
return int($self->price * 0.15);
}
}
{
package Simple::Tax;
use Moose::Role;
requires 'price';
sub calculate_tax {
my $self = shift;
return int($self->price * 0.05);
}
}
{
package A;
use Moose;
has price => ( is => "rw", isa => 'Int' ); #price in pennies
if ($ENV{TEST_A}) {
with "Simple::Tax";
} else {
with "Complex::Tax";
}
}
my $o = A->new(price => 100);
print $o->calculate_tax, " cents\n";
Is one of these better than the other, is there something horrible about either of them, and is there a better way I haven't thought of yet.
My first suggestion would be something like
MooseX::Traits
and then specify the different roles at object creation:But this opens the door to an
A
being created without either Role being applied. So thinking about it further, I think you've got an X/Y problem. IfSimple::Tax
is only ever used to mock upComplex::Tax
in a test environment you can do several things to override the Complex::Tax implementation.For example you could just define Simple::Tax like so:
Then always have A compose
Complex::Tax
and apply Simple::Tax to it only during tests (usingapply_all_roles
).If however you need Simple::Tax and Complex::Tax both in production (and not simply for testing) your best bet is refactor from a composition relationship (does) to a delegation relationship (has).
Then if you want to override it you simply pass in a new
tax_calculator
:Because
handles
will delegate all of the methods from the role as new proxies this is practically identical to having composed the role yourself.