Suppose I have multiple roles, each one defining a set of items:
package A;
use Moose::Role;
sub items () { qw/apple orange/ }
package B;
use Moose::Role;
with 'A';
sub items () { qw/watermelon/ }
package C;
use Moose::Role;
sub items () { qw/banana/ }
Suppose I use them in another class and I want to collect all those items:
package Foo;
use Moose;
with qw(B C);
sub do_something {
my $self = shift;
my @items = ???; # How can I get apple, orange, watermelon, banana here?
....
}
One possible solution is to adopt MooseX::ComposedBehavior but its POD says (at the time of writing, of course) that its API "is not quite stable" and also that "the current implementation is something of a hack, and should be replaced by a more robust one". Thus I'm investigating whether this could be accomplished without relying on such a "hack".
Warning: if you are reading this in the future, please go to check the POD of MooseX::ComposedBehavior (current version: 0.003) because it might have changed in the mean time. Things change quickly. CPAN authors release new versions. What's "not quite stable" at the moment might become more stable in the future. There might even be other modules. Check yourself.
Ideally there should be something like: my @items = map $_->items, @ISA;
However that won't work with Moose. Are there any nicer and more reliable solutions?
Update: I ended up with this three-line solution:
package A;
use Moose::Role;
sub items () { qw/apple orange/ }
package B;
use Moose::Role;
with 'A';
sub items () { qw/watermelon/ }
package C;
use Moose::Role;
sub items () { qw/banana/ }
package Foo;
use Moose;
with qw(B C);
sub items () {}
sub do_something {
my $self = shift;
my @items = map $_->execute, grep $_,
map $_->get_method('items'),
$self->meta->calculate_all_roles_with_inheritance;
...
}
Update: As various people requested me in the #moose IRC channel I removed my previous assertion that MooseX::ComposedBehavior "is not stable" and replaced it with literal text taken from its POD.
Update: I wrote a MooseX::Collect module which allows the following syntax:
package Foo;
use Moose;
use MooseX::Collect;
collect 'items';
with qw(B C);
sub do_something {
my $self = shift;
my @items = $self->items;
...
}
You need to use around
:
package A;
use Moose::Role;
requires 'items';
around items => sub {
my ($orig, $self, @args) = @_;
return ($self->$orig(@args), qw/apple orange/);
};
package B;
use Moose::Role;
requires 'items';
with 'A'; # not required, do it if you want it
around items => sub {
my ($orig, $self, @args) = @_;
return ($self->$orig(@args), qw/watermelon/);
};
package C;
use Moose::Role;
requires 'items';
around items => sub {
my ($orig, $self, @args) = @_;
return ($self->$orig(@args), qw/banana/);
};
package Class;
use Moose;
with qw/B C/;
sub items {}
But in general, using classes to represent data is wrong, that's what instances of classes are for. It's hard to provide further advice since your example is so trival. What are you really trying to do?