tobyink / p5-moox-press

Perl 5 distribution MooX-Press; see homepage for downloads and documentation.
https://metacpan.org/release/MooX-Press
4 stars 0 forks source link

Modified `has` not used after modifying it via application of a role via begin #3

Open djerius opened 2 years ago

djerius commented 2 years ago

I'd like to apply a role to a MooX::Press generated class which modifies has. Here's the role:

package VOTable::Order;

use v5.10;

use Moo::Role;
use Moo::_Utils;
use Role::Hooks;

Role::Hooks->after_apply(
    __PACKAGE__,
    sub {
        my $class = $_[1];
        say STDERR "After apply in $class";
        Moo::_Utils::_install_modifier( $class, after => has => sub { say "$class\::has($_[0])" } );
    }
);

1;

And here's the Moo::Xpress class:

package VOTable;

use v5.10;

use strict;
use warnings;

use MooX::Press (
    prefix        => __PACKAGE__,
    'class:Field' => {
        begin => sub {
            my $class = shift;
            Moo::Role->apply_roles_to_package( $class, 'VOTable::Order' );
        },
        has => ['id'],
    },
);

1;

Here's a test class which is equivalent to the above generated one:

package VOTable::Snap;
    use Moo;
    with 'VOTable::Order';
    has id => ( is => 'ro' );
1;

And here's what I get when I load the classes:

$ perl  testlib/lib/perl5/VOTable.pm
After apply in VOTable::Field

$ plx perl  testlib/lib/perl5/VOTable/Snap.pm
After apply in VOTable::Snap
VOTable::Snap::has(id)

I'm guessing it's not finding the modified version of has.

tobyink commented 2 years ago

The way MooX::Press gets a reference to has is:

# I hate this...
$_cached_moo_helper{"$package\::$helpername"} ||= eval sprintf(
   'do { package %s; use Moo%s; my $coderef = \&%s; no Moo%s; $coderef };',
   $package,
   $is_role ? '::Role' : '',
   $helpername,
   $is_role ? '::Role' : '',
);

So yes, it will get a "fresh" copy of has, and not the one you've wrapped.

Not sure what a good solution for this is.

tobyink commented 2 years ago

Perhaps something like:

$_cached_moo_helper{"$package\::$helpername"} ||=
   do { no strict 'refs'; \&{"$package\::$helpername"} } ||
   eval sprintf(
      'do { package %s; use Moo%s; my $coderef = \&%s; no Moo%s; $coderef };',
      $package,
      $is_role ? '::Role' : '',
      $helpername,
      $is_role ? '::Role' : '',
   );
djerius commented 2 years ago

That works for my case, but unfortunately it breaks tests:

t/91factoryroles-zylite.t ..... Dubious, test returned 255 (wstat 65280, 0xff00)
No subtests run 
t/91factoryroles.t ............ Undefined subroutine &MyApp::with called at /tmp/MooX-Press-0.086/lib/MooX/Press.pm line 1842.
BEGIN failed--compilation aborted at t/91factoryroles.t line 21.
t/91factoryroles.t ............ Dubious, test returned 255 (wstat 65280, 0xff00)
No subtests run