Reworded question - sorry, it is a bit long.
Have a simplyfied package for example
package My;
use Moose;
use namespace::sweep;
sub cmd1 {1}
sub smd2 {2}
__PACKAGE__->meta->make_immutable;
1;
I want allow to others extending the My
with another methods, such
package My::Cmd3;
use Moose;
extends 'My';
sub cmd3 {3}
1;
This allows to use the methods from the "base" My
and My::Cmd3
with the next:
use My::Cmd3;
my $obj = My::Cmd3->new();
say $obj->cmd1(); #from the base My
say $obj->cmd3(); #from the My::Cmd3;
But this isn't what I want. I don't want use My::Cmd3;
, (here will be more extension packages), I want use My;
.
Using roles is NICER, like:
package My;
use Moose;
with 'My::Cmd3';
sub cmd1 {1}
sub cmd2 {2}
__PACKAGE__->meta->make_immutable;
1;
package My::Cmd3;
use Moose::Role;
use namespace::autoclean;
sub cmd3 {3};
no Moose::Role;
1;
This allows me:
use My;
my $obj = My->new();
say $obj->cmd1();
say $obj->cmd3(); #from the role-package
But when someone make an My::Cmd4
will need change the base My
package to add with My::Cmd4
. ;(
I'm looking for a way, how to achieve the next:
use My;
#and load all needed packages on demand with the interface like the next
my $obj = My->new( commands => [qw(Cmd3 Cmd4)] );
#what should load the methods from the "base" My and from the wanted extensions too
say $obj->cmd1(); # from the base My package
say $obj->cmd3(); # from the "extension" package My::Cmd3
say $obj->cmd4(); # from the My::Cmd4
So, the what I have now:
package My;
use Moose;
has 'commands' => (
is => 'rw',
isa => 'ArrayRef[Str]|Undef', #??
default => sub { undef },
);
# WHAT HERE?
# need something here, what loads the packages My::Names... based on the supplied "commands"
# probably the BUILD { ... } ???
sub cmd1 {1}
sub smd2 {2}
__PACKAGE__->meta->make_immutable;
1;
Designing an right object hierarchy is my everlasting problem.. ;(
I'm absolutely sure than this isn't should be an big problem, only need some pointers what I should study; and therefore Would be nice to know some CPAN modules, what using such technique ...
So the questions:
My
to the e.g. My::Base
and load the on-demand as other My::Something
or should they remain in the My
? And why?my $obj = My->new(....);
my @methods = $obj->meta->get_all_methods();
This has only Moose
and I couldn't use something smaller as Moo
, right?
Ps: Sorry again for the extremelly long question.
Here is a solution that fills in your WHAT HERE?
section, with the extensions remaining as roles.
package My;
use Moose;
use Class::Load 'load_class';
has commands => (
is => 'ro',
isa => 'ArrayRef',
default => sub { [ ] },
);
sub BUILD {
my ($self) = @_;
my $namespace = __PACKAGE__;
foreach ( @{ $self->commands } ) {
my $role = "$namespace::$_";
load_class $role; # load the module
$role->meta->apply($self); # apply the role to the object
}
return;
}
...
Notes:
require My::Role
but the module deals with some issues with loading modules at runtime. Here I have used Class::Load, but a number of alternatives exist including Module::Load.cmd1
and cmd2
in this base class unless you have a reason for separating them out and loading them on demand also.BUILD
method which in Moose is invoked automatically after construction.commands
to be undef
so I don't need to check for it - If there are no commands, then it can be left as an empty arrayref.You could also use a module that gives you the infrastructure for applying the roles without you having to write it yourself. Here I have used MooseX::Traits, but again there are a number of alternatives listed here: https://metacpan.org/pod/Task::Moose#Traits-Roles
package My;
use Moose;
with 'MooseX::Traits';
has '+_trait_namespace' => ( default => 'My' );
sub cmd1 {1}
sub cmd2 {2}
__PACKAGE__->meta->make_immutable;
1;
# your roles remain unchanged
Then to use the class:
use My;
my $obj = My->with_traits(qw[ Cmd3 Cmd4 ])->new;
# or my $obj = My->new_with_traits( traits => [qw( Cmd3 Cmd4 )] );
say $obj->cmd1;
say $obj->cmd3;
say $obj->cmd4;
It is still possible to do something like this with Moo if you don't want to use Moose:
use Moo::Role ();
my $class = Moo::Role->create_class_with_roles( 'My2', 'My::Cmd3', 'My::Cmd4' );
my $obj = $class->new;
say $obj->cmd1;
say $obj->cmd3;
say $obj->cmd4;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With