Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl Moose augment vs around

Tags:

perl

moose

Trying to understand Moose:

use Modern::Perl;

package FOO {
    use Moose;
    sub rep { say "  <report></report>"; }
    sub doc {
        say "<document>";
        inner();
        say "</document>";
    }
}

package BAR {
    use Moose;
    extends 'FOO';

    around 'rep' => sub {
        my $orig = shift;
        my $self = shift;
        say "<document>";
        $self->$orig(@_);
        say "</document>";
    };

    augment 'doc' => sub {
        say "  <report></report>";
    };
}

package main {
    BAR->new->rep;
    say "===";
    BAR->new->doc;
}

Produces...

<document>
  <report></report>
</document>
===
<document>
  <report></report>
</document>

... the same result. When desinging the "model (object hierarchy)" - based on what I should decide when to use around and when augment?

Here are probably other (deeper) things what i currently didn't understand yet.

Can please someone provide an "more deep" explanation, because reading tru the Moose/Manual/MethodModifiers obviously not helped enough...

like image 209
cajwine Avatar asked Mar 21 '23 03:03

cajwine


2 Answers

augment and around do rather different things. augment is designed to make this sort of pattern easier:

package Document {
  use Moose;
  sub make_document {
    my $self = shift;
    return "<doc>" . $self->_document_innards . "</doc>"
  }
  # stub; override in child class
  sub _document_innards {
    my $self = shift;
    return "";
  }
}

package Invoice {
  use Moose;
  extends 'Document';
  sub _document_innards {
    my $self = shift;
    return "Give me money!";
  }
}

With augment it becomes:

package Document {
  use Moose;
  sub make_document {
    my $self = shift;
    return "<doc>" . inner() . "</doc>"
  }
}

package Invoice {
  use Moose;
  extends 'Document';
  augment make_document => sub {
    my $self = shift;
    return "Give me money!";
  };
}

On the other hand, around is used as a replacement for doing $self->SUPER::method(@args) because SUPER can't work in roles (the notion of which package to check superclasses for is bound at compile-time, so $self->SUPER::method(@args) would check superclasses of the role (i.e. none) instead of superclasses of the class that consumed the role. If you're not using roles, then SUPER can still be used in Moose classes just fine. TLDR: SUPER is broken by roles, so Moose gives you around as an alternative.

Another thing to compare is override which is a bit like around, but gives you this super() function which is perhaps slightly cleaner than $self->$orig(@_). It also has an "there can be only one" feature. If two roles try to provide an around modifier for the same method, that's fine: they both get to wrap the method (though the order in which they are applied is undefined). If two roles try to provide an override modifier, that's an error.

The implementation of augment is somewhat fragile in my experience, so that in my book is a reason to avoid it. Don't try to replace it with around, because they do rather different things. Instead, replace it with the pattern used in my first example above.

like image 57
tobyink Avatar answered Mar 29 '23 00:03

tobyink


Using around should always be your first instinct. As (Moose creator) Stevan Little says about augment:

Thankfully, only a small percentage of people actually grok this feature and of those people only a handful of them are crazy enough to try and use it.

like image 21
Dave Cross Avatar answered Mar 29 '23 00:03

Dave Cross