Can I order order method modifiers loaded as part of traits?

159 views Asked by At

This is a follow up to a previous question. if I have multiple plugins/traits with around modifiers, is it possible to ensure a certain execution order (seeing as how I can't be sure which will actually get loaded)? or can I really only control that in code I write and with documentation?

Example: I have 3 Roles each with an around and each can be loaded optionally as a Trait but if more than 1 is loaded they have to be loaded in a certain order to work right together. e.g. loading Trait A B C in that order with work fine but loading it like Trait B A C will result in wonky behavior.

Is there a way I can prevent the user (programmer) from loading them in the wrong order.

3

There are 3 answers

0
perigrin On BEST ANSWER

Short answer is "No". While method modifiers are ordered, Roles are naturally composed un-ordered. This conflict of composition is one of the reasons method modifiers in Roles are fraught with peril and you rarely see it used for anything complex in the wild.

Users, also, are particularly known for being unordered. Moose provides few tools to enforce the ordered composition of Roles (by design). There is certainly nothing in user space that will constrain them.

That said, you can use Ether's suggestion in a different reply, creating a meta-Trait that consists of the traits composed in the proper order. Alternatively you can jump off the deep end and re-write the Role Composition pieces of Moose to guarantee ordered composition. I honestly can't recommend either of these approaches, both seem to be plastering over what to me would be a clear indication of poor design.

Without knowing more about your particular problem I can't suggest a better solution though. I would generically look for something like registering a callback and triggering that somehow rather than relying upon method modifiers.

1
cjm On

To quote Moose::Manual::MethodModifiers (right before WHY USE THEM?).

When there are multiple modifiers of the same type, the before and around modifiers run from the last added to the first, and after modifiers run from first added to last:

 before 2
     before 1
         around 2
             around 1
                 primary
             around 1
         around 2
     after 1
 after 2

The idea is that you are modifying the method as it exists right at that moment.

2
Ether On

Is there a way I can prevent the user (programmer) from loading them in the wrong order.

This is a very subjective answer and depends on the architecture of your system, which you have not described. But one way is that you could apply the roles "yourself" in another role, which is then solely applied to the class:

package OmniRole;
use Moose::Role;

with 'RoleA',' RoleB', 'RoleC';  # these roles contain the 'around' method modifier


package User::Class;
use Moose;

with 'OmniRole';    # wraps method 'foo' several times to do magic.