Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl Moose Method Modifiers: Call 'around' before 'before' and 'after'

I'm using Moose and I need to wrap method calls in my project. It's important that my wrapping code be the most outer modifier. What I've done so far is put my method modifiers in a Moose Role and then applied that role at the end of my class like this:

use Moose::Util;
Moose::Util::apply_all_roles(__PACKAGE__->meta, ('App:Roles::CustomRole'));
__PACKAGE__->meta->make_immutable;

This allows me to be reasonably sure that my my role's modifiers are defined last, therefore giving me the correct behavior for "before" and "after." (The "before" and "after" in the role are called very first and very last.)

I originally thought this would be sufficient, but I now really need to wrap methods in a similar way with "around." Class::MOP, which Moose is built on, applies "around" modifiers very first, therefore they're called after "before" and before "after."

For more detail, here is the current calling order of my modifiers:

CUSTOM ROLE before
    before 2
       before 1
           CUSTOM ROLE around
               around
                   method
               around
           CUSTOM ROLE around
       after 1
    after 2
 CUSTOM ROLE AFTER

I really need something like this:

CUSTOM ROLE before
    CUSTOM ROLE around
        before 2
           before 1
               around
                   method
               around
           after 1
        after 2
    CUSTOM ROLE around
 CUSTOM ROLE AFTER

Any ideas on how to get my "around" modifier to be applied / called where I want it to? I know I could do some symbol table hacking (like Class::MOP is already doing) but I'd really rather not.

like image 354
Matt Wood Avatar asked Nov 09 '09 17:11

Matt Wood


2 Answers

Simplest solution is to have CUSTOM ROLE define a method that calls the main method and then wrap that.

role MyRole { 
    required 'wrapped_method';
    method custom_role_base_wrapper { $self->wrapped_method(@_) }

    around custom_role_base_wrapper { ... }
    before custom_role_base_wrapper { ... }
}

The problem you're having is that you're trying to have the CUSTOM ROLE around wrap something other than a method. Which is not what it is designed to do. Other than writing similar symbol table hackery like you've suggested (probably you could argue one of the Moose people into exposing an API in Class::MOP to help get there), the only other solution I can think of is the one above.

If you don't want the extra call stack frame that custom_role_base_wrapper will add, you should look at Yuval's Sub::Call::Tail or using goto to manipulate the call stack.

like image 148
perigrin Avatar answered Nov 15 '22 04:11

perigrin


I'm fairly new to Moose, but why do you do this:

use Moose::Util;
Moose::Util::apply_all_roles(__PACKAGE__->meta, ('App:Roles::CustomRole'));

rather than simply this?

with 'App:Roles::CustomRole';

Regarding your question, it's a bit of a hack, but could you split your around method into before and after methods and apply the role at the end of your class definition (so it is applied in your desired order)? You could use private attributes to save state between the two methods if absolutely necessary.

like image 38
Ether Avatar answered Nov 15 '22 04:11

Ether