Mason methods scope - override

2019-06-26 08:23发布

问题:

Using Mason2. Have 3 components.

/Base.mc
/tmp/Base.mc
/tmp/index.mc

The /tmp/index.mc with a content:

hello from <% $m->request_path %></br>
<% $.Some %>

the $.Some is an method defined in the /Base.mc:

<%augment wrap><% inner() %></%augment>

<%method Some>
The default "Some" method defined in the <% __PACKAGE__ %>
</%method>

the /tmp/Base.mc contains only

<%augment wrap><% inner() %></%augment>

Requesting /tmp/index prints:

hello from /tmp/index
The default "Some" method defined in the MC0::Base_mc

Now added the Some method into /tmp/Base.mc

<%method Some>
Redefined "Some" method in <% __PACKAGE__ %>
</%method>

Requesting /tmp/index again, it prints:

hello from /tmp/index
Redefined "Some" method in MC0::tmp_Base_mc

It honored the redefined Some method in the wrapped /tmp/Base.mc

The question is:

If Mason allows redefine methods as above, what is the purpose of the <%override method>? Does the <%override Some> something differently? (when i tested, it prints the same).

EDIT Maybe the question can be reduced to the following perl code.

use 5.014;
use warnings;

package My;
use Moose;
sub some { say "some from " . __PACKAGE__ }

package My2;
use Moose;
extends 'My';
sub some { say "another some from " . __PACKAGE__ }
#the above line is an correct way to refefine a sub "some"?
#so don;t need to use the
#override 'some' => sub { say "another some from " . __PACKAGE__ };

package main;
use My2;
my $m = My2->new();
$m->some();

in both cases (e.g. "plain" redefine and redefine with "override") prints:

another some from My2

So, the only difference is the possibility of calling the super() in the some with override? and sorry if I missed some basic knowlegde... ;(

回答1:

override in Mason implements the method modifier override in Moose; Moose's override is syntactic sugar for the standard OO method for overriding a parent method, but with some restrictions if the method takes arguments. From the Moose docs for override:

override 'display_name' => sub {
    my $self = shift;
    return super() . q{, } . $self->title(); };

The call to super() is almost the same as calling $self->SUPER::display_name. The difference is that the arguments passed to the superclass's method will always be the same as the ones passed to the method modifier, and cannot be changed. All arguments passed to super() are ignored, as are any changes made to @_ before super() is called.

To give an example from your Moose classes above, let's give some some arguments:

package My;
use Moose;
sub some {
    my $self = shift;
    say "   " . __PACKAGE__ . " method 'some' args: " . join " ", @_;
}

Output after creating a My object and calling $obj->some('pip', 'pop'):

My method 'some' args: pip pop

Now let's look at My2. Define some as a normal package method:

package My2;
use Moose;
extends 'My';
sub some {
    my $self = shift;
    say "   # running 'some'";
    say "   " . __PACKAGE__ . " method 'some' args: " . join " ", @_;
    @_ = reverse @_;
    say "   # running \$self->SUPER::some with no args";
    $self->SUPER::some();
    say "   # running \$self->SUPER::some with reversed args";
    $self->SUPER::some( @_ );
    say "   # running super() with no args";
    super();
    say "   # running super() with reversed args";
    super( @_ );
};

Create a My2 object, and call $obj->some('pip','pop'). Output:

# running 'some'
My2 method 'some' args: pip pop
# running $self->SUPER::some with no args
My method 'some' args: 
# running $self->SUPER::some with reversed args
My method 'some' args: pop pip
# running super() with no args
# running super() with reversed args
Arguments passed to super() are ignored at test.pl line 29.

Things to note:

  • super() does nothing in a redefined method;
  • super() cannot take arguments;
  • $self->SUPER::some does not automatically get passed any arguments;
  • arguments to $self->SUPER::some can be altered.

Now redefine the some method using override:

override 'some' => sub {
    my $self = shift;
    say "   # running 'some'";
    say "   " . __PACKAGE__ . " method 'some' args: " . join " ", @_;
    @_ = reverse @_;
    say "   # running \$self->SUPER::some with no args";
    $self->SUPER::some();
    say "   # running \$self->SUPER::some with reversed args";
    $self->SUPER::some( @_ );
    say "   # running super() with no args";
    super();
    say "   # running super() with reversed args";
    super( @_ );
};

Output:

# running 'some'
My2 method 'some' args: pip pop
# running $self->SUPER::some with no args
My method 'some' args: 
# running $self->SUPER::some with reversed args
My method 'some' args: pop pip
# running super() with no args
My method 'some' args: pip pop
# running super() with reversed args
Arguments passed to super() are ignored at test.pl line 29.
My method 'some' args: pip pop

Things to note:

  • the super() method now correctly calls the superclass method some;
  • super() cannot take arguments; it automatically uses the same @_ that you pass to the subclass method;
  • arguments to $self->SUPER::some can be altered.

It is basically up to you how you implement methods in your subclasses, but this should have illustrated the differences between override and standard method redefinition.



标签: perl mason