Of course you can `requires` attributes!
During the patch -p2 hackathon, two seasoned Perl programmers told me that
requires attributes". This sounded weird to me, as attributes are exposed as methods and besides, I'm doing that all the time in my current work project.
Let's look at the simplest possible example:
package MyRole; use Moo::Role; requires 'my_attr'; package MyClass; use Moo; with 'MyRole'; has my_attr => ( is => 'ro' );
And then run it:
`Can't apply MyRole to MyClass - missing my_attr at /home/book/perl5/lib/perl5/Role/Tiny.pm line 317.`.
Indeed, that looks like it doesn't work.
Let's consider what the code does for a moment:
requireschecks if a subroutine
my_attrexists in the
has 'my_attr'adds the
my_attrsubroutine (an accessor) in the
Why doesn't this work? First, we should keep in mind that our beloved Perl has a complex organisation of compile-time and runtime phases. Although
Moose) code is declarative, most of it is nonetheless executed during a runtime phase. Second, we can fix this without looking at the code of any of
Role::Tiny. Simply use our knowledge of Perl, and realize what is happening.
with is asking the roles to check (among other things) if the methods they require actually exist in the calling class. But this line of code is executed before the
has, which actually inserts
my_attr in the class namespace. So
my_attr doesn't exist in
MyClass at the time
with is called.
The fix is simple enough. Let's simply swap the
package MyRole; use Moo::Role; requires 'my_attr'; package MyClass; use Moo; has my_attr => ( is => 'ro' ); with 'MyRole';
Which compiles (and runs) just fine.
I think my
with declarations are going to move to the bottom of my files as I design more of my code around roles.
It works in Moo, but there are some bugs with
requiresand attributes in Moose.
A cleaner way (in my opinion, yours may differ), is to use forward declarations. The promise a subroutine will be there, even if it doesn't exist at compile time. That means you can put your with statement wherever you like. I've encountered cases with nested roles where even a trailing with doesn't solve the problem.
I noticed the issue and I trusted in developers desgin, so I supposed the right way was to put attributes in the MyRole package.
If an attribute needs some method in its builder or default sub, it can be added in the requires list. For example you can require the builder sub.
Sorry for replying a few month later, but your code example gives an error:
The only way I've found to avoid this without putting the
withat end is the following: